]> git.sesse.net Git - vlc/blob - modules/gui/skins2/controls/ctrl_checkbox.cpp
Qt4 - should fix the double-free and the freezing on quit. Ref #1348
[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     m_rVariable.delObserver( this );
115 }
116
117
118 void CtrlCheckbox::handleEvent( EvtGeneric &rEvent )
119 {
120     m_fsm.handleTransition( rEvent.getAsString() );
121 }
122
123
124 bool CtrlCheckbox::mouseOver( int x, int y ) const
125 {
126     if( m_pImgCurrent )
127     {
128         return m_pImgCurrent->hit( x, y );
129     }
130     else
131     {
132         return false;
133     }
134 }
135
136
137 void CtrlCheckbox::draw( OSGraphics &rImage, int xDest, int yDest )
138 {
139     if( m_pImgCurrent )
140     {
141         // Draw the current image
142         m_pImgCurrent->draw( rImage, xDest, yDest );
143     }
144 }
145
146
147 void CtrlCheckbox::setImage( AnimBitmap *pImg )
148 {
149     AnimBitmap *pOldImg = m_pImgCurrent;
150     m_pImgCurrent = pImg;
151
152     if( pOldImg )
153     {
154         pOldImg->stopAnim();
155         pOldImg->delObserver( this );
156     }
157
158     if( pImg )
159     {
160         pImg->startAnim();
161         pImg->addObserver( this );
162     }
163
164     notifyLayoutMaxSize( pOldImg, pImg );
165 }
166
167
168 void CtrlCheckbox::CmdUpOverDownOver::execute()
169 {
170     m_pParent->captureMouse();
171     m_pParent->setImage( m_pParent->m_pImgDown );
172 }
173
174
175 void CtrlCheckbox::CmdDownOverUpOver::execute()
176 {
177     m_pParent->releaseMouse();
178
179     // There is a little trick here: since we update the image of the control
180     // before executing the command, there is no way that the observed variable
181     // can have changed, so changeButton() has not been called, and m_pImgUp is
182     // still the "old" up state. That's why we don't use it, and use the other
183     // one instead. Otherwise, we would notice a "phantom effect", where the
184     // old up image is displayed for a few milliseconds, until the variable is
185     // updated and the correct up image is displayed.
186     // Executing the action before refreshing the state wouldn't work, because
187     // the variable may be updated asynchronously (when triggered by a callback
188     // from an object variable).
189
190     // Invert the state variable
191     if( m_pParent->m_pImgUp == &m_pParent->m_imgUp1 )
192         m_pParent->setImage( &m_pParent->m_imgUp2 );
193     else
194         m_pParent->setImage( &m_pParent->m_imgUp1 );
195
196     // Execute the command
197     m_pParent->m_pCommand->execute();
198 }
199
200
201 void CtrlCheckbox::CmdDownOverDown::execute()
202 {
203     m_pParent->setImage( m_pParent->m_pImgUp );
204 }
205
206
207 void CtrlCheckbox::CmdDownDownOver::execute()
208 {
209     m_pParent->setImage( m_pParent->m_pImgDown );
210 }
211
212
213 void CtrlCheckbox::CmdUpUpOver::execute()
214 {
215     m_pParent->setImage( m_pParent->m_pImgOver );
216 }
217
218
219 void CtrlCheckbox::CmdUpOverUp::execute()
220 {
221     m_pParent->setImage( m_pParent->m_pImgUp );
222 }
223
224
225 void CtrlCheckbox::CmdDownUp::execute()
226 {
227     m_pParent->releaseMouse();
228 }
229
230
231 void CtrlCheckbox::CmdUpHidden::execute()
232 {
233     m_pParent->setImage( NULL );
234 }
235
236
237 void CtrlCheckbox::CmdHiddenUp::execute()
238 {
239     m_pParent->setImage( m_pParent->m_pImgUp );
240 }
241
242
243 void CtrlCheckbox::onVarBoolUpdate( VarBool &rVariable )
244 {
245     changeButton();
246 }
247
248
249 void CtrlCheckbox::onUpdate( Subject<AnimBitmap> &rBitmap, void *arg )
250 {
251     notifyLayout();
252 }
253
254
255 void CtrlCheckbox::changeButton()
256 {
257     // Are we using the first set of images or the second one?
258     if( m_pImgUp == &m_imgUp1 )
259     {
260         m_pImgUp = &m_imgUp2;
261         m_pImgOver = &m_imgOver2;
262         m_pImgDown = &m_imgDown2;
263         m_pTooltip = &m_tooltip2;
264         m_pCommand = &m_rCommand2;
265     }
266     else
267     {
268         m_pImgUp = &m_imgUp1;
269         m_pImgOver = &m_imgOver1;
270         m_pImgDown = &m_imgDown1;
271         m_pTooltip = &m_tooltip1;
272         m_pCommand = &m_rCommand1;
273     }
274     // XXX: We assume that the checkbox is up
275     setImage( m_pImgUp );
276
277     // Notify the window the tooltip has changed
278     notifyTooltipChange();
279     // Refresh
280     notifyLayout();
281 }
282