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 <svx/zoomsliderctrl.hxx>
22 #include <comphelper/propertyvalue.hxx>
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>
31 #include <basegfx/utils/zoomtools.hxx>
32 #include <bitmaps.hlst>
33 #include <com/sun/star/beans/PropertyValue.hpp>
37 SFX_IMPL_STATUSBAR_CONTROL( SvxZoomSliderControl
, SvxZoomSliderItem
);
39 struct SvxZoomSliderControl::SvxZoomSliderControl_Impl
41 sal_uInt16 mnCurrentZoom
;
44 sal_uInt16 mnSliderCenter
;
45 std::vector
< tools::Long
> maSnappingPointOffsets
;
46 std::vector
< sal_uInt16
> maSnappingPointZooms
;
48 Image maIncreaseButton
;
49 Image maDecreaseButton
;
51 bool mbDraggingStarted
;
53 SvxZoomSliderControl_Impl() :
59 mbDraggingStarted( false ) {}
62 const tools::Long nSliderXOffset
= 20;
63 const tools::Long nSnappingEpsilon
= 5; // snapping epsilon in pixels
64 const tools::Long nSnappingPointsMinDist
= nSnappingEpsilon
; // minimum distance of two adjacent snapping points
66 // nOffset refers to the origin of the control:
68 sal_uInt16
SvxZoomSliderControl::Offset2Zoom( tools::Long nOffset
) const
70 const tools::Long nControlWidth
= getControlRect().GetWidth();
73 if ( nOffset
< nSliderXOffset
)
74 return mxImpl
->mnMinZoom
;
76 if ( nOffset
> nControlWidth
- nSliderXOffset
)
77 return mxImpl
->mnMaxZoom
;
79 // check for snapping points:
80 sal_uInt16 nCount
= 0;
81 for ( const tools::Long nCurrent
: mxImpl
->maSnappingPointOffsets
)
83 if ( std::abs(nCurrent
- nOffset
) < nSnappingEpsilon
)
86 nRet
= mxImpl
->maSnappingPointZooms
[ nCount
];
94 if ( nOffset
< nControlWidth
/ 2 )
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 );
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 );
114 if ( nRet
< mxImpl
->mnMinZoom
)
115 nRet
= mxImpl
->mnMinZoom
;
116 else if ( nRet
> mxImpl
->mnMaxZoom
)
117 nRet
= mxImpl
->mnMaxZoom
;
122 // returns the offset to the left control border
123 tools::Long
SvxZoomSliderControl::Zoom2Offset( sal_uInt16 nCurrentZoom
) const
125 const tools::Long nControlWidth
= getControlRect().GetWidth();
126 tools::Long nRet
= nSliderXOffset
;
128 const tools::Long nHalfSliderWidth
= nControlWidth
/2 - nSliderXOffset
;
130 if ( nCurrentZoom
<= mxImpl
->mnSliderCenter
)
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;
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
;
150 SvxZoomSliderControl::SvxZoomSliderControl( sal_uInt16 _nSlotId
, sal_uInt16 _nId
, StatusBar
& rStatusBar
) :
151 SfxStatusBarControl( _nSlotId
, _nId
, rStatusBar
),
152 mxImpl( new SvxZoomSliderControl_Impl
)
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
);
159 SvxZoomSliderControl::~SvxZoomSliderControl()
163 void SvxZoomSliderControl::StateChangedAtStatusBarControl( sal_uInt16
/*nSID*/, SfxItemState eState
, const SfxPoolItem
* pState
)
165 if ( (SfxItemState::DEFAULT
!= eState
) || pState
->IsVoidItem() )
167 GetStatusBar().SetItemText( GetId(), "" );
168 mxImpl
->mbValuesSet
= false;
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;
179 if ( mxImpl
->mnSliderCenter
== mxImpl
->mnMaxZoom
)
180 mxImpl
->mnSliderCenter
= mxImpl
->mnMinZoom
+ static_cast<sal_uInt16
>((mxImpl
->mnMaxZoom
- mxImpl
->mnMinZoom
) * 0.5);
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" );
189 const css::uno::Sequence
< sal_Int32
> rSnappingPoints
= static_cast<const SvxZoomSliderItem
*>( pState
)->GetSnappingPoints();
190 mxImpl
->maSnappingPointOffsets
.clear();
191 mxImpl
->maSnappingPointZooms
.clear();
193 // get all snapping points:
194 std::set
< sal_uInt16
> aTmpSnappingPoints
;
195 for ( const sal_Int32 nSnappingPoint
: rSnappingPoints
)
197 aTmpSnappingPoints
.insert( static_cast<sal_uInt16
>(nSnappingPoint
) );
200 // remove snapping points that are too close to each other:
201 tools::Long nLastOffset
= 0;
203 for ( const sal_uInt16 nCurrent
: aTmpSnappingPoints
)
205 const tools::Long nCurrentOffset
= Zoom2Offset( nCurrent
);
207 if ( nCurrentOffset
- nLastOffset
>= nSnappingPointsMinDist
)
209 mxImpl
->maSnappingPointOffsets
.push_back( nCurrentOffset
);
210 mxImpl
->maSnappingPointZooms
.push_back( nCurrent
);
211 nLastOffset
= nCurrentOffset
;
219 void SvxZoomSliderControl::Paint( const UserDrawEvent
& rUsrEvt
)
221 if ( !mxImpl
->mbValuesSet
)
224 const tools::Rectangle aControlRect
= getControlRect();
225 vcl::RenderContext
* pDev
= rUsrEvt
.GetRenderContext();
226 tools::Rectangle aRect
= rUsrEvt
.GetRect();
227 tools::Rectangle aSlider
= aRect
;
229 tools::Long nSliderHeight
= 1 * pDev
->GetDPIScaleFactor();
230 tools::Long nSnappingHeight
= 2 * pDev
->GetDPIScaleFactor();
232 aSlider
.AdjustTop((aControlRect
.GetHeight() - nSliderHeight
)/2 );
233 aSlider
.SetBottom( aSlider
.Top() + nSliderHeight
- 1 );
234 aSlider
.AdjustLeft(nSliderXOffset
);
235 aSlider
.AdjustRight( -nSliderXOffset
);
237 Color aOldLineColor
= pDev
->GetLineColor();
238 Color aOldFillColor
= pDev
->GetFillColor();
240 const StyleSettings
& rStyleSettings
= Application::GetSettings().GetStyleSettings();
241 pDev
->SetLineColor( rStyleSettings
.GetDarkShadowColor() );
242 pDev
->SetFillColor( rStyleSettings
.GetDarkShadowColor() );
245 pDev
->DrawRect( aSlider
);
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() );
251 // draw snapping points:
252 for ( const auto& rSnappingPoint
: mxImpl
->maSnappingPointOffsets
)
254 tools::Long nSnapPosX
= aRect
.Left() + rSnappingPoint
;
256 pDev
->DrawRect( tools::Rectangle( nSnapPosX
- 1, aSlider
.Top() - nSnappingHeight
,
257 nSnapPosX
, aSlider
.Bottom() + nSnappingHeight
) );
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
);
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
);
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
);
277 pDev
->SetLineColor( aOldLineColor
);
278 pDev
->SetFillColor( aOldFillColor
);
281 bool SvxZoomSliderControl::MouseButtonDown( const MouseEvent
& rEvt
)
283 if ( !mxImpl
->mbValuesSet
)
286 const tools::Rectangle aControlRect
= getControlRect();
287 const Point aPoint
= rEvt
.GetPosPixel();
288 const sal_Int32 nXDiff
= aPoint
.X() - aControlRect
.Left();
290 tools::Long nIncDecWidth
= mxImpl
->maIncreaseButton
.GetSizePixel().Width();
292 const tools::Long nButtonLeftOffset
= (nSliderXOffset
- nIncDecWidth
)/2;
293 const tools::Long nButtonRightOffset
= (nSliderXOffset
+ nIncDecWidth
)/2;
295 const tools::Long nOldZoom
= mxImpl
->mnCurrentZoom
;
298 if ( nXDiff
>= nButtonLeftOffset
&& nXDiff
<= nButtonRightOffset
)
299 mxImpl
->mnCurrentZoom
= basegfx::zoomtools::zoomOut( mxImpl
->mnCurrentZoom
);
301 else if ( nXDiff
>= aControlRect
.GetWidth() - nSliderXOffset
+ nButtonLeftOffset
&&
302 nXDiff
<= aControlRect
.GetWidth() - nSliderXOffset
+ nButtonRightOffset
)
303 mxImpl
->mnCurrentZoom
= basegfx::zoomtools::zoomIn( mxImpl
->mnCurrentZoom
);
305 else if( nXDiff
>= nSliderXOffset
&& nXDiff
<= aControlRect
.GetWidth() - nSliderXOffset
)
307 mxImpl
->mnCurrentZoom
= Offset2Zoom( nXDiff
);
308 mxImpl
->mbDraggingStarted
= true;
311 if ( mxImpl
->mnCurrentZoom
< mxImpl
->mnMinZoom
)
312 mxImpl
->mnCurrentZoom
= mxImpl
->mnMinZoom
;
313 else if ( mxImpl
->mnCurrentZoom
> mxImpl
->mnMaxZoom
)
314 mxImpl
->mnCurrentZoom
= mxImpl
->mnMaxZoom
;
316 if ( nOldZoom
== mxImpl
->mnCurrentZoom
)
324 bool SvxZoomSliderControl::MouseButtonUp( const MouseEvent
& )
326 mxImpl
->mbDraggingStarted
= false;
330 bool SvxZoomSliderControl::MouseMove( const MouseEvent
& rEvt
)
332 if ( !mxImpl
->mbValuesSet
)
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();
340 // check mouse move with button pressed
341 if ( 1 == nButtons
&& mxImpl
->mbDraggingStarted
)
343 if ( nXDiff
>= nSliderXOffset
&& nXDiff
<= aControlRect
.GetWidth() - nSliderXOffset
)
345 mxImpl
->mnCurrentZoom
= Offset2Zoom( nXDiff
);
353 tools::Long nIncDecWidth
= mxImpl
->maIncreaseButton
.GetSizePixel().Width();
355 const tools::Long nButtonLeftOffset
= (nSliderXOffset
- nIncDecWidth
)/2;
356 const tools::Long nButtonRightOffset
= (nSliderXOffset
+ nIncDecWidth
)/2;
359 if ( nXDiff
>= nButtonLeftOffset
&& nXDiff
<= nButtonRightOffset
)
360 GetStatusBar().SetQuickHelpText(GetId(), SvxResId(RID_SVXSTR_ZOOM_OUT
));
362 else if ( nXDiff
>= aControlRect
.GetWidth() - nSliderXOffset
+ nButtonLeftOffset
&&
363 nXDiff
<= aControlRect
.GetWidth() - nSliderXOffset
+ nButtonRightOffset
)
364 GetStatusBar().SetQuickHelpText(GetId(), SvxResId(RID_SVXSTR_ZOOM_IN
));
366 // don't hide the slider and its handle with a tooltip during zooming
367 GetStatusBar().SetQuickHelpText(GetId(), "");
372 void SvxZoomSliderControl::forceRepaint() const
374 GetStatusBar().SetItemData(GetId(), nullptr);
377 void SvxZoomSliderControl::repaintAndExecute()
381 // commit state change
382 SvxZoomSliderItem
aZoomSliderItem(mxImpl
->mnCurrentZoom
);
385 aZoomSliderItem
.QueryValue(any
);
387 css::uno::Sequence
<css::beans::PropertyValue
> aArgs
{ comphelper::makePropertyValue("ZoomSlider",
392 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */