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 // coverity[ tainted_data_return : FALSE ] version 2023.12.2
126 const tools::Long nControlWidth
= getControlRect().GetWidth();
127 tools::Long nRet
= nSliderXOffset
;
129 const tools::Long nHalfSliderWidth
= nControlWidth
/2 - nSliderXOffset
;
131 if ( nCurrentZoom
<= mxImpl
->mnSliderCenter
)
133 nCurrentZoom
= nCurrentZoom
- mxImpl
->mnMinZoom
;
134 const tools::Long nFirstHalfRange
= mxImpl
->mnSliderCenter
- mxImpl
->mnMinZoom
;
135 const tools::Long nSliderPixelPerZoomPercent
= 1000 * nHalfSliderWidth
/ nFirstHalfRange
;
136 const tools::Long nOffset
= (nSliderPixelPerZoomPercent
* nCurrentZoom
) / 1000;
141 nCurrentZoom
= nCurrentZoom
- mxImpl
->mnSliderCenter
;
142 const tools::Long nSecondHalfRange
= mxImpl
->mnMaxZoom
- mxImpl
->mnSliderCenter
;
143 const tools::Long nSliderPixelPerZoomPercent
= 1000 * nHalfSliderWidth
/ nSecondHalfRange
;
144 const tools::Long nOffset
= (nSliderPixelPerZoomPercent
* nCurrentZoom
) / 1000;
145 nRet
+= nHalfSliderWidth
+ nOffset
;
151 SvxZoomSliderControl::SvxZoomSliderControl( sal_uInt16 _nSlotId
, sal_uInt16 _nId
, StatusBar
& rStatusBar
) :
152 SfxStatusBarControl( _nSlotId
, _nId
, rStatusBar
),
153 mxImpl( new SvxZoomSliderControl_Impl
)
155 mxImpl
->maSliderButton
= Image(StockImage::Yes
, RID_SVXBMP_SLIDERBUTTON
);
156 mxImpl
->maIncreaseButton
= Image(StockImage::Yes
, RID_SVXBMP_SLIDERINCREASE
);
157 mxImpl
->maDecreaseButton
= Image(StockImage::Yes
, RID_SVXBMP_SLIDERDECREASE
);
160 SvxZoomSliderControl::~SvxZoomSliderControl()
164 void SvxZoomSliderControl::StateChangedAtStatusBarControl( sal_uInt16
/*nSID*/, SfxItemState eState
, const SfxPoolItem
* pState
)
166 if (SfxItemState::DEFAULT
!= eState
|| SfxItemState::DISABLED
== eState
)
168 GetStatusBar().SetItemText( GetId(), u
""_ustr
);
169 mxImpl
->mbValuesSet
= false;
173 assert( dynamic_cast<const SvxZoomSliderItem
*>( pState
) && "invalid item type: should be a SvxZoomSliderItem" );
174 mxImpl
->mnCurrentZoom
= static_cast<const SvxZoomSliderItem
*>( pState
)->GetValue();
175 mxImpl
->mnMinZoom
= static_cast<const SvxZoomSliderItem
*>( pState
)->GetMinZoom();
176 mxImpl
->mnMaxZoom
= static_cast<const SvxZoomSliderItem
*>( pState
)->GetMaxZoom();
177 mxImpl
->mnSliderCenter
= 100;
178 mxImpl
->mbValuesSet
= true;
180 if ( mxImpl
->mnSliderCenter
== mxImpl
->mnMaxZoom
)
181 mxImpl
->mnSliderCenter
= mxImpl
->mnMinZoom
+ static_cast<sal_uInt16
>((mxImpl
->mnMaxZoom
- mxImpl
->mnMinZoom
) * 0.5);
184 DBG_ASSERT( mxImpl
->mnMinZoom
<= mxImpl
->mnCurrentZoom
&&
185 mxImpl
->mnMinZoom
< mxImpl
->mnSliderCenter
&&
186 mxImpl
->mnMaxZoom
>= mxImpl
->mnCurrentZoom
&&
187 mxImpl
->mnMaxZoom
> mxImpl
->mnSliderCenter
,
188 "Looks like the zoom slider item is corrupted" );
190 const css::uno::Sequence
< sal_Int32
> rSnappingPoints
= static_cast<const SvxZoomSliderItem
*>( pState
)->GetSnappingPoints();
191 mxImpl
->maSnappingPointOffsets
.clear();
192 mxImpl
->maSnappingPointZooms
.clear();
194 // get all snapping points:
195 std::set
< sal_uInt16
> aTmpSnappingPoints
;
196 for ( const sal_Int32 nSnappingPoint
: rSnappingPoints
)
198 aTmpSnappingPoints
.insert( static_cast<sal_uInt16
>(nSnappingPoint
) );
201 // remove snapping points that are too close to each other:
202 tools::Long nLastOffset
= 0;
204 for ( const sal_uInt16 nCurrent
: aTmpSnappingPoints
)
206 const tools::Long nCurrentOffset
= Zoom2Offset( nCurrent
);
208 if ( nCurrentOffset
- nLastOffset
>= nSnappingPointsMinDist
)
210 mxImpl
->maSnappingPointOffsets
.push_back( nCurrentOffset
);
211 mxImpl
->maSnappingPointZooms
.push_back( nCurrent
);
212 nLastOffset
= nCurrentOffset
;
220 void SvxZoomSliderControl::Paint( const UserDrawEvent
& rUsrEvt
)
222 if ( !mxImpl
->mbValuesSet
)
225 const tools::Rectangle aControlRect
= getControlRect();
226 vcl::RenderContext
* pDev
= rUsrEvt
.GetRenderContext();
227 tools::Rectangle aRect
= rUsrEvt
.GetRect();
228 tools::Rectangle aSlider
= aRect
;
230 tools::Long nSliderHeight
= 1 * pDev
->GetDPIScaleFactor();
231 tools::Long nSnappingHeight
= 2 * pDev
->GetDPIScaleFactor();
233 aSlider
.AdjustTop((aControlRect
.GetHeight() - nSliderHeight
)/2 );
234 aSlider
.SetBottom( aSlider
.Top() + nSliderHeight
- 1 );
235 aSlider
.AdjustLeft(nSliderXOffset
);
236 aSlider
.AdjustRight( -nSliderXOffset
);
238 pDev
->Push(vcl::PushFlags::LINECOLOR
| vcl::PushFlags::FILLCOLOR
);
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
);
280 bool SvxZoomSliderControl::MouseButtonDown( const MouseEvent
& rEvt
)
282 if ( !mxImpl
->mbValuesSet
)
285 const tools::Rectangle aControlRect
= getControlRect();
286 const Point aPoint
= rEvt
.GetPosPixel();
287 const sal_Int32 nXDiff
= aPoint
.X() - aControlRect
.Left();
289 tools::Long nIncDecWidth
= mxImpl
->maIncreaseButton
.GetSizePixel().Width();
291 const tools::Long nButtonLeftOffset
= (nSliderXOffset
- nIncDecWidth
)/2;
292 const tools::Long nButtonRightOffset
= (nSliderXOffset
+ nIncDecWidth
)/2;
294 const tools::Long nOldZoom
= mxImpl
->mnCurrentZoom
;
297 if ( nXDiff
>= nButtonLeftOffset
&& nXDiff
<= nButtonRightOffset
)
298 mxImpl
->mnCurrentZoom
= basegfx::zoomtools::zoomOut( mxImpl
->mnCurrentZoom
);
300 else if ( nXDiff
>= aControlRect
.GetWidth() - nSliderXOffset
+ nButtonLeftOffset
&&
301 nXDiff
<= aControlRect
.GetWidth() - nSliderXOffset
+ nButtonRightOffset
)
302 mxImpl
->mnCurrentZoom
= basegfx::zoomtools::zoomIn( mxImpl
->mnCurrentZoom
);
304 else if( nXDiff
>= nSliderXOffset
&& nXDiff
<= aControlRect
.GetWidth() - nSliderXOffset
)
306 mxImpl
->mnCurrentZoom
= Offset2Zoom( nXDiff
);
307 mxImpl
->mbDraggingStarted
= true;
310 if ( mxImpl
->mnCurrentZoom
< mxImpl
->mnMinZoom
)
311 mxImpl
->mnCurrentZoom
= mxImpl
->mnMinZoom
;
312 else if ( mxImpl
->mnCurrentZoom
> mxImpl
->mnMaxZoom
)
313 mxImpl
->mnCurrentZoom
= mxImpl
->mnMaxZoom
;
315 if ( nOldZoom
== mxImpl
->mnCurrentZoom
)
323 bool SvxZoomSliderControl::MouseButtonUp( const MouseEvent
& )
325 mxImpl
->mbDraggingStarted
= false;
329 bool SvxZoomSliderControl::MouseMove( const MouseEvent
& rEvt
)
331 if ( !mxImpl
->mbValuesSet
)
334 const short nButtons
= rEvt
.GetButtons();
335 const tools::Rectangle aControlRect
= getControlRect();
336 const Point aPoint
= rEvt
.GetPosPixel();
337 const sal_Int32 nXDiff
= aPoint
.X() - aControlRect
.Left();
339 // check mouse move with button pressed
340 if ( 1 == nButtons
&& mxImpl
->mbDraggingStarted
)
342 if ( nXDiff
>= nSliderXOffset
&& nXDiff
<= aControlRect
.GetWidth() - nSliderXOffset
)
344 mxImpl
->mnCurrentZoom
= Offset2Zoom( nXDiff
);
352 tools::Long nIncDecWidth
= mxImpl
->maIncreaseButton
.GetSizePixel().Width();
354 const tools::Long nButtonLeftOffset
= (nSliderXOffset
- nIncDecWidth
)/2;
355 const tools::Long nButtonRightOffset
= (nSliderXOffset
+ nIncDecWidth
)/2;
358 if ( nXDiff
>= nButtonLeftOffset
&& nXDiff
<= nButtonRightOffset
)
359 GetStatusBar().SetQuickHelpText(GetId(), SvxResId(RID_SVXSTR_ZOOM_OUT
));
361 else if ( nXDiff
>= aControlRect
.GetWidth() - nSliderXOffset
+ nButtonLeftOffset
&&
362 nXDiff
<= aControlRect
.GetWidth() - nSliderXOffset
+ nButtonRightOffset
)
363 GetStatusBar().SetQuickHelpText(GetId(), SvxResId(RID_SVXSTR_ZOOM_IN
));
365 // don't hide the slider and its handle with a tooltip during zooming
366 GetStatusBar().SetQuickHelpText(GetId(), u
""_ustr
);
371 void SvxZoomSliderControl::forceRepaint() const
373 GetStatusBar().SetItemData(GetId(), nullptr);
376 void SvxZoomSliderControl::repaintAndExecute()
380 // commit state change
381 SvxZoomSliderItem
aZoomSliderItem(mxImpl
->mnCurrentZoom
);
384 aZoomSliderItem
.QueryValue(any
);
386 css::uno::Sequence
<css::beans::PropertyValue
> aArgs
{ comphelper::makePropertyValue(u
"ZoomSlider"_ustr
,
391 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */