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