1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_svx.hxx"
26 
27 // include ---------------------------------------------------------------
28 #include <svx/zoomsliderctrl.hxx>
29 #ifndef _STATUS_HXX //autogen
30 #include <vcl/status.hxx>
31 #endif
32 #ifndef _MENU_HXX //autogen
33 #include <vcl/menu.hxx>
34 #endif
35 #include <vcl/image.hxx>
36 #include <svx/zoomslideritem.hxx>
37 #include <svx/dialmgr.hxx>
38 #include <svx/dialogs.hrc>
39 
40 #include <set>
41 
42 // -----------------------------------------------------------------------
43 
44 SFX_IMPL_STATUSBAR_CONTROL( SvxZoomSliderControl, SvxZoomSliderItem );
45 
46 // -----------------------------------------------------------------------
47 
48 struct SvxZoomSliderControl::SvxZoomSliderControl_Impl
49 {
50     sal_uInt16                   mnCurrentZoom;
51     sal_uInt16                   mnMinZoom;
52     sal_uInt16                   mnMaxZoom;
53     sal_uInt16                   mnSliderCenter;
54     std::vector< long >      maSnappingPointOffsets;
55     std::vector< sal_uInt16 >    maSnappingPointZooms;
56     Image                    maSliderButton;
57     Image                    maIncreaseButton;
58     Image                    maDecreaseButton;
59     bool                     mbValuesSet;
60     bool                     mbOmitPaint;
61 
62     SvxZoomSliderControl_Impl() :
63         mnCurrentZoom( 0 ),
64         mnMinZoom( 0 ),
65         mnMaxZoom( 0 ),
66         mnSliderCenter( 0 ),
67         maSnappingPointOffsets(),
68         maSnappingPointZooms(),
69         maSliderButton(),
70         maIncreaseButton(),
71         maDecreaseButton(),
72         mbValuesSet( false ),
73         mbOmitPaint( false ) {}
74 };
75 
76 // -----------------------------------------------------------------------
77 
78 const long nButtonWidth   = 10;
79 const long nButtonHeight  = 10;
80 const long nIncDecWidth   = 11;
81 const long nIncDecHeight  = 11;
82 const long nSliderHeight  = 2;
83 const long nSnappingHeight = 4;
84 const long nSliderXOffset = 20;
85 const long nSnappingEpsilon = 5; // snapping epsilon in pixels
86 const long nSnappingPointsMinDist = nSnappingEpsilon; // minimum distance of two adjacent snapping points
87 
88 // -----------------------------------------------------------------------
89 
90 // nOffset referes to the origin of the control:
91 // + ----------- -
92 sal_uInt16 SvxZoomSliderControl::Offset2Zoom( long nOffset ) const
93 {
94     const long nControlWidth = getControlRect().GetWidth();
95     sal_uInt16 nRet = 0;
96 
97     if ( nOffset < nSliderXOffset )
98         return mpImpl->mnMinZoom;;
99 
100     if ( nOffset > nControlWidth - nSliderXOffset )
101         return mpImpl->mnMaxZoom;
102 
103     // check for snapping points:
104     sal_uInt16 nCount = 0;
105     std::vector< long >::iterator aSnappingPointIter;
106     for ( aSnappingPointIter = mpImpl->maSnappingPointOffsets.begin();
107           aSnappingPointIter != mpImpl->maSnappingPointOffsets.end();
108           ++aSnappingPointIter )
109     {
110         const long nCurrent = *aSnappingPointIter;
111         if ( Abs(nCurrent - nOffset) < nSnappingEpsilon )
112         {
113             nOffset = nCurrent;
114             nRet = mpImpl->maSnappingPointZooms[ nCount ];
115             break;
116         }
117         ++nCount;
118     }
119 
120     if ( 0 == nRet )
121     {
122         if ( nOffset < nControlWidth / 2 )
123         {
124             // first half of slider
125             const long nFirstHalfRange = mpImpl->mnSliderCenter - mpImpl->mnMinZoom;
126             const long nHalfSliderWidth = nControlWidth/2 - nSliderXOffset;
127             const long nZoomPerSliderPixel = (1000 * nFirstHalfRange) / nHalfSliderWidth;
128             const long nOffsetToSliderLeft = nOffset - nSliderXOffset;
129             nRet = mpImpl->mnMinZoom + sal_uInt16( nOffsetToSliderLeft * nZoomPerSliderPixel / 1000 );
130         }
131         else
132         {
133             // second half of slider
134             const long nSecondHalfRange = mpImpl->mnMaxZoom - mpImpl->mnSliderCenter;
135             const long nHalfSliderWidth = nControlWidth/2 - nSliderXOffset;
136             const long nZoomPerSliderPixel = 1000 * nSecondHalfRange / nHalfSliderWidth;
137             const long nOffsetToSliderCenter = nOffset - nControlWidth/2;
138             nRet = mpImpl->mnSliderCenter + sal_uInt16( nOffsetToSliderCenter * nZoomPerSliderPixel / 1000 );
139         }
140     }
141 
142     if ( nRet < mpImpl->mnMinZoom )
143         nRet = mpImpl->mnMinZoom;
144     else if ( nRet > mpImpl->mnMaxZoom )
145         nRet = mpImpl->mnMaxZoom;
146 
147     return nRet;
148 }
149 
150 // returns the offset to the left control border
151 long SvxZoomSliderControl::Zoom2Offset( sal_uInt16 nCurrentZoom ) const
152 {
153     const long nControlWidth = getControlRect().GetWidth();
154     long nRet = nSliderXOffset;
155 
156     const long nHalfSliderWidth = nControlWidth/2 - nSliderXOffset;
157 
158     if ( nCurrentZoom <= mpImpl->mnSliderCenter )
159     {
160         nCurrentZoom = nCurrentZoom - mpImpl->mnMinZoom;
161         const long nFirstHalfRange = mpImpl->mnSliderCenter - mpImpl->mnMinZoom;
162         const long nSliderPixelPerZoomPercent = 1000 * nHalfSliderWidth  / nFirstHalfRange;
163         const long nOffset = (nSliderPixelPerZoomPercent * nCurrentZoom) / 1000;
164         nRet += nOffset;
165     }
166     else
167     {
168         nCurrentZoom = nCurrentZoom - mpImpl->mnSliderCenter;
169         const long nSecondHalfRange = mpImpl->mnMaxZoom - mpImpl->mnSliderCenter;
170         const long nSliderPixelPerZoomPercent = 1000 * nHalfSliderWidth  / nSecondHalfRange;
171         const long nOffset = (nSliderPixelPerZoomPercent * nCurrentZoom) / 1000;
172         nRet += nHalfSliderWidth + nOffset;
173     }
174 
175     return nRet;
176 }
177 
178 // -----------------------------------------------------------------------
179 
180 SvxZoomSliderControl::SvxZoomSliderControl( sal_uInt16 _nSlotId,  sal_uInt16 _nId, StatusBar& _rStb ) :
181     SfxStatusBarControl( _nSlotId, _nId, _rStb ),
182     mpImpl( new SvxZoomSliderControl_Impl )
183 {
184     const sal_Bool bHC = GetStatusBar().GetSettings().GetStyleSettings().GetHighContrastMode();
185     mpImpl->maSliderButton   = Image( SVX_RES( bHC ? RID_SVXBMP_SLIDERBUTTON_HC : RID_SVXBMP_SLIDERBUTTON ) );
186     mpImpl->maIncreaseButton = Image( SVX_RES( bHC ? RID_SVXBMP_SLIDERINCREASE_HC : RID_SVXBMP_SLIDERINCREASE ) );
187     mpImpl->maDecreaseButton = Image( SVX_RES( bHC ? RID_SVXBMP_SLIDERDECREASE_HC : RID_SVXBMP_SLIDERDECREASE ) );
188 }
189 
190 // -----------------------------------------------------------------------
191 
192 SvxZoomSliderControl::~SvxZoomSliderControl()
193 {
194 	delete mpImpl;
195 }
196 
197 // -----------------------------------------------------------------------
198 
199 void SvxZoomSliderControl::StateChanged( sal_uInt16 /*nSID*/, SfxItemState eState, const SfxPoolItem* pState )
200 {
201     if ( (SFX_ITEM_AVAILABLE != eState) || pState->ISA( SfxVoidItem ) )
202     {
203         GetStatusBar().SetItemText( GetId(), String() );
204         mpImpl->mbValuesSet   = false;
205     }
206     else
207     {
208         OSL_ENSURE( pState->ISA( SvxZoomSliderItem ), "invalid item type: should be a SvxZoomSliderItem" );
209         mpImpl->mnCurrentZoom = static_cast<const SvxZoomSliderItem*>( pState )->GetValue();
210         mpImpl->mnMinZoom     = static_cast<const SvxZoomSliderItem*>( pState )->GetMinZoom();
211         mpImpl->mnMaxZoom     = static_cast<const SvxZoomSliderItem*>( pState )->GetMaxZoom();
212         mpImpl->mnSliderCenter= 100;
213         mpImpl->mbValuesSet   = true;
214 
215         if ( mpImpl->mnSliderCenter == mpImpl->mnMaxZoom )
216             mpImpl->mnSliderCenter = mpImpl->mnMinZoom + (sal_uInt16)((mpImpl->mnMaxZoom - mpImpl->mnMinZoom) * 0.5);
217 
218 
219         DBG_ASSERT( mpImpl->mnMinZoom <= mpImpl->mnCurrentZoom &&
220                     mpImpl->mnMinZoom <  mpImpl->mnSliderCenter &&
221                     mpImpl->mnMaxZoom >= mpImpl->mnCurrentZoom &&
222                     mpImpl->mnMaxZoom > mpImpl->mnSliderCenter,
223                     "Looks like the zoom slider item is corrupted" );
224 
225         const com::sun::star::uno::Sequence < sal_Int32 > rSnappingPoints = static_cast<const SvxZoomSliderItem*>( pState )->GetSnappingPoints();
226         mpImpl->maSnappingPointOffsets.clear();
227         mpImpl->maSnappingPointZooms.clear();
228 
229         // get all snapping points:
230         std::set< sal_uInt16 > aTmpSnappingPoints;
231         for ( sal_uInt16 j = 0; j < rSnappingPoints.getLength(); ++j )
232         {
233             const sal_Int32 nSnappingPoint = rSnappingPoints[j];
234             aTmpSnappingPoints.insert( (sal_uInt16)nSnappingPoint );
235         }
236 
237         // remove snapping points that are to close to each other:
238         std::set< sal_uInt16 >::iterator aSnappingPointIter;
239         long nLastOffset = 0;
240 
241         for ( aSnappingPointIter = aTmpSnappingPoints.begin(); aSnappingPointIter != aTmpSnappingPoints.end(); ++aSnappingPointIter )
242         {
243             const sal_uInt16 nCurrent = *aSnappingPointIter;
244             const long nCurrentOffset = Zoom2Offset( nCurrent );
245 
246             if ( nCurrentOffset - nLastOffset >= nSnappingPointsMinDist )
247             {
248                 mpImpl->maSnappingPointOffsets.push_back( nCurrentOffset );
249                 mpImpl->maSnappingPointZooms.push_back( nCurrent );
250                 nLastOffset = nCurrentOffset;
251             }
252         }
253     }
254 
255     if ( !mpImpl->mbOmitPaint && GetStatusBar().AreItemsVisible() )
256         GetStatusBar().SetItemData( GetId(), 0 );    // force repaint
257 }
258 
259 // -----------------------------------------------------------------------
260 
261 void SvxZoomSliderControl::Paint( const UserDrawEvent& rUsrEvt )
262 {
263     if ( !mpImpl->mbValuesSet || mpImpl->mbOmitPaint )
264         return;
265 
266     const Rectangle     aControlRect = getControlRect();
267     OutputDevice*       pDev =  rUsrEvt.GetDevice();
268     Rectangle           aRect = rUsrEvt.GetRect();
269     Rectangle           aSlider = aRect;
270 
271     aSlider.Top()   += (aControlRect.GetHeight() - nSliderHeight)/2 - 1;
272     aSlider.Bottom() = aSlider.Top() + nSliderHeight;
273     aSlider.Left()  += nSliderXOffset;
274     aSlider.Right() -= nSliderXOffset;
275 
276     Color               aOldLineColor = pDev->GetLineColor();
277     Color               aOldFillColor = pDev->GetFillColor();
278 
279     pDev->SetLineColor( Color( COL_GRAY ) );
280     pDev->SetFillColor( Color( COL_GRAY ) );
281 
282     // draw snapping points:
283     std::vector< long >::iterator aSnappingPointIter;
284     for ( aSnappingPointIter = mpImpl->maSnappingPointOffsets.begin();
285           aSnappingPointIter != mpImpl->maSnappingPointOffsets.end();
286           ++aSnappingPointIter )
287     {
288         Rectangle aSnapping( aRect );
289         aSnapping.Bottom()   = aSlider.Top();
290         aSnapping.Top() = aSnapping.Bottom() - nSnappingHeight;
291         aSnapping.Left() += *aSnappingPointIter;
292         aSnapping.Right() = aSnapping.Left();
293         pDev->DrawRect( aSnapping );
294 
295         aSnapping.Top() += nSnappingHeight + nSliderHeight;
296         aSnapping.Bottom() += nSnappingHeight + nSliderHeight;
297         pDev->DrawRect( aSnapping );
298     }
299 
300     // draw slider
301     Rectangle aFirstLine( aSlider );
302     aFirstLine.Bottom() = aFirstLine.Top();
303 
304     Rectangle aSecondLine( aSlider );
305     aSecondLine.Top() = aSecondLine.Bottom();
306 
307     Rectangle aLeft( aSlider );
308     aLeft.Right() = aLeft.Left();
309 
310     Rectangle aRight( aSlider );
311     aRight.Left() = aRight.Right();
312 
313     pDev->SetLineColor( Color ( COL_WHITE ) );
314     pDev->SetFillColor( Color ( COL_WHITE ) );
315     pDev->DrawRect( aSecondLine );
316     pDev->DrawRect( aRight );
317 
318     pDev->SetLineColor( Color( COL_GRAY ) );
319     pDev->SetFillColor( Color( COL_GRAY ) );
320     pDev->DrawRect( aFirstLine );
321     pDev->DrawRect( aLeft );
322 
323     // draw slider button
324     Point aImagePoint = aRect.TopLeft();
325     aImagePoint.X() += Zoom2Offset( mpImpl->mnCurrentZoom );
326     aImagePoint.X() -= nButtonWidth/2;
327     aImagePoint.Y() += (aControlRect.GetHeight() - nButtonHeight)/2;
328     pDev->DrawImage( aImagePoint, mpImpl->maSliderButton );
329 
330     // draw decrease button
331     aImagePoint = aRect.TopLeft();
332     aImagePoint.X() += (nSliderXOffset - nIncDecWidth)/2;
333     aImagePoint.Y() += (aControlRect.GetHeight() - nIncDecHeight)/2;
334     pDev->DrawImage( aImagePoint, mpImpl->maDecreaseButton );
335 
336     // draw increase button
337     aImagePoint.X() = aRect.TopLeft().X() + aControlRect.GetWidth() - nIncDecWidth - (nSliderXOffset - nIncDecWidth)/2;
338     pDev->DrawImage( aImagePoint, mpImpl->maIncreaseButton );
339 
340     pDev->SetLineColor( aOldLineColor );
341     pDev->SetFillColor( aOldFillColor );
342 }
343 
344 // -----------------------------------------------------------------------
345 
346 sal_Bool SvxZoomSliderControl::MouseButtonDown( const MouseEvent & rEvt )
347 {
348     if ( !mpImpl->mbValuesSet )
349         return sal_True;;
350 
351     const Rectangle aControlRect = getControlRect();
352     const Point aPoint = rEvt.GetPosPixel();
353     const sal_Int32 nXDiff = aPoint.X() - aControlRect.Left();
354 
355     const long nButtonLeftOffset = (nSliderXOffset - nIncDecWidth)/2;
356     const long nButtonRightOffset = (nSliderXOffset + nIncDecWidth)/2;
357 
358     const long nOldZoom = mpImpl->mnCurrentZoom;
359 
360     // click to - button
361     if ( nXDiff >= nButtonLeftOffset && nXDiff <= nButtonRightOffset )
362         mpImpl->mnCurrentZoom = mpImpl->mnCurrentZoom - 5;
363     // click to + button
364     else if ( nXDiff >= aControlRect.GetWidth() - nSliderXOffset + nButtonLeftOffset &&
365               nXDiff <= aControlRect.GetWidth() - nSliderXOffset + nButtonRightOffset )
366         mpImpl->mnCurrentZoom = mpImpl->mnCurrentZoom + 5;
367     // click to slider
368     else if( nXDiff >= nSliderXOffset && nXDiff <= aControlRect.GetWidth() - nSliderXOffset )
369         mpImpl->mnCurrentZoom = Offset2Zoom( nXDiff );
370 
371     if ( mpImpl->mnCurrentZoom < mpImpl->mnMinZoom )
372         mpImpl->mnCurrentZoom = mpImpl->mnMinZoom;
373     else if ( mpImpl->mnCurrentZoom > mpImpl->mnMaxZoom )
374         mpImpl->mnCurrentZoom = mpImpl->mnMaxZoom;
375 
376     if ( nOldZoom == mpImpl->mnCurrentZoom )
377         return sal_True;
378 
379     if ( GetStatusBar().AreItemsVisible() )
380         GetStatusBar().SetItemData( GetId(), 0 );    // force repaint
381 
382     mpImpl->mbOmitPaint = true; // optimization: paint before executing command,
383                                 // then omit painting which is triggered by the execute function
384 
385     SvxZoomSliderItem aZoomSliderItem( mpImpl->mnCurrentZoom );
386 
387     ::com::sun::star::uno::Any a;
388     aZoomSliderItem.QueryValue( a );
389 
390     ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue > aArgs( 1 );
391     aArgs[0].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ZoomSlider" ));
392     aArgs[0].Value = a;
393 
394     execute( aArgs );
395 
396     mpImpl->mbOmitPaint = false;
397 
398     return sal_True;
399 }
400 
401 // -----------------------------------------------------------------------
402 
403 sal_Bool SvxZoomSliderControl::MouseMove( const MouseEvent & rEvt )
404 {
405     if ( !mpImpl->mbValuesSet )
406         return sal_True;;
407 
408     const short nButtons = rEvt.GetButtons();
409 
410     // check mouse move with button pressed
411     if ( 1 == nButtons )
412     {
413         const Rectangle aControlRect = getControlRect();
414         const Point aPoint = rEvt.GetPosPixel();
415         const sal_Int32 nXDiff = aPoint.X() - aControlRect.Left();
416 
417         if ( nXDiff >= nSliderXOffset && nXDiff <= aControlRect.GetWidth() - nSliderXOffset )
418         {
419             mpImpl->mnCurrentZoom = Offset2Zoom( nXDiff );
420 
421             if ( GetStatusBar().AreItemsVisible() )
422                 GetStatusBar().SetItemData( GetId(), 0 );    // force repaint
423 
424             mpImpl->mbOmitPaint = true; // optimization: paint before executing command,
425                                         // then omit painting which is triggered by the execute function
426 
427             // commit state change
428             SvxZoomSliderItem aZoomSliderItem( mpImpl->mnCurrentZoom );
429 
430             ::com::sun::star::uno::Any a;
431             aZoomSliderItem.QueryValue( a );
432 
433             ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue > aArgs( 1 );
434             aArgs[0].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ZoomSlider" ));
435             aArgs[0].Value = a;
436 
437             execute( aArgs );
438 
439             mpImpl->mbOmitPaint = false;
440         }
441     }
442 
443     return sal_True;
444 }
445