1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "controller/SlsScrollBarManager.hxx"
22 #include "SlideSorter.hxx"
23 #include "controller/SlideSorterController.hxx"
24 #include "controller/SlsVisibleAreaManager.hxx"
25 #include "model/SlideSorterModel.hxx"
26 #include "model/SlsPageDescriptor.hxx"
27 #include "view/SlideSorterView.hxx"
28 #include "view/SlsLayouter.hxx"
29 #include "view/SlsTheme.hxx"
33 #include <boost/limits.hpp>
35 #include <vcl/scrbar.hxx>
37 namespace sd
{ namespace slidesorter
{ namespace controller
{
39 ScrollBarManager::ScrollBarManager (SlideSorter
& rSlideSorter
)
40 : mrSlideSorter(rSlideSorter
),
41 mpHorizontalScrollBar(mrSlideSorter
.GetHorizontalScrollBar()),
42 mpVerticalScrollBar(mrSlideSorter
.GetVerticalScrollBar()),
43 mnHorizontalPosition (0),
44 mnVerticalPosition (0),
45 maScrollBorder (20,20),
46 mnHorizontalScrollFactor (0.15),
47 mnVerticalScrollFactor (0.25),
48 mpScrollBarFiller(mrSlideSorter
.GetScrollBarFiller()),
50 maAutoScrollOffset(0,0),
51 mbIsAutoScrollActive(false),
52 mpContentWindow(mrSlideSorter
.GetContentWindow()),
55 // Hide the scroll bars by default to prevent display errors while
56 // switching between view shells: In the short time between initiating
57 // such a switch and the final rearrangement of UI controls the scroll
58 // bars and the filler where displayed in the upper left corner of the
60 mpHorizontalScrollBar
->Hide();
61 mpVerticalScrollBar
->Hide();
62 mpScrollBarFiller
->Hide();
64 maAutoScrollTimer
.SetTimeout(25);
65 maAutoScrollTimer
.SetTimeoutHdl (
66 LINK(this, ScrollBarManager
, AutoScrollTimeoutHandler
));
69 ScrollBarManager::~ScrollBarManager()
73 void ScrollBarManager::Connect()
75 if (mpVerticalScrollBar
!= nullptr)
77 mpVerticalScrollBar
->SetScrollHdl (
78 LINK(this, ScrollBarManager
, VerticalScrollBarHandler
));
80 if (mpHorizontalScrollBar
!= nullptr)
82 mpHorizontalScrollBar
->SetScrollHdl(
83 LINK(this, ScrollBarManager
, HorizontalScrollBarHandler
));
87 void ScrollBarManager::Disconnect()
89 if (mpVerticalScrollBar
!= nullptr)
91 mpVerticalScrollBar
->SetScrollHdl (Link
<>());
93 if (mpHorizontalScrollBar
!= nullptr)
95 mpHorizontalScrollBar
->SetScrollHdl (Link
<>());
99 /** Placing the scroll bars is an iterative process. The visibility of one
100 scroll bar affects the remaining size and thus may lead to the other
101 scroll bar becoming visible.
103 First we determine the visibility of the horizontal scroll bar. After
104 that we do the same for the vertical scroll bar. To have an initial
105 value for the required size we call the layouter before that. When one
106 of the two scroll bars is made visible then the size of the browser
107 window changes and a second call to the layouter becomes necessary.
108 That call is made anyway after this method returns.
110 Rectangle
ScrollBarManager::PlaceScrollBars (
111 const Rectangle
& rAvailableArea
,
112 const bool bIsHorizontalScrollBarAllowed
,
113 const bool bIsVerticalScrollBarAllowed
)
115 Rectangle
aRemainingSpace (DetermineScrollBarVisibilities(
117 bIsHorizontalScrollBarAllowed
,
118 bIsVerticalScrollBarAllowed
));
120 if (mpHorizontalScrollBar
!=nullptr && mpHorizontalScrollBar
->IsVisible())
121 PlaceHorizontalScrollBar (rAvailableArea
);
123 if (mpVerticalScrollBar
!=nullptr && mpVerticalScrollBar
->IsVisible())
124 PlaceVerticalScrollBar (rAvailableArea
);
126 if (mpScrollBarFiller
!=nullptr && mpScrollBarFiller
->IsVisible())
127 PlaceFiller (rAvailableArea
);
129 return aRemainingSpace
;
132 void ScrollBarManager::PlaceHorizontalScrollBar (const Rectangle
& aAvailableArea
)
134 // Save the current relative position.
135 mnHorizontalPosition
= double(mpHorizontalScrollBar
->GetThumbPos())
136 / double(mpHorizontalScrollBar
->GetRange().Len());
138 // Place the scroll bar.
139 Size
aScrollBarSize (mpHorizontalScrollBar
->GetSizePixel());
140 mpHorizontalScrollBar
->SetPosSizePixel (
141 Point(aAvailableArea
.Left(),
142 aAvailableArea
.Bottom()-aScrollBarSize
.Height()+1),
143 Size (aAvailableArea
.GetWidth() - GetVerticalScrollBarWidth(),
144 aScrollBarSize
.Height()));
146 // Restore the relative position.
147 mpHorizontalScrollBar
->SetThumbPos(
148 (long)(0.5 + mnHorizontalPosition
* mpHorizontalScrollBar
->GetRange().Len()));
151 void ScrollBarManager::PlaceVerticalScrollBar (const Rectangle
& aArea
)
153 const sal_Int32
nThumbPosition (mpVerticalScrollBar
->GetThumbPos());
155 // Place the scroll bar.
156 Size
aScrollBarSize (mpVerticalScrollBar
->GetSizePixel());
157 Point
aPosition (aArea
.Right()-aScrollBarSize
.Width()+1, aArea
.Top());
158 Size
aSize (aScrollBarSize
.Width(), aArea
.GetHeight() - GetHorizontalScrollBarHeight());
159 mpVerticalScrollBar
->SetPosSizePixel(aPosition
, aSize
);
161 // Restore the position.
162 mpVerticalScrollBar
->SetThumbPos(static_cast<long>(nThumbPosition
));
163 mnVerticalPosition
= nThumbPosition
/ double(mpVerticalScrollBar
->GetRange().Len());
166 void ScrollBarManager::PlaceFiller (const Rectangle
& aArea
)
168 mpScrollBarFiller
->SetPosSizePixel(
170 aArea
.Right()-mpVerticalScrollBar
->GetSizePixel().Width()+1,
171 aArea
.Bottom()-mpHorizontalScrollBar
->GetSizePixel().Height()+1),
173 mpVerticalScrollBar
->GetSizePixel().Width(),
174 mpHorizontalScrollBar
->GetSizePixel().Height()));
177 void ScrollBarManager::UpdateScrollBars (bool bResetThumbPosition
, bool bUseScrolling
)
179 Rectangle
aModelArea (mrSlideSorter
.GetView().GetModelArea());
180 sd::Window
*pWindow (mrSlideSorter
.GetContentWindow());
181 Size
aWindowModelSize (pWindow
->PixelToLogic(pWindow
->GetSizePixel()));
183 // The horizontal scroll bar is only shown when the window is
184 // horizontally smaller than the view.
185 if (mpHorizontalScrollBar
!= nullptr && mpHorizontalScrollBar
->IsVisible())
187 mpHorizontalScrollBar
->Show();
188 mpHorizontalScrollBar
->SetRange (
189 Range(aModelArea
.Left(), aModelArea
.Right()));
190 if (bResetThumbPosition
)
192 mpHorizontalScrollBar
->SetThumbPos (0);
193 mnHorizontalPosition
= 0;
196 mnHorizontalPosition
=
197 double(mpHorizontalScrollBar
->GetThumbPos())
198 / double(mpHorizontalScrollBar
->GetRange().Len());
200 mpHorizontalScrollBar
->SetVisibleSize (aWindowModelSize
.Width());
202 const long nWidth (mpContentWindow
->PixelToLogic(
203 mpContentWindow
->GetSizePixel()).Width());
204 // Make the line size about 10% of the visible width.
205 mpHorizontalScrollBar
->SetLineSize (nWidth
/ 10);
206 // Make the page size about 90% of the visible width.
207 mpHorizontalScrollBar
->SetPageSize ((nWidth
* 9) / 10);
211 mnHorizontalPosition
= 0;
214 // The vertical scroll bar is always shown.
215 if (mpVerticalScrollBar
!= nullptr && mpVerticalScrollBar
->IsVisible())
217 mpVerticalScrollBar
->SetRange (
218 Range(aModelArea
.Top(), aModelArea
.Bottom()));
219 if (bResetThumbPosition
)
221 mpVerticalScrollBar
->SetThumbPos (0);
222 mnVerticalPosition
= 0;
226 double(mpVerticalScrollBar
->GetThumbPos())
227 / double(mpVerticalScrollBar
->GetRange().Len());
229 mpVerticalScrollBar
->SetVisibleSize (aWindowModelSize
.Height());
231 const long nHeight (mpContentWindow
->PixelToLogic(
232 mpContentWindow
->GetSizePixel()).Height());
233 // Make the line size about 10% of the visible height.
234 mpVerticalScrollBar
->SetLineSize (nHeight
/ 10);
235 // Make the page size about 90% of the visible height.
236 mpVerticalScrollBar
->SetPageSize ((nHeight
* 9) / 10);
240 mnVerticalPosition
= 0;
243 double nEps (::std::numeric_limits
<double>::epsilon());
244 if (fabs(mnHorizontalPosition
-pWindow
->GetVisibleX()) > nEps
245 || fabs(mnVerticalPosition
-pWindow
->GetVisibleY()) > nEps
)
247 mrSlideSorter
.GetView().InvalidatePageObjectVisibilities();
249 pWindow
->SetVisibleXY(mnHorizontalPosition
, mnVerticalPosition
);
251 SetWindowOrigin(mnHorizontalPosition
, mnVerticalPosition
);
255 IMPL_LINK(ScrollBarManager
, VerticalScrollBarHandler
, ScrollBar
*, pScrollBar
)
258 && pScrollBar
==mpVerticalScrollBar
.get()
259 && pScrollBar
->IsVisible()
260 && mrSlideSorter
.GetContentWindow())
262 double nRelativePosition
= double(pScrollBar
->GetThumbPos())
263 / double(pScrollBar
->GetRange().Len());
264 mrSlideSorter
.GetView().InvalidatePageObjectVisibilities();
265 mrSlideSorter
.GetContentWindow()->SetVisibleXY(-1, nRelativePosition
);
266 mrSlideSorter
.GetController().GetVisibleAreaManager().DeactivateCurrentSlideTracking();
268 return sal_IntPtr(true);
271 IMPL_LINK(ScrollBarManager
, HorizontalScrollBarHandler
, ScrollBar
*, pScrollBar
)
274 && pScrollBar
==mpHorizontalScrollBar
.get()
275 && pScrollBar
->IsVisible()
276 && mrSlideSorter
.GetContentWindow())
278 double nRelativePosition
= double(pScrollBar
->GetThumbPos())
279 / double(pScrollBar
->GetRange().Len());
280 mrSlideSorter
.GetView().InvalidatePageObjectVisibilities();
281 mrSlideSorter
.GetContentWindow()->SetVisibleXY(nRelativePosition
, -1);
282 mrSlideSorter
.GetController().GetVisibleAreaManager().DeactivateCurrentSlideTracking();
284 return sal_IntPtr(true);
287 void ScrollBarManager::SetWindowOrigin (
288 double nHorizontalPosition
,
289 double nVerticalPosition
)
291 mnHorizontalPosition
= nHorizontalPosition
;
292 mnVerticalPosition
= nVerticalPosition
;
294 sd::Window
*pWindow (mrSlideSorter
.GetContentWindow());
295 Size
aViewSize (pWindow
->GetViewSize());
297 (long int) (mnHorizontalPosition
* aViewSize
.Width()),
298 (long int) (mnVerticalPosition
* aViewSize
.Height()));
300 pWindow
->SetWinViewPos (aOrigin
);
301 pWindow
->UpdateMapMode ();
302 pWindow
->Invalidate ();
305 /** Determining the visibility of the scroll bars is quite complicated. The
306 visibility of one influences that of the other because showing a scroll
307 bar makes the available space smaller and may lead to the need of
308 displaying the other.
309 To solve this we test all four combinations of showing or hiding each
310 scroll bar and use the best one. The best one is that combination that
311 a) shows the least number of scroll bars with preference of showing the
312 vertical over showing the horizontal and
313 b) when not showing a scroll bar the area used by the page objects fits
314 into the available area in the scroll bars orientation.
316 Rectangle
ScrollBarManager::DetermineScrollBarVisibilities (
317 const Rectangle
& rAvailableArea
,
318 const bool bIsHorizontalScrollBarAllowed
,
319 const bool bIsVerticalScrollBarAllowed
)
321 // Test which combination of scroll bars is the best.
322 bool bShowHorizontal
= false;
323 bool bShowVertical
= false;
324 if (mrSlideSorter
.GetModel().GetPageCount() == 0)
326 // No pages => no scroll bars.
328 else if (TestScrollBarVisibilities(false, false, rAvailableArea
))
330 // Nothing to be done.
332 else if (bIsHorizontalScrollBarAllowed
333 && TestScrollBarVisibilities(true, false, rAvailableArea
))
335 bShowHorizontal
= true;
337 else if (bIsVerticalScrollBarAllowed
338 && TestScrollBarVisibilities(false, true, rAvailableArea
))
340 bShowVertical
= true;
344 bShowHorizontal
= true;
345 bShowVertical
= true;
348 // Make the visibility of the scroll bars permanent.
349 mpVerticalScrollBar
->Show(bShowVertical
);
350 mpHorizontalScrollBar
->Show(bShowHorizontal
);
351 mpScrollBarFiller
->Show(bShowVertical
&& bShowHorizontal
);
353 // Adapt the remaining space accordingly.
354 Rectangle
aRemainingSpace (rAvailableArea
);
356 aRemainingSpace
.Right() -= mpVerticalScrollBar
->GetSizePixel().Width();
358 aRemainingSpace
.Bottom() -= mpHorizontalScrollBar
->GetSizePixel().Height();
360 return aRemainingSpace
;
363 bool ScrollBarManager::TestScrollBarVisibilities (
364 bool bHorizontalScrollBarVisible
,
365 bool bVerticalScrollBarVisible
,
366 const Rectangle
& rAvailableArea
)
368 model::SlideSorterModel
& rModel (mrSlideSorter
.GetModel());
370 // Adapt the available size by subtracting the sizes of the scroll bars
371 // visible in this combination.
372 Size
aBrowserSize (rAvailableArea
.GetSize());
373 if (bHorizontalScrollBarVisible
)
374 aBrowserSize
.Height() -= mpHorizontalScrollBar
->GetSizePixel().Height();
375 if (bVerticalScrollBarVisible
)
376 aBrowserSize
.Width() -= mpVerticalScrollBar
->GetSizePixel().Width();
378 // Tell the view to rearrange its page objects and check whether the
379 // page objects can be shown without clipping.
380 bool bRearrangeSuccess (mrSlideSorter
.GetView().GetLayouter().Rearrange (
381 mrSlideSorter
.GetView().GetOrientation(),
383 rModel
.GetPageDescriptor(0)->GetPage()->GetSize(),
384 rModel
.GetPageCount()));
386 if (bRearrangeSuccess
)
388 Size aPageSize
= mrSlideSorter
.GetView().GetLayouter().GetTotalBoundingBox().GetSize();
389 Size aWindowModelSize
= mpContentWindow
->PixelToLogic(aBrowserSize
);
391 // The content may be clipped, i.e. not fully visible, in one
392 // direction only when the scroll bar is visible in that direction.
393 if (aPageSize
.Width() > aWindowModelSize
.Width())
394 if ( ! bHorizontalScrollBarVisible
)
396 if (aPageSize
.Height() > aWindowModelSize
.Height())
397 if ( ! bVerticalScrollBarVisible
)
406 void ScrollBarManager::SetTopLeft(const Point
& rNewTopLeft
)
408 if (( ! mpVerticalScrollBar
409 || mpVerticalScrollBar
->GetThumbPos() == rNewTopLeft
.Y())
410 && ( ! mpHorizontalScrollBar
411 || mpHorizontalScrollBar
->GetThumbPos() == rNewTopLeft
.X()))
414 // Flush pending repaints before scrolling to avoid temporary artifacts.
415 mrSlideSorter
.GetContentWindow()->Update();
417 if (mpVerticalScrollBar
)
419 mpVerticalScrollBar
->SetThumbPos(rNewTopLeft
.Y());
420 mnVerticalPosition
= rNewTopLeft
.Y() / double(mpVerticalScrollBar
->GetRange().Len());
422 if (mpHorizontalScrollBar
)
424 mpHorizontalScrollBar
->SetThumbPos(rNewTopLeft
.X());
425 mnHorizontalPosition
= rNewTopLeft
.X() / double(mpHorizontalScrollBar
->GetRange().Len());
428 mrSlideSorter
.GetContentWindow()->SetVisibleXY(mnHorizontalPosition
, mnVerticalPosition
);
429 mrSlideSorter
.GetView().InvalidatePageObjectVisibilities();
432 int ScrollBarManager::GetVerticalScrollBarWidth() const
434 if (mpVerticalScrollBar
!= nullptr && mpVerticalScrollBar
->IsVisible())
435 return mpVerticalScrollBar
->GetSizePixel().Width();
440 int ScrollBarManager::GetHorizontalScrollBarHeight() const
442 if (mpHorizontalScrollBar
!= nullptr && mpHorizontalScrollBar
->IsVisible())
443 return mpHorizontalScrollBar
->GetSizePixel().Height();
448 void ScrollBarManager::CalcAutoScrollOffset (const Point
& rMouseWindowPosition
)
450 sd::Window
*pWindow (mrSlideSorter
.GetContentWindow());
455 Size aWindowSize
= pWindow
->GetOutputSizePixel();
456 Rectangle
aWindowArea (pWindow
->GetPosPixel(), aWindowSize
);
457 Rectangle
aViewPixelArea (
458 pWindow
->LogicToPixel(mrSlideSorter
.GetView().GetModelArea()));
460 if (aWindowSize
.Width() > maScrollBorder
.Width() * 3
461 && mpHorizontalScrollBar
!= nullptr
462 && mpHorizontalScrollBar
->IsVisible())
464 if (rMouseWindowPosition
.X() < maScrollBorder
.Width()
465 && aWindowArea
.Left() > aViewPixelArea
.Left())
467 nDx
= -1 + (int)(mnHorizontalScrollFactor
468 * (rMouseWindowPosition
.X() - maScrollBorder
.Width()));
471 if (rMouseWindowPosition
.X() >= (aWindowSize
.Width() - maScrollBorder
.Width())
472 && aWindowArea
.Right() < aViewPixelArea
.Right())
474 nDx
= 1 + (int)(mnHorizontalScrollFactor
475 * (rMouseWindowPosition
.X() - aWindowSize
.Width()
476 + maScrollBorder
.Width()));
480 if (aWindowSize
.Height() > maScrollBorder
.Height() * 3
481 && aWindowSize
.Height() < aViewPixelArea
.GetHeight())
483 if (rMouseWindowPosition
.Y() < maScrollBorder
.Height()
484 && aWindowArea
.Top() > aViewPixelArea
.Top())
486 nDy
= -1 + (int)(mnVerticalScrollFactor
487 * (rMouseWindowPosition
.Y() - maScrollBorder
.Height()));
490 if (rMouseWindowPosition
.Y() >= (aWindowSize
.Height() - maScrollBorder
.Height())
491 && aWindowArea
.Bottom() < aViewPixelArea
.Bottom())
493 nDy
= 1 + (int)(mnVerticalScrollFactor
494 * (rMouseWindowPosition
.Y() - aWindowSize
.Height()
495 + maScrollBorder
.Height()));
499 maAutoScrollOffset
= Size(nDx
,nDy
);
502 bool ScrollBarManager::AutoScroll (
503 const Point
& rMouseWindowPosition
,
504 const ::boost::function
<void()>& rAutoScrollFunctor
)
506 maAutoScrollFunctor
= rAutoScrollFunctor
;
507 CalcAutoScrollOffset(rMouseWindowPosition
);
509 if ( ! mbIsAutoScrollActive
)
510 bResult
= RepeatAutoScroll();
515 void ScrollBarManager::StopAutoScroll()
517 maAutoScrollTimer
.Stop();
518 mbIsAutoScrollActive
= false;
521 bool ScrollBarManager::RepeatAutoScroll()
523 if (maAutoScrollOffset
!= Size(0,0))
525 if (mrSlideSorter
.GetViewShell() != NULL
)
527 mrSlideSorter
.GetViewShell()->Scroll(
528 maAutoScrollOffset
.Width(),
529 maAutoScrollOffset
.Height());
530 mrSlideSorter
.GetView().InvalidatePageObjectVisibilities();
532 if (maAutoScrollFunctor
)
533 maAutoScrollFunctor();
535 mbIsAutoScrollActive
= true;
536 maAutoScrollTimer
.Start();
542 clearAutoScrollFunctor();
543 mbIsAutoScrollActive
= false;
547 void ScrollBarManager::clearAutoScrollFunctor()
549 maAutoScrollFunctor
= ::boost::function
<void()>();
552 IMPL_LINK_NOARG_TYPED(ScrollBarManager
, AutoScrollTimeoutHandler
, Timer
*, void)
557 void ScrollBarManager::Scroll(
558 const Orientation eOrientation
,
560 const sal_Int32 nDistance
)
562 bool bIsVertical (false);
563 switch (eOrientation
)
565 case Orientation_Horizontal
: bIsVertical
= false; break;
566 case Orientation_Vertical
: bIsVertical
= true; break;
568 OSL_ASSERT(eOrientation
==Orientation_Horizontal
|| eOrientation
==Orientation_Vertical
);
573 mpHorizontalScrollBar
? mpHorizontalScrollBar
->GetThumbPos() : 0,
574 mpVerticalScrollBar
? mpVerticalScrollBar
->GetThumbPos() : 0);
579 aNewTopLeft
.Y() += nDistance
;
581 aNewTopLeft
.X() += nDistance
;
586 view::Layouter
& rLayouter (mrSlideSorter
.GetView().GetLayouter());
588 // Calculate estimate of new location.
590 aNewTopLeft
.Y() += nDistance
* rLayouter
.GetPageObjectSize().Height();
592 aNewTopLeft
.X() += nDistance
* rLayouter
.GetPageObjectSize().Width();
594 // Adapt location to show whole slides.
598 const sal_Int32
nIndex (rLayouter
.GetIndexAtPoint(
599 Point(aNewTopLeft
.X(), aNewTopLeft
.Y()+mpVerticalScrollBar
->GetVisibleSize()),
601 aNewTopLeft
.Y() = rLayouter
.GetPageObjectBox(nIndex
,true).Bottom()
602 - mpVerticalScrollBar
->GetVisibleSize();
606 const sal_Int32
nIndex (rLayouter
.GetIndexAtPoint(
607 Point(aNewTopLeft
.X(), aNewTopLeft
.Y()),
609 aNewTopLeft
.Y() = rLayouter
.GetPageObjectBox(nIndex
,true).Top();
614 const sal_Int32
nIndex (rLayouter
.GetIndexAtPoint(
615 Point(aNewTopLeft
.X()+mpVerticalScrollBar
->GetVisibleSize(), aNewTopLeft
.Y()),
617 aNewTopLeft
.X() = rLayouter
.GetPageObjectBox(nIndex
,true).Right()
618 - mpVerticalScrollBar
->GetVisibleSize();
622 const sal_Int32
nIndex (rLayouter
.GetIndexAtPoint(
623 Point(aNewTopLeft
.X(), aNewTopLeft
.Y()),
625 aNewTopLeft
.X() = rLayouter
.GetPageObjectBox(nIndex
,true).Left();
629 mrSlideSorter
.GetController().GetVisibleAreaManager().DeactivateCurrentSlideTracking();
630 SetTopLeft(aNewTopLeft
);
633 } } } // end of namespace ::sd::slidesorter::controller
635 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */