]> git.sesse.net Git - vlc/blob - modules/gui/skins2/controls/ctrl_checkbox.cpp
skins2: better cleanup for button and checkbox
[vlc] / modules / gui / skins2 / controls / ctrl_checkbox.cpp
1 /*****************************************************************************
2  * ctrl_checkbox.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_checkbox.hpp"
26 #include "../events/evt_generic.hpp"
27 #include "../commands/cmd_generic.hpp"
28 #include "../src/generic_bitmap.hpp"
29 #include "../src/os_factory.hpp"
30 #include "../src/os_graphics.hpp"
31 #include "../utils/var_bool.hpp"
32
33
34 CtrlCheckbox::CtrlCheckbox( intf_thread_t *pIntf,
35                             const GenericBitmap &rBmpUp1,
36                             const GenericBitmap &rBmpOver1,
37                             const GenericBitmap &rBmpDown1,
38                             const GenericBitmap &rBmpUp2,
39                             const GenericBitmap &rBmpOver2,
40                             const GenericBitmap &rBmpDown2,
41                             CmdGeneric &rCommand1, CmdGeneric &rCommand2,
42                             const UString &rTooltip1,
43                             const UString &rTooltip2,
44                             VarBool &rVariable, const UString &rHelp,
45                             VarBool *pVisible ):
46     CtrlGeneric( pIntf, rHelp, pVisible ), m_fsm( pIntf ),
47     m_rVariable( rVariable ),
48     m_rCommand1( rCommand1 ), m_rCommand2( rCommand2 ),
49     m_tooltip1( rTooltip1 ), m_tooltip2( rTooltip2 ),
50     m_imgUp1( pIntf, rBmpUp1 ), m_imgOver1( pIntf, rBmpOver1 ),
51     m_imgDown1( pIntf, rBmpDown1 ), m_imgUp2( pIntf, rBmpUp2 ),
52     m_imgOver2( pIntf, rBmpOver2 ), m_imgDown2( pIntf, rBmpDown2 ),
53     m_cmdUpOverDownOver( this ), m_cmdDownOverUpOver( this ),
54     m_cmdDownOverDown( this ), m_cmdDownDownOver( this ),
55     m_cmdUpOverUp( this ), m_cmdUpUpOver( this ),
56     m_cmdDownUp( this ), m_cmdUpHidden( this ),
57     m_cmdHiddenUp( this )
58 {
59     // States
60     m_fsm.addState( "up" );
61     m_fsm.addState( "down" );
62     m_fsm.addState( "upOver" );
63     m_fsm.addState( "downOver" );
64     m_fsm.addState( "hidden" );
65
66     // Transitions
67     m_fsm.addTransition( "upOver", "mouse:left:down", "downOver",
68                          &m_cmdUpOverDownOver );
69     m_fsm.addTransition( "upOver", "mouse:left:dblclick", "downOver",
70                          &m_cmdUpOverDownOver );
71     m_fsm.addTransition( "downOver", "mouse:left:up", "upOver",
72                          &m_cmdDownOverUpOver );
73     m_fsm.addTransition( "downOver", "leave", "down", &m_cmdDownOverDown );
74     m_fsm.addTransition( "down", "enter", "downOver", &m_cmdDownDownOver );
75     m_fsm.addTransition( "upOver", "leave", "up", &m_cmdUpOverUp );
76     m_fsm.addTransition( "up", "enter", "upOver", &m_cmdUpUpOver );
77     m_fsm.addTransition( "down", "mouse:left:up", "up", &m_cmdDownUp );
78     // XXX: It would be easy to use a "ANY" initial state to handle these
79     // four lines in only one. But till now it isn't worthwhile...
80     m_fsm.addTransition( "up", "special:hide", "hidden", &m_cmdUpHidden );
81     m_fsm.addTransition( "down", "special:hide", "hidden", &m_cmdUpHidden );
82     m_fsm.addTransition( "upOver", "special:hide", "hidden", &m_cmdUpHidden );
83     m_fsm.addTransition( "downOver", "special:hide", "hidden", &m_cmdUpHidden );
84     m_fsm.addTransition( "hidden", "special:show", "up", &m_cmdHiddenUp );
85
86     // Observe the variable
87     m_rVariable.addObserver( this );
88
89     // Initial state
90     m_fsm.setState( "up" );
91     if( !m_rVariable.get() )
92     {
93         m_pImgUp = &m_imgUp1;
94         m_pImgOver = &m_imgOver1;
95         m_pImgDown = &m_imgDown1;
96         m_pImgCurrent = m_pImgUp;
97         m_pCommand = &m_rCommand1;
98         m_pTooltip = &m_tooltip1;
99     }
100     else
101     {
102         m_pImgUp = &m_imgUp2;
103         m_pImgOver = &m_imgOver2;
104         m_pImgDown = &m_imgDown2;
105         m_pImgCurrent = m_pImgUp;
106         m_pCommand = &m_rCommand2;
107         m_pTooltip = &m_tooltip2;
108     }
109 }
110
111
112 CtrlCheckbox::~CtrlCheckbox()
113 {
114     if( m_pImgCurrent )
115     {
116         m_pImgCurrent->stopAnim();
117         m_pImgCurrent->delObserver( this );
118     }
119     m_rVariable.delObserver( this );
120 }
121
122
123 void CtrlCheckbox::handleEvent( EvtGeneric &rEvent )
124 {
125     m_fsm.handleTransition( rEvent.getAsString() );
126 }
127
128
129 bool CtrlCheckbox::mouseOver( int x, int y ) const
130 {
131     if( m_pImgCurrent )
132     {
133         return m_pImgCurrent->hit( x, y );
134     }
135     else
136     {
137         return false;
138     }
139 }
140
141
142 void CtrlCheckbox::draw( OSGraphics &rImage, int xDest, int yDest, int w, int h )
143 {
144     if( !m_pImgCurrent )
145         return;
146
147     const Position *pPos = getPosition();
148     // rect region( pPos->getLeft(), pPos->getTop(),
149     //              pPos->getWidth(), pPos->getHeight() );
150     rect region( pPos->getLeft(), pPos->getTop(),
151                  m_pImgCurrent->getWidth(), m_pImgCurrent->getHeight() );
152     rect clip( xDest, yDest, w, h );
153     rect inter;
154     if( rect::intersect( region, clip, &inter ) )
155     {
156         // Draw the current image
157         m_pImgCurrent->draw( rImage,
158                       inter.x, inter.y, inter.width, inter.height,
159                       inter.x - pPos->getLeft(),
160                       inter.y - pPos->getTop() );
161     }
162 }
163
164
165 void CtrlCheckbox::setImage( AnimBitmap *pImg )
166 {
167     if( pImg == m_pImgCurrent )
168         return;
169
170     AnimBitmap *pOldImg = m_pImgCurrent;
171     m_pImgCurrent = pImg;
172
173     if( pOldImg )
174     {
175         pOldImg->stopAnim();
176         pOldImg->delObserver( this );
177     }
178
179     if( pImg )
180     {
181         pImg->startAnim();
182         pImg->addObserver( this );
183     }
184
185     notifyLayoutMaxSize( pOldImg, pImg );
186 }
187
188
189 void CtrlCheckbox::CmdUpOverDownOver::execute()
190 {
191     m_pParent->captureMouse();
192     m_pParent->setImage( m_pParent->m_pImgDown );
193 }
194
195
196 void CtrlCheckbox::CmdDownOverUpOver::execute()
197 {
198     m_pParent->releaseMouse();
199
200     // There is a little trick here: since we update the image of the control
201     // before executing the command, there is no way that the observed variable
202     // can have changed, so changeButton() has not been called, and m_pImgUp is
203     // still the "old" up state. That's why we don't use it, and use the other
204     // one instead. Otherwise, we would notice a "phantom effect", where the
205     // old up image is displayed for a few milliseconds, until the variable is
206     // updated and the correct up image is displayed.
207     // Executing the action before refreshing the state wouldn't work, because
208     // the variable may be updated asynchronously (when triggered by a callback
209     // from an object variable).
210
211     // Invert the state variable
212     if( m_pParent->m_pImgUp == &m_pParent->m_imgUp1 )
213         m_pParent->setImage( &m_pParent->m_imgUp2 );
214     else
215         m_pParent->setImage( &m_pParent->m_imgUp1 );
216
217     // Execute the command
218     m_pParent->m_pCommand->execute();
219 }
220
221
222 void CtrlCheckbox::CmdDownOverDown::execute()
223 {
224     m_pParent->setImage( m_pParent->m_pImgUp );
225 }
226
227
228 void CtrlCheckbox::CmdDownDownOver::execute()
229 {
230     m_pParent->setImage( m_pParent->m_pImgDown );
231 }
232
233
234 void CtrlCheckbox::CmdUpUpOver::execute()
235 {
236     m_pParent->setImage( m_pParent->m_pImgOver );
237 }
238
239
240 void CtrlCheckbox::CmdUpOverUp::execute()
241 {
242     m_pParent->setImage( m_pParent->m_pImgUp );
243 }
244
245
246 void CtrlCheckbox::CmdDownUp::execute()
247 {
248     m_pParent->releaseMouse();
249 }
250
251
252 void CtrlCheckbox::CmdUpHidden::execute()
253 {
254     m_pParent->setImage( NULL );
255 }
256
257
258 void CtrlCheckbox::CmdHiddenUp::execute()
259 {
260     m_pParent->setImage( m_pParent->m_pImgUp );
261 }
262
263
264 void CtrlCheckbox::onVarBoolUpdate( VarBool &rVariable )
265 {
266     changeButton();
267 }
268
269
270 void CtrlCheckbox::onUpdate( Subject<AnimBitmap> &rBitmap, void *arg )
271 {
272     notifyLayout( m_pImgCurrent->getWidth(), m_pImgCurrent->getHeight() );
273 }
274
275
276 void CtrlCheckbox::changeButton()
277 {
278     // Are we using the first set of images or the second one?
279     if( m_pImgUp == &m_imgUp1 )
280     {
281         m_pImgUp = &m_imgUp2;
282         m_pImgOver = &m_imgOver2;
283         m_pImgDown = &m_imgDown2;
284         m_pTooltip = &m_tooltip2;
285         m_pCommand = &m_rCommand2;
286     }
287     else
288     {
289         m_pImgUp = &m_imgUp1;
290         m_pImgOver = &m_imgOver1;
291         m_pImgDown = &m_imgDown1;
292         m_pTooltip = &m_tooltip1;
293         m_pCommand = &m_rCommand1;
294     }
295     // XXX: We assume that the checkbox is up
296     setImage( m_pImgUp );
297
298     // Notify the window the tooltip has changed
299     notifyTooltipChange();
300 }
301