1 /*************************************************************************
2 *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26 ************************************************************************/
27 #include "precompiled_sc.hxx"
28 #ifndef _SC_ZOOMSLIDERTBCONTRL_HXX
29 #include "tbzoomsliderctrl.hxx"
30 #endif
31 #ifndef _SV_IMAGE_HXX
32 #include <vcl/image.hxx>
33 #endif
34 #ifndef _SV_TOOLBOX_HXX
35 #include <vcl/toolbox.hxx>
36 #endif
37 #ifndef _SV_SVAPP_HXX
38 #include <vcl/svapp.hxx>
39 #endif
40 #ifndef _SV_GRADIENT_HXX
41 #include <vcl/gradient.hxx>
42 #endif
43 #include <svl/itemset.hxx>
44 #include <sfx2/viewfrm.hxx>
45 #include <sfx2/objsh.hxx>
46 #include <svx/zoomslideritem.hxx>
47 #include <svx/dialmgr.hxx>
48 #include <svx/dialogs.hrc>
49 #include <set>
50 #include "docsh.hxx"
51 #include "stlpool.hxx"
52 #include "scitems.hxx"
53 #include "printfun.hxx"
54 
55 //========================================================================
56 // class ScZoomSliderControl ---------------------------------------
57 //========================================================================
58 
59 // -----------------------------------------------------------------------
60 
61 SFX_IMPL_TOOLBOX_CONTROL( ScZoomSliderControl, SvxZoomSliderItem );
62 
63 // -----------------------------------------------------------------------
64 
65 ScZoomSliderControl::ScZoomSliderControl(
66     sal_uInt16     nSlotId,
67     sal_uInt16	   nId,
68     ToolBox&   rTbx )
69     :SfxToolBoxControl( nSlotId, nId, rTbx )
70 {
71     rTbx.Invalidate();
72 }
73 
74 // -----------------------------------------------------------------------
75 
76 __EXPORT ScZoomSliderControl::~ScZoomSliderControl()
77 {
78 
79 }
80 
81 // -----------------------------------------------------------------------
82 
83 void ScZoomSliderControl::StateChanged( sal_uInt16 /*nSID*/, SfxItemState eState,
84                                        const SfxPoolItem* pState )
85 {
86     sal_uInt16                  nId	 = GetId();
87     ToolBox&                rTbx = GetToolBox();
88     ScZoomSliderWnd*        pBox = (ScZoomSliderWnd*)(rTbx.GetItemWindow( nId ));
89     DBG_ASSERT( pBox ,"Control not found!" );
90 
91     if ( SFX_ITEM_AVAILABLE != eState || pState->ISA( SfxVoidItem ) )
92     {
93         SvxZoomSliderItem aZoomSliderItem( 100 );
94         pBox->Disable();
95         pBox->UpdateFromItem( &aZoomSliderItem );
96     }
97     else
98     {
99         pBox->Enable();
100         DBG_ASSERT( pState->ISA( SvxZoomSliderItem ), "invalid item type" );
101         const SvxZoomSliderItem* pZoomSliderItem = dynamic_cast< const SvxZoomSliderItem* >( pState );
102 
103         DBG_ASSERT( pZoomSliderItem, "Sc::ScZoomSliderControl::StateChanged(), wrong item type!" );
104         if( pZoomSliderItem )
105             pBox->UpdateFromItem( pZoomSliderItem );
106     }
107 }
108 
109 // -----------------------------------------------------------------------
110 
111 Window* ScZoomSliderControl::CreateItemWindow( Window *pParent )
112 {
113     // #i98000# Don't try to get a value via SfxViewFrame::Current here.
114     // The view's value is always notified via StateChanged later.
115     ScZoomSliderWnd* pSlider    = new ScZoomSliderWnd( pParent,
116         ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatchProvider >( m_xFrame->getController(),
117         ::com::sun::star::uno::UNO_QUERY ), m_xFrame, 100 );
118     return  pSlider;
119 }
120 
121 // -----------------------------------------------------------------------
122 
123 struct ScZoomSliderWnd::ScZoomSliderWnd_Impl
124 {
125     sal_uInt16                   mnCurrentZoom;
126     sal_uInt16                   mnMinZoom;
127     sal_uInt16                   mnMaxZoom;
128     sal_uInt16                   mnSliderCenter;
129     std::vector< long >      maSnappingPointOffsets;
130     std::vector< sal_uInt16 >    maSnappingPointZooms;
131     Image                    maSliderButton;
132     Image                    maIncreaseButton;
133     Image                    maDecreaseButton;
134     bool                     mbValuesSet;
135     bool                     mbOmitPaint;
136 
137     ScZoomSliderWnd_Impl( sal_uInt16 nCurrentZoom ) :
138         mnCurrentZoom( nCurrentZoom ),
139         mnMinZoom( 10 ),
140         mnMaxZoom( 400 ),
141         mnSliderCenter( 100 ),
142         maSnappingPointOffsets(),
143         maSnappingPointZooms(),
144         maSliderButton(),
145         maIncreaseButton(),
146         maDecreaseButton(),
147         mbValuesSet( true ),
148         mbOmitPaint( false )
149         {
150 
151         }
152 };
153 
154 // -----------------------------------------------------------------------
155 
156 const long nButtonWidth     = 10;
157 const long nButtonHeight    = 10;
158 const long nIncDecWidth     = 11;
159 const long nIncDecHeight    = 11;
160 const long nSliderHeight    = 2;      //
161 const long nSliderWidth     = 4;      //
162 const long nSnappingHeight  = 4;
163 const long nSliderXOffset   = 20;
164 const long nSnappingEpsilon = 5; // snapping epsilon in pixels
165 const long nSnappingPointsMinDist = nSnappingEpsilon; // minimum distance of two adjacent snapping points
166 
167 
168 // -----------------------------------------------------------------------
169 
170 sal_uInt16 ScZoomSliderWnd::Offset2Zoom( long nOffset ) const
171 {
172     Size aSliderWindowSize = GetOutputSizePixel();
173     const long nControlWidth = aSliderWindowSize.Width();
174     sal_uInt16 nRet = 0;
175 
176     if( nOffset < nSliderXOffset )
177         return mpImpl->mnMinZoom;
178     if( nOffset > nControlWidth - nSliderXOffset )
179         return mpImpl->mnMaxZoom;
180 
181     // check for snapping points:
182     sal_uInt16 nCount = 0;
183     std::vector< long >::iterator aSnappingPointIter;
184     for ( aSnappingPointIter = mpImpl->maSnappingPointOffsets.begin();
185         aSnappingPointIter != mpImpl->maSnappingPointOffsets.end();
186         ++aSnappingPointIter )
187     {
188         const long nCurrent = *aSnappingPointIter;
189         if ( Abs(nCurrent - nOffset) < nSnappingEpsilon )
190         {
191             nOffset = nCurrent;
192             nRet = mpImpl->maSnappingPointZooms[ nCount ];
193             break;
194         }
195         ++nCount;
196     }
197 
198     if( 0 == nRet )
199     {
200         if( nOffset < nControlWidth / 2 )
201         {
202             // first half of slider
203             const long nFirstHalfRange      = mpImpl->mnSliderCenter - mpImpl->mnMinZoom;
204             const long nHalfSliderWidth     = nControlWidth/2 - nSliderXOffset;
205             const long nZoomPerSliderPixel  = (1000 * nFirstHalfRange) / nHalfSliderWidth;
206             const long nOffsetToSliderLeft  = nOffset - nSliderXOffset;
207             nRet = mpImpl->mnMinZoom + sal_uInt16( nOffsetToSliderLeft * nZoomPerSliderPixel / 1000 );
208         }
209         else
210         {
211             // second half of slider
212             const long nSecondHalfRange         = mpImpl->mnMaxZoom - mpImpl->mnSliderCenter;
213             const long nHalfSliderWidth         = nControlWidth/2 - nSliderXOffset;
214             const long nZoomPerSliderPixel      = 1000 * nSecondHalfRange / nHalfSliderWidth;
215             const long nOffsetToSliderCenter    = nOffset - nControlWidth/2;
216             nRet = mpImpl->mnSliderCenter + sal_uInt16( nOffsetToSliderCenter * nZoomPerSliderPixel / 1000 );
217         }
218     }
219 
220     if( nRet < mpImpl->mnMinZoom )
221         return mpImpl->mnMinZoom;
222 
223     else if( nRet > mpImpl->mnMaxZoom )
224         return mpImpl->mnMaxZoom;
225 
226     return nRet;
227 }
228 
229 // -----------------------------------------------------------------------
230 
231 long ScZoomSliderWnd::Zoom2Offset( sal_uInt16 nCurrentZoom ) const
232 {
233     Size aSliderWindowSize = GetOutputSizePixel();
234     const long nControlWidth = aSliderWindowSize.Width();
235     long  nRect = nSliderXOffset;
236 
237     const long nHalfSliderWidth = nControlWidth/2 - nSliderXOffset;
238     if( nCurrentZoom <= mpImpl->mnSliderCenter )
239     {
240         nCurrentZoom = nCurrentZoom - mpImpl->mnMinZoom;
241         const long nFirstHalfRange = mpImpl->mnSliderCenter - mpImpl->mnMinZoom;
242         const long nSliderPixelPerZoomPercent = 1000 * nHalfSliderWidth  / nFirstHalfRange;
243         const long nOffset = (nSliderPixelPerZoomPercent * nCurrentZoom) / 1000;
244         nRect += nOffset;
245     }
246     else
247     {
248         nCurrentZoom = nCurrentZoom - mpImpl->mnSliderCenter;
249         const long nSecondHalfRange = mpImpl->mnMaxZoom - mpImpl->mnSliderCenter;
250         const long nSliderPixelPerZoomPercent = 1000 * nHalfSliderWidth  / nSecondHalfRange;
251         const long nOffset = (nSliderPixelPerZoomPercent * nCurrentZoom) / 1000;
252         nRect += nHalfSliderWidth + nOffset;
253     }
254     return nRect;
255 }
256 
257 // -----------------------------------------------------------------------
258 
259 
260 ScZoomSliderWnd::ScZoomSliderWnd( Window* pParent, const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatchProvider >& rDispatchProvider,
261                 const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& _xFrame , sal_uInt16 nCurrentZoom ):
262                 Window( pParent ),
263                 mpImpl( new ScZoomSliderWnd_Impl( nCurrentZoom ) ),
264                 aLogicalSize( 115, 40 ),
265                 m_xDispatchProvider( rDispatchProvider ),
266                 m_xFrame( _xFrame )
267 {
268     sal_Bool bIsHC                  = GetSettings().GetStyleSettings().GetHighContrastMode();
269     mpImpl->maSliderButton      = Image( SVX_RES( bIsHC ? RID_SVXBMP_SLIDERBUTTON_HC : RID_SVXBMP_SLIDERBUTTON ) );
270     mpImpl->maIncreaseButton    = Image( SVX_RES( bIsHC ? RID_SVXBMP_SLIDERINCREASE_HC : RID_SVXBMP_SLIDERINCREASE ) );
271     mpImpl->maDecreaseButton    = Image( SVX_RES( bIsHC ? RID_SVXBMP_SLIDERDECREASE_HC : RID_SVXBMP_SLIDERDECREASE ) );
272     Size  aSliderSize           = LogicToPixel( Size( aLogicalSize), MapMode( MAP_10TH_MM ) );
273     SetSizePixel( Size( aSliderSize.Width() * nSliderWidth-1, aSliderSize.Height() + nSliderHeight ) );
274 }
275 
276 // -----------------------------------------------------------------------
277 
278 ScZoomSliderWnd::~ScZoomSliderWnd()
279 {
280     delete mpImpl;
281 }
282 
283 // -----------------------------------------------------------------------
284 
285 void ScZoomSliderWnd::MouseButtonDown( const MouseEvent& rMEvt )
286 {
287     if ( !mpImpl->mbValuesSet )
288         return ;
289     Size aSliderWindowSize = GetOutputSizePixel();
290 
291     const Point aPoint = rMEvt.GetPosPixel();
292 
293     const long nButtonLeftOffset    = ( nSliderXOffset - nIncDecWidth )/2;
294     const long nButtonRightOffset   = ( nSliderXOffset + nIncDecWidth )/2;
295 
296     const long nOldZoom = mpImpl->mnCurrentZoom;
297 
298     // click to - button
299     if ( aPoint.X() >= nButtonLeftOffset && aPoint.X() <= nButtonRightOffset )
300     {
301         mpImpl->mnCurrentZoom = mpImpl->mnCurrentZoom - 5;
302     }
303     // click to + button
304     else if ( aPoint.X() >= aSliderWindowSize.Width() - nSliderXOffset + nButtonLeftOffset &&
305               aPoint.X() <= aSliderWindowSize.Width() - nSliderXOffset + nButtonRightOffset )
306     {
307         mpImpl->mnCurrentZoom = mpImpl->mnCurrentZoom + 5;
308     }
309     else if( aPoint.X() >= nSliderXOffset && aPoint.X() <= aSliderWindowSize.Width() - nSliderXOffset )
310     {
311         mpImpl->mnCurrentZoom = Offset2Zoom( aPoint.X() );
312     }
313 
314     if( mpImpl->mnCurrentZoom < mpImpl->mnMinZoom )
315         mpImpl->mnCurrentZoom = mpImpl->mnMinZoom;
316     else if( mpImpl->mnCurrentZoom > mpImpl->mnMaxZoom )
317         mpImpl->mnCurrentZoom = mpImpl->mnMaxZoom;
318 
319     if( nOldZoom == mpImpl->mnCurrentZoom )
320         return ;
321 
322     Rectangle aRect( Point( 0, 0 ), aSliderWindowSize );
323 
324     Paint( aRect );
325     mpImpl->mbOmitPaint = true;
326 
327     SvxZoomSliderItem   aZoomSliderItem( mpImpl->mnCurrentZoom );
328 
329     ::com::sun::star::uno::Any  a;
330     aZoomSliderItem.QueryValue( a );
331 
332     ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue > aArgs( 1 );
333     aArgs[0].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ScalingFactor" ));
334     aArgs[0].Value = a;
335 
336     SfxToolBoxControl::Dispatch( m_xDispatchProvider, String::CreateFromAscii(".uno:ScalingFactor"), aArgs );
337 
338     mpImpl->mbOmitPaint = false;
339 }
340 
341 // -----------------------------------------------------------------------
342 
343 void ScZoomSliderWnd::MouseMove( const MouseEvent& rMEvt )
344 {
345     if ( !mpImpl->mbValuesSet )
346         return ;
347 
348     Size aSliderWindowSize   = GetOutputSizePixel();
349     const long nControlWidth = aSliderWindowSize.Width();
350     const short nButtons     = rMEvt.GetButtons();
351 
352     // check mouse move with button pressed
353     if ( 1 == nButtons )
354     {
355         const Point aPoint = rMEvt.GetPosPixel();
356 
357         if ( aPoint.X() >= nSliderXOffset && aPoint.X() <= nControlWidth - nSliderXOffset )
358         {
359             mpImpl->mnCurrentZoom = Offset2Zoom( aPoint.X() );
360 
361             Rectangle aRect( Point( 0, 0 ), aSliderWindowSize  );
362             Paint( aRect );
363 
364             mpImpl->mbOmitPaint = true; // optimization: paint before executing command,
365 
366             // commit state change
367             SvxZoomSliderItem aZoomSliderItem( mpImpl->mnCurrentZoom );
368 
369             ::com::sun::star::uno::Any a;
370             aZoomSliderItem.QueryValue( a );
371 
372             ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue > aArgs( 1 );
373             aArgs[0].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ScalingFactor" ));
374             aArgs[0].Value = a;
375 
376             SfxToolBoxControl::Dispatch( m_xDispatchProvider, String::CreateFromAscii(".uno:ScalingFactor"), aArgs );
377 
378             mpImpl->mbOmitPaint = false;
379         }
380     }
381 }
382 
383 // -----------------------------------------------------------------------
384 
385 void ScZoomSliderWnd::UpdateFromItem( const SvxZoomSliderItem* pZoomSliderItem )
386 {
387     if( pZoomSliderItem )
388     {
389         mpImpl->mnCurrentZoom = pZoomSliderItem->GetValue();
390         mpImpl->mnMinZoom     = pZoomSliderItem->GetMinZoom();
391         mpImpl->mnMaxZoom     = pZoomSliderItem->GetMaxZoom();
392 
393         DBG_ASSERT( mpImpl->mnMinZoom <= mpImpl->mnCurrentZoom &&
394             mpImpl->mnMinZoom <  mpImpl->mnSliderCenter &&
395             mpImpl->mnMaxZoom >= mpImpl->mnCurrentZoom &&
396             mpImpl->mnMaxZoom > mpImpl->mnSliderCenter,
397             "Looks like the zoom slider item is corrupted" );
398        const com::sun::star::uno::Sequence < sal_Int32 > rSnappingPoints = pZoomSliderItem->GetSnappingPoints();
399        mpImpl->maSnappingPointOffsets.clear();
400        mpImpl->maSnappingPointZooms.clear();
401 
402        // get all snapping points:
403        std::set< sal_uInt16 > aTmpSnappingPoints;
404        for ( sal_uInt16 j = 0; j < rSnappingPoints.getLength(); ++j )
405        {
406            const sal_Int32 nSnappingPoint = rSnappingPoints[j];
407            aTmpSnappingPoints.insert( (sal_uInt16)nSnappingPoint );
408        }
409 
410        // remove snapping points that are to close to each other:
411        std::set< sal_uInt16 >::iterator aSnappingPointIter;
412        long nLastOffset = 0;
413 
414        for ( aSnappingPointIter = aTmpSnappingPoints.begin(); aSnappingPointIter != aTmpSnappingPoints.end(); ++aSnappingPointIter )
415        {
416            const sal_uInt16 nCurrent = *aSnappingPointIter;
417            const long nCurrentOffset = Zoom2Offset( nCurrent );
418 
419            if ( nCurrentOffset - nLastOffset >= nSnappingPointsMinDist )
420            {
421                mpImpl->maSnappingPointOffsets.push_back( nCurrentOffset );
422                mpImpl->maSnappingPointZooms.push_back( nCurrent );
423                nLastOffset = nCurrentOffset;
424            }
425        }
426     }
427 
428     Size aSliderWindowSize = GetOutputSizePixel();
429     Rectangle aRect( Point( 0, 0 ), aSliderWindowSize );
430 
431     if ( !mpImpl->mbOmitPaint )
432        Paint(aRect);
433 }
434 
435 // -----------------------------------------------------------------------
436 
437 void ScZoomSliderWnd::Paint( const Rectangle& rRect )
438 {
439     DoPaint( rRect );
440 }
441 
442 // -----------------------------------------------------------------------
443 
444 void ScZoomSliderWnd::DoPaint( const Rectangle& /*rRect*/ )
445 {
446     if( mpImpl->mbOmitPaint )
447         return;
448 
449     Size aSliderWindowSize = GetOutputSizePixel();
450     Rectangle aRect( Point( 0, 0 ), aSliderWindowSize );
451 
452     VirtualDevice* pVDev = new VirtualDevice( *this );
453     pVDev->SetOutputSizePixel( aSliderWindowSize );
454 
455     Rectangle   aSlider = aRect;
456 
457     aSlider.Top()     += ( aSliderWindowSize.Height() - nSliderHeight )/2 - 1;
458     aSlider.Bottom()   = aSlider.Top() + nSliderHeight;
459     aSlider.Left()    += nSliderXOffset;
460     aSlider.Right()   -= nSliderXOffset;
461 
462     Rectangle aFirstLine( aSlider );
463     aFirstLine.Bottom() = aFirstLine.Top();
464 
465     Rectangle aSecondLine( aSlider );
466     aSecondLine.Top() = aSecondLine.Bottom();
467 
468     Rectangle aLeft( aSlider );
469     aLeft.Right() = aLeft.Left();
470 
471     Rectangle aRight( aSlider );
472     aRight.Left() = aRight.Right();
473 
474     // draw VirtualDevice's background color
475     Color  aStartColor,aEndColor;
476     aStartColor = GetSettings().GetStyleSettings().GetFaceColor();
477     aEndColor   = GetSettings().GetStyleSettings().GetFaceColor();
478     if( aEndColor.IsDark() )
479         aStartColor = aEndColor;
480 
481     Gradient g;
482     g.SetAngle( 0 );
483     g.SetStyle( GRADIENT_LINEAR );
484 
485     g.SetStartColor( aStartColor );
486     g.SetEndColor( aEndColor );
487     pVDev->DrawGradient( aRect, g );
488 
489     // draw slider
490     pVDev->SetLineColor( Color ( COL_WHITE ) );
491     pVDev->DrawRect( aSecondLine );
492     pVDev->DrawRect( aRight );
493 
494     pVDev->SetLineColor( Color( COL_GRAY ) );
495     pVDev->DrawRect( aFirstLine );
496     pVDev->DrawRect( aLeft );
497 
498     // draw snapping points:
499     std::vector< long >::iterator aSnappingPointIter;
500     for ( aSnappingPointIter = mpImpl->maSnappingPointOffsets.begin();
501         aSnappingPointIter != mpImpl->maSnappingPointOffsets.end();
502         ++aSnappingPointIter )
503     {
504         pVDev->SetLineColor( Color( COL_GRAY ) );
505         Rectangle aSnapping( aRect );
506         aSnapping.Bottom()   = aSlider.Top();
507         aSnapping.Top() = aSnapping.Bottom() - nSnappingHeight;
508         aSnapping.Left() += *aSnappingPointIter;
509         aSnapping.Right() = aSnapping.Left();
510         pVDev->DrawRect( aSnapping );
511 
512         aSnapping.Top() += nSnappingHeight + nSliderHeight;
513         aSnapping.Bottom() += nSnappingHeight + nSliderHeight;
514         pVDev->DrawRect( aSnapping );
515     }
516 
517     // draw slider button
518     Point aImagePoint = aRect.TopLeft();
519     aImagePoint.X() += Zoom2Offset( mpImpl->mnCurrentZoom );
520     aImagePoint.X() -= nButtonWidth/2;
521     aImagePoint.Y() += ( aSliderWindowSize.Height() - nButtonHeight)/2;
522     pVDev->DrawImage( aImagePoint, mpImpl->maSliderButton );
523 
524     // draw decrease button
525     aImagePoint = aRect.TopLeft();
526     aImagePoint.X() += (nSliderXOffset - nIncDecWidth)/2;
527     aImagePoint.Y() += ( aSliderWindowSize.Height() - nIncDecHeight)/2;
528     pVDev->DrawImage( aImagePoint, mpImpl->maDecreaseButton );
529 
530     // draw increase button
531     aImagePoint.X() = aRect.TopLeft().X() + aSliderWindowSize.Width() - nIncDecWidth - (nSliderXOffset - nIncDecWidth)/2;
532     pVDev->DrawImage( aImagePoint, mpImpl->maIncreaseButton );
533 
534     DrawOutDev( Point(0, 0), aSliderWindowSize, Point(0, 0), aSliderWindowSize, *pVDev );
535 
536     delete pVDev;
537 
538 }
539 
540 // -----------------------------------------------------------------------
541