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