]> git.sesse.net Git - vlc/blob - modules/gui/skins2/src/generic_window.cpp
a09ff4fabe90ddf49c1b60f65835ade4b70d1e82
[vlc] / modules / gui / skins2 / src / generic_window.cpp
1 /*****************************************************************************
2  * generic_window.cpp
3  *****************************************************************************
4  * Copyright (C) 2003 VideoLAN
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 "generic_window.hpp"
26 #include "generic_layout.hpp"
27 #include "os_graphics.hpp"
28 #include "os_window.hpp"
29 #include "os_factory.hpp"
30 #include "theme.hpp"
31 #include "dialogs.hpp"
32 #include "var_manager.hpp"
33 #include "../commands/cmd_on_top.hpp"
34 #include "../controls/ctrl_generic.hpp"
35 #include "../events/evt_enter.hpp"
36 #include "../events/evt_focus.hpp"
37 #include "../events/evt_leave.hpp"
38 #include "../events/evt_motion.hpp"
39 #include "../events/evt_mouse.hpp"
40 #include "../events/evt_key.hpp"
41 #include "../events/evt_refresh.hpp"
42 #include "../events/evt_special.hpp"
43 #include "../events/evt_scroll.hpp"
44 #include "../utils/position.hpp"
45 #include "../utils/ustring.hpp"
46
47 #include <vlc_keys.h>
48
49
50 GenericWindow::GenericWindow( intf_thread_t *pIntf, int left, int top,
51                               WindowManager &rWindowManager,
52                               bool dragDrop, bool playOnDrop ):
53     SkinObject( pIntf ), m_rWindowManager( rWindowManager ),
54     m_left( left ), m_top( top ), m_width( 0 ), m_height( 0 ),
55     m_pActiveLayout( NULL ), m_pLastHitControl( NULL ),
56     m_pCapturingControl( NULL ), m_pFocusControl( NULL ), m_varVisible( pIntf )
57 {
58     // Register as a moving window
59     m_rWindowManager.registerWindow( *this );
60
61     // Get the OSFactory
62     OSFactory *pOsFactory = OSFactory::instance( getIntf() );
63
64     // Create an OSWindow to handle OS specific processing
65     m_pOsWindow = pOsFactory->createOSWindow( *this, dragDrop, playOnDrop );
66
67     // Observe the visibility variable
68     m_varVisible.addObserver( this );
69 }
70
71
72 GenericWindow::~GenericWindow()
73 {
74     m_varVisible.delObserver( this );
75     // Unregister from the window manager
76     m_rWindowManager.unregisterWindow( *this );
77
78     if( m_pOsWindow )
79     {
80         delete m_pOsWindow;
81     }
82 }
83
84
85 void GenericWindow::processEvent( EvtFocus &rEvtFocus )
86 {
87 //    fprintf(stderr, rEvtFocus.getAsString().c_str()) ;
88 }
89
90
91 void GenericWindow::processEvent( EvtMotion &rEvtMotion )
92 {
93     // New control hit by the mouse
94     CtrlGeneric *pNewHitControl =
95         findHitControl( rEvtMotion.getXPos() - m_left,
96                         rEvtMotion.getYPos() - m_top );
97
98     setLastHit( pNewHitControl );
99
100     /// Update the help text
101     VarManager *pVarManager = VarManager::instance( getIntf() );
102     if( pNewHitControl )
103     {
104         pVarManager->getHelpText().set( pNewHitControl->getHelpText() );
105     }
106
107     // Send a motion event to the hit control, or to the control
108     // that captured the mouse, if any
109     CtrlGeneric *pActiveControl = pNewHitControl;
110     if( m_pCapturingControl )
111     {
112         pActiveControl = m_pCapturingControl;
113     }
114     if( pActiveControl )
115     {
116         // Compute the coordinates relative to the window
117         int xPos = rEvtMotion.getXPos() - m_left;
118         int yPos = rEvtMotion.getYPos() - m_top;
119         // Send a motion event
120         EvtMotion evt( getIntf(), xPos, yPos );
121         pActiveControl->handleEvent( evt );
122     }
123 }
124
125
126 void GenericWindow::processEvent( EvtLeave &rEvtLeave )
127 {
128     // No more hit control
129     setLastHit( NULL );
130
131     if( !m_pCapturingControl )
132     {
133         m_rWindowManager.hideTooltip();
134     }
135 }
136
137
138 void GenericWindow::processEvent( EvtMouse &rEvtMouse )
139 {
140     // Get the control hit by the mouse
141     CtrlGeneric *pNewHitControl = findHitControl( rEvtMouse.getXPos(),
142                                                   rEvtMouse.getYPos() );
143     setLastHit( pNewHitControl );
144
145     // Change the focused control
146     if( rEvtMouse.getAction() == EvtMouse::kDown )
147     {
148         // Raise all the windows
149         m_rWindowManager.raiseAll( *this );
150
151         if( pNewHitControl && pNewHitControl->isFocusable() )
152         {
153             // If a new control gains the focus, the previous one loses it
154             if( m_pFocusControl && m_pFocusControl != pNewHitControl )
155             {
156                 EvtFocus evt( getIntf(), false );
157                 m_pFocusControl->handleEvent( evt );
158             }
159             if( pNewHitControl != m_pFocusControl )
160             {
161                 m_pFocusControl = pNewHitControl;
162                 EvtFocus evt( getIntf(), false );
163                 pNewHitControl->handleEvent( evt );
164             }
165         }
166         else if( m_pFocusControl )
167         {
168             // The previous control loses the focus
169             EvtFocus evt( getIntf(), false );
170             m_pFocusControl->handleEvent( evt );
171             m_pFocusControl = NULL;
172         }
173     }
174
175     // Send a mouse event to the hit control, or to the control
176     // that captured the mouse, if any
177     CtrlGeneric *pActiveControl = pNewHitControl;
178     if( m_pCapturingControl )
179     {
180         pActiveControl = m_pCapturingControl;
181     }
182     if( pActiveControl )
183     {
184         pActiveControl->handleEvent( rEvtMouse );
185     }
186 }
187
188
189 void GenericWindow::processEvent( EvtKey &rEvtKey )
190 {
191     // Forward the event to the focused control, if any
192     if( m_pFocusControl )
193     {
194         m_pFocusControl->handleEvent( rEvtKey );
195     }
196
197     // Only do the action when the key is down
198     else if( rEvtKey.getAsString().find( "key:down") != string::npos )
199     {
200         //XXX not to be hardcoded !
201         // Ctrl-S = Change skin
202         if( (rEvtKey.getMod() & EvtInput::kModCtrl) &&
203             rEvtKey.getKey() == 's' )
204         {
205             Dialogs *pDialogs = Dialogs::instance( getIntf() );
206             if( pDialogs != NULL )
207             {
208                 pDialogs->showChangeSkin();
209             }
210             return;
211         }
212
213         //XXX not to be hardcoded !
214         // Ctrl-T = Toggle on top
215         if( (rEvtKey.getMod() & EvtInput::kModCtrl) &&
216             rEvtKey.getKey() == 't' )
217         {
218             CmdOnTop cmd( getIntf() );
219             cmd.execute();
220             return;
221         }
222
223         vlc_value_t val;
224         // Set the key
225         val.i_int = rEvtKey.getKey();
226         // Set the modifiers
227         if( rEvtKey.getMod() & EvtInput::kModAlt )
228         {
229             val.i_int |= KEY_MODIFIER_ALT;
230         }
231         if( rEvtKey.getMod() & EvtInput::kModCtrl )
232         {
233             val.i_int |= KEY_MODIFIER_CTRL;
234         }
235         if( rEvtKey.getMod() & EvtInput::kModShift )
236         {
237             val.i_int |= KEY_MODIFIER_SHIFT;
238         }
239
240         var_Set( getIntf()->p_vlc, "key-pressed", val );
241     }
242 }
243
244
245 void GenericWindow::processEvent( EvtRefresh &rEvtRefresh )
246 {
247     // Refresh the given area
248     refresh( rEvtRefresh.getXStart(), rEvtRefresh.getYStart(),
249              rEvtRefresh.getWidth(), rEvtRefresh.getHeight() );
250 }
251
252
253 void GenericWindow::processEvent( EvtScroll &rEvtScroll )
254 {
255     // Raise the windows
256     raise();
257
258     // Get the control hit by the mouse
259     CtrlGeneric *pNewHitControl = findHitControl( rEvtScroll.getXPos(),
260                                                   rEvtScroll.getYPos());
261
262     setLastHit( pNewHitControl );
263
264     // Send a mouse event to the hit control, or to the control
265     // that captured the mouse, if any
266     CtrlGeneric *pActiveControl = pNewHitControl;
267
268     if( m_pCapturingControl )
269     {
270         pActiveControl = m_pCapturingControl;
271     }
272     if( pActiveControl )
273     {
274         pActiveControl->handleEvent( rEvtScroll );
275     }
276 }
277
278
279 void GenericWindow::forwardEvent( EvtGeneric &rEvt, CtrlGeneric &rCtrl )
280 {
281     // XXX: We should do some checks here
282     rCtrl.handleEvent( rEvt );
283 }
284
285
286 void GenericWindow::show()
287 {
288     m_varVisible.set( true );
289 }
290
291
292 void GenericWindow::hide()
293 {
294     m_varVisible.set( false );
295 }
296
297
298 void GenericWindow::refresh( int left, int top, int width, int height )
299 {
300     if( m_pActiveLayout )
301     {
302         m_pActiveLayout->getImage()->copyToWindow( *m_pOsWindow, left, top,
303                                                    width, height, left, top );
304     }
305 }
306
307
308 void GenericWindow::move( int left, int top )
309 {
310     // Update the window coordinates
311     m_left = left;
312     m_top = top;
313
314     m_pOsWindow->moveResize( left, top, m_width, m_height );
315 }
316
317
318 void GenericWindow::resize( int width, int height )
319 {
320     // Update the window size
321     m_width = width;
322     m_height = height;
323
324     m_pOsWindow->moveResize( m_left, m_top, width, height );
325 }
326
327
328 void GenericWindow::raise() const
329 {
330     m_pOsWindow->raise();
331 }
332
333
334 void GenericWindow::setOpacity( uint8_t value )
335 {
336     m_pOsWindow->setOpacity( value );
337 }
338
339
340 void GenericWindow::toggleOnTop( bool onTop ) const
341 {
342     m_pOsWindow->toggleOnTop( onTop );
343 }
344
345
346 void GenericWindow::setActiveLayout( GenericLayout *pLayout )
347 {
348     pLayout->setWindow( this );
349     m_pActiveLayout = pLayout;
350     // Get the size of the layout and resize the window
351     m_width = pLayout->getWidth();
352     m_height = pLayout->getHeight();
353     m_pOsWindow->moveResize( m_left, m_top, m_width, m_height );
354     updateShape();
355     pLayout->refreshAll();
356 }
357
358
359 void GenericWindow::updateShape()
360 {
361     // Set the shape of the window
362     if( m_pActiveLayout )
363     {
364         OSGraphics *pImage = m_pActiveLayout->getImage();
365         if( pImage )
366         {
367             pImage->applyMaskToWindow( *m_pOsWindow );
368         }
369     }
370 }
371
372
373 const list<Anchor*> GenericWindow::getAnchorList() const
374 {
375     return m_anchorList;
376 }
377
378
379 void GenericWindow::addAnchor( Anchor *pAnchor )
380 {
381     m_anchorList.push_back( pAnchor );
382 }
383
384
385 void GenericWindow::onControlCapture( const CtrlGeneric &rCtrl )
386 {
387     // Set the capturing control
388     m_pCapturingControl = (CtrlGeneric*) &rCtrl;
389 }
390
391
392 void GenericWindow::onControlRelease( const CtrlGeneric &rCtrl )
393 {
394     // Release the capturing control
395     if( m_pCapturingControl == &rCtrl )
396     {
397         m_pCapturingControl = NULL;
398     }
399     else
400     {
401         msg_Dbg( getIntf(), "Control had not captured the mouse" );
402     }
403
404     // Send an enter event to the control under the mouse, if it doesn't
405     // have received it yet
406     if( m_pLastHitControl && m_pLastHitControl != &rCtrl )
407     {
408         EvtEnter evt( getIntf() );
409         m_pLastHitControl->handleEvent( evt );
410
411         // Show the tooltip
412         m_rWindowManager.hideTooltip();
413         UString tipText = m_pLastHitControl->getTooltipText();
414         if( tipText.length() > 0 )
415         {
416             // Set the tooltip text variable
417             VarManager *pVarManager = VarManager::instance( getIntf() );
418             pVarManager->getTooltipText().set( tipText );
419             m_rWindowManager.showTooltip();
420         }
421     }
422 }
423
424
425 void GenericWindow::onTooltipChange( const CtrlGeneric &rCtrl )
426 {
427     // Check that the control is the active one
428     if( m_pLastHitControl && m_pLastHitControl == &rCtrl )
429     {
430         // Set the tooltip text variable
431         VarManager *pVarManager = VarManager::instance( getIntf() );
432         pVarManager->getTooltipText().set( rCtrl.getTooltipText() );
433     }
434 }
435
436
437 void GenericWindow::onUpdate( Subject<VarBool> &rVariable )
438 {
439     if( m_varVisible.get() )
440     {
441         innerShow();
442     }
443     else
444     {
445         innerHide();
446     }
447 }
448
449
450 void GenericWindow::innerShow()
451 {
452     // First, refresh the layout and update the shape of the window
453     if( m_pActiveLayout )
454     {
455         updateShape();
456         m_pActiveLayout->refreshAll();
457     }
458
459     if( m_pOsWindow )
460     {
461         m_pOsWindow->show( m_left, m_top );
462     }
463 }
464
465
466 void GenericWindow::innerHide()
467 {
468     if( m_pOsWindow )
469     {
470         m_pOsWindow->hide();
471     }
472 }
473
474
475 CtrlGeneric *GenericWindow::findHitControl( int xPos, int yPos )
476 {
477     if( m_pActiveLayout == NULL )
478     {
479         return NULL;
480     }
481
482     // Get the controls in the active layout
483     const list<LayeredControl> &ctrlList = m_pActiveLayout->getControlList();
484     list<LayeredControl>::const_reverse_iterator iter;
485
486     // New control hit by the mouse
487     CtrlGeneric *pNewHitControl = NULL;
488
489     // Loop on the control list to find the uppest hit control
490     for( iter = ctrlList.rbegin(); iter != ctrlList.rend(); iter++ )
491     {
492         // Get the position of the control in the layout
493         const Position *pos = (*iter).m_pControl->getPosition();
494         if( pos != NULL )
495         {
496             // Compute the coordinates of the mouse relative to the control
497             int xRel = xPos - pos->getLeft();
498             int yRel = yPos - pos->getTop();
499
500             CtrlGeneric *pCtrl = (*iter).m_pControl;
501             // Control hit ?
502             if( pCtrl->isVisible() && pCtrl->mouseOver( xRel, yRel ) )
503             {
504                 pNewHitControl = (*iter).m_pControl;
505                 break;
506             }
507         }
508         else
509         {
510             msg_Dbg( getIntf(), "Control at NULL position" );
511         }
512     }
513
514     // If the hit control has just been entered, send it an enter event
515     if( pNewHitControl && (pNewHitControl != m_pLastHitControl) )
516     {
517         // Don't send the event if another control captured the mouse
518         if( !m_pCapturingControl || (m_pCapturingControl == pNewHitControl ) )
519         {
520             EvtEnter evt( getIntf() );
521             pNewHitControl->handleEvent( evt );
522
523             if( !m_pCapturingControl )
524             {
525                 // Show the tooltip
526                 m_rWindowManager.hideTooltip();
527                 UString tipText = pNewHitControl->getTooltipText();
528                 if( tipText.length() > 0 )
529                 {
530                     // Set the tooltip text variable
531                     VarManager *pVarManager = VarManager::instance( getIntf() );
532                     pVarManager->getTooltipText().set( tipText );
533                     m_rWindowManager.showTooltip();
534                 }
535             }
536         }
537     }
538
539     return pNewHitControl;
540 }
541
542
543
544 void GenericWindow::setLastHit( CtrlGeneric *pNewHitControl )
545 {
546     // Send a leave event to the left control
547     if( m_pLastHitControl && (pNewHitControl != m_pLastHitControl) )
548     {
549         // Don't send the event if another control captured the mouse
550         if( !m_pCapturingControl || (m_pCapturingControl == m_pLastHitControl))
551         {
552             EvtLeave evt( getIntf() );
553             m_pLastHitControl->handleEvent( evt );
554         }
555     }
556
557     m_pLastHitControl = pNewHitControl;
558 }
559