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