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