]> git.sesse.net Git - vlc/blob - modules/gui/qt4/extensions_manager.cpp
Extensions/Qt: fix the deactivation race condition
[vlc] / modules / gui / qt4 / extensions_manager.cpp
1 /*****************************************************************************
2  * extensions_manager.cpp: Extensions manager for Qt
3  ****************************************************************************
4  * Copyright (C) 2009-2010 VideoLAN and authors
5  * $Id$
6  *
7  * Authors: Jean-Philippe AndrĂ© < jpeg # videolan.org >
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #include "extensions_manager.hpp"
25 #include "input_manager.hpp"
26 #include "dialogs/extensions.hpp"
27
28 #include "assert.h"
29
30 #include <QMenu>
31 #include <QAction>
32 #include <QSignalMapper>
33 #include <QIcon>
34
35 #define MENU_MAP(a,e) ((uint32_t)( (((uint16_t)a) << 16) | ((uint16_t)e) ))
36 #define MENU_GET_ACTION(a) ( (uint16_t)( ((uint32_t)a) >> 16 ) )
37 #define MENU_GET_EXTENSION(a) ( (uint16_t)( ((uint32_t)a) & 0xFFFF ) )
38
39 ExtensionsManager* ExtensionsManager::instance = NULL;
40
41 ExtensionsManager::ExtensionsManager( intf_thread_t *_p_intf, QObject *parent )
42         : QObject( parent ), p_intf( _p_intf ), p_extensions_manager( NULL )
43         , p_edp( NULL )
44 {
45     assert( ExtensionsManager::instance == NULL );
46     instance = this;
47
48     menuMapper = new QSignalMapper( this );
49     CONNECT( menuMapper, mapped( int ), this, triggerMenu( int ) );
50     CONNECT( THEMIM, inputChanged( input_thread_t* ),
51              this, inputChanged( input_thread_t* ) );
52     b_unloading = false;
53     b_failed = false;
54 }
55
56 ExtensionsManager::~ExtensionsManager()
57 {
58     ExtensionsDialogProvider::killInstance();
59     if( p_extensions_manager )
60     {
61         module_unneed( p_extensions_manager, p_extensions_manager->p_module );
62         vlc_object_release( p_extensions_manager );
63     }
64 }
65
66 bool ExtensionsManager::loadExtensions()
67 {
68     if( !p_extensions_manager )
69     {
70         p_extensions_manager = ( extensions_manager_t* )
71                     vlc_object_create( p_intf, sizeof( extensions_manager_t ) );
72         if( !p_extensions_manager )
73         {
74             b_failed = true;
75             emit extensionsUpdated();
76             return false;
77         }
78         vlc_object_attach( p_extensions_manager, p_intf );
79
80         p_extensions_manager->p_module =
81                 module_need( p_extensions_manager, "extension", NULL, false );
82
83         if( !p_extensions_manager->p_module )
84         {
85             msg_Err( p_intf, "Unable to load extensions module" );
86             vlc_object_release( p_extensions_manager );
87             p_extensions_manager = NULL;
88             b_failed = true;
89             emit extensionsUpdated();
90             return false;
91         }
92
93         /* Initialize dialog provider */
94         p_edp = ExtensionsDialogProvider::getInstance( p_intf,
95                                                        p_extensions_manager );
96         if( !p_edp )
97         {
98             msg_Err( p_intf, "Unable to create dialogs provider for extensions" );
99             module_unneed( p_extensions_manager,
100                            p_extensions_manager->p_module );
101             vlc_object_release( p_extensions_manager );
102             p_extensions_manager = NULL;
103             b_failed = true;
104             emit extensionsUpdated();
105             return false;
106         }
107         b_unloading = false;
108     }
109     b_failed = false;
110     emit extensionsUpdated();
111     return true;
112 }
113
114 void ExtensionsManager::unloadExtensions()
115 {
116     if( !p_extensions_manager )
117         return;
118     b_unloading = true;
119     module_unneed( p_extensions_manager, p_extensions_manager->p_module );
120     vlc_object_release( p_extensions_manager );
121     p_extensions_manager = NULL;
122     emit extensionsUpdated();
123     ExtensionsDialogProvider::killInstance();
124 }
125
126 void ExtensionsManager::reloadExtensions()
127 {
128     unloadExtensions();
129     loadExtensions();
130     emit extensionsUpdated();
131 }
132
133 void ExtensionsManager::menu( QMenu *current )
134 {
135     assert( current != NULL );
136     if( !isLoaded() )
137     {
138         // This case can happen: do nothing
139         return;
140     }
141
142     vlc_mutex_lock( &p_extensions_manager->lock );
143
144     QAction *action;
145     extension_t *p_ext = NULL;
146     int i_ext = 0;
147     FOREACH_ARRAY( p_ext, p_extensions_manager->extensions )
148     {
149         bool b_Active = extension_IsActivated( p_extensions_manager, p_ext );
150
151         if( b_Active && extension_HasMenu( p_extensions_manager, p_ext ) )
152         {
153             QMenu *submenu = new QMenu( qfu( p_ext->psz_title ) );
154             char **ppsz_titles = NULL;
155             uint16_t *pi_ids = NULL;
156             size_t i_num = 0;
157             action = current->addMenu( submenu );
158
159             action->setCheckable( true );
160             action->setChecked( true );
161
162             if( extension_GetMenu( p_extensions_manager, p_ext,
163                                    &ppsz_titles, &pi_ids ) == VLC_SUCCESS )
164             {
165                 for( int i = 0; ppsz_titles[i] != NULL; ++i )
166                 {
167                     ++i_num;
168                     action = submenu->addAction( qfu( ppsz_titles[i] ) );
169                     menuMapper->setMapping( action,
170                                             MENU_MAP( pi_ids[i], i_ext ) );
171                     CONNECT( action, triggered(), menuMapper, map() );
172                 }
173                 if( !i_num )
174                 {
175                     action = submenu->addAction( qtr( "Empty" ) );
176                     action->setEnabled( false );
177                 }
178             }
179             else
180             {
181                 msg_Warn( p_intf, "Could not get menu for extension '%s'",
182                           p_ext->psz_title );
183                 action = submenu->addAction( qtr( "Empty" ) );
184                 action->setEnabled( false );
185             }
186
187             submenu->addSeparator();
188             action = submenu->addAction( QIcon( ":/menu/quit" ),
189                                          qtr( "Deactivate" ) );
190             menuMapper->setMapping( action, MENU_MAP( 0, i_ext ) );
191             CONNECT( action, triggered(), menuMapper, map() );
192         }
193         else
194         {
195             action = current->addAction( qfu( p_ext->psz_title ) );
196             menuMapper->setMapping( action, MENU_MAP( 0, i_ext ) );
197             CONNECT( action, triggered(), menuMapper, map() );
198
199             if( !extension_TriggerOnly( p_extensions_manager, p_ext ) )
200             {
201                 action->setCheckable( true );
202                 action->setChecked( b_Active );
203             }
204         }
205         i_ext++;
206     }
207     FOREACH_END()
208
209     vlc_mutex_unlock( &p_extensions_manager->lock );
210 }
211
212 void ExtensionsManager::triggerMenu( int id )
213 {
214     uint16_t i_ext = MENU_GET_EXTENSION( id );
215     uint16_t i_action = MENU_GET_ACTION( id );
216
217     vlc_mutex_lock( &p_extensions_manager->lock );
218
219     if( (int) i_ext > p_extensions_manager->extensions.i_size )
220     {
221         msg_Dbg( p_intf, "can't trigger extension with wrong id %d",
222                  (int) i_ext );
223         return;
224     }
225
226     extension_t *p_ext = ARRAY_VAL( p_extensions_manager->extensions, i_ext );
227     assert( p_ext != NULL);
228
229     vlc_mutex_unlock( &p_extensions_manager->lock );
230
231     if( i_action == 0 )
232     {
233         msg_Dbg( p_intf, "activating or triggering extension '%s'",
234                  p_ext->psz_title );
235
236         if( extension_TriggerOnly( p_extensions_manager, p_ext ) )
237         {
238             extension_Trigger( p_extensions_manager, p_ext );
239         }
240         else
241         {
242             if( !extension_IsActivated( p_extensions_manager, p_ext ) )
243                 extension_Activate( p_extensions_manager, p_ext );
244             else
245                 extension_Deactivate( p_extensions_manager, p_ext );
246         }
247     }
248     else
249     {
250         msg_Dbg( p_intf, "triggering extension '%s', on menu with id = 0x%x",
251                  p_ext->psz_title, i_action );
252
253         extension_TriggerMenu( p_extensions_manager, p_ext, i_action );
254     }
255 }
256
257 void ExtensionsManager::inputChanged( input_thread_t* p_input )
258 {
259     if( p_input )
260         vlc_object_hold( p_input );
261     vlc_mutex_lock( &p_extensions_manager->lock );
262
263     extension_t *p_ext;
264     FOREACH_ARRAY( p_ext, p_extensions_manager->extensions )
265     {
266         if( extension_IsActivated( p_extensions_manager, p_ext ) )
267         {
268             extension_SetInput( p_extensions_manager, p_ext, p_input );
269         }
270     }
271     FOREACH_END()
272
273     vlc_mutex_unlock( &p_extensions_manager->lock );
274     if( p_input )
275         vlc_object_release( p_input );
276 }