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 "PresenterScrollBar.hxx"
21 #include "PresenterBitmapContainer.hxx"
22 #include "PresenterCanvasHelper.hxx"
23 #include "PresenterGeometryHelper.hxx"
24 #include "PresenterPaintManager.hxx"
25 #include "PresenterTimer.hxx"
26 #include "PresenterUIPainter.hxx"
27 #include <com/sun/star/awt/PosSize.hpp>
28 #include <com/sun/star/awt/XWindowPeer.hpp>
29 #include <com/sun/star/rendering/CompositeOperation.hpp>
30 #include <com/sun/star/rendering/XPolyPolygon2D.hpp>
31 
32 #include <algorithm>
33 #include <memory>
34 #include <math.h>
35 
36 using namespace ::com::sun::star;
37 using namespace ::com::sun::star::uno;
38 
39 const double gnScrollBarGap (10);
40 
41 namespace sdext::presenter {
42 
43 //===== PresenterScrollBar::MousePressRepeater ================================
44 
45 class PresenterScrollBar::MousePressRepeater
46     : public std::enable_shared_from_this<MousePressRepeater>
47 {
48 public:
49     explicit MousePressRepeater (const ::rtl::Reference<PresenterScrollBar>& rpScrollBar);
50     void Dispose();
51     void Start (const PresenterScrollBar::Area& reArea);
52     void Stop();
53     void SetMouseArea (const PresenterScrollBar::Area& reArea);
54 
55 private:
56     void Callback ();
57     void Execute();
58 
59     sal_Int32 mnMousePressRepeaterTaskId;
60     ::rtl::Reference<PresenterScrollBar> mpScrollBar;
61     PresenterScrollBar::Area meMouseArea;
62 };
63 
64 //===== PresenterScrollBar ====================================================
65 
66 std::weak_ptr<PresenterBitmapContainer> PresenterScrollBar::mpSharedBitmaps;
67 
PresenterScrollBar(const Reference<XComponentContext> & rxComponentContext,const Reference<awt::XWindow> & rxParentWindow,const std::shared_ptr<PresenterPaintManager> & rpPaintManager,const::std::function<void (double)> & rThumbMotionListener)68 PresenterScrollBar::PresenterScrollBar (
69     const Reference<XComponentContext>& rxComponentContext,
70     const Reference<awt::XWindow>& rxParentWindow,
71     const std::shared_ptr<PresenterPaintManager>& rpPaintManager,
72     const ::std::function<void (double)>& rThumbMotionListener)
73     : PresenterScrollBarInterfaceBase(m_aMutex),
74       mxComponentContext(rxComponentContext),
75       mxWindow(),
76       mxCanvas(),
77       mxPresenterHelper(),
78       mpPaintManager(rpPaintManager),
79       mnThumbPosition(0),
80       mnTotalSize(0),
81       mnThumbSize(0),
82       mnLineHeight(10),
83       maDragAnchor(-1,-1),
84       maThumbMotionListener(rThumbMotionListener),
85       meButtonDownArea(None),
86       meMouseMoveArea(None),
87       mbIsNotificationActive(false),
88       mpBitmaps(),
89       mpPrevButtonDescriptor(),
90       mpNextButtonDescriptor(),
91       mpPagerStartDescriptor(),
92       mpPagerCenterDescriptor(),
93       mpPagerEndDescriptor(),
94       mpThumbStartDescriptor(),
95       mpThumbCenterDescriptor(),
96       mpThumbEndDescriptor(),
97       mpMousePressRepeater(std::make_shared<MousePressRepeater>(this)),
98       mpBackgroundBitmap(),
99       mpCanvasHelper(new PresenterCanvasHelper())
100 {
101     try
102     {
103         Reference<lang::XMultiComponentFactory> xFactory (rxComponentContext->getServiceManager());
104         if ( ! xFactory.is())
105             throw RuntimeException();
106 
107         mxPresenterHelper.set(
108             xFactory->createInstanceWithContext(
109                 "com.sun.star.comp.Draw.PresenterHelper",
110                 rxComponentContext),
111             UNO_QUERY_THROW);
112 
113         if (mxPresenterHelper.is())
114             mxWindow = mxPresenterHelper->createWindow(rxParentWindow,
115                 false,
116                 false,
117                 false,
118                 false);
119 
120         // Make the background transparent.  The slide show paints its own background.
121         Reference<awt::XWindowPeer> xPeer (mxWindow, UNO_QUERY_THROW);
122         xPeer->setBackground(0xff000000);
123 
124         mxWindow->setVisible(true);
125         mxWindow->addWindowListener(this);
126         mxWindow->addPaintListener(this);
127         mxWindow->addMouseListener(this);
128         mxWindow->addMouseMotionListener(this);
129     }
130     catch (RuntimeException&)
131     {
132     }
133 }
134 
~PresenterScrollBar()135 PresenterScrollBar::~PresenterScrollBar()
136 {
137 }
138 
disposing()139 void SAL_CALL PresenterScrollBar::disposing()
140 {
141     mpMousePressRepeater->Dispose();
142 
143     if (mxWindow.is())
144     {
145         mxWindow->removeWindowListener(this);
146         mxWindow->removePaintListener(this);
147         mxWindow->removeMouseListener(this);
148         mxWindow->removeMouseMotionListener(this);
149 
150         Reference<lang::XComponent> xComponent = mxWindow;
151         mxWindow = nullptr;
152         if (xComponent.is())
153             xComponent->dispose();
154     }
155 
156     mpBitmaps.reset();
157 }
158 
SetVisible(const bool bIsVisible)159 void PresenterScrollBar::SetVisible (const bool bIsVisible)
160 {
161     if (mxWindow.is())
162         mxWindow->setVisible(bIsVisible);
163 }
164 
SetPosSize(const css::geometry::RealRectangle2D & rBox)165 void PresenterScrollBar::SetPosSize (const css::geometry::RealRectangle2D& rBox)
166 {
167     if (mxWindow.is())
168     {
169         mxWindow->setPosSize(
170             sal_Int32(floor(rBox.X1)),
171             sal_Int32(ceil(rBox.Y1)),
172             sal_Int32(ceil(rBox.X2-rBox.X1)),
173             sal_Int32(floor(rBox.Y2-rBox.Y1)),
174             awt::PosSize::POSSIZE);
175         UpdateBorders();
176     }
177 }
178 
SetThumbPosition(double nPosition,const bool bAsynchronousUpdate)179 void PresenterScrollBar::SetThumbPosition (
180     double nPosition,
181     const bool bAsynchronousUpdate)
182 {
183     nPosition = ValidateThumbPosition(nPosition);
184 
185     if (nPosition == mnThumbPosition || mbIsNotificationActive)
186         return;
187 
188     mnThumbPosition = nPosition;
189 
190     UpdateBorders();
191     Repaint(GetRectangle(Total), bAsynchronousUpdate);
192 
193     mbIsNotificationActive = true;
194     try
195     {
196         maThumbMotionListener(mnThumbPosition);
197     }
198     catch (Exception&)
199     {
200     }
201     mbIsNotificationActive = false;
202 }
203 
204 
SetTotalSize(const double nTotalSize)205 void PresenterScrollBar::SetTotalSize (const double nTotalSize)
206 {
207     if (mnTotalSize != nTotalSize)
208     {
209         mnTotalSize = nTotalSize + 1;
210         UpdateBorders();
211         Repaint(GetRectangle(Total), false);
212     }
213 }
214 
SetThumbSize(const double nThumbSize)215 void PresenterScrollBar::SetThumbSize (const double nThumbSize)
216 {
217     OSL_ASSERT(nThumbSize>=0);
218     if (mnThumbSize != nThumbSize)
219     {
220         mnThumbSize = nThumbSize;
221         UpdateBorders();
222         Repaint(GetRectangle(Total), false);
223     }
224 }
225 
226 
SetLineHeight(const double nLineHeight)227 void PresenterScrollBar::SetLineHeight (const double nLineHeight)
228 {
229     mnLineHeight = nLineHeight;
230 }
231 
232 
SetCanvas(const Reference<css::rendering::XCanvas> & rxCanvas)233 void PresenterScrollBar::SetCanvas (const Reference<css::rendering::XCanvas>& rxCanvas)
234 {
235     if (mxCanvas == rxCanvas)
236         return;
237 
238     mxCanvas = rxCanvas;
239     if (!mxCanvas.is())
240         return;
241 
242     if (mpBitmaps == nullptr)
243     {
244         mpBitmaps = mpSharedBitmaps.lock();
245         if (!mpBitmaps)
246         {
247             try
248             {
249                 mpBitmaps = std::make_shared<PresenterBitmapContainer>(
250                     "PresenterScreenSettings/ScrollBar/Bitmaps",
251                     std::shared_ptr<PresenterBitmapContainer>(),
252                     mxComponentContext,
253                     mxCanvas);
254                 mpSharedBitmaps = mpBitmaps;
255             }
256             catch(Exception&)
257             {
258                 OSL_ASSERT(false);
259             }
260         }
261         UpdateBitmaps();
262         UpdateBorders();
263     }
264 
265     Repaint(GetRectangle(Total), false);
266 }
267 
SetBackground(const SharedBitmapDescriptor & rpBackgroundBitmap)268 void PresenterScrollBar::SetBackground (const SharedBitmapDescriptor& rpBackgroundBitmap)
269 {
270     mpBackgroundBitmap = rpBackgroundBitmap;
271 }
272 
CheckValues()273 void PresenterScrollBar::CheckValues()
274 {
275     mnThumbPosition = ValidateThumbPosition(mnThumbPosition);
276 }
277 
ValidateThumbPosition(double nPosition)278 double PresenterScrollBar::ValidateThumbPosition (double nPosition)
279 {
280     if (nPosition + mnThumbSize > mnTotalSize)
281         nPosition = mnTotalSize - mnThumbSize;
282     if (nPosition < 0)
283         nPosition = 0;
284     return nPosition;
285 }
286 
Paint(const awt::Rectangle & rUpdateBox)287 void PresenterScrollBar::Paint (
288     const awt::Rectangle& rUpdateBox)
289 {
290     if ( ! mxCanvas.is() || ! mxWindow.is())
291     {
292         OSL_ASSERT(mxCanvas.is());
293         OSL_ASSERT(mxWindow.is());
294         return;
295     }
296 
297     if (PresenterGeometryHelper::AreRectanglesDisjoint (rUpdateBox, mxWindow->getPosSize()))
298         return;
299 
300     PaintBackground(rUpdateBox);
301     PaintComposite(rUpdateBox, PagerUp,
302         mpPagerStartDescriptor, mpPagerCenterDescriptor, SharedBitmapDescriptor());
303     PaintComposite(rUpdateBox, PagerDown,
304         SharedBitmapDescriptor(), mpPagerCenterDescriptor, mpPagerEndDescriptor);
305     PaintComposite(rUpdateBox, Thumb,
306         mpThumbStartDescriptor, mpThumbCenterDescriptor, mpThumbEndDescriptor);
307     PaintBitmap(rUpdateBox, PrevButton, mpPrevButtonDescriptor);
308     PaintBitmap(rUpdateBox, NextButton, mpNextButtonDescriptor);
309 
310     Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
311     if (xSpriteCanvas.is())
312         xSpriteCanvas->updateScreen(false);
313 }
314 
315 //----- XWindowListener -------------------------------------------------------
316 
windowResized(const css::awt::WindowEvent &)317 void SAL_CALL PresenterScrollBar::windowResized (const css::awt::WindowEvent&) {}
318 
windowMoved(const css::awt::WindowEvent &)319 void SAL_CALL PresenterScrollBar::windowMoved (const css::awt::WindowEvent&) {}
320 
windowShown(const css::lang::EventObject &)321 void SAL_CALL PresenterScrollBar::windowShown (const css::lang::EventObject&) {}
322 
windowHidden(const css::lang::EventObject &)323 void SAL_CALL PresenterScrollBar::windowHidden (const css::lang::EventObject&) {}
324 
325 //----- XPaintListener --------------------------------------------------------
326 
windowPaint(const css::awt::PaintEvent & rEvent)327 void SAL_CALL PresenterScrollBar::windowPaint (const css::awt::PaintEvent& rEvent)
328 {
329     if (mxWindow.is())
330     {
331         awt::Rectangle aRepaintBox (rEvent.UpdateRect);
332         const awt::Rectangle aWindowBox (mxWindow->getPosSize());
333         aRepaintBox.X += aWindowBox.X;
334         aRepaintBox.Y += aWindowBox.Y;
335         Paint(aRepaintBox);
336 
337         Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
338         if (xSpriteCanvas.is())
339             xSpriteCanvas->updateScreen(false);
340     }
341 }
342 
343 //----- XMouseListener --------------------------------------------------------
344 
mousePressed(const css::awt::MouseEvent & rEvent)345 void SAL_CALL PresenterScrollBar::mousePressed (const css::awt::MouseEvent& rEvent)
346 {
347     maDragAnchor.X = rEvent.X;
348     maDragAnchor.Y = rEvent.Y;
349     meButtonDownArea = GetArea(rEvent.X, rEvent.Y);
350 
351     mpMousePressRepeater->Start(meButtonDownArea);
352 }
353 
mouseReleased(const css::awt::MouseEvent &)354 void SAL_CALL PresenterScrollBar::mouseReleased (const css::awt::MouseEvent&)
355 {
356     mpMousePressRepeater->Stop();
357 
358     if (mxPresenterHelper.is())
359         mxPresenterHelper->releaseMouse(mxWindow);
360 }
361 
mouseEntered(const css::awt::MouseEvent &)362 void SAL_CALL PresenterScrollBar::mouseEntered (const css::awt::MouseEvent&) {}
363 
mouseExited(const css::awt::MouseEvent &)364 void SAL_CALL PresenterScrollBar::mouseExited (const css::awt::MouseEvent&)
365 {
366     if (meMouseMoveArea != None)
367     {
368         const Area eOldMouseMoveArea (meMouseMoveArea);
369         meMouseMoveArea = None;
370         Repaint(GetRectangle(eOldMouseMoveArea), true);
371     }
372     meButtonDownArea = None;
373     meMouseMoveArea = None;
374 
375     mpMousePressRepeater->Stop();
376 }
377 
378 //----- XMouseMotionListener --------------------------------------------------
379 
mouseMoved(const css::awt::MouseEvent & rEvent)380 void SAL_CALL PresenterScrollBar::mouseMoved (const css::awt::MouseEvent& rEvent)
381 {
382     const Area eArea (GetArea(rEvent.X, rEvent.Y));
383     if (eArea != meMouseMoveArea)
384     {
385         const Area eOldMouseMoveArea (meMouseMoveArea);
386         meMouseMoveArea = eArea;
387         if (eOldMouseMoveArea != None)
388             Repaint(GetRectangle(eOldMouseMoveArea), meMouseMoveArea==None);
389         if (meMouseMoveArea != None)
390             Repaint(GetRectangle(meMouseMoveArea), true);
391     }
392     mpMousePressRepeater->SetMouseArea(eArea);
393 }
394 
mouseDragged(const css::awt::MouseEvent & rEvent)395 void SAL_CALL PresenterScrollBar::mouseDragged (const css::awt::MouseEvent& rEvent)
396 {
397     if (meButtonDownArea != Thumb)
398         return;
399 
400     mpMousePressRepeater->Stop();
401 
402     if (mxPresenterHelper.is())
403         mxPresenterHelper->captureMouse(mxWindow);
404 
405     const double nDragDistance (GetDragDistance(rEvent.X,rEvent.Y));
406     UpdateDragAnchor(nDragDistance);
407     if (nDragDistance != 0)
408     {
409         SetThumbPosition(mnThumbPosition + nDragDistance, false);
410     }
411 }
412 
413 //----- lang::XEventListener --------------------------------------------------
414 
disposing(const css::lang::EventObject & rEvent)415 void SAL_CALL PresenterScrollBar::disposing (const css::lang::EventObject& rEvent)
416 {
417     if (rEvent.Source == mxWindow)
418         mxWindow = nullptr;
419 }
420 
421 
GetRectangle(const Area eArea) const422 geometry::RealRectangle2D const & PresenterScrollBar::GetRectangle (const Area eArea) const
423 {
424     OSL_ASSERT(eArea>=0 && eArea<AreaCount);
425 
426     return maBox[eArea];
427 }
428 
Repaint(const geometry::RealRectangle2D & rBox,const bool bAsynchronousUpdate)429 void PresenterScrollBar::Repaint (
430     const geometry::RealRectangle2D& rBox,
431     const bool bAsynchronousUpdate)
432 {
433     if (mpPaintManager != nullptr)
434         mpPaintManager->Invalidate(
435             mxWindow,
436             PresenterGeometryHelper::ConvertRectangle(rBox),
437             bAsynchronousUpdate);
438 }
439 
PaintBackground(const css::awt::Rectangle & rUpdateBox)440 void PresenterScrollBar::PaintBackground(
441     const css::awt::Rectangle& rUpdateBox)
442 {
443     if (!mpBackgroundBitmap)
444         return;
445 
446     const awt::Rectangle aWindowBox (mxWindow->getPosSize());
447     mpCanvasHelper->Paint(
448         mpBackgroundBitmap,
449         mxCanvas,
450         rUpdateBox,
451         aWindowBox,
452         awt::Rectangle());
453 }
454 
PaintBitmap(const css::awt::Rectangle & rUpdateBox,const Area eArea,const SharedBitmapDescriptor & rpBitmaps)455 void PresenterScrollBar::PaintBitmap(
456     const css::awt::Rectangle& rUpdateBox,
457     const Area eArea,
458     const SharedBitmapDescriptor& rpBitmaps)
459 {
460     const geometry::RealRectangle2D aLocalBox (GetRectangle(eArea));
461     const awt::Rectangle aWindowBox (mxWindow->getPosSize());
462     geometry::RealRectangle2D aBox (aLocalBox);
463     aBox.X1 += aWindowBox.X;
464     aBox.Y1 += aWindowBox.Y;
465     aBox.X2 += aWindowBox.X;
466     aBox.Y2 += aWindowBox.Y;
467 
468     Reference<rendering::XBitmap> xBitmap (GetBitmap(eArea,rpBitmaps));
469 
470     if (!xBitmap.is())
471         return;
472 
473     Reference<rendering::XPolyPolygon2D> xClipPolygon (
474         PresenterGeometryHelper::CreatePolygon(
475             PresenterGeometryHelper::Intersection(rUpdateBox,
476                 PresenterGeometryHelper::ConvertRectangle(aBox)),
477             mxCanvas->getDevice()));
478 
479     const rendering::ViewState aViewState (
480         geometry::AffineMatrix2D(1,0,0, 0,1,0),
481         xClipPolygon);
482 
483     const geometry::IntegerSize2D aBitmapSize (xBitmap->getSize());
484     rendering::RenderState aRenderState (
485         geometry::AffineMatrix2D(
486             1,0,aBox.X1 + (aBox.X2-aBox.X1 - aBitmapSize.Width)/2,
487             0,1,aBox.Y1 + (aBox.Y2-aBox.Y1 - aBitmapSize.Height)/2),
488         nullptr,
489         Sequence<double>(4),
490         rendering::CompositeOperation::SOURCE);
491 
492     mxCanvas->drawBitmap(
493         xBitmap,
494         aViewState,
495         aRenderState);
496 }
497 
GetArea(const double nX,const double nY) const498 PresenterScrollBar::Area PresenterScrollBar::GetArea (const double nX, const double nY) const
499 {
500     const geometry::RealPoint2D aPoint(nX, nY);
501 
502     if (PresenterGeometryHelper::IsInside(GetRectangle(Pager), aPoint))
503     {
504         if (PresenterGeometryHelper::IsInside(GetRectangle(Thumb), aPoint))
505             return Thumb;
506         else if (PresenterGeometryHelper::IsInside(GetRectangle(PagerUp), aPoint))
507             return PagerUp;
508         else if (PresenterGeometryHelper::IsInside(GetRectangle(PagerDown), aPoint))
509             return PagerDown;
510     }
511     else if (PresenterGeometryHelper::IsInside(GetRectangle(PrevButton), aPoint))
512         return PrevButton;
513     else if (PresenterGeometryHelper::IsInside(GetRectangle(NextButton), aPoint))
514         return NextButton;
515 
516     return None;
517 }
518 
UpdateWidthOrHeight(sal_Int32 & rSize,const SharedBitmapDescriptor & rpDescriptor)519 void PresenterScrollBar::UpdateWidthOrHeight (
520     sal_Int32& rSize,
521     const SharedBitmapDescriptor& rpDescriptor)
522 {
523     if (rpDescriptor)
524     {
525         Reference<rendering::XBitmap> xBitmap (rpDescriptor->GetNormalBitmap());
526         if (xBitmap.is())
527         {
528             const geometry::IntegerSize2D aBitmapSize (xBitmap->getSize());
529             const sal_Int32 nBitmapSize = static_cast<sal_Int32>(GetMinor(aBitmapSize.Width, aBitmapSize.Height));
530             if (nBitmapSize > rSize)
531                 rSize = nBitmapSize;
532         }
533     }
534 }
535 
GetBitmap(const Area eArea,const SharedBitmapDescriptor & rpBitmaps) const536 css::uno::Reference<css::rendering::XBitmap> PresenterScrollBar::GetBitmap (
537     const Area eArea,
538     const SharedBitmapDescriptor& rpBitmaps) const
539 {
540     if (!rpBitmaps)
541         return nullptr;
542     else
543         return rpBitmaps->GetBitmap(GetBitmapMode(eArea));
544 }
545 
GetBitmapMode(const Area eArea) const546 PresenterBitmapContainer::BitmapDescriptor::Mode PresenterScrollBar::GetBitmapMode (
547     const Area eArea) const
548 {
549     if (IsDisabled(eArea))
550         return PresenterBitmapContainer::BitmapDescriptor::Disabled;
551     else if (eArea == meMouseMoveArea)
552         return PresenterBitmapContainer::BitmapDescriptor::MouseOver;
553     else
554         return PresenterBitmapContainer::BitmapDescriptor::Normal;
555 }
556 
IsDisabled(const Area eArea) const557 bool PresenterScrollBar::IsDisabled (const Area eArea) const
558 {
559     OSL_ASSERT(eArea>=0 && eArea<AreaCount);
560 
561     return ! maEnabledState[eArea];
562 }
563 
564 //===== PresenterVerticalScrollBar ============================================
565 
PresenterVerticalScrollBar(const Reference<XComponentContext> & rxComponentContext,const Reference<awt::XWindow> & rxParentWindow,const std::shared_ptr<PresenterPaintManager> & rpPaintManager,const::std::function<void (double)> & rThumbMotionListener)566 PresenterVerticalScrollBar::PresenterVerticalScrollBar (
567     const Reference<XComponentContext>& rxComponentContext,
568     const Reference<awt::XWindow>& rxParentWindow,
569     const std::shared_ptr<PresenterPaintManager>& rpPaintManager,
570     const ::std::function<void (double)>& rThumbMotionListener)
571     : PresenterScrollBar(rxComponentContext, rxParentWindow, rpPaintManager, rThumbMotionListener),
572       mnScrollBarWidth(0)
573 {
574 }
575 
~PresenterVerticalScrollBar()576 PresenterVerticalScrollBar::~PresenterVerticalScrollBar()
577 {
578 }
579 
GetDragDistance(const sal_Int32,const sal_Int32 nY) const580 double PresenterVerticalScrollBar::GetDragDistance (const sal_Int32, const sal_Int32 nY) const
581 {
582     const double nDistance (nY - maDragAnchor.Y);
583     if (nDistance == 0)
584         return 0;
585     else
586     {
587         const awt::Rectangle aWindowBox (mxWindow->getPosSize());
588         const double nBarWidth (aWindowBox.Width);
589         const double nPagerHeight (aWindowBox.Height - 2*nBarWidth);
590         const double nDragDistance (mnTotalSize / nPagerHeight * nDistance);
591         if (nDragDistance + mnThumbPosition < 0)
592             return -mnThumbPosition;
593         else if (mnThumbPosition + nDragDistance > mnTotalSize-mnThumbSize)
594             return mnTotalSize-mnThumbSize-mnThumbPosition;
595         else
596             return nDragDistance;
597     }
598 }
599 
UpdateDragAnchor(const double nDragDistance)600 void PresenterVerticalScrollBar::UpdateDragAnchor (const double nDragDistance)
601 {
602     const awt::Rectangle aWindowBox (mxWindow->getPosSize());
603     const double nBarWidth (aWindowBox.Width);
604     const double nPagerHeight (aWindowBox.Height - 2*nBarWidth);
605     maDragAnchor.Y += nDragDistance * nPagerHeight /  mnTotalSize;
606 }
607 
GetSize() const608 sal_Int32 PresenterVerticalScrollBar::GetSize() const
609 {
610     return mnScrollBarWidth;
611 }
612 
GetMinor(const double nX,const double) const613 double PresenterVerticalScrollBar::GetMinor (const double nX, const double) const
614 {
615     return nX;
616 }
617 
UpdateBorders()618 void PresenterVerticalScrollBar::UpdateBorders()
619 {
620     const awt::Rectangle aWindowBox (mxWindow->getPosSize());
621     double nBottom = aWindowBox.Height;
622 
623     if (mpNextButtonDescriptor)
624     {
625         Reference<rendering::XBitmap> xBitmap (mpNextButtonDescriptor->GetNormalBitmap());
626         if (xBitmap.is())
627         {
628             geometry::IntegerSize2D aSize (xBitmap->getSize());
629             maBox[NextButton] = geometry::RealRectangle2D(
630                 0, nBottom - aSize.Height, aWindowBox.Width, nBottom);
631             nBottom -= aSize.Height + gnScrollBarGap;
632         }
633     }
634     if (mpPrevButtonDescriptor)
635     {
636         Reference<rendering::XBitmap> xBitmap (mpPrevButtonDescriptor->GetNormalBitmap());
637         if (xBitmap.is())
638         {
639             geometry::IntegerSize2D aSize (xBitmap->getSize());
640             maBox[PrevButton] = geometry::RealRectangle2D(
641                 0, nBottom - aSize.Height, aWindowBox.Width, nBottom);
642             nBottom -= aSize.Height + gnScrollBarGap;
643         }
644     }
645     const double nPagerHeight (nBottom);
646     maBox[Pager] = geometry::RealRectangle2D(
647         0,0, aWindowBox.Width, nBottom);
648     if (mnTotalSize < 1)
649     {
650         maBox[Thumb] = maBox[Pager];
651 
652         // Set up the enabled/disabled states.
653         maEnabledState[PrevButton] = false;
654         maEnabledState[PagerUp] = false;
655         maEnabledState[NextButton] = false;
656         maEnabledState[PagerDown] = false;
657         maEnabledState[Thumb] = false;
658     }
659     else
660     {
661         const double nThumbSize = ::std::min(mnThumbSize,mnTotalSize);
662         const double nThumbPosition = ::std::clamp(mnThumbPosition, 0.0, mnTotalSize - nThumbSize);
663         maBox[Thumb] = geometry::RealRectangle2D(
664             0, nThumbPosition / mnTotalSize * nPagerHeight,
665             aWindowBox.Width,
666                 (nThumbPosition+nThumbSize) / mnTotalSize * nPagerHeight);
667 
668         // Set up the enabled/disabled states.
669         maEnabledState[PrevButton] = nThumbPosition>0;
670         maEnabledState[PagerUp] = nThumbPosition>0;
671         maEnabledState[NextButton] = nThumbPosition+nThumbSize < mnTotalSize;
672         maEnabledState[PagerDown] = nThumbPosition+nThumbSize < mnTotalSize;
673         maEnabledState[Thumb] = nThumbSize < mnTotalSize;
674     }
675     maBox[PagerUp] = geometry::RealRectangle2D(
676         maBox[Pager].X1, maBox[Pager].Y1, maBox[Pager].X2, maBox[Thumb].Y1-1);
677     maBox[PagerDown] = geometry::RealRectangle2D(
678         maBox[Pager].X1, maBox[Thumb].Y2+1, maBox[Pager].X2, maBox[Pager].Y2);
679     maBox[Total] = PresenterGeometryHelper::Union(
680         PresenterGeometryHelper::Union(maBox[PrevButton], maBox[NextButton]),
681         maBox[Pager]);
682 }
683 
UpdateBitmaps()684 void PresenterVerticalScrollBar::UpdateBitmaps()
685 {
686     if (mpBitmaps == nullptr)
687         return;
688 
689     mpPrevButtonDescriptor = mpBitmaps->GetBitmap("Up");
690     mpNextButtonDescriptor = mpBitmaps->GetBitmap("Down");
691     mpPagerStartDescriptor = mpBitmaps->GetBitmap("PagerTop");
692     mpPagerCenterDescriptor = mpBitmaps->GetBitmap("PagerVertical");
693     mpPagerEndDescriptor = mpBitmaps->GetBitmap("PagerBottom");
694     mpThumbStartDescriptor = mpBitmaps->GetBitmap("ThumbTop");
695     mpThumbCenterDescriptor = mpBitmaps->GetBitmap("ThumbVertical");
696     mpThumbEndDescriptor = mpBitmaps->GetBitmap("ThumbBottom");
697 
698     mnScrollBarWidth = 0;
699     UpdateWidthOrHeight(mnScrollBarWidth, mpPrevButtonDescriptor);
700     UpdateWidthOrHeight(mnScrollBarWidth, mpNextButtonDescriptor);
701     UpdateWidthOrHeight(mnScrollBarWidth, mpPagerStartDescriptor);
702     UpdateWidthOrHeight(mnScrollBarWidth, mpPagerCenterDescriptor);
703     UpdateWidthOrHeight(mnScrollBarWidth, mpPagerEndDescriptor);
704     UpdateWidthOrHeight(mnScrollBarWidth, mpThumbStartDescriptor);
705     UpdateWidthOrHeight(mnScrollBarWidth, mpThumbCenterDescriptor);
706     UpdateWidthOrHeight(mnScrollBarWidth, mpThumbEndDescriptor);
707     if (mnScrollBarWidth == 0)
708         mnScrollBarWidth = 20;
709 }
710 
PaintComposite(const css::awt::Rectangle & rUpdateBox,const Area eArea,const SharedBitmapDescriptor & rpStartBitmaps,const SharedBitmapDescriptor & rpCenterBitmaps,const SharedBitmapDescriptor & rpEndBitmaps)711 void PresenterVerticalScrollBar::PaintComposite(
712     const css::awt::Rectangle& rUpdateBox,
713     const Area eArea,
714     const SharedBitmapDescriptor& rpStartBitmaps,
715     const SharedBitmapDescriptor& rpCenterBitmaps,
716     const SharedBitmapDescriptor& rpEndBitmaps)
717 {
718     const awt::Rectangle aWindowBox (mxWindow->getPosSize());
719     geometry::RealRectangle2D aBox (GetRectangle(eArea));
720     aBox.X1 += aWindowBox.X;
721     aBox.Y1 += aWindowBox.Y;
722     aBox.X2 += aWindowBox.X;
723     aBox.Y2 += aWindowBox.Y;
724 
725     // Get bitmaps and sizes.
726 
727     PresenterUIPainter::PaintVerticalBitmapComposite(
728         mxCanvas,
729         rUpdateBox,
730         (eArea == Thumb
731             ? PresenterGeometryHelper::ConvertRectangleWithConstantSize(aBox)
732             : PresenterGeometryHelper::ConvertRectangle(aBox)),
733         GetBitmap(eArea, rpStartBitmaps),
734         GetBitmap(eArea, rpCenterBitmaps),
735         GetBitmap(eArea, rpEndBitmaps));
736 }
737 
738 //===== PresenterScrollBar::MousePressRepeater ================================
739 
MousePressRepeater(const::rtl::Reference<PresenterScrollBar> & rpScrollBar)740 PresenterScrollBar::MousePressRepeater::MousePressRepeater (
741     const ::rtl::Reference<PresenterScrollBar>& rpScrollBar)
742     : mnMousePressRepeaterTaskId(PresenterTimer::NotAValidTaskId),
743       mpScrollBar(rpScrollBar),
744       meMouseArea(PresenterScrollBar::None)
745 {
746 }
747 
Dispose()748 void PresenterScrollBar::MousePressRepeater::Dispose()
749 {
750     Stop();
751     mpScrollBar = nullptr;
752 }
753 
Start(const PresenterScrollBar::Area & reArea)754 void PresenterScrollBar::MousePressRepeater::Start (const PresenterScrollBar::Area& reArea)
755 {
756     meMouseArea = reArea;
757 
758     if (mnMousePressRepeaterTaskId == PresenterTimer::NotAValidTaskId)
759     {
760         // Execute key press operation at least this one time.
761         Execute();
762 
763         // Schedule repeated executions.
764         auto pThis(shared_from_this());
765         mnMousePressRepeaterTaskId = PresenterTimer::ScheduleRepeatedTask (
766             mpScrollBar->GetComponentContext(),
767             [pThis] (TimeValue const&) { return pThis->Callback(); },
768             500000000,
769             250000000);
770     }
771     else
772     {
773         // There is already an active repeating task.
774     }
775 }
776 
Stop()777 void PresenterScrollBar::MousePressRepeater::Stop()
778 {
779     if (mnMousePressRepeaterTaskId != PresenterTimer::NotAValidTaskId)
780     {
781         const sal_Int32 nTaskId (mnMousePressRepeaterTaskId);
782         mnMousePressRepeaterTaskId = PresenterTimer::NotAValidTaskId;
783         PresenterTimer::CancelTask(nTaskId);
784     }
785 }
786 
SetMouseArea(const PresenterScrollBar::Area & reArea)787 void PresenterScrollBar::MousePressRepeater::SetMouseArea(const PresenterScrollBar::Area& reArea)
788 {
789     if (meMouseArea != reArea)
790     {
791         if (mnMousePressRepeaterTaskId != PresenterTimer::NotAValidTaskId)
792         {
793             Stop();
794         }
795     }
796 }
797 
Callback()798 void PresenterScrollBar::MousePressRepeater::Callback ()
799 {
800     if (!mpScrollBar)
801     {
802         Stop();
803         return;
804     }
805 
806     Execute();
807 }
808 
Execute()809 void PresenterScrollBar::MousePressRepeater::Execute()
810 {
811     const double nThumbPosition (mpScrollBar->GetThumbPosition());
812     switch (meMouseArea)
813     {
814         case PrevButton:
815             mpScrollBar->SetThumbPosition(nThumbPosition - mpScrollBar->GetLineHeight(), true);
816             break;
817 
818         case NextButton:
819             mpScrollBar->SetThumbPosition(nThumbPosition + mpScrollBar->GetLineHeight(), true);
820             break;
821 
822         case PagerUp:
823             mpScrollBar->SetThumbPosition(nThumbPosition - mpScrollBar->GetThumbSize()*0.8, true);
824             break;
825 
826         case PagerDown:
827             mpScrollBar->SetThumbPosition(nThumbPosition + mpScrollBar->GetThumbSize()*0.8, true);
828             break;
829 
830         default:
831             break;
832     }
833 }
834 
835 } // end of namespace ::sdext::presenter
836 
837 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
838