Line data Source code
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 <svx/zoomslideritem.hxx>
27 : #include <svx/dialmgr.hxx>
28 : #include <svx/dialogs.hrc>
29 : #include <basegfx/tools/zoomtools.hxx>
30 :
31 : #include <set>
32 :
33 734 : SFX_IMPL_STATUSBAR_CONTROL( SvxZoomSliderControl, SvxZoomSliderItem );
34 :
35 588 : struct SvxZoomSliderControl::SvxZoomSliderControl_Impl
36 : {
37 : sal_uInt16 mnCurrentZoom;
38 : sal_uInt16 mnMinZoom;
39 : sal_uInt16 mnMaxZoom;
40 : sal_uInt16 mnSliderCenter;
41 : std::vector< long > maSnappingPointOffsets;
42 : std::vector< sal_uInt16 > maSnappingPointZooms;
43 : Image maSliderButton;
44 : Image maIncreaseButton;
45 : Image maDecreaseButton;
46 : bool mbValuesSet;
47 : bool mbOmitPaint;
48 : bool mbDraggingStarted;
49 :
50 588 : SvxZoomSliderControl_Impl() :
51 : mnCurrentZoom( 0 ),
52 : mnMinZoom( 0 ),
53 : mnMaxZoom( 0 ),
54 : mnSliderCenter( 0 ),
55 : maSnappingPointOffsets(),
56 : maSnappingPointZooms(),
57 : maSliderButton(),
58 : maIncreaseButton(),
59 : maDecreaseButton(),
60 : mbValuesSet( false ),
61 : mbOmitPaint( false ),
62 588 : mbDraggingStarted( false ) {}
63 : };
64 :
65 : const long nSliderXOffset = 20;
66 : const long nSnappingEpsilon = 5; // snapping epsilon in pixels
67 : const long nSnappingPointsMinDist = nSnappingEpsilon; // minimum distance of two adjacent snapping points
68 :
69 : // nOffset referes to the origin of the control:
70 : // + ----------- -
71 0 : sal_uInt16 SvxZoomSliderControl::Offset2Zoom( long nOffset ) const
72 : {
73 0 : const long nControlWidth = getControlRect().GetWidth();
74 0 : sal_uInt16 nRet = 0;
75 :
76 0 : if ( nOffset < nSliderXOffset )
77 0 : return mxImpl->mnMinZoom;
78 :
79 0 : if ( nOffset > nControlWidth - nSliderXOffset )
80 0 : return mxImpl->mnMaxZoom;
81 :
82 : // check for snapping points:
83 0 : sal_uInt16 nCount = 0;
84 0 : std::vector< long >::iterator aSnappingPointIter;
85 0 : for ( aSnappingPointIter = mxImpl->maSnappingPointOffsets.begin();
86 0 : aSnappingPointIter != mxImpl->maSnappingPointOffsets.end();
87 : ++aSnappingPointIter )
88 : {
89 0 : const long nCurrent = *aSnappingPointIter;
90 0 : if ( std::abs(nCurrent - nOffset) < nSnappingEpsilon )
91 : {
92 0 : nOffset = nCurrent;
93 0 : nRet = mxImpl->maSnappingPointZooms[ nCount ];
94 0 : break;
95 : }
96 0 : ++nCount;
97 : }
98 :
99 0 : if ( 0 == nRet )
100 : {
101 0 : if ( nOffset < nControlWidth / 2 )
102 : {
103 : // first half of slider
104 0 : const long nFirstHalfRange = mxImpl->mnSliderCenter - mxImpl->mnMinZoom;
105 0 : const long nHalfSliderWidth = nControlWidth/2 - nSliderXOffset;
106 0 : const long nZoomPerSliderPixel = (1000 * nFirstHalfRange) / nHalfSliderWidth;
107 0 : const long nOffsetToSliderLeft = nOffset - nSliderXOffset;
108 0 : nRet = mxImpl->mnMinZoom + sal_uInt16( nOffsetToSliderLeft * nZoomPerSliderPixel / 1000 );
109 : }
110 : else
111 : {
112 : // second half of slider
113 0 : const long nSecondHalfRange = mxImpl->mnMaxZoom - mxImpl->mnSliderCenter;
114 0 : const long nHalfSliderWidth = nControlWidth/2 - nSliderXOffset;
115 0 : const long nZoomPerSliderPixel = 1000 * nSecondHalfRange / nHalfSliderWidth;
116 0 : const long nOffsetToSliderCenter = nOffset - nControlWidth/2;
117 0 : nRet = mxImpl->mnSliderCenter + sal_uInt16( nOffsetToSliderCenter * nZoomPerSliderPixel / 1000 );
118 : }
119 : }
120 :
121 0 : if ( nRet < mxImpl->mnMinZoom )
122 0 : nRet = mxImpl->mnMinZoom;
123 0 : else if ( nRet > mxImpl->mnMaxZoom )
124 0 : nRet = mxImpl->mnMaxZoom;
125 :
126 0 : return nRet;
127 : }
128 :
129 : // returns the offset to the left control border
130 5739 : long SvxZoomSliderControl::Zoom2Offset( sal_uInt16 nCurrentZoom ) const
131 : {
132 5739 : const long nControlWidth = getControlRect().GetWidth();
133 5739 : long nRet = nSliderXOffset;
134 :
135 5739 : const long nHalfSliderWidth = nControlWidth/2 - nSliderXOffset;
136 :
137 5739 : if ( nCurrentZoom <= mxImpl->mnSliderCenter )
138 : {
139 5722 : nCurrentZoom = nCurrentZoom - mxImpl->mnMinZoom;
140 5722 : const long nFirstHalfRange = mxImpl->mnSliderCenter - mxImpl->mnMinZoom;
141 5722 : const long nSliderPixelPerZoomPercent = 1000 * nHalfSliderWidth / nFirstHalfRange;
142 5722 : const long nOffset = (nSliderPixelPerZoomPercent * nCurrentZoom) / 1000;
143 5722 : nRet += nOffset;
144 : }
145 : else
146 : {
147 17 : nCurrentZoom = nCurrentZoom - mxImpl->mnSliderCenter;
148 17 : const long nSecondHalfRange = mxImpl->mnMaxZoom - mxImpl->mnSliderCenter;
149 17 : const long nSliderPixelPerZoomPercent = 1000 * nHalfSliderWidth / nSecondHalfRange;
150 17 : const long nOffset = (nSliderPixelPerZoomPercent * nCurrentZoom) / 1000;
151 17 : nRet += nHalfSliderWidth + nOffset;
152 : }
153 :
154 5739 : return nRet;
155 : }
156 :
157 588 : SvxZoomSliderControl::SvxZoomSliderControl( sal_uInt16 _nSlotId, sal_uInt16 _nId, StatusBar& rStatusBar ) :
158 : SfxStatusBarControl( _nSlotId, _nId, rStatusBar ),
159 588 : mxImpl( new SvxZoomSliderControl_Impl )
160 : {
161 588 : mxImpl->maSliderButton = Image( SVX_RES( RID_SVXBMP_SLIDERBUTTON ) );
162 588 : mxImpl->maIncreaseButton = Image( SVX_RES( RID_SVXBMP_SLIDERINCREASE ) );
163 588 : mxImpl->maDecreaseButton = Image( SVX_RES( RID_SVXBMP_SLIDERDECREASE ) );
164 :
165 : //#ifndef MACOSX
166 588 : sal_Int32 nScaleFactor = rStatusBar.GetDPIScaleFactor();
167 588 : if (nScaleFactor != 1)
168 : {
169 0 : Image arr[3] = {mxImpl->maSliderButton, mxImpl->maIncreaseButton, mxImpl->maDecreaseButton};
170 :
171 0 : for (int i = 0; i < 3; i++)
172 : {
173 0 : BitmapEx aBitmap = arr[i].GetBitmapEx();
174 : //Use Lanczos scaling for the slider button because it does a better job with circles
175 0 : aBitmap.Scale(nScaleFactor, nScaleFactor, i == 0 ? BmpScaleFlag::Lanczos : BmpScaleFlag::Fast);
176 0 : arr[i] = Image(aBitmap);
177 0 : }
178 0 : mxImpl->maSliderButton = arr[0];
179 0 : mxImpl->maIncreaseButton = arr[1];
180 0 : mxImpl->maDecreaseButton = arr[2];
181 : }
182 : //#endif
183 588 : }
184 :
185 1176 : SvxZoomSliderControl::~SvxZoomSliderControl()
186 : {
187 1176 : }
188 :
189 996 : void SvxZoomSliderControl::StateChanged( sal_uInt16 /*nSID*/, SfxItemState eState, const SfxPoolItem* pState )
190 : {
191 996 : if ( (SfxItemState::DEFAULT != eState) || pState->ISA( SfxVoidItem ) )
192 : {
193 0 : GetStatusBar().SetItemText( GetId(), "" );
194 0 : mxImpl->mbValuesSet = false;
195 : }
196 : else
197 : {
198 : OSL_ENSURE( pState->ISA( SvxZoomSliderItem ), "invalid item type: should be a SvxZoomSliderItem" );
199 996 : mxImpl->mnCurrentZoom = static_cast<const SvxZoomSliderItem*>( pState )->GetValue();
200 996 : mxImpl->mnMinZoom = static_cast<const SvxZoomSliderItem*>( pState )->GetMinZoom();
201 996 : mxImpl->mnMaxZoom = static_cast<const SvxZoomSliderItem*>( pState )->GetMaxZoom();
202 996 : mxImpl->mnSliderCenter= 100;
203 996 : mxImpl->mbValuesSet = true;
204 :
205 996 : if ( mxImpl->mnSliderCenter == mxImpl->mnMaxZoom )
206 0 : mxImpl->mnSliderCenter = mxImpl->mnMinZoom + (sal_uInt16)((mxImpl->mnMaxZoom - mxImpl->mnMinZoom) * 0.5);
207 :
208 :
209 : DBG_ASSERT( mxImpl->mnMinZoom <= mxImpl->mnCurrentZoom &&
210 : mxImpl->mnMinZoom < mxImpl->mnSliderCenter &&
211 : mxImpl->mnMaxZoom >= mxImpl->mnCurrentZoom &&
212 : mxImpl->mnMaxZoom > mxImpl->mnSliderCenter,
213 : "Looks like the zoom slider item is corrupted" );
214 :
215 996 : const com::sun::star::uno::Sequence < sal_Int32 > rSnappingPoints = static_cast<const SvxZoomSliderItem*>( pState )->GetSnappingPoints();
216 996 : mxImpl->maSnappingPointOffsets.clear();
217 996 : mxImpl->maSnappingPointZooms.clear();
218 :
219 : // get all snapping points:
220 1992 : std::set< sal_uInt16 > aTmpSnappingPoints;
221 2718 : for ( sal_Int32 j = 0; j < rSnappingPoints.getLength(); ++j )
222 : {
223 1722 : const sal_Int32 nSnappingPoint = rSnappingPoints[j];
224 1722 : aTmpSnappingPoints.insert( (sal_uInt16)nSnappingPoint );
225 : }
226 :
227 : // remove snapping points that are to close to each other:
228 996 : std::set< sal_uInt16 >::iterator aSnappingPointIter;
229 996 : long nLastOffset = 0;
230 :
231 2650 : for ( aSnappingPointIter = aTmpSnappingPoints.begin(); aSnappingPointIter != aTmpSnappingPoints.end(); ++aSnappingPointIter )
232 : {
233 1654 : const sal_uInt16 nCurrent = *aSnappingPointIter;
234 1654 : const long nCurrentOffset = Zoom2Offset( nCurrent );
235 :
236 1654 : if ( nCurrentOffset - nLastOffset >= nSnappingPointsMinDist )
237 : {
238 1290 : mxImpl->maSnappingPointOffsets.push_back( nCurrentOffset );
239 1290 : mxImpl->maSnappingPointZooms.push_back( nCurrent );
240 1290 : nLastOffset = nCurrentOffset;
241 : }
242 996 : }
243 : }
244 :
245 996 : if (!mxImpl->mbOmitPaint)
246 996 : forceRepaint();
247 996 : }
248 :
249 4085 : void SvxZoomSliderControl::Paint( const UserDrawEvent& rUsrEvt )
250 : {
251 4085 : if ( !mxImpl->mbValuesSet || mxImpl->mbOmitPaint )
252 4085 : return;
253 :
254 4085 : const Rectangle aControlRect = getControlRect();
255 4085 : OutputDevice* pDev = rUsrEvt.GetDevice();
256 4085 : Rectangle aRect = rUsrEvt.GetRect();
257 4085 : Rectangle aSlider = aRect;
258 :
259 4085 : long nSliderHeight = 2 * pDev->GetDPIScaleFactor();
260 4085 : long nSnappingHeight = 4 * pDev->GetDPIScaleFactor();
261 :
262 4085 : aSlider.Top() += (aControlRect.GetHeight() - nSliderHeight)/2;
263 4085 : aSlider.Bottom() = aSlider.Top() + nSliderHeight - 1;
264 4085 : aSlider.Left() += nSliderXOffset;
265 4085 : aSlider.Right() -= nSliderXOffset;
266 :
267 4085 : Color aOldLineColor = pDev->GetLineColor();
268 4085 : Color aOldFillColor = pDev->GetFillColor();
269 :
270 4085 : const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
271 4085 : pDev->SetLineColor( rStyleSettings.GetShadowColor() );
272 4085 : pDev->SetFillColor( rStyleSettings.GetShadowColor() );
273 :
274 :
275 : // draw snapping points:
276 4085 : std::vector< long >::iterator aSnappingPointIter;
277 28329 : for ( aSnappingPointIter = mxImpl->maSnappingPointOffsets.begin();
278 18886 : aSnappingPointIter != mxImpl->maSnappingPointOffsets.end();
279 : ++aSnappingPointIter )
280 : {
281 5358 : long nSnapPosX = aRect.Left() + *aSnappingPointIter;
282 :
283 5358 : pDev->DrawRect( Rectangle( nSnapPosX - 1, aSlider.Top() - nSnappingHeight,
284 10716 : nSnapPosX, aSlider.Bottom() + nSnappingHeight ) );
285 : }
286 :
287 : // draw slider
288 4085 : pDev->DrawRect( aSlider );
289 :
290 : // draw slider button
291 4085 : Point aImagePoint = aRect.TopLeft();
292 4085 : aImagePoint.X() += Zoom2Offset( mxImpl->mnCurrentZoom );
293 4085 : aImagePoint.X() -= mxImpl->maSliderButton.GetSizePixel().Width()/2;
294 4085 : aImagePoint.Y() += (aControlRect.GetHeight() - mxImpl->maSliderButton.GetSizePixel().Height())/2;
295 4085 : pDev->DrawImage( aImagePoint, mxImpl->maSliderButton );
296 :
297 : // draw decrease button
298 4085 : aImagePoint = aRect.TopLeft();
299 4085 : aImagePoint.X() += (nSliderXOffset - mxImpl->maDecreaseButton.GetSizePixel().Width())/2;
300 4085 : aImagePoint.Y() += (aControlRect.GetHeight() - mxImpl->maDecreaseButton.GetSizePixel().Height())/2;
301 4085 : pDev->DrawImage( aImagePoint, mxImpl->maDecreaseButton );
302 :
303 : // draw increase button
304 4085 : aImagePoint.X() = aRect.TopLeft().X() + aControlRect.GetWidth() - mxImpl->maIncreaseButton.GetSizePixel().Width() - (nSliderXOffset - mxImpl->maIncreaseButton.GetSizePixel().Height())/2;
305 4085 : pDev->DrawImage( aImagePoint, mxImpl->maIncreaseButton );
306 :
307 4085 : pDev->SetLineColor( aOldLineColor );
308 4085 : pDev->SetFillColor( aOldFillColor );
309 : }
310 :
311 0 : bool SvxZoomSliderControl::MouseButtonDown( const MouseEvent & rEvt )
312 : {
313 0 : if ( !mxImpl->mbValuesSet )
314 0 : return true;
315 :
316 0 : const Rectangle aControlRect = getControlRect();
317 0 : const Point aPoint = rEvt.GetPosPixel();
318 0 : const sal_Int32 nXDiff = aPoint.X() - aControlRect.Left();
319 :
320 0 : long nIncDecWidth = mxImpl->maIncreaseButton.GetSizePixel().Width();
321 :
322 0 : const long nButtonLeftOffset = (nSliderXOffset - nIncDecWidth)/2;
323 0 : const long nButtonRightOffset = (nSliderXOffset + nIncDecWidth)/2;
324 :
325 0 : const long nOldZoom = mxImpl->mnCurrentZoom;
326 :
327 : // click to - button
328 0 : if ( nXDiff >= nButtonLeftOffset && nXDiff <= nButtonRightOffset )
329 0 : mxImpl->mnCurrentZoom = basegfx::zoomtools::zoomOut( static_cast<int>(mxImpl->mnCurrentZoom) );
330 : // click to + button
331 0 : else if ( nXDiff >= aControlRect.GetWidth() - nSliderXOffset + nButtonLeftOffset &&
332 0 : nXDiff <= aControlRect.GetWidth() - nSliderXOffset + nButtonRightOffset )
333 0 : mxImpl->mnCurrentZoom = basegfx::zoomtools::zoomIn( static_cast<int>(mxImpl->mnCurrentZoom) );
334 : // click to slider
335 0 : else if( nXDiff >= nSliderXOffset && nXDiff <= aControlRect.GetWidth() - nSliderXOffset )
336 : {
337 0 : mxImpl->mnCurrentZoom = Offset2Zoom( nXDiff );
338 0 : mxImpl->mbDraggingStarted = true;
339 : }
340 :
341 0 : if ( mxImpl->mnCurrentZoom < mxImpl->mnMinZoom )
342 0 : mxImpl->mnCurrentZoom = mxImpl->mnMinZoom;
343 0 : else if ( mxImpl->mnCurrentZoom > mxImpl->mnMaxZoom )
344 0 : mxImpl->mnCurrentZoom = mxImpl->mnMaxZoom;
345 :
346 0 : if ( nOldZoom == mxImpl->mnCurrentZoom )
347 0 : return true;
348 :
349 0 : repaintAndExecute();
350 :
351 0 : return true;
352 : }
353 :
354 0 : bool SvxZoomSliderControl::MouseButtonUp( const MouseEvent & )
355 : {
356 0 : mxImpl->mbDraggingStarted = false;
357 0 : return true;
358 : }
359 :
360 0 : bool SvxZoomSliderControl::MouseMove( const MouseEvent & rEvt )
361 : {
362 0 : if ( !mxImpl->mbValuesSet )
363 0 : return true;
364 :
365 0 : const short nButtons = rEvt.GetButtons();
366 0 : const Rectangle aControlRect = getControlRect();
367 0 : const Point aPoint = rEvt.GetPosPixel();
368 0 : const sal_Int32 nXDiff = aPoint.X() - aControlRect.Left();
369 :
370 : // check mouse move with button pressed
371 0 : if ( 1 == nButtons && mxImpl->mbDraggingStarted )
372 : {
373 0 : if ( nXDiff >= nSliderXOffset && nXDiff <= aControlRect.GetWidth() - nSliderXOffset )
374 : {
375 0 : mxImpl->mnCurrentZoom = Offset2Zoom( nXDiff );
376 :
377 0 : repaintAndExecute();
378 : }
379 : }
380 :
381 : // Tooltips
382 :
383 0 : long nIncDecWidth = mxImpl->maIncreaseButton.GetSizePixel().Width();
384 :
385 0 : const long nButtonLeftOffset = (nSliderXOffset - nIncDecWidth)/2;
386 0 : const long nButtonRightOffset = (nSliderXOffset + nIncDecWidth)/2;
387 :
388 : // click to - button
389 0 : if ( nXDiff >= nButtonLeftOffset && nXDiff <= nButtonRightOffset )
390 0 : GetStatusBar().SetQuickHelpText(GetId(), SVX_RESSTR(RID_SVXSTR_ZOOM_OUT));
391 : // click to + button
392 0 : else if ( nXDiff >= aControlRect.GetWidth() - nSliderXOffset + nButtonLeftOffset &&
393 0 : nXDiff <= aControlRect.GetWidth() - nSliderXOffset + nButtonRightOffset )
394 0 : GetStatusBar().SetQuickHelpText(GetId(), SVX_RESSTR(RID_SVXSTR_ZOOM_IN));
395 : else
396 0 : GetStatusBar().SetQuickHelpText(GetId(), SVX_RESSTR(RID_SVXSTR_ZOOM));
397 :
398 0 : return true;
399 : }
400 :
401 996 : void SvxZoomSliderControl::forceRepaint() const
402 : {
403 996 : if (GetStatusBar().AreItemsVisible())
404 996 : GetStatusBar().SetItemData(GetId(), 0);
405 996 : }
406 :
407 0 : void SvxZoomSliderControl::repaintAndExecute()
408 : {
409 0 : forceRepaint();
410 :
411 0 : mxImpl->mbOmitPaint = true; // optimization: paint before executing command,
412 : // then omit painting which is triggered by the execute function
413 :
414 : // commit state change
415 0 : SvxZoomSliderItem aZoomSliderItem(mxImpl->mnCurrentZoom);
416 :
417 0 : css::uno::Any any;
418 0 : aZoomSliderItem.QueryValue(any);
419 :
420 0 : css::uno::Sequence<css::beans::PropertyValue> aArgs(1);
421 0 : aArgs[0].Name = "ZoomSlider";
422 0 : aArgs[0].Value = any;
423 :
424 0 : execute(aArgs);
425 :
426 0 : mxImpl->mbOmitPaint = false;
427 0 : }
428 :
429 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|