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