]> git.sesse.net Git - vlc/blob - modules/gui/macosx_dialog_provider/dialogProvider.m
d0505e7acc829e13e151eca99a6fbb0e14eafd3b
[vlc] / modules / gui / macosx_dialog_provider / dialogProvider.m
1 /*****************************************************************************
2  * dialogProvider.m: Minimal Dialog Provider for Mac OS X
3  *****************************************************************************
4  * Copyright (C) 2009-2010 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Felix Paul Kühne <fkuehne at videolan dot 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 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #import <stdlib.h>                                      /* malloc(), free() */
28 #import <string.h>
29
30 #ifdef HAVE_CONFIG_H
31 # import "config.h"
32 #endif
33
34 #import <vlc_common.h>
35 #import <vlc_plugin.h>
36 #import <vlc_dialog.h>
37 #import <vlc_interface.h>
38
39 #import <Cocoa/Cocoa.h>
40 #import "VLCLoginPanel.h"
41 #import "VLCProgressPanel.h"
42
43 /*****************************************************************************
44  * Prototypes
45  *****************************************************************************/
46 static int  OpenIntf(vlc_object_t *);
47 static void CloseIntf(vlc_object_t *);
48 static void Run(intf_thread_t * );
49
50 static int DisplayError(vlc_object_t *,const char *,vlc_value_t,vlc_value_t,void * );
51 static int DisplayCritical(vlc_object_t *,const char *,vlc_value_t,vlc_value_t,void * );
52 static int DisplayQuestion(vlc_object_t *,const char *,vlc_value_t,vlc_value_t,void * );
53 static int DisplayLogin(vlc_object_t *,const char *,vlc_value_t,vlc_value_t,void * );
54 static int DisplayProgressPanelAction(vlc_object_t *,const char *,vlc_value_t,vlc_value_t,void * );
55
56 static void updateProgressPanel (void *, const char *, float);
57 static bool checkProgressPanel (void *);
58 static void destroyProgressPanel (void *);
59
60 @interface VLCDialogDisplayer : NSObject
61 {
62     VLCProgressPanel *_currentProgressBarPanel;
63 }
64
65 + (NSDictionary *)dictionaryForDialog:(const char *)title :(const char *)message :(const char *)yes :(const char *)no :(const char *)cancel;
66
67 - (void)displayError:(NSDictionary *)dialog;
68 - (void)displayCritical:(NSDictionary *)dialog;
69 - (NSNumber *)displayQuestion:(NSDictionary *)dialog;
70 - (NSDictionary *)displayLogin:(NSDictionary *)dialog;
71
72 - (void)displayProgressBar:(NSDictionary *)dict;
73 - (void)updateProgressPanel:(NSDictionary *)dict;
74 - (void)destroyProgressPanel;
75 - (NSNumber *)checkProgressPanel;
76
77 - (id)resultFromSelectorOnMainThread:(SEL)sel withObject:(id)object;
78 @end
79
80 static inline NSDictionary *DictFromDialogFatal(dialog_fatal_t *dialog) {
81     return [VLCDialogDisplayer dictionaryForDialog:dialog->title :dialog->message :NULL :NULL :NULL];
82 }
83 static inline NSDictionary *DictFromDialogLogin(dialog_login_t *dialog) {
84     return [VLCDialogDisplayer dictionaryForDialog:dialog->title :dialog->message :NULL :NULL :NULL];
85 }
86 static inline NSDictionary *DictFromDialogQuestion(dialog_question_t *dialog) {
87     return [VLCDialogDisplayer dictionaryForDialog:dialog->title :dialog->message :dialog->yes :dialog->no :dialog->cancel];
88 }
89 static inline NSDictionary *DictFromDialogProgressBar(dialog_progress_bar_t *dialog) {
90     return [VLCDialogDisplayer dictionaryForDialog:dialog->title :dialog->message :NULL :NULL :dialog->cancel];
91 }
92
93 struct intf_sys_t
94 {
95     VLCDialogDisplayer *displayer;
96
97     vlc_mutex_t lock;
98     vlc_cond_t wait;
99 };
100
101
102 /*****************************************************************************
103  * Module descriptor
104  *****************************************************************************/
105
106 vlc_module_begin()
107     /* Minimal interface. see intf.m */
108     set_shortname("Mac OS X Dialogs")
109     add_shortcut("macosx_dialog_provider")
110     add_shortcut("miosx")
111     set_description("Minimal Mac OS X Dialog Provider")
112     set_capability("interface", 50)
113     set_callbacks(OpenIntf, CloseIntf)
114     set_category(CAT_INTERFACE)
115     set_subcategory(SUBCAT_INTERFACE_MAIN)
116 vlc_module_end()
117
118 /*****************************************************************************
119  * OpenIntf: initialize interface
120  *****************************************************************************/
121 int OpenIntf(vlc_object_t *p_this)
122 {
123     intf_thread_t *p_intf = (intf_thread_t*) p_this;
124
125     p_intf->p_sys = malloc(sizeof(intf_sys_t));
126     if(!p_intf->p_sys)
127         return VLC_ENOMEM;
128
129     memset(p_intf->p_sys,0,sizeof(*p_intf->p_sys));
130
131     p_intf->p_sys->displayer = [[VLCDialogDisplayer alloc] init];
132
133     /* subscribe to various interactive dialogues */
134     var_Create(p_intf,"dialog-error",VLC_VAR_ADDRESS);
135     var_AddCallback(p_intf,"dialog-error",DisplayError,p_intf);
136     var_Create(p_intf,"dialog-critical",VLC_VAR_ADDRESS);
137     var_AddCallback(p_intf,"dialog-critical",DisplayCritical,p_intf);
138     var_Create(p_intf,"dialog-login",VLC_VAR_ADDRESS);
139     var_AddCallback(p_intf,"dialog-login",DisplayLogin,p_intf);
140     var_Create(p_intf,"dialog-question",VLC_VAR_ADDRESS);
141     var_AddCallback(p_intf,"dialog-question",DisplayQuestion,p_intf);
142     var_Create(p_intf,"dialog-progress-bar",VLC_VAR_ADDRESS);
143     var_AddCallback(p_intf,"dialog-progress-bar",DisplayProgressPanelAction,p_intf);
144     dialog_Register(p_intf);
145     
146     msg_Dbg(p_intf,"Mac OS X dialog provider initialised");
147     
148     return VLC_SUCCESS;
149 }
150
151 /*****************************************************************************
152  * CloseIntf: destroy interface
153  *****************************************************************************/
154 void CloseIntf(vlc_object_t *p_this)
155 {
156     intf_thread_t *p_intf = (intf_thread_t*) p_this;
157
158     /* unsubscribe from the interactive dialogues */
159     dialog_Unregister(p_intf);
160     var_DelCallback(p_intf,"dialog-error",DisplayError,p_intf);
161     var_DelCallback(p_intf,"dialog-critical",DisplayCritical,p_intf);
162     var_DelCallback(p_intf,"dialog-login",DisplayLogin,p_intf);
163     var_DelCallback(p_intf,"dialog-question",DisplayQuestion,p_intf);
164     var_DelCallback(p_intf,"dialog-progress-bar",DisplayProgressPanelAction,p_intf);
165     
166     [p_intf->p_sys->displayer release];
167
168     msg_Dbg(p_intf,"Mac OS X dialog provider closed");
169     free(p_intf->p_sys);
170 }
171
172
173 /*****************************************************************************
174  * Callbacks triggered by the "dialog-*" variables
175  *****************************************************************************/
176 static int DisplayError(vlc_object_t *p_this, const char *type, vlc_value_t previous, vlc_value_t value, void *data)
177 {
178     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
179     dialog_fatal_t *dialog = value.p_address;
180     intf_thread_t *p_intf = (intf_thread_t*) p_this;
181     intf_sys_t *sys = p_intf->p_sys;
182     [sys->displayer performSelectorOnMainThread:@selector(displayError:) withObject:DictFromDialogFatal(dialog) waitUntilDone:NO];
183     [pool release];
184     return VLC_SUCCESS;
185 }
186
187 static int DisplayCritical(vlc_object_t *p_this, const char *type, vlc_value_t previous, vlc_value_t value, void *data)
188 {
189     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
190     dialog_fatal_t *dialog = value.p_address;
191     intf_thread_t *p_intf = (intf_thread_t*) p_this;
192     intf_sys_t *sys = p_intf->p_sys;
193     [sys->displayer performSelectorOnMainThread:@selector(displayCritical:) withObject:DictFromDialogFatal(dialog) waitUntilDone:NO];
194     [pool release];
195     return VLC_SUCCESS;
196 }
197
198 static int DisplayQuestion(vlc_object_t *p_this, const char *type, vlc_value_t previous, vlc_value_t value, void *data)
199 {
200     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
201     dialog_question_t *dialog = value.p_address;
202     intf_thread_t *p_intf = (intf_thread_t*) p_this;
203     intf_sys_t *sys = p_intf->p_sys;
204     dialog->answer = [[sys->displayer resultFromSelectorOnMainThread:@selector(displayQuestion:) withObject:DictFromDialogQuestion(dialog)] intValue];
205     [pool release];
206     return VLC_SUCCESS;
207 }
208
209 static int DisplayLogin(vlc_object_t *p_this, const char *type, vlc_value_t previous, vlc_value_t value, void *data)
210 {
211     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
212     dialog_login_t *dialog = value.p_address;
213     intf_thread_t *p_intf = (intf_thread_t*) p_this;
214     intf_sys_t *sys = p_intf->p_sys;
215     NSDictionary *dict = [sys->displayer resultFromSelectorOnMainThread:@selector(displayCritical:) withObject:DictFromDialogLogin(dialog)];
216     if (dict) {
217         *dialog->username = strdup([[dict objectForKey:@"username"] UTF8String]);
218         *dialog->password = strdup([[dict objectForKey:@"password"] UTF8String]);
219     }
220     [pool release];
221     return VLC_SUCCESS;
222 }
223
224 static int DisplayProgressPanelAction(vlc_object_t *p_this, const char *type, vlc_value_t previous, vlc_value_t value, void *data)
225 {
226     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
227     dialog_progress_bar_t *dialog = value.p_address;
228     intf_thread_t *p_intf = (intf_thread_t*) p_this;
229     intf_sys_t *sys = p_intf->p_sys;
230
231     [sys->displayer performSelectorOnMainThread:@selector(displayProgressBar) withObject:DictFromDialogProgressBar(dialog) waitUntilDone:YES];
232
233     dialog->pf_update = updateProgressPanel;
234     dialog->pf_check = checkProgressPanel;
235     dialog->pf_destroy = destroyProgressPanel;
236     dialog->p_sys = p_intf->p_sys;
237
238     [pool release];
239     return VLC_SUCCESS;
240 }
241
242 void updateProgressPanel (void *priv, const char *text, float value)
243 {
244     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
245     intf_sys_t *sys = (intf_sys_t *)priv;
246
247     NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
248                           [NSNumber numberWithFloat:value], @"value",
249                           text ? [NSString stringWithUTF8String:text] : nil, @"text",
250                           nil];
251                           
252     [sys->displayer performSelectorOnMainThread:@selector(updateProgressPanel) withObject:dict waitUntilDone:YES];
253
254     [pool release];
255 }
256
257 void destroyProgressPanel (void *priv)
258 {
259     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
260     intf_sys_t *sys = (intf_sys_t *)priv;
261
262     [sys->displayer performSelectorOnMainThread:@selector(destroyProgressPanel) withObject:nil waitUntilDone:YES];
263     
264     [pool release];
265 }
266
267 bool checkProgressPanel (void *priv)
268 {
269     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
270     intf_sys_t *sys = (intf_sys_t *)priv;
271     BOOL ret;
272
273     ret = [[sys->displayer resultFromSelectorOnMainThread:@selector(checkProgressPanel) withObject:nil] boolValue];
274
275     [pool release];
276     return ret;
277 }
278
279
280 @implementation VLCDialogDisplayer
281 - (void)dealloc
282 {
283     assert(!_currentProgressBarPanel); // This has to be closed on main thread.
284     [super dealloc];
285 }
286
287 + (NSDictionary *)dictionaryForDialog:(const char *)title :(const char *)message :(const char *)yes :(const char *)no :(const char *)cancel
288 {
289     NSMutableDictionary *dict = [NSMutableDictionary dictionary];
290     if (title)
291         [dict setObject:[NSString stringWithUTF8String:title] forKey:@"title"];
292     if (message)
293         [dict setObject:[NSString stringWithUTF8String:message] forKey:@"message"];
294     if (yes)
295         [dict setObject:[NSString stringWithUTF8String:yes] forKey:@"yes"];
296     if (no)
297         [dict setObject:[NSString stringWithUTF8String:no] forKey:@"no"];
298     if (cancel)
299         [dict setObject:[NSString stringWithUTF8String:cancel] forKey:@"cancel"];
300     
301     return dict;
302 }
303 #define VLCAssertIsMainThread() assert([NSThread isMainThread])
304
305 - (void)displayError:(NSDictionary *)dialog
306 {
307     VLCAssertIsMainThread();
308     
309     NSRunInformationalAlertPanel([dialog objectForKey:@"title"],
310                                  [dialog objectForKey:@"message"],
311                                  @"OK", nil, nil);
312 }
313
314 - (void)displayCritical:(NSDictionary *)dialog
315 {
316     VLCAssertIsMainThread();
317     
318     NSRunCriticalAlertPanel([dialog objectForKey:@"title"],
319                                  [dialog objectForKey:@"message"],
320                                  @"OK", nil, nil);
321 }
322
323 - (NSNumber *)displayQuestion:(NSDictionary *)dialog
324 {
325     VLCAssertIsMainThread();
326     
327     NSInteger alertRet = 0;
328     
329     NSAlert *alert = [NSAlert alertWithMessageText:[dialog objectForKey:@"title"]
330                               defaultButton:[dialog objectForKey:@"yes"]
331                             alternateButton:[dialog objectForKey:@"no"] 
332                                 otherButton:[dialog objectForKey:@"cancel"]
333                   informativeTextWithFormat:[dialog objectForKey:@"message"]];
334     [alert setAlertStyle:NSInformationalAlertStyle];
335     alertRet = [alert runModal];
336
337     int ret;
338     switch (alertRet) {
339         case NSAlertDefaultReturn:
340             ret = 1;
341             break;
342         case NSAlertAlternateReturn:
343             ret = 2;
344             break;
345         case NSAlertOtherReturn:
346             ret = 3;
347             break;
348         default:
349             assert(0);
350             ret = 0;
351             break;
352     }
353
354     return [NSNumber numberWithInt:ret];
355 }
356
357 - (NSDictionary *)displayLogin:(NSDictionary *)dialog
358 {
359     VLCAssertIsMainThread();
360
361     VLCLoginPanel *panel = [[VLCLoginPanel alloc] init];
362     [panel createContentView];
363     [panel setDialogTitle:[dialog objectForKey:@"title"]];
364     [panel setDialogMessage:[dialog objectForKey:@"message"]];
365     [panel center];
366     NSInteger ret = [NSApp runModalForWindow:panel];
367     [panel close];
368     
369     if (!ret)
370         return nil;
371     
372     return [NSDictionary dictionaryWithObjectsAndKeys:
373             [panel userName], @"username",
374             [panel password], @"password",
375             nil];
376 }
377
378 - (void)displayProgressBar:(NSDictionary *)dialog
379 {
380     VLCAssertIsMainThread();
381
382     if(_currentProgressBarPanel)
383         [self destroyProgressPanel];
384
385     assert(!_currentProgressBarPanel);
386     _currentProgressBarPanel = [[VLCProgressPanel alloc] init];
387     [_currentProgressBarPanel createContentView];
388     [_currentProgressBarPanel setDialogTitle:[dialog objectForKey:@"title"]];
389     [_currentProgressBarPanel setDialogMessage:[dialog objectForKey:@"message"]];
390     [_currentProgressBarPanel setCancelButtonLabel:[dialog objectForKey:@"cancel"]];
391
392     [_currentProgressBarPanel center];
393     [_currentProgressBarPanel makeKeyAndOrderFront:nil];
394 }
395
396 - (void)updateProgressPanel:(NSDictionary *)dict
397 {
398     VLCAssertIsMainThread();
399
400     assert(_currentProgressBarPanel);
401     [_currentProgressBarPanel setDialogMessage:[dict objectForKey:@"text"] ?: @""];
402     [_currentProgressBarPanel setProgressAsDouble:[[dict objectForKey:@"value"] doubleValue] * 1000.];
403 }
404
405 - (void)destroyProgressPanel
406 {
407     VLCAssertIsMainThread();
408
409     [_currentProgressBarPanel close];
410     [_currentProgressBarPanel release];
411     _currentProgressBarPanel = nil;
412 }
413
414 - (NSNumber *)checkProgressPanel
415 {
416     VLCAssertIsMainThread();
417     
418     return [NSNumber numberWithBool:[_currentProgressBarPanel isCancelled]];
419 }
420
421
422 /**
423  * Helper to execute a function on main thread and get its return value.
424  */
425 - (void)execute:(NSDictionary *)dict
426 {
427     SEL sel = [[dict objectForKey:@"sel"] pointerValue];
428     id * result = [[dict objectForKey:@"result"] pointerValue];
429     id object = [dict objectForKey:@"object"];
430     *result = [self performSelector:sel withObject:object];
431     [*result retain]; // Balanced in -resultFromSelectorOnMainThread
432 }
433
434 - (id)resultFromSelectorOnMainThread:(SEL)sel withObject:(id)object
435 {
436     id result = nil;
437     [NSDictionary dictionaryWithObjectsAndKeys:
438      [NSValue valueWithPointer:sel], @"sel",
439      [NSValue valueWithPointer:&result], @"result",
440      object, @"object", nil];
441     [self performSelectorOnMainThread:@selector(execute:) withObject:object waitUntilDone:YES];
442     return [result autorelease];
443 }
444 @end
445                                   
446                                   
447