LibreOffice Module svx (master)  1
zoomsliderctrl.cxx
Go to the documentation of this file.
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 <svx/zoomsliderctrl.hxx>
21 
23 #include <vcl/status.hxx>
24 #include <vcl/image.hxx>
25 #include <vcl/svapp.hxx>
26 #include <vcl/settings.hxx>
27 #include <vcl/event.hxx>
28 #include <svx/zoomslideritem.hxx>
29 #include <svx/dialmgr.hxx>
30 #include <svx/strings.hrc>
32 #include <bitmaps.hlst>
33 #include <com/sun/star/beans/PropertyValue.hpp>
34 
35 #include <set>
36 
38 
40 {
41  sal_uInt16 mnCurrentZoom;
42  sal_uInt16 mnMinZoom;
43  sal_uInt16 mnMaxZoom;
44  sal_uInt16 mnSliderCenter;
45  std::vector< tools::Long > maSnappingPointOffsets;
46  std::vector< sal_uInt16 > maSnappingPointZooms;
52 
54  mnCurrentZoom( 0 ),
55  mnMinZoom( 0 ),
56  mnMaxZoom( 0 ),
57  mnSliderCenter( 0 ),
58  mbValuesSet( false ),
59  mbDraggingStarted( false ) {}
60 };
61 
63 const tools::Long nSnappingEpsilon = 5; // snapping epsilon in pixels
64 const tools::Long nSnappingPointsMinDist = nSnappingEpsilon; // minimum distance of two adjacent snapping points
65 
66 // nOffset refers to the origin of the control:
67 // + ----------- -
69 {
70  const tools::Long nControlWidth = getControlRect().GetWidth();
71  sal_uInt16 nRet = 0;
72 
73  if ( nOffset < nSliderXOffset )
74  return mxImpl->mnMinZoom;
75 
76  if ( nOffset > nControlWidth - nSliderXOffset )
77  return mxImpl->mnMaxZoom;
78 
79  // check for snapping points:
80  sal_uInt16 nCount = 0;
81  for ( const tools::Long nCurrent : mxImpl->maSnappingPointOffsets )
82  {
83  if ( std::abs(nCurrent - nOffset) < nSnappingEpsilon )
84  {
85  nOffset = nCurrent;
86  nRet = mxImpl->maSnappingPointZooms[ nCount ];
87  break;
88  }
89  ++nCount;
90  }
91 
92  if ( 0 == nRet )
93  {
94  if ( nOffset < nControlWidth / 2 )
95  {
96  // first half of slider
97  const tools::Long nFirstHalfRange = mxImpl->mnSliderCenter - mxImpl->mnMinZoom;
98  const tools::Long nHalfSliderWidth = nControlWidth/2 - nSliderXOffset;
99  const tools::Long nZoomPerSliderPixel = (1000 * nFirstHalfRange) / nHalfSliderWidth;
100  const tools::Long nOffsetToSliderLeft = nOffset - nSliderXOffset;
101  nRet = mxImpl->mnMinZoom + sal_uInt16( nOffsetToSliderLeft * nZoomPerSliderPixel / 1000 );
102  }
103  else
104  {
105  // second half of slider
106  const tools::Long nSecondHalfRange = mxImpl->mnMaxZoom - mxImpl->mnSliderCenter;
107  const tools::Long nHalfSliderWidth = nControlWidth/2 - nSliderXOffset;
108  const tools::Long nZoomPerSliderPixel = 1000 * nSecondHalfRange / nHalfSliderWidth;
109  const tools::Long nOffsetToSliderCenter = nOffset - nControlWidth/2;
110  nRet = mxImpl->mnSliderCenter + sal_uInt16( nOffsetToSliderCenter * nZoomPerSliderPixel / 1000 );
111  }
112  }
113 
114  if ( nRet < mxImpl->mnMinZoom )
115  nRet = mxImpl->mnMinZoom;
116  else if ( nRet > mxImpl->mnMaxZoom )
117  nRet = mxImpl->mnMaxZoom;
118 
119  return nRet;
120 }
121 
122 // returns the offset to the left control border
123 tools::Long SvxZoomSliderControl::Zoom2Offset( sal_uInt16 nCurrentZoom ) const
124 {
125  const tools::Long nControlWidth = getControlRect().GetWidth();
127 
128  const tools::Long nHalfSliderWidth = nControlWidth/2 - nSliderXOffset;
129 
130  if ( nCurrentZoom <= mxImpl->mnSliderCenter )
131  {
132  nCurrentZoom = nCurrentZoom - mxImpl->mnMinZoom;
133  const tools::Long nFirstHalfRange = mxImpl->mnSliderCenter - mxImpl->mnMinZoom;
134  const tools::Long nSliderPixelPerZoomPercent = 1000 * nHalfSliderWidth / nFirstHalfRange;
135  const tools::Long nOffset = (nSliderPixelPerZoomPercent * nCurrentZoom) / 1000;
136  nRet += nOffset;
137  }
138  else
139  {
140  nCurrentZoom = nCurrentZoom - mxImpl->mnSliderCenter;
141  const tools::Long nSecondHalfRange = mxImpl->mnMaxZoom - mxImpl->mnSliderCenter;
142  const tools::Long nSliderPixelPerZoomPercent = 1000 * nHalfSliderWidth / nSecondHalfRange;
143  const tools::Long nOffset = (nSliderPixelPerZoomPercent * nCurrentZoom) / 1000;
144  nRet += nHalfSliderWidth + nOffset;
145  }
146 
147  return nRet;
148 }
149 
150 SvxZoomSliderControl::SvxZoomSliderControl( sal_uInt16 _nSlotId, sal_uInt16 _nId, StatusBar& rStatusBar ) :
151  SfxStatusBarControl( _nSlotId, _nId, rStatusBar ),
152  mxImpl( new SvxZoomSliderControl_Impl )
153 {
154  mxImpl->maSliderButton = Image(StockImage::Yes, RID_SVXBMP_SLIDERBUTTON);
155  mxImpl->maIncreaseButton = Image(StockImage::Yes, RID_SVXBMP_SLIDERINCREASE);
156  mxImpl->maDecreaseButton = Image(StockImage::Yes, RID_SVXBMP_SLIDERDECREASE);
157 }
158 
160 {
161 }
162 
163 void SvxZoomSliderControl::StateChangedAtStatusBarControl( sal_uInt16 /*nSID*/, SfxItemState eState, const SfxPoolItem* pState )
164 {
165  if ( (SfxItemState::DEFAULT != eState) || pState->IsVoidItem() )
166  {
167  GetStatusBar().SetItemText( GetId(), "" );
168  mxImpl->mbValuesSet = false;
169  }
170  else
171  {
172  assert( dynamic_cast<const SvxZoomSliderItem*>( pState) && "invalid item type: should be a SvxZoomSliderItem" );
173  mxImpl->mnCurrentZoom = static_cast<const SvxZoomSliderItem*>( pState )->GetValue();
174  mxImpl->mnMinZoom = static_cast<const SvxZoomSliderItem*>( pState )->GetMinZoom();
175  mxImpl->mnMaxZoom = static_cast<const SvxZoomSliderItem*>( pState )->GetMaxZoom();
176  mxImpl->mnSliderCenter= 100;
177  mxImpl->mbValuesSet = true;
178 
179  if ( mxImpl->mnSliderCenter == mxImpl->mnMaxZoom )
180  mxImpl->mnSliderCenter = mxImpl->mnMinZoom + static_cast<sal_uInt16>((mxImpl->mnMaxZoom - mxImpl->mnMinZoom) * 0.5);
181 
182 
183  DBG_ASSERT( mxImpl->mnMinZoom <= mxImpl->mnCurrentZoom &&
184  mxImpl->mnMinZoom < mxImpl->mnSliderCenter &&
185  mxImpl->mnMaxZoom >= mxImpl->mnCurrentZoom &&
186  mxImpl->mnMaxZoom > mxImpl->mnSliderCenter,
187  "Looks like the zoom slider item is corrupted" );
188 
189  const css::uno::Sequence < sal_Int32 > rSnappingPoints = static_cast<const SvxZoomSliderItem*>( pState )->GetSnappingPoints();
190  mxImpl->maSnappingPointOffsets.clear();
191  mxImpl->maSnappingPointZooms.clear();
192 
193  // get all snapping points:
194  std::set< sal_uInt16 > aTmpSnappingPoints;
195  for ( const sal_Int32 nSnappingPoint : rSnappingPoints )
196  {
197  aTmpSnappingPoints.insert( static_cast<sal_uInt16>(nSnappingPoint) );
198  }
199 
200  // remove snapping points that are too close to each other:
201  tools::Long nLastOffset = 0;
202 
203  for ( const sal_uInt16 nCurrent : aTmpSnappingPoints )
204  {
205  const tools::Long nCurrentOffset = Zoom2Offset( nCurrent );
206 
207  if ( nCurrentOffset - nLastOffset >= nSnappingPointsMinDist )
208  {
209  mxImpl->maSnappingPointOffsets.push_back( nCurrentOffset );
210  mxImpl->maSnappingPointZooms.push_back( nCurrent );
211  nLastOffset = nCurrentOffset;
212  }
213  }
214  }
215 
216  forceRepaint();
217 }
218 
220 {
221  if ( !mxImpl->mbValuesSet )
222  return;
223 
224  const tools::Rectangle aControlRect = getControlRect();
225  vcl::RenderContext* pDev = rUsrEvt.GetRenderContext();
226  tools::Rectangle aRect = rUsrEvt.GetRect();
227  tools::Rectangle aSlider = aRect;
228 
231 
232  aSlider.AdjustTop((aControlRect.GetHeight() - nSliderHeight)/2 );
233  aSlider.SetBottom( aSlider.Top() + nSliderHeight - 1 );
234  aSlider.AdjustLeft(nSliderXOffset );
235  aSlider.AdjustRight( -nSliderXOffset );
236 
237  Color aOldLineColor = pDev->GetLineColor();
238  Color aOldFillColor = pDev->GetFillColor();
239 
240  const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
241  pDev->SetLineColor( rStyleSettings.GetDarkShadowColor() );
242  pDev->SetFillColor( rStyleSettings.GetDarkShadowColor() );
243 
244  // draw slider
245  pDev->DrawRect( aSlider );
246  // shadow
247  pDev->SetLineColor( rStyleSettings.GetShadowColor() );
248  pDev->DrawLine(Point(aSlider.Left()+1,aSlider.Bottom()+1), Point(aSlider.Right()+1,aSlider.Bottom()+1));
249  pDev->SetLineColor( rStyleSettings.GetDarkShadowColor() );
250 
251  // draw snapping points:
252  for ( const auto& rSnappingPoint : mxImpl->maSnappingPointOffsets )
253  {
254  tools::Long nSnapPosX = aRect.Left() + rSnappingPoint;
255 
256  pDev->DrawRect( tools::Rectangle( nSnapPosX - 1, aSlider.Top() - nSnappingHeight,
257  nSnapPosX, aSlider.Bottom() + nSnappingHeight ) );
258  }
259 
260  // draw slider button
261  Point aImagePoint = aRect.TopLeft();
262  aImagePoint.AdjustX(Zoom2Offset( mxImpl->mnCurrentZoom ) );
263  aImagePoint.AdjustX( -(mxImpl->maSliderButton.GetSizePixel().Width()/2) );
264  aImagePoint.AdjustY((aControlRect.GetHeight() - mxImpl->maSliderButton.GetSizePixel().Height())/2 );
265  pDev->DrawImage( aImagePoint, mxImpl->maSliderButton );
266 
267  // draw decrease button
268  aImagePoint = aRect.TopLeft();
269  aImagePoint.AdjustX((nSliderXOffset - mxImpl->maDecreaseButton.GetSizePixel().Width())/2 );
270  aImagePoint.AdjustY((aControlRect.GetHeight() - mxImpl->maDecreaseButton.GetSizePixel().Height())/2 );
271  pDev->DrawImage( aImagePoint, mxImpl->maDecreaseButton );
272 
273  // draw increase button
274  aImagePoint.setX( aRect.Left() + aControlRect.GetWidth() - mxImpl->maIncreaseButton.GetSizePixel().Width() - (nSliderXOffset - mxImpl->maIncreaseButton.GetSizePixel().Height())/2 );
275  pDev->DrawImage( aImagePoint, mxImpl->maIncreaseButton );
276 
277  pDev->SetLineColor( aOldLineColor );
278  pDev->SetFillColor( aOldFillColor );
279 }
280 
282 {
283  if ( !mxImpl->mbValuesSet )
284  return true;
285 
286  const tools::Rectangle aControlRect = getControlRect();
287  const Point aPoint = rEvt.GetPosPixel();
288  const sal_Int32 nXDiff = aPoint.X() - aControlRect.Left();
289 
290  tools::Long nIncDecWidth = mxImpl->maIncreaseButton.GetSizePixel().Width();
291 
292  const tools::Long nButtonLeftOffset = (nSliderXOffset - nIncDecWidth)/2;
293  const tools::Long nButtonRightOffset = (nSliderXOffset + nIncDecWidth)/2;
294 
295  const tools::Long nOldZoom = mxImpl->mnCurrentZoom;
296 
297  // click to - button
298  if ( nXDiff >= nButtonLeftOffset && nXDiff <= nButtonRightOffset )
299  mxImpl->mnCurrentZoom = basegfx::zoomtools::zoomOut( static_cast<int>(mxImpl->mnCurrentZoom) );
300  // click to + button
301  else if ( nXDiff >= aControlRect.GetWidth() - nSliderXOffset + nButtonLeftOffset &&
302  nXDiff <= aControlRect.GetWidth() - nSliderXOffset + nButtonRightOffset )
303  mxImpl->mnCurrentZoom = basegfx::zoomtools::zoomIn( static_cast<int>(mxImpl->mnCurrentZoom) );
304  // click to slider
305  else if( nXDiff >= nSliderXOffset && nXDiff <= aControlRect.GetWidth() - nSliderXOffset )
306  {
307  mxImpl->mnCurrentZoom = Offset2Zoom( nXDiff );
308  mxImpl->mbDraggingStarted = true;
309  }
310 
311  if ( mxImpl->mnCurrentZoom < mxImpl->mnMinZoom )
312  mxImpl->mnCurrentZoom = mxImpl->mnMinZoom;
313  else if ( mxImpl->mnCurrentZoom > mxImpl->mnMaxZoom )
314  mxImpl->mnCurrentZoom = mxImpl->mnMaxZoom;
315 
316  if ( nOldZoom == mxImpl->mnCurrentZoom )
317  return true;
318 
320 
321  return true;
322 }
323 
325 {
326  mxImpl->mbDraggingStarted = false;
327  return true;
328 }
329 
331 {
332  if ( !mxImpl->mbValuesSet )
333  return true;
334 
335  const short nButtons = rEvt.GetButtons();
336  const tools::Rectangle aControlRect = getControlRect();
337  const Point aPoint = rEvt.GetPosPixel();
338  const sal_Int32 nXDiff = aPoint.X() - aControlRect.Left();
339 
340  // check mouse move with button pressed
341  if ( 1 == nButtons && mxImpl->mbDraggingStarted )
342  {
343  if ( nXDiff >= nSliderXOffset && nXDiff <= aControlRect.GetWidth() - nSliderXOffset )
344  {
345  mxImpl->mnCurrentZoom = Offset2Zoom( nXDiff );
346 
348  }
349  }
350 
351  // Tooltips
352 
353  tools::Long nIncDecWidth = mxImpl->maIncreaseButton.GetSizePixel().Width();
354 
355  const tools::Long nButtonLeftOffset = (nSliderXOffset - nIncDecWidth)/2;
356  const tools::Long nButtonRightOffset = (nSliderXOffset + nIncDecWidth)/2;
357 
358  // click to - button
359  if ( nXDiff >= nButtonLeftOffset && nXDiff <= nButtonRightOffset )
360  GetStatusBar().SetQuickHelpText(GetId(), SvxResId(RID_SVXSTR_ZOOM_OUT));
361  // click to + button
362  else if ( nXDiff >= aControlRect.GetWidth() - nSliderXOffset + nButtonLeftOffset &&
363  nXDiff <= aControlRect.GetWidth() - nSliderXOffset + nButtonRightOffset )
364  GetStatusBar().SetQuickHelpText(GetId(), SvxResId(RID_SVXSTR_ZOOM_IN));
365  else
366  // don't hide the slider and its handle with a tooltip during zooming
368 
369  return true;
370 }
371 
373 {
374  GetStatusBar().SetItemData(GetId(), nullptr);
375 }
376 
378 {
379  forceRepaint();
380 
381  // commit state change
382  SvxZoomSliderItem aZoomSliderItem(mxImpl->mnCurrentZoom);
383 
384  css::uno::Any any;
385  aZoomSliderItem.QueryValue(any);
386 
387  css::uno::Sequence<css::beans::PropertyValue> aArgs{ comphelper::makePropertyValue("ZoomSlider",
388  any) };
389  execute(aArgs);
390 }
391 
392 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const Color & GetShadowColor() const
void DrawImage(const Point &rPos, const Image &rImage, DrawImageFlags nStyle=DrawImageFlags::NONE)
tools::Long AdjustRight(tools::Long nHorzMoveDelta)
const tools::Long nSliderHeight
std::string GetValue
const tools::Rectangle & GetRect() const
constexpr tools::Long Left() const
tools::Long Zoom2Offset(sal_uInt16 nZoom) const
long Long
css::beans::PropertyValue makePropertyValue(const OUString &rName, T &&rValue)
const StyleSettings & GetStyleSettings() const
static const AllSettings & GetSettings()
void SetItemText(sal_uInt16 nItemId, const OUString &rText, int nCharsWidth=-1)
void forceRepaint() const
SFX_IMPL_STATUSBAR_CONTROL(SvxZoomSliderControl, SvxZoomSliderItem)
virtual bool MouseMove(const MouseEvent &rEvt) override
virtual bool MouseButtonDown(const MouseEvent &) override
sal_uInt16 GetButtons() const
void SetItemData(sal_uInt16 nItemId, void *pNewData)
int nCount
OUString SvxResId(TranslateId aId)
Definition: dialmgr.cxx:24
std::unique_ptr< SvxZoomSliderControl_Impl > mxImpl
constexpr tools::Long GetWidth() const
void DrawLine(const Point &rStartPt, const Point &rEndPt)
virtual bool IsVoidItem() const
void DrawRect(const tools::Rectangle &rRect)
virtual void StateChangedAtStatusBarControl(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem *pState) override
sal_uInt16 GetId() const
tools::Long zoomOut(tools::Long nCurrent)
const Color & GetDarkShadowColor() const
void SetLineColor()
virtual bool QueryValue(css::uno::Any &rVal, sal_uInt8 nMemberId=0) const override
SvxZoomSliderControl(sal_uInt16 _nSlotId, sal_uInt16 _nId, StatusBar &_rStb)
const tools::Long nSliderXOffset
vcl::RenderContext * GetRenderContext() const
#define DBG_ASSERT(sCon, aError)
virtual ~SvxZoomSliderControl() override
sal_uInt16 Offset2Zoom(tools::Long nOffset) const
tools::Long zoomIn(tools::Long nCurrent)
const Any & any
constexpr tools::Long Right() const
void SetFillColor()
const Color & GetLineColor() const
constexpr tools::Long Top() const
const tools::Long nIncDecWidth
virtual bool MouseButtonUp(const MouseEvent &) override
const tools::Long nSnappingEpsilon
constexpr void SetBottom(tools::Long v)
virtual void Paint(const UserDrawEvent &rEvt) override
float GetDPIScaleFactor() const
constexpr Point TopLeft() const
tools::Long AdjustTop(tools::Long nVertMoveDelta)
constexpr tools::Long Bottom() const
void SetQuickHelpText(sal_uInt16 nItemId, const OUString &rText)
SfxItemState
StatusBar & GetStatusBar() const
const Point & GetPosPixel() const
const tools::Long nSnappingPointsMinDist
tools::Long AdjustLeft(tools::Long nHorzMoveDelta)
const tools::Long nSnappingHeight
const Color & GetFillColor() const
constexpr tools::Long GetHeight() const