]> git.sesse.net Git - vlc/blob - modules/gui/skins2/controls/ctrl_slider.cpp
694a74411000934a5adc64bef825231ad909d846
[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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, 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,void*> &rVariable,
163                                  void *arg  )
164 {
165     // The position has changed
166     refreshLayout();
167 }
168
169
170 void CtrlSliderCursor::CmdOverDown::execute()
171 {
172     EvtMouse *pEvtMouse = (EvtMouse*)m_pParent->m_pEvt;
173
174     // Compute the resize factors
175     float factorX, factorY;
176     m_pParent->getResizeFactors( factorX, factorY );
177
178     // Get the position of the control
179     const Position *pPos = m_pParent->getPosition();
180
181     // Compute the offset
182     int tempX, tempY;
183     m_pParent->m_rCurve.getPoint( m_pParent->m_rVariable.get(), tempX, tempY );
184     m_pParent->m_xOffset = pEvtMouse->getXPos() - pPos->getLeft()
185                        - (int)(tempX * factorX);
186     m_pParent->m_yOffset = pEvtMouse->getYPos() - pPos->getTop()
187                        - (int)(tempY * factorY);
188
189     m_pParent->captureMouse();
190     m_pParent->m_pImg = m_pParent->m_pImgDown;
191     m_pParent->refreshLayout();
192 }
193
194
195 void CtrlSliderCursor::CmdDownOver::execute()
196 {
197     // Save the position
198     m_pParent->m_lastPercentage = m_pParent->m_rVariable.get();
199
200     m_pParent->releaseMouse();
201     m_pParent->m_pImg = m_pParent->m_pImgUp;
202     m_pParent->refreshLayout();
203 }
204
205
206 void CtrlSliderCursor::CmdUpOver::execute()
207 {
208     m_pParent->m_pImg = m_pParent->m_pImgOver;
209     m_pParent->refreshLayout();
210 }
211
212
213 void CtrlSliderCursor::CmdOverUp::execute()
214 {
215     m_pParent->m_pImg = m_pParent->m_pImgUp;
216     m_pParent->refreshLayout();
217 }
218
219
220 void CtrlSliderCursor::CmdMove::execute()
221 {
222     EvtMouse *pEvtMouse = (EvtMouse*)m_pParent->m_pEvt;
223
224     // Get the position of the control
225     const Position *pPos = m_pParent->getPosition();
226
227     // Compute the resize factors
228     float factorX, factorY;
229     m_pParent->getResizeFactors( factorX, factorY );
230
231     // Compute the relative position of the centre of the cursor
232     float relX = pEvtMouse->getXPos() - pPos->getLeft() - m_pParent->m_xOffset;
233     float relY = pEvtMouse->getYPos() - pPos->getTop() - m_pParent->m_yOffset;
234     // Ponderate with the resize factors
235     int relXPond = (int)(relX / factorX);
236     int relYPond = (int)(relY / factorY);
237
238     // Update the position
239     if( m_pParent->m_rCurve.getMinDist( relXPond, relYPond ) < RANGE )
240     {
241         float percentage = m_pParent->m_rCurve.getNearestPercent( relXPond,
242                                                               relYPond );
243         m_pParent->m_rVariable.set( percentage );
244     }
245     else
246     {
247         m_pParent->m_rVariable.set( m_pParent->m_lastPercentage );
248     }
249 }
250
251 void CtrlSliderCursor::CmdScroll::execute()
252 {
253     EvtScroll *pEvtScroll = (EvtScroll*)m_pParent->m_pEvt;
254
255     int direction = pEvtScroll->getDirection();
256
257     float percentage = m_pParent->m_rVariable.get();
258     if( direction == EvtScroll::kUp )
259     {
260         percentage += SCROLL_STEP;
261     }
262     else
263     {
264         percentage -= SCROLL_STEP;
265     }
266
267     m_pParent->m_rVariable.set( percentage );
268 }
269
270
271 void CtrlSliderCursor::getResizeFactors( float &rFactorX,
272                                          float &rFactorY ) const
273 {
274     // Get the position of the control
275     const Position *pPos = getPosition();
276
277     rFactorX = 1.0;
278     rFactorY = 1.0;
279
280     // Compute the resize factors
281     if( m_width > 0 )
282     {
283         rFactorX = (float)pPos->getWidth() / (float)m_width;
284     }
285     if( m_height > 0 )
286     {
287         rFactorY = (float)pPos->getHeight() / (float)m_height;
288     }
289 }
290
291
292 void CtrlSliderCursor::refreshLayout()
293 {
294     if( m_pImg )
295     {
296         // Compute the resize factors
297         float factorX, factorY;
298         getResizeFactors( factorX, factorY );
299
300         notifyLayout( (int)(m_rCurve.getWidth() * factorX) + m_pImg->getWidth(),
301                       (int)(m_rCurve.getHeight() * factorY) + m_pImg->getHeight(),
302                       - m_pImg->getWidth() / 2,
303                       - m_pImg->getHeight() / 2 );
304     }
305     else
306         notifyLayout();
307 }
308
309
310 CtrlSliderBg::CtrlSliderBg( intf_thread_t *pIntf,
311                             const Bezier &rCurve, VarPercent &rVariable,
312                             int thickness, GenericBitmap *pBackground,
313                             int nbHoriz, int nbVert, int padHoriz, int padVert,
314                             VarBool *pVisible, const UString &rHelp ):
315     CtrlGeneric( pIntf, rHelp, pVisible ), m_pCursor( NULL ),
316     m_rVariable( rVariable ), m_thickness( thickness ), m_rCurve( rCurve ),
317     m_width( rCurve.getWidth() ), m_height( rCurve.getHeight() ),
318     m_pImgSeq( NULL ), m_nbHoriz( nbHoriz ), m_nbVert( nbVert ),
319     m_padHoriz( padHoriz ), m_padVert( padVert ), m_bgWidth( 0 ),
320     m_bgHeight( 0 ), m_position( 0 )
321 {
322     if( pBackground )
323     {
324         // Build the background image sequence
325         OSFactory *pOsFactory = OSFactory::instance( getIntf() );
326         m_pImgSeq = pOsFactory->createOSGraphics( pBackground->getWidth(),
327                                                   pBackground->getHeight() );
328         m_pImgSeq->drawBitmap( *pBackground, 0, 0 );
329
330         m_bgWidth = (pBackground->getWidth() + m_padHoriz) / nbHoriz;
331         m_bgHeight = (pBackground->getHeight() + m_padVert) / nbVert;
332
333         // Observe the position variable
334         m_rVariable.addObserver( this );
335
336         // Initial position
337         m_position = (int)( m_rVariable.get() * (m_nbHoriz * m_nbVert - 1) );
338     }
339 }
340
341
342 CtrlSliderBg::~CtrlSliderBg()
343 {
344     m_rVariable.delObserver( this );
345     delete m_pImgSeq;
346 }
347
348
349 bool CtrlSliderBg::mouseOver( int x, int y ) const
350 {
351     // Compute the resize factors
352     float factorX, factorY;
353     getResizeFactors( factorX, factorY );
354
355     return (m_rCurve.getMinDist( (int)(x / factorX), (int)(y / factorY),
356                                  factorX, factorY ) < m_thickness );
357 }
358
359
360 void CtrlSliderBg::draw( OSGraphics &rImage, int xDest, int yDest )
361 {
362     if( m_pImgSeq )
363     {
364         // Locate the right image in the background bitmap
365         int x = m_bgWidth * ( m_position % m_nbHoriz );
366         int y = m_bgHeight * ( m_position / m_nbHoriz );
367         // Draw the background image
368         rImage.drawGraphics( *m_pImgSeq, x, y, xDest, yDest,
369                              m_bgWidth - m_padHoriz, m_bgHeight - m_padVert );
370     }
371 }
372
373
374 void CtrlSliderBg::handleEvent( EvtGeneric &rEvent )
375 {
376     if( rEvent.getAsString().find( "mouse:left:down" ) != string::npos )
377     {
378         // Compute the resize factors
379         float factorX, factorY;
380         getResizeFactors( factorX, factorY );
381
382         // Get the position of the control
383         const Position *pPos = getPosition();
384
385         // Get the value corresponding to the position of the mouse
386         EvtMouse &rEvtMouse = (EvtMouse&)rEvent;
387         int x = rEvtMouse.getXPos();
388         int y = rEvtMouse.getYPos();
389         m_rVariable.set( m_rCurve.getNearestPercent(
390                             (int)((x - pPos->getLeft()) / factorX),
391                             (int)((y - pPos->getTop()) / factorY) ) );
392
393         // Forward the clic to the cursor
394         EvtMouse evt( getIntf(), x, y, EvtMouse::kLeft, EvtMouse::kDown );
395         TopWindow *pWin = getWindow();
396         if( pWin && m_pCursor )
397         {
398             EvtEnter evtEnter( getIntf() );
399             // XXX It was not supposed to be implemented like that !!
400             pWin->forwardEvent( evtEnter, *m_pCursor );
401             pWin->forwardEvent( evt, *m_pCursor );
402         }
403     }
404     else if( rEvent.getAsString().find( "scroll" ) != string::npos )
405     {
406         int direction = ((EvtScroll&)rEvent).getDirection();
407
408         float percentage = m_rVariable.get();
409         if( direction == EvtScroll::kUp )
410         {
411             percentage += SCROLL_STEP;
412         }
413         else
414         {
415             percentage -= SCROLL_STEP;
416         }
417
418         m_rVariable.set( percentage );
419     }
420 }
421
422
423 void CtrlSliderBg::associateCursor( CtrlSliderCursor &rCursor )
424 {
425     m_pCursor = &rCursor;
426 }
427
428
429 void CtrlSliderBg::onUpdate( Subject<VarPercent, void*> &rVariable, void*arg )
430 {
431     m_position = (int)( m_rVariable.get() * (m_nbHoriz * m_nbVert - 1) );
432     notifyLayout( m_bgWidth, m_bgHeight );
433 }
434
435
436 void CtrlSliderBg::getResizeFactors( float &rFactorX, float &rFactorY ) const
437 {
438     // Get the position of the control
439     const Position *pPos = getPosition();
440
441     rFactorX = 1.0;
442     rFactorY = 1.0;
443
444     // Compute the resize factors
445     if( m_width > 0 )
446     {
447         rFactorX = (float)pPos->getWidth() / (float)m_width;
448     }
449     if( m_height > 0 )
450     {
451         rFactorY = (float)pPos->getHeight() / (float)m_height;
452     }
453 }
454