0e8808db47355ab3b758a96c961d8d11baba8078
[vlc.git] / modules / gui / macosx / VLCExtensionsManager.m
1 /*****************************************************************************
2  * VLCExtensionsManager.m: Extensions manager for Cocoa
3  ****************************************************************************
4  * Copyright (C) 2009-2012 VideoLAN and authors
5  * $Id$
6  *
7  * Authors: Brendon Justin <brendonjustin@gmail.com>,
8  *          Jean-Philippe AndrĂ© < jpeg # videolan.org >
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 #import "VLCExtensionsManager.h"
26
27 #import "VLCExtensionsDialogProvider.h"
28
29 #import <vlc_modules.h>
30 #import "assert.h"
31
32 #define MENU_MAP(a,e) ((uint32_t)((((uint16_t)a) << 16) | ((uint16_t)e)))
33 #define MENU_GET_ACTION(a) ((uint16_t)(((uint32_t)a) >> 16))
34 #define MENU_GET_EXTENSION(a) ((uint16_t)(((uint32_t)a) & 0xFFFF))
35
36 @interface VLCExtensionsManager()
37 {
38     extensions_manager_t *p_extensions_manager;
39     VLCExtensionsDialogProvider *_extensionDialogProvider;
40
41     NSMutableDictionary *p_extDict;
42
43     BOOL b_failed; ///< Flag set to true if we could not load the module
44 }
45 @end
46
47 @implementation VLCExtensionsManager
48
49 - (id)init
50 {
51     self = [super init];
52
53     if (self) {
54         p_extensions_manager = NULL;
55         _extensionDialogProvider = [[VLCExtensionsDialogProvider alloc] init];
56
57         p_extDict = [[NSMutableDictionary alloc] init];
58
59         _isUnloading = false;
60         b_failed = false;
61     }
62
63     return self;
64 }
65
66 - (void)buildMenu:(NSMenu *)extMenu
67 {
68     assert(extMenu != nil);
69
70     /* check if stuff was loaded, which can fail */
71     if (![self isLoaded]) {
72         BOOL b_ret = [self loadExtensions];
73         if (b_ret == NO)
74             return;
75     }
76     intf_thread_t *p_intf = getIntf();
77
78     vlc_mutex_lock(&p_extensions_manager->lock);
79
80     extension_t *p_ext = NULL;
81     int i_ext = 0;
82
83     FOREACH_ARRAY(p_ext, p_extensions_manager->extensions) {
84         bool b_Active = extension_IsActivated(p_extensions_manager, p_ext);
85
86         NSString *titleString = toNSStr(p_ext->psz_title);
87
88         if (b_Active && extension_HasMenu(p_extensions_manager, p_ext)) {
89             NSMenu *submenu = [[NSMenu alloc] initWithTitle:titleString];
90             NSMenuItem *submenuItem = [extMenu addItemWithTitle:titleString
91                                                          action:nil
92                                                   keyEquivalent:@""];
93
94             [extMenu setSubmenu:submenu forItem:submenuItem];
95
96             char **ppsz_titles = NULL;
97             uint16_t *pi_ids = NULL;
98             size_t i_num = 0;
99
100             if (extension_GetMenu(p_extensions_manager, p_ext, &ppsz_titles, &pi_ids) == VLC_SUCCESS) {
101                 for (int i = 0; ppsz_titles[i] != NULL; ++i) {
102                     ++i_num;
103                     titleString = toNSStr(ppsz_titles[i]);
104                     NSMenuItem *menuItem = [submenu addItemWithTitle:titleString
105                                                               action:@selector(triggerMenu:)
106                                                        keyEquivalent:@""];
107                     [menuItem setTarget:self];
108                     menuItem.tag = MENU_MAP(pi_ids[i], i_ext);
109
110                     free(ppsz_titles[i]);
111                 }
112                 if (!i_num) {
113                     NSMenuItem *menuItem = [submenu addItemWithTitle:@"Empty"
114                                                               action:@selector(triggerMenu:)
115                                                        keyEquivalent:@""];
116                     [menuItem setEnabled:NO];
117                 }
118                 free(ppsz_titles);
119                 free(pi_ids);
120             } else {
121                 msg_Warn(p_intf, "Could not get menu for extension '%s'",
122                           p_ext->psz_title);
123                 NSMenuItem *menuItem = [submenu addItemWithTitle:@"Empty"
124                                                           action:@selector(triggerMenu:)
125                                                    keyEquivalent:@""];
126                 [menuItem setEnabled:NO];
127             }
128
129             [submenu addItem:[NSMenuItem separatorItem]];
130
131             NSMenuItem *deactivateItem = [submenu addItemWithTitle:@"Deactivate"
132                                                             action:@selector(triggerMenu:)
133                                                      keyEquivalent:@""];
134             [deactivateItem setTarget:self];
135             deactivateItem.tag = MENU_MAP(0, i_ext);
136         }
137         else
138         {
139             NSMenuItem *menuItem = [extMenu addItemWithTitle:titleString
140                                                      action:@selector(triggerMenu:)
141                                               keyEquivalent:@""];
142             [menuItem setTarget:self];
143
144             if (!extension_TriggerOnly(p_extensions_manager, p_ext)) {
145                 if (b_Active)
146                     [menuItem setState:NSOnState];
147             }
148             menuItem.tag = MENU_MAP(0, i_ext);
149         }
150         i_ext++;
151     }
152     FOREACH_END()
153
154     vlc_mutex_unlock(&p_extensions_manager->lock);
155 }
156
157 - (BOOL)loadExtensions
158 {
159     intf_thread_t *p_intf = getIntf();
160     if (!p_extensions_manager) {
161         p_extensions_manager = (extensions_manager_t*)vlc_object_create(p_intf, sizeof(extensions_manager_t));
162         if (!p_extensions_manager) {
163             b_failed = true;
164             return false;
165         }
166
167         p_extensions_manager->p_module = module_need(p_extensions_manager, "extension", NULL, false);
168
169         if (!p_extensions_manager->p_module) {
170             msg_Err(p_intf, "Unable to load extensions module");
171             vlc_object_release(p_extensions_manager);
172             p_extensions_manager = NULL;
173             b_failed = true;
174             return false;
175         }
176
177         _isUnloading = false;
178     }
179     b_failed = false;
180     return true;
181 }
182
183 - (void)unloadExtensions
184 {
185     if (!p_extensions_manager)
186         return;
187     _isUnloading = true;
188
189     module_unneed(p_extensions_manager, p_extensions_manager->p_module);
190     vlc_object_release(p_extensions_manager);
191     p_extensions_manager = NULL;
192 }
193
194 - (void)reloadExtensions
195 {
196     [self unloadExtensions];
197     [self loadExtensions];
198 }
199
200 - (void)triggerMenu:(id)sender
201 {
202     NSLog(@"%s", __PRETTY_FUNCTION__);
203     intf_thread_t *p_intf = getIntf();
204     uint32_t identifier = [(NSMenuItem *)sender tag];
205
206     uint16_t i_ext = MENU_GET_EXTENSION(identifier);
207     uint16_t i_action = MENU_GET_ACTION(identifier);
208
209     vlc_mutex_lock(&p_extensions_manager->lock);
210
211     if ((int) i_ext > p_extensions_manager->extensions.i_size) {
212         msg_Dbg(p_intf, "can't trigger extension with wrong id %d",
213                  (int) i_ext);
214         return;
215     }
216
217     extension_t *p_ext = ARRAY_VAL(p_extensions_manager->extensions, i_ext);
218     assert(p_ext != NULL);
219
220     vlc_mutex_unlock(&p_extensions_manager->lock);
221
222     if (i_action == 0) {
223         msg_Dbg(p_intf, "activating or triggering extension '%s', id %d",
224                  p_ext->psz_title, i_ext);
225
226         if (extension_TriggerOnly(p_extensions_manager, p_ext)) {
227             extension_Trigger(p_extensions_manager, p_ext);
228         } else {
229             if (!extension_IsActivated(p_extensions_manager, p_ext))
230                 extension_Activate(p_extensions_manager, p_ext);
231             else
232                 extension_Deactivate(p_extensions_manager, p_ext);
233         }
234     }
235     else
236     {
237         msg_Dbg(p_intf, "triggering extension '%s', on menu with id = 0x%x",
238                  p_ext->psz_title, i_action);
239
240         extension_TriggerMenu(p_extensions_manager, p_ext, i_action);
241     }
242 }
243
244 - (void)inputChanged:(input_thread_t *)p_input
245 {
246     //This is unlikely, but can happen if no extension modules can be loaded.
247     if (p_extensions_manager == NULL)
248         return;
249     vlc_mutex_lock(&p_extensions_manager->lock);
250
251     extension_t *p_ext;
252     FOREACH_ARRAY(p_ext, p_extensions_manager->extensions) {
253         if (extension_IsActivated(p_extensions_manager, p_ext))
254             extension_SetInput(p_extensions_manager, p_ext, p_input);
255     }
256     FOREACH_END()
257
258     vlc_mutex_unlock(&p_extensions_manager->lock);
259 }
260
261 - (void)playingChanged:(int)state
262 {
263     //This is unlikely, but can happen if no extension modules can be loaded.
264     if (p_extensions_manager == NULL)
265         return;
266     vlc_mutex_lock(&p_extensions_manager->lock);
267
268     extension_t *p_ext;
269     FOREACH_ARRAY(p_ext, p_extensions_manager->extensions) {
270         if (extension_IsActivated(p_extensions_manager, p_ext))
271             extension_PlayingChanged(p_extensions_manager, p_ext, state);
272     }
273     FOREACH_END()
274
275     vlc_mutex_unlock(&p_extensions_manager->lock);
276 }
277
278 - (void)metaChanged:(input_item_t *)p_input
279 {
280     //This is unlikely, but can happen if no extension modules can be loaded.
281     if (p_extensions_manager == NULL)
282         return;
283     vlc_mutex_lock(&p_extensions_manager->lock);
284     extension_t *p_ext;
285     FOREACH_ARRAY(p_ext, p_extensions_manager->extensions) {
286         if (extension_IsActivated(p_extensions_manager, p_ext))
287             extension_MetaChanged(p_extensions_manager, p_ext);
288     }
289     FOREACH_END()
290     vlc_mutex_unlock(&p_extensions_manager->lock);
291 }
292
293 - (void)dealloc
294 {
295     intf_thread_t *p_intf = getIntf();
296     msg_Dbg(p_intf, "Deinitializing extensions manager");
297
298     _extensionDialogProvider = nil;
299     if (p_extensions_manager)
300         vlc_object_release(p_extensions_manager);
301 }
302
303 - (BOOL)isLoaded
304 {
305     return p_extensions_manager != NULL;
306 }
307
308 - (BOOL)cannotLoad
309 {
310     return self.isUnloading || b_failed;
311 }
312
313 @end