]> git.sesse.net Git - vlc/blob - modules/gui/skins2/controls/ctrl_slider.cpp
* winamp2.xml: added the Equalizer and Playlist windows.
[vlc] / modules / gui / skins2 / controls / ctrl_slider.cpp
1 /*****************************************************************************
2  * ctrl_slider.cpp
3  *****************************************************************************
4  * Copyright (C) 2003 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Cyril Deguet     <asmax@via.ecp.fr>
8  *          Olivier Teulière <ipkiss@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program 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 General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 #include "ctrl_slider.hpp"
26 #include "../events/evt_enter.hpp"
27 #include "../events/evt_mouse.hpp"
28 #include "../events/evt_scroll.hpp"
29 #include "../src/generic_bitmap.hpp"
30 #include "../src/top_window.hpp"
31 #include "../src/os_factory.hpp"
32 #include "../src/os_graphics.hpp"
33 #include "../utils/position.hpp"
34 #include "../utils/var_percent.hpp"
35
36
37 #define RANGE 40
38 #define SCROLL_STEP 0.05f
39
40
41 CtrlSliderCursor::CtrlSliderCursor( intf_thread_t *pIntf,
42                                     const GenericBitmap &rBmpUp,
43                                     const GenericBitmap &rBmpOver,
44                                     const GenericBitmap &rBmpDown,
45                                     const Bezier &rCurve,
46                                     VarPercent &rVariable,
47                                     VarBool *pVisible,
48                                     const UString &rTooltip,
49                                     const UString &rHelp ):
50     CtrlGeneric( pIntf, rHelp, pVisible ), m_fsm( pIntf ),
51     m_rVariable( rVariable ), m_tooltip( rTooltip ),
52     m_width( rCurve.getWidth() ), m_height( rCurve.getHeight() ),
53     m_cmdOverDown( this ), m_cmdDownOver( this ),
54     m_cmdOverUp( this ), m_cmdUpOver( this ),
55     m_cmdMove( this ), m_cmdScroll( this ),
56     m_lastPercentage( 0 ), m_xOffset( 0 ), m_yOffset( 0 ),
57     m_pEvt( NULL ), m_rCurve( rCurve )
58 {
59     // Build the images of the cursor
60     OSFactory *pOsFactory = OSFactory::instance( getIntf() );
61     m_pImgUp = pOsFactory->createOSGraphics( rBmpUp.getWidth(),
62                                              rBmpUp.getHeight() );
63     m_pImgUp->drawBitmap( rBmpUp, 0, 0 );
64     m_pImgDown = pOsFactory->createOSGraphics( rBmpDown.getWidth(),
65                                                rBmpDown.getHeight() );
66     m_pImgDown->drawBitmap( rBmpDown, 0, 0 );
67     m_pImgOver = pOsFactory->createOSGraphics( rBmpOver.getWidth(),
68                                                rBmpOver.getHeight() );
69     m_pImgOver->drawBitmap( rBmpOver, 0, 0 );
70
71     // States
72     m_fsm.addState( "up" );
73     m_fsm.addState( "over" );
74     m_fsm.addState( "down" );
75
76     // Transitions
77     m_fsm.addTransition( "over", "mouse:left:down", "down",
78                          &m_cmdOverDown );
79     m_fsm.addTransition( "down", "mouse:left:up", "over",
80                          &m_cmdDownOver );
81     m_fsm.addTransition( "over", "leave", "up", &m_cmdOverUp );
82     m_fsm.addTransition( "up", "enter", "over", &m_cmdUpOver );
83     m_fsm.addTransition( "down", "motion", "down", &m_cmdMove );
84     m_fsm.addTransition( "over", "scroll", "over", &m_cmdScroll );
85
86     // Initial state
87     m_fsm.setState( "up" );
88     m_pImg = m_pImgUp;
89
90     // Observe the position variable
91     m_rVariable.addObserver( this );
92
93     // Initial position of the cursor
94     m_lastPercentage = m_rVariable.get();
95 }
96
97
98 CtrlSliderCursor::~CtrlSliderCursor()
99 {
100     m_rVariable.delObserver( this );
101     SKINS_DELETE( m_pImgUp );
102     SKINS_DELETE( m_pImgDown );
103     SKINS_DELETE( m_pImgOver );
104 }
105
106
107 void CtrlSliderCursor::handleEvent( EvtGeneric &rEvent )
108 {
109     // Save the event to use it in callbacks
110     m_pEvt = &rEvent;
111
112     m_fsm.handleTransition( rEvent.getAsString() );
113 }
114
115
116 bool CtrlSliderCursor::mouseOver( int x, int y ) const
117 {
118     if( m_pImg )
119     {
120         // Compute the position of the cursor
121         int xPos, yPos;
122         m_rCurve.getPoint( m_rVariable.get(), xPos, yPos );
123
124         // Compute the resize factors
125         float factorX, factorY;
126         getResizeFactors( factorX, factorY );
127         xPos = (int)(xPos * factorX);
128         yPos = (int)(yPos * factorY);
129
130         return m_pImg->hit( x - xPos + m_pImg->getWidth() / 2,
131                             y - yPos + m_pImg->getHeight() / 2 );
132     }
133     else
134     {
135         return false;
136     }
137 }
138
139
140 void CtrlSliderCursor::draw( OSGraphics &rImage, int xDest, int yDest )
141 {
142     if( m_pImg )
143     {
144         // Compute the position of the cursor
145         int xPos, yPos;
146         m_rCurve.getPoint( m_rVariable.get(), xPos, yPos );
147
148         // Compute the resize factors
149         float factorX, factorY;
150         getResizeFactors( factorX, factorY );
151         xPos = (int)(xPos * factorX);
152         yPos = (int)(yPos * factorY);
153
154         // Draw the current image
155         rImage.drawGraphics( *m_pImg, 0, 0,
156                              xDest + xPos - m_pImg->getWidth() / 2,
157                              yDest + yPos - m_pImg->getHeight() / 2 );
158     }
159 }
160
161
162 void CtrlSliderCursor::onUpdate( Subject<VarPercent> &rVariable )
163 {
164     // The position has changed
165     if( m_pImg )
166     {
167         notifyLayout( m_rCurve.getWidth() + m_pImg->getWidth(),
168                       m_rCurve.getHeight() + m_pImg->getHeight(),
169                       - m_pImg->getWidth() / 2,
170                       - m_pImg->getHeight() / 2 );
171     }
172     else
173         notifyLayout();
174 }
175
176
177 void CtrlSliderCursor::CmdOverDown::execute()
178 {
179     EvtMouse *pEvtMouse = (EvtMouse*)m_pParent->m_pEvt;
180
181     // Compute the resize factors
182     float factorX, factorY;
183     m_pParent->getResizeFactors( factorX, factorY );
184
185     // Get the position of the control
186     const Position *pPos = m_pParent->getPosition();
187
188     // Compute the offset
189     int tempX, tempY;
190     m_pParent->m_rCurve.getPoint( m_pParent->m_rVariable.get(), tempX, tempY );
191     m_pParent->m_xOffset = pEvtMouse->getXPos() - pPos->getLeft()
192                        - (int)(tempX * factorX);
193     m_pParent->m_yOffset = pEvtMouse->getYPos() - pPos->getTop()
194                        - (int)(tempY * factorY);
195
196     m_pParent->captureMouse();
197     m_pParent->m_pImg = m_pParent->m_pImgDown;
198     if( m_pParent->m_pImg )
199     {
200         m_pParent->notifyLayout(
201             m_pParent->m_rCurve.getWidth() + m_pParent->m_pImg->getWidth(),
202             m_pParent->m_rCurve.getHeight() + m_pParent->m_pImg->getHeight(),
203             - m_pParent->m_pImg->getWidth() / 2,
204             - m_pParent->m_pImg->getHeight() / 2 );
205     }
206     else
207         m_pParent->notifyLayout();
208 }
209
210
211 void CtrlSliderCursor::CmdDownOver::execute()
212 {
213     // Save the position
214     m_pParent->m_lastPercentage = m_pParent->m_rVariable.get();
215
216     m_pParent->releaseMouse();
217     m_pParent->m_pImg = m_pParent->m_pImgUp;
218     if( m_pParent->m_pImg )
219     {
220         m_pParent->notifyLayout(
221             m_pParent->m_rCurve.getWidth() + m_pParent->m_pImg->getWidth(),
222             m_pParent->m_rCurve.getHeight() + m_pParent->m_pImg->getHeight(),
223             - m_pParent->m_pImg->getWidth() / 2,
224             - m_pParent->m_pImg->getHeight() / 2 );
225     }
226     else
227         m_pParent->notifyLayout();
228 }
229
230
231 void CtrlSliderCursor::CmdUpOver::execute()
232 {
233     m_pParent->m_pImg = m_pParent->m_pImgOver;
234     if( m_pParent->m_pImg )
235     {
236         m_pParent->notifyLayout(
237             m_pParent->m_rCurve.getWidth() + m_pParent->m_pImg->getWidth(),
238             m_pParent->m_rCurve.getHeight() + m_pParent->m_pImg->getHeight(),
239             - m_pParent->m_pImg->getWidth() / 2,
240             - m_pParent->m_pImg->getHeight() / 2 );
241     }
242     else
243         m_pParent->notifyLayout();
244 }
245
246
247 void CtrlSliderCursor::CmdOverUp::execute()
248 {
249     m_pParent->m_pImg = m_pParent->m_pImgUp;
250     if( m_pParent->m_pImg )
251     {
252         m_pParent->notifyLayout(
253             m_pParent->m_rCurve.getWidth() + m_pParent->m_pImg->getWidth(),
254             m_pParent->m_rCurve.getHeight() + m_pParent->m_pImg->getHeight(),
255             - m_pParent->m_pImg->getWidth() / 2,
256             - m_pParent->m_pImg->getHeight() / 2 );
257     }
258     else
259         m_pParent->notifyLayout();
260 }
261
262
263 void CtrlSliderCursor::CmdMove::execute()
264 {
265     EvtMouse *pEvtMouse = (EvtMouse*)m_pParent->m_pEvt;
266
267     // Get the position of the control
268     const Position *pPos = m_pParent->getPosition();
269
270     // Compute the resize factors
271     float factorX, factorY;
272     m_pParent->getResizeFactors( factorX, factorY );
273
274     // Compute the relative position of the centre of the cursor
275     float relX = pEvtMouse->getXPos() - pPos->getLeft() - m_pParent->m_xOffset;
276     float relY = pEvtMouse->getYPos() - pPos->getTop() - m_pParent->m_yOffset;
277     // Ponderate with the resize factors
278     int relXPond = (int)(relX / factorX);
279     int relYPond = (int)(relY / factorY);
280
281     // Update the position
282     if( m_pParent->m_rCurve.getMinDist( relXPond, relYPond ) < RANGE )
283     {
284         float percentage = m_pParent->m_rCurve.getNearestPercent( relXPond,
285                                                               relYPond );
286         m_pParent->m_rVariable.set( percentage );
287     }
288     else
289     {
290         m_pParent->m_rVariable.set( m_pParent->m_lastPercentage );
291     }
292 }
293
294 void CtrlSliderCursor::CmdScroll::execute()
295 {
296     EvtScroll *pEvtScroll = (EvtScroll*)m_pParent->m_pEvt;
297
298     int direction = pEvtScroll->getDirection();
299
300     float percentage = m_pParent->m_rVariable.get();
301     if( direction == EvtScroll::kUp )
302     {
303         percentage += SCROLL_STEP;
304     }
305     else
306     {
307         percentage -= SCROLL_STEP;
308     }
309
310     m_pParent->m_rVariable.set( percentage );
311 }
312
313
314 void CtrlSliderCursor::getResizeFactors( float &rFactorX,
315                                          float &rFactorY ) const
316 {
317     // Get the position of the control
318     const Position *pPos = getPosition();
319
320     rFactorX = 1.0;
321     rFactorY = 1.0;
322
323     // Compute the resize factors
324     if( m_width > 0 )
325     {
326         rFactorX = (float)pPos->getWidth() / (float)m_width;
327     }
328     if( m_height > 0 )
329     {
330         rFactorY = (float)pPos->getHeight() / (float)m_height;
331     }
332 }
333
334
335
336 CtrlSliderBg::CtrlSliderBg( intf_thread_t *pIntf, CtrlSliderCursor &rCursor,
337                             const Bezier &rCurve, VarPercent &rVariable,
338                             int thickness, GenericBitmap *pBackground,
339                             int nbImages, VarBool *pVisible,
340                             const UString &rHelp ):
341     CtrlGeneric( pIntf, rHelp, pVisible ), m_rCursor( rCursor ),
342     m_rVariable( rVariable ), m_thickness( thickness ), m_rCurve( rCurve ),
343     m_width( rCurve.getWidth() ), m_height( rCurve.getHeight() ),
344     m_pImgSeq( NULL ), m_nbImages( nbImages ), m_bgWidth( 0 ),
345     m_bgHeight( 0 ), m_position( 0 )
346 {
347     if( pBackground )
348     {
349         // Build the background image sequence
350         OSFactory *pOsFactory = OSFactory::instance( getIntf() );
351         m_pImgSeq = pOsFactory->createOSGraphics( pBackground->getWidth(),
352                                                   pBackground->getHeight() );
353         m_pImgSeq->drawBitmap( *pBackground, 0, 0 );
354
355         m_bgWidth = pBackground->getWidth();
356         m_bgHeight = pBackground->getHeight() / nbImages;
357
358         // Observe the position variable
359         m_rVariable.addObserver( this );
360
361         // Initial position
362         m_position = (int)( m_rVariable.get() * (m_nbImages - 1) );
363     }
364 }
365
366
367 CtrlSliderBg::~CtrlSliderBg()
368 {
369     m_rVariable.delObserver( this );
370     delete m_pImgSeq;
371 }
372
373
374 bool CtrlSliderBg::mouseOver( int x, int y ) const
375 {
376     // Compute the resize factors
377     float factorX, factorY;
378     getResizeFactors( factorX, factorY );
379
380     return (m_rCurve.getMinDist( (int)(x / factorX),
381                                  (int)(y / factorY) ) < m_thickness );
382 }
383
384
385 void CtrlSliderBg::draw( OSGraphics &rImage, int xDest, int yDest )
386 {
387     if( m_pImgSeq )
388     {
389         // Draw the background image
390         // XXX the "-2" is a hack for winamp skins...
391         rImage.drawGraphics( *m_pImgSeq, 0, m_position * m_bgHeight,
392                              xDest, yDest, m_bgWidth, m_bgHeight - 2);
393     }
394 }
395
396
397 void CtrlSliderBg::handleEvent( EvtGeneric &rEvent )
398 {
399     if( rEvent.getAsString().find( "mouse:left:down" ) != string::npos )
400     {
401         // Compute the resize factors
402         float factorX, factorY;
403         getResizeFactors( factorX, factorY );
404
405         // Get the position of the control
406         const Position *pPos = getPosition();
407
408         // Get the value corresponding to the position of the mouse
409         EvtMouse &rEvtMouse = (EvtMouse&)rEvent;
410         int x = rEvtMouse.getXPos();
411         int y = rEvtMouse.getYPos();
412         m_rVariable.set( m_rCurve.getNearestPercent(
413                             (int)((x - pPos->getLeft()) / factorX),
414                             (int)((y - pPos->getTop()) / factorY) ) );
415
416         // Forward the clic to the cursor
417         EvtMouse evt( getIntf(), x, y, EvtMouse::kLeft, EvtMouse::kDown );
418         TopWindow *pWin = getWindow();
419         if( pWin )
420         {
421             EvtEnter evtEnter( getIntf() );
422             // XXX It was not supposed to be implemented like that !!
423             pWin->forwardEvent( evtEnter, m_rCursor );
424             pWin->forwardEvent( evt, m_rCursor );
425         }
426     }
427     else if( rEvent.getAsString().find( "scroll" ) != string::npos )
428     {
429         int direction = ((EvtScroll&)rEvent).getDirection();
430
431         float percentage = m_rVariable.get();
432         if( direction == EvtScroll::kUp )
433         {
434             percentage += SCROLL_STEP;
435         }
436         else
437         {
438             percentage -= SCROLL_STEP;
439         }
440
441         m_rVariable.set( percentage );
442     }
443 }
444
445
446 void CtrlSliderBg::onUpdate( Subject<VarPercent> &rVariable )
447 {
448     m_position = (int)( m_rVariable.get() * (m_nbImages - 1) );
449     notifyLayout( m_bgWidth, m_bgHeight );
450 }
451
452
453 void CtrlSliderBg::getResizeFactors( float &rFactorX, float &rFactorY ) const
454 {
455     // Get the position of the control
456     const Position *pPos = getPosition();
457
458     rFactorX = 1.0;
459     rFactorY = 1.0;
460
461     // Compute the resize factors
462     if( m_width > 0 )
463     {
464         rFactorX = (float)pPos->getWidth() / (float)m_width;
465     }
466     if( m_height > 0 )
467     {
468         rFactorY = (float)pPos->getHeight() / (float)m_height;
469     }
470 }
471