]> git.sesse.net Git - vlc/blob - modules/gui/skins2/controls/ctrl_slider.cpp
6b1883b91ce61998c6c7c43c60b4f216c960dc94
[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/scaled_bitmap.hpp"
31 #include "../src/top_window.hpp"
32 #include "../src/os_factory.hpp"
33 #include "../src/os_graphics.hpp"
34 #include "../utils/position.hpp"
35 #include "../utils/var_percent.hpp"
36
37
38 #define RANGE 40
39 #define SCROLL_STEP 0.05f
40
41
42 CtrlSliderCursor::CtrlSliderCursor( intf_thread_t *pIntf,
43                                     const GenericBitmap &rBmpUp,
44                                     const GenericBitmap &rBmpOver,
45                                     const GenericBitmap &rBmpDown,
46                                     const Bezier &rCurve,
47                                     VarPercent &rVariable,
48                                     VarBool *pVisible,
49                                     const UString &rTooltip,
50                                     const UString &rHelp ):
51     CtrlGeneric( pIntf, rHelp, pVisible ), m_fsm( pIntf ),
52     m_rVariable( rVariable ), m_tooltip( rTooltip ),
53     m_width( rCurve.getWidth() ), m_height( rCurve.getHeight() ),
54     m_cmdOverDown( this ), m_cmdDownOver( this ),
55     m_cmdOverUp( this ), m_cmdUpOver( this ),
56     m_cmdMove( this ), m_cmdScroll( this ),
57     m_lastPercentage( 0 ), m_xOffset( 0 ), m_yOffset( 0 ),
58     m_pEvt( NULL ), m_rCurve( rCurve )
59 {
60     // Build the images of the cursor
61     OSFactory *pOsFactory = OSFactory::instance( getIntf() );
62     m_pImgUp = pOsFactory->createOSGraphics( rBmpUp.getWidth(),
63                                              rBmpUp.getHeight() );
64     m_pImgUp->drawBitmap( rBmpUp, 0, 0 );
65     m_pImgDown = pOsFactory->createOSGraphics( rBmpDown.getWidth(),
66                                                rBmpDown.getHeight() );
67     m_pImgDown->drawBitmap( rBmpDown, 0, 0 );
68     m_pImgOver = pOsFactory->createOSGraphics( rBmpOver.getWidth(),
69                                                rBmpOver.getHeight() );
70     m_pImgOver->drawBitmap( rBmpOver, 0, 0 );
71
72     // States
73     m_fsm.addState( "up" );
74     m_fsm.addState( "over" );
75     m_fsm.addState( "down" );
76
77     // Transitions
78     m_fsm.addTransition( "over", "mouse:left:down", "down",
79                          &m_cmdOverDown );
80     m_fsm.addTransition( "down", "mouse:left:up", "over",
81                          &m_cmdDownOver );
82     m_fsm.addTransition( "over", "leave", "up", &m_cmdOverUp );
83     m_fsm.addTransition( "up", "enter", "over", &m_cmdUpOver );
84     m_fsm.addTransition( "down", "motion", "down", &m_cmdMove );
85     m_fsm.addTransition( "over", "scroll", "over", &m_cmdScroll );
86
87     // Initial state
88     m_fsm.setState( "up" );
89     m_pImg = m_pImgUp;
90
91     // Observe the position variable
92     m_rVariable.addObserver( this );
93
94     // Initial position of the cursor
95     m_lastPercentage = m_rVariable.get();
96 }
97
98
99 CtrlSliderCursor::~CtrlSliderCursor()
100 {
101     m_rVariable.delObserver( this );
102     delete m_pImgUp;
103     delete m_pImgDown;
104     delete m_pImgOver;
105 }
106
107
108 void CtrlSliderCursor::handleEvent( EvtGeneric &rEvent )
109 {
110     // Save the event to use it in callbacks
111     m_pEvt = &rEvent;
112
113     m_fsm.handleTransition( rEvent.getAsString() );
114 }
115
116
117 bool CtrlSliderCursor::mouseOver( int x, int y ) const
118 {
119     if( m_pImg )
120     {
121         // Compute the position of the cursor
122         int xPos, yPos;
123         m_rCurve.getPoint( m_rVariable.get(), xPos, yPos );
124
125         // Compute the resize factors
126         float factorX, factorY;
127         getResizeFactors( factorX, factorY );
128         xPos = (int)(xPos * factorX);
129         yPos = (int)(yPos * factorY);
130
131         return m_pImg->hit( x - xPos + m_pImg->getWidth() / 2,
132                             y - yPos + m_pImg->getHeight() / 2 );
133     }
134     else
135     {
136         return false;
137     }
138 }
139
140
141 void CtrlSliderCursor::draw( OSGraphics &rImage, int xDest, int yDest )
142 {
143     if( m_pImg )
144     {
145         // Compute the position of the cursor
146         int xPos, yPos;
147         m_rCurve.getPoint( m_rVariable.get(), xPos, yPos );
148
149         // Compute the resize factors
150         float factorX, factorY;
151         getResizeFactors( factorX, factorY );
152         xPos = (int)(xPos * factorX);
153         yPos = (int)(yPos * factorY);
154
155         // Draw the current image
156         rImage.drawGraphics( *m_pImg, 0, 0,
157                              xDest + xPos - m_pImg->getWidth() / 2,
158                              yDest + yPos - m_pImg->getHeight() / 2 );
159     }
160 }
161
162
163 void CtrlSliderCursor::onUpdate( Subject<VarPercent> &rVariable,
164                                  void *arg  )
165 {
166     // The position has changed
167     refreshLayout();
168 }
169
170
171 void CtrlSliderCursor::CmdOverDown::execute()
172 {
173     EvtMouse *pEvtMouse = (EvtMouse*)m_pParent->m_pEvt;
174
175     // Compute the resize factors
176     float factorX, factorY;
177     m_pParent->getResizeFactors( factorX, factorY );
178
179     // Get the position of the control
180     const Position *pPos = m_pParent->getPosition();
181
182     // Compute the offset
183     int tempX, tempY;
184     m_pParent->m_rCurve.getPoint( m_pParent->m_rVariable.get(), tempX, tempY );
185     m_pParent->m_xOffset = pEvtMouse->getXPos() - pPos->getLeft()
186                        - (int)(tempX * factorX);
187     m_pParent->m_yOffset = pEvtMouse->getYPos() - pPos->getTop()
188                        - (int)(tempY * factorY);
189
190     m_pParent->captureMouse();
191     m_pParent->m_pImg = m_pParent->m_pImgDown;
192     m_pParent->refreshLayout();
193 }
194
195
196 void CtrlSliderCursor::CmdDownOver::execute()
197 {
198     // Save the position
199     m_pParent->m_lastPercentage = m_pParent->m_rVariable.get();
200
201     m_pParent->releaseMouse();
202     m_pParent->m_pImg = m_pParent->m_pImgUp;
203     m_pParent->refreshLayout();
204 }
205
206
207 void CtrlSliderCursor::CmdUpOver::execute()
208 {
209     m_pParent->m_pImg = m_pParent->m_pImgOver;
210     m_pParent->refreshLayout();
211 }
212
213
214 void CtrlSliderCursor::CmdOverUp::execute()
215 {
216     m_pParent->m_pImg = m_pParent->m_pImgUp;
217     m_pParent->refreshLayout();
218 }
219
220
221 void CtrlSliderCursor::CmdMove::execute()
222 {
223     EvtMouse *pEvtMouse = (EvtMouse*)m_pParent->m_pEvt;
224
225     // Get the position of the control
226     const Position *pPos = m_pParent->getPosition();
227
228     // Compute the resize factors
229     float factorX, factorY;
230     m_pParent->getResizeFactors( factorX, factorY );
231
232     // Compute the relative position of the centre of the cursor
233     float relX = pEvtMouse->getXPos() - pPos->getLeft() - m_pParent->m_xOffset;
234     float relY = pEvtMouse->getYPos() - pPos->getTop() - m_pParent->m_yOffset;
235     // Ponderate with the resize factors
236     int relXPond = (int)(relX / factorX);
237     int relYPond = (int)(relY / factorY);
238
239     // Update the position
240     if( m_pParent->m_rCurve.getMinDist( relXPond, relYPond ) < RANGE )
241     {
242         float percentage = m_pParent->m_rCurve.getNearestPercent( relXPond,
243                                                               relYPond );
244         m_pParent->m_rVariable.set( percentage );
245     }
246     else
247     {
248         m_pParent->m_rVariable.set( m_pParent->m_lastPercentage );
249     }
250 }
251
252 void CtrlSliderCursor::CmdScroll::execute()
253 {
254     EvtScroll *pEvtScroll = (EvtScroll*)m_pParent->m_pEvt;
255
256     int direction = pEvtScroll->getDirection();
257
258     float percentage = m_pParent->m_rVariable.get();
259     if( direction == EvtScroll::kUp )
260     {
261         percentage += SCROLL_STEP;
262     }
263     else
264     {
265         percentage -= SCROLL_STEP;
266     }
267
268     m_pParent->m_rVariable.set( percentage );
269 }
270
271
272 void CtrlSliderCursor::getResizeFactors( float &rFactorX,
273                                          float &rFactorY ) const
274 {
275     // Get the position of the control
276     const Position *pPos = getPosition();
277
278     rFactorX = 1.0;
279     rFactorY = 1.0;
280
281     // Compute the resize factors
282     if( m_width > 0 )
283     {
284         rFactorX = (float)pPos->getWidth() / (float)m_width;
285     }
286     if( m_height > 0 )
287     {
288         rFactorY = (float)pPos->getHeight() / (float)m_height;
289     }
290 }
291
292
293 void CtrlSliderCursor::refreshLayout()
294 {
295     if( m_pImg )
296     {
297         // Compute the resize factors
298         float factorX, factorY;
299         getResizeFactors( factorX, factorY );
300
301         notifyLayout( (int)(m_rCurve.getWidth() * factorX) + m_pImg->getWidth(),
302                       (int)(m_rCurve.getHeight() * factorY) + m_pImg->getHeight(),
303                       - m_pImg->getWidth() / 2,
304                       - m_pImg->getHeight() / 2 );
305     }
306     else
307         notifyLayout();
308 }
309
310
311 CtrlSliderBg::CtrlSliderBg( intf_thread_t *pIntf,
312                             const Bezier &rCurve, VarPercent &rVariable,
313                             int thickness, GenericBitmap *pBackground,
314                             int nbHoriz, int nbVert, int padHoriz, int padVert,
315                             VarBool *pVisible, const UString &rHelp ):
316     CtrlGeneric( pIntf, rHelp, pVisible ), m_pCursor( NULL ),
317     m_rVariable( rVariable ), m_thickness( thickness ), m_rCurve( rCurve ),
318     m_width( rCurve.getWidth() ), m_height( rCurve.getHeight() ),
319     m_pImgSeq( pBackground ), m_nbHoriz( nbHoriz ), m_nbVert( nbVert ),
320     m_padHoriz( padHoriz ), m_padVert( padVert ), m_bgWidth( 0 ),
321     m_bgHeight( 0 ), m_position( 0 )
322 {
323     if( pBackground )
324     {
325         // Build the background image sequence
326         // Note: we suppose that the last padding is not included in the
327         // given image
328         // TODO: we should probably change this assumption, as it would make
329         // the code a bit simpler and it would be more natural for the skins
330         // designers
331         m_bgWidth = (pBackground->getWidth() + m_padHoriz) / nbHoriz;
332         m_bgHeight = (pBackground->getHeight() + m_padVert) / nbVert;
333
334         // Observe the position variable
335         m_rVariable.addObserver( this );
336
337         // Initial position
338         m_position = (int)( m_rVariable.get() * (m_nbHoriz * m_nbVert - 1) );
339     }
340 }
341
342
343 CtrlSliderBg::~CtrlSliderBg()
344 {
345     m_rVariable.delObserver( this );
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         if( m_bgWidth > 0 && m_bgHeight > 0 )
365         {
366             // Compute the resize factors
367             float factorX, factorY;
368             getResizeFactors( factorX, factorY );
369
370             // Rescale the image with the actual size of the control
371             ScaledBitmap bmp( getIntf(), *m_pImgSeq,
372                  m_bgWidth * m_nbHoriz - (int)(m_padHoriz * factorX),
373                  m_bgHeight * m_nbVert - (int)(m_padVert * factorY) );
374
375             // Locate the right image in the background bitmap
376             int x = m_bgWidth * ( m_position % m_nbHoriz );
377             int y = m_bgHeight * ( m_position / m_nbHoriz );
378             // Draw the background image
379             rImage.drawBitmap( bmp, x, y, xDest, yDest,
380                                m_bgWidth - (int)(m_padHoriz * factorX),
381                                m_bgHeight - (int)(m_padVert * factorY) );
382         }
383     }
384 }
385
386
387 void CtrlSliderBg::handleEvent( EvtGeneric &rEvent )
388 {
389     if( rEvent.getAsString().find( "mouse:left:down" ) != string::npos )
390     {
391         // Compute the resize factors
392         float factorX, factorY;
393         getResizeFactors( factorX, factorY );
394
395         // Get the position of the control
396         const Position *pPos = getPosition();
397
398         // Get the value corresponding to the position of the mouse
399         EvtMouse &rEvtMouse = (EvtMouse&)rEvent;
400         int x = rEvtMouse.getXPos();
401         int y = rEvtMouse.getYPos();
402         m_rVariable.set( m_rCurve.getNearestPercent(
403                             (int)((x - pPos->getLeft()) / factorX),
404                             (int)((y - pPos->getTop()) / factorY) ) );
405
406         // Forward the clic to the cursor
407         EvtMouse evt( getIntf(), x, y, EvtMouse::kLeft, EvtMouse::kDown );
408         TopWindow *pWin = getWindow();
409         if( pWin && m_pCursor )
410         {
411             EvtEnter evtEnter( getIntf() );
412             // XXX It was not supposed to be implemented like that !!
413             pWin->forwardEvent( evtEnter, *m_pCursor );
414             pWin->forwardEvent( evt, *m_pCursor );
415         }
416     }
417     else if( rEvent.getAsString().find( "scroll" ) != string::npos )
418     {
419         int direction = ((EvtScroll&)rEvent).getDirection();
420
421         float percentage = m_rVariable.get();
422         if( direction == EvtScroll::kUp )
423         {
424             percentage += SCROLL_STEP;
425         }
426         else
427         {
428             percentage -= SCROLL_STEP;
429         }
430
431         m_rVariable.set( percentage );
432     }
433 }
434
435
436 void CtrlSliderBg::onResize()
437 {
438     if( m_pImgSeq )
439     {
440         // Compute only the new size of an elementary image.
441         // The actual resizing is done in the draw() method for now...
442
443         // Compute the resize factors
444         float factorX, factorY;
445         getResizeFactors( factorX, factorY );
446
447         // Size of one elementary background image (padding included)
448         m_bgWidth = (int)((m_pImgSeq->getWidth() + m_padHoriz) * factorX / m_nbHoriz);
449         m_bgHeight = (int)((m_pImgSeq->getHeight() + m_padVert) * factorY / m_nbVert);
450     }
451 }
452
453
454 void CtrlSliderBg::associateCursor( CtrlSliderCursor &rCursor )
455 {
456     m_pCursor = &rCursor;
457 }
458
459
460 void CtrlSliderBg::onUpdate( Subject<VarPercent> &rVariable, void*arg )
461 {
462     m_position = (int)( m_rVariable.get() * (m_nbHoriz * m_nbVert - 1) );
463     notifyLayout( m_bgWidth, m_bgHeight );
464 }
465
466
467 void CtrlSliderBg::getResizeFactors( float &rFactorX, float &rFactorY ) const
468 {
469     // Get the position of the control
470     const Position *pPos = getPosition();
471
472     rFactorX = 1.0;
473     rFactorY = 1.0;
474
475     // Compute the resize factors
476     if( m_width > 0 )
477     {
478         rFactorX = (float)pPos->getWidth() / (float)m_width;
479     }
480     if( m_height > 0 )
481     {
482         rFactorY = (float)pPos->getHeight() / (float)m_height;
483     }
484 }
485