Bump version to 6.4.7.2.M8
[LibreOffice.git] / svx / source / stbctrls / zoomsliderctrl.cxx
blob7cf9ff0c86dcab97917a0207b5a69053434ea27f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
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>
30 #include <basegfx/utils/zoomtools.hxx>
31 #include <bitmaps.hlst>
32 #include <com/sun/star/beans/PropertyValue.hpp>
34 #include <set>
36 SFX_IMPL_STATUSBAR_CONTROL( SvxZoomSliderControl, SvxZoomSliderItem );
38 struct SvxZoomSliderControl::SvxZoomSliderControl_Impl
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;
46 Image maSliderButton;
47 Image maIncreaseButton;
48 Image maDecreaseButton;
49 bool mbValuesSet;
50 bool mbDraggingStarted;
52 SvxZoomSliderControl_Impl() :
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 ) {}
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
70 // nOffset refers to the origin of the control:
71 // + ----------- -
72 sal_uInt16 SvxZoomSliderControl::Offset2Zoom( long nOffset ) const
74 const long nControlWidth = getControlRect().GetWidth();
75 sal_uInt16 nRet = 0;
77 if ( nOffset < nSliderXOffset )
78 return mxImpl->mnMinZoom;
80 if ( nOffset > nControlWidth - nSliderXOffset )
81 return mxImpl->mnMaxZoom;
83 // check for snapping points:
84 sal_uInt16 nCount = 0;
85 for ( const long nCurrent : mxImpl->maSnappingPointOffsets )
87 if ( std::abs(nCurrent - nOffset) < nSnappingEpsilon )
89 nOffset = nCurrent;
90 nRet = mxImpl->maSnappingPointZooms[ nCount ];
91 break;
93 ++nCount;
96 if ( 0 == nRet )
98 if ( nOffset < nControlWidth / 2 )
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 );
107 else
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 );
118 if ( nRet < mxImpl->mnMinZoom )
119 nRet = mxImpl->mnMinZoom;
120 else if ( nRet > mxImpl->mnMaxZoom )
121 nRet = mxImpl->mnMaxZoom;
123 return nRet;
126 // returns the offset to the left control border
127 long SvxZoomSliderControl::Zoom2Offset( sal_uInt16 nCurrentZoom ) const
129 const long nControlWidth = getControlRect().GetWidth();
130 long nRet = nSliderXOffset;
132 const long nHalfSliderWidth = nControlWidth/2 - nSliderXOffset;
134 if ( nCurrentZoom <= mxImpl->mnSliderCenter )
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;
142 else
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;
151 return nRet;
154 SvxZoomSliderControl::SvxZoomSliderControl( sal_uInt16 _nSlotId, sal_uInt16 _nId, StatusBar& rStatusBar ) :
155 SfxStatusBarControl( _nSlotId, _nId, rStatusBar ),
156 mxImpl( new SvxZoomSliderControl_Impl )
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);
163 SvxZoomSliderControl::~SvxZoomSliderControl()
167 void SvxZoomSliderControl::StateChanged( sal_uInt16 /*nSID*/, SfxItemState eState, const SfxPoolItem* pState )
169 if ( (SfxItemState::DEFAULT != eState) || pState->IsVoidItem() )
171 GetStatusBar().SetItemText( GetId(), "" );
172 mxImpl->mbValuesSet = false;
174 else
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;
183 if ( mxImpl->mnSliderCenter == mxImpl->mnMaxZoom )
184 mxImpl->mnSliderCenter = mxImpl->mnMinZoom + static_cast<sal_uInt16>((mxImpl->mnMaxZoom - mxImpl->mnMinZoom) * 0.5);
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" );
193 const css::uno::Sequence < sal_Int32 > rSnappingPoints = static_cast<const SvxZoomSliderItem*>( pState )->GetSnappingPoints();
194 mxImpl->maSnappingPointOffsets.clear();
195 mxImpl->maSnappingPointZooms.clear();
197 // get all snapping points:
198 std::set< sal_uInt16 > aTmpSnappingPoints;
199 for ( const sal_Int32 nSnappingPoint : rSnappingPoints )
201 aTmpSnappingPoints.insert( static_cast<sal_uInt16>(nSnappingPoint) );
204 // remove snapping points that are too close to each other:
205 long nLastOffset = 0;
207 for ( const sal_uInt16 nCurrent : aTmpSnappingPoints )
209 const long nCurrentOffset = Zoom2Offset( nCurrent );
211 if ( nCurrentOffset - nLastOffset >= nSnappingPointsMinDist )
213 mxImpl->maSnappingPointOffsets.push_back( nCurrentOffset );
214 mxImpl->maSnappingPointZooms.push_back( nCurrent );
215 nLastOffset = nCurrentOffset;
220 forceRepaint();
223 void SvxZoomSliderControl::Paint( const UserDrawEvent& rUsrEvt )
225 if ( !mxImpl->mbValuesSet )
226 return;
228 const tools::Rectangle aControlRect = getControlRect();
229 vcl::RenderContext* pDev = rUsrEvt.GetRenderContext();
230 tools::Rectangle aRect = rUsrEvt.GetRect();
231 tools::Rectangle aSlider = aRect;
233 long nSliderHeight = 1 * pDev->GetDPIScaleFactor();
234 long nSnappingHeight = 2 * pDev->GetDPIScaleFactor();
236 aSlider.AdjustTop((aControlRect.GetHeight() - nSliderHeight)/2 );
237 aSlider.SetBottom( aSlider.Top() + nSliderHeight - 1 );
238 aSlider.AdjustLeft(nSliderXOffset );
239 aSlider.AdjustRight( -nSliderXOffset );
241 Color aOldLineColor = pDev->GetLineColor();
242 Color aOldFillColor = pDev->GetFillColor();
244 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
245 pDev->SetLineColor( rStyleSettings.GetDarkShadowColor() );
246 pDev->SetFillColor( rStyleSettings.GetDarkShadowColor() );
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() );
255 // draw snapping points:
256 for ( const auto& rSnappingPoint : mxImpl->maSnappingPointOffsets )
258 long nSnapPosX = aRect.Left() + rSnappingPoint;
260 pDev->DrawRect( tools::Rectangle( nSnapPosX - 1, aSlider.Top() - nSnappingHeight,
261 nSnapPosX, aSlider.Bottom() + nSnappingHeight ) );
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 );
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 );
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 );
281 pDev->SetLineColor( aOldLineColor );
282 pDev->SetFillColor( aOldFillColor );
285 bool SvxZoomSliderControl::MouseButtonDown( const MouseEvent & rEvt )
287 if ( !mxImpl->mbValuesSet )
288 return true;
290 const tools::Rectangle aControlRect = getControlRect();
291 const Point aPoint = rEvt.GetPosPixel();
292 const sal_Int32 nXDiff = aPoint.X() - aControlRect.Left();
294 long nIncDecWidth = mxImpl->maIncreaseButton.GetSizePixel().Width();
296 const long nButtonLeftOffset = (nSliderXOffset - nIncDecWidth)/2;
297 const long nButtonRightOffset = (nSliderXOffset + nIncDecWidth)/2;
299 const long nOldZoom = mxImpl->mnCurrentZoom;
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 )
311 mxImpl->mnCurrentZoom = Offset2Zoom( nXDiff );
312 mxImpl->mbDraggingStarted = true;
315 if ( mxImpl->mnCurrentZoom < mxImpl->mnMinZoom )
316 mxImpl->mnCurrentZoom = mxImpl->mnMinZoom;
317 else if ( mxImpl->mnCurrentZoom > mxImpl->mnMaxZoom )
318 mxImpl->mnCurrentZoom = mxImpl->mnMaxZoom;
320 if ( nOldZoom == mxImpl->mnCurrentZoom )
321 return true;
323 repaintAndExecute();
325 return true;
328 bool SvxZoomSliderControl::MouseButtonUp( const MouseEvent & )
330 mxImpl->mbDraggingStarted = false;
331 return true;
334 bool SvxZoomSliderControl::MouseMove( const MouseEvent & rEvt )
336 if ( !mxImpl->mbValuesSet )
337 return true;
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();
344 // check mouse move with button pressed
345 if ( 1 == nButtons && mxImpl->mbDraggingStarted )
347 if ( nXDiff >= nSliderXOffset && nXDiff <= aControlRect.GetWidth() - nSliderXOffset )
349 mxImpl->mnCurrentZoom = Offset2Zoom( nXDiff );
351 repaintAndExecute();
355 // Tooltips
357 long nIncDecWidth = mxImpl->maIncreaseButton.GetSizePixel().Width();
359 const long nButtonLeftOffset = (nSliderXOffset - nIncDecWidth)/2;
360 const long nButtonRightOffset = (nSliderXOffset + nIncDecWidth)/2;
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
371 GetStatusBar().SetQuickHelpText(GetId(), "");
373 return true;
376 void SvxZoomSliderControl::forceRepaint() const
378 GetStatusBar().SetItemData(GetId(), nullptr);
381 void SvxZoomSliderControl::repaintAndExecute()
383 forceRepaint();
385 // commit state change
386 SvxZoomSliderItem aZoomSliderItem(mxImpl->mnCurrentZoom);
388 css::uno::Any any;
389 aZoomSliderItem.QueryValue(any);
391 css::uno::Sequence<css::beans::PropertyValue> aArgs(1);
392 aArgs[0].Name = "ZoomSlider";
393 aArgs[0].Value = any;
395 execute(aArgs);
398 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */