]> git.sesse.net Git - vlc/blob - modules/gui/macosx_dialog_provider/dialogProvider.m
b60dfd51ff82a5ab7cccab0770d4c49391a69822
[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  *          Pierre d'Herbemont <pdherbemont # videolan dot>
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 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #import <stdlib.h>                                      /* malloc(), free() */
29 #import <string.h>
30
31 #ifdef HAVE_CONFIG_H
32 # import "config.h"
33 #endif
34
35 #import <vlc_common.h>
36 #import <vlc_plugin.h>
37 #import <vlc_dialog.h>
38 #import <vlc_extensions.h>
39
40 #import "dialogProvider.h"
41
42 /*****************************************************************************
43  * Prototypes
44  *****************************************************************************/
45 static int  OpenIntf(vlc_object_t *);
46 static void CloseIntf(vlc_object_t *);
47 static void Run(intf_thread_t * );
48
49 static int DisplayError(vlc_object_t *,const char *,vlc_value_t,vlc_value_t,void * );
50 static int DisplayCritical(vlc_object_t *,const char *,vlc_value_t,vlc_value_t,void * );
51 static int DisplayQuestion(vlc_object_t *,const char *,vlc_value_t,vlc_value_t,void * );
52 static int DisplayLogin(vlc_object_t *,const char *,vlc_value_t,vlc_value_t,void * );
53 static int DisplayProgressPanelAction(vlc_object_t *,const char *,vlc_value_t,vlc_value_t,void * );
54 static int DisplayExtension(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 VLCDialogButton : NSButton
61 {
62     extension_widget_t *widget;
63 }
64 @property (readwrite) extension_widget_t *widget;
65 @end
66
67 @implementation VLCDialogButton
68 @synthesize widget;
69 @end
70
71 @interface VLCDialogPopUpButton : NSPopUpButton
72 {
73     extension_widget_t *widget;
74 }
75 @property (readwrite) extension_widget_t *widget;
76 @end
77
78 @implementation VLCDialogPopUpButton
79 @synthesize widget;
80 @end
81
82
83 @interface VLCDialogTextField : NSTextField
84 {
85     extension_widget_t *widget;
86 }
87 @property (readwrite) extension_widget_t *widget;
88 @end
89
90 @implementation VLCDialogTextField
91 @synthesize widget;
92 @end
93
94 @interface VLCDialogWindow : NSWindow
95 {
96     extension_dialog_t *dialog;
97 }
98 @property (readwrite) extension_dialog_t *dialog;
99 @end
100
101 @implementation VLCDialogWindow
102 @synthesize dialog;
103 @end
104
105
106 @interface VLCDialogList : NSTableView
107 {
108     extension_widget_t *widget;
109     NSMutableArray *contentArray;
110 }
111 @property (readwrite) extension_widget_t *widget;
112 @property (readwrite, retain) NSMutableArray *contentArray;
113 @end
114
115 @implementation VLCDialogList
116 @synthesize widget;
117 @synthesize contentArray;
118
119 - (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
120 {
121     return [contentArray count];
122 }
123
124 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
125 {
126     return [[contentArray objectAtIndex:rowIndex] objectForKey:@"text"];
127 }
128 @end
129
130 @interface VLCDialogGridView : NSView {
131     NSUInteger _rowCount, _colCount;
132     NSMutableArray *_gridedViews;
133 }
134
135 - (NSSize)flexSize:(NSSize)size;
136 - (void)removeSubview:(NSView *)view;
137 @end
138
139
140 // Move this to separate file
141 @implementation VLCDialogGridView
142
143 - (void)dealloc
144 {
145     [_gridedViews release];
146     [super dealloc];
147 }
148
149 - (void)recomputeCount
150 {
151     _colCount = 0;
152     _rowCount = 0;
153     for (NSDictionary *obj in _gridedViews)
154     {
155         NSUInteger row = [[obj objectForKey:@"row"] intValue];
156         NSUInteger col = [[obj objectForKey:@"col"] intValue];
157         if (col + 1 > _colCount)
158             _colCount = col + 1;
159         if (row + 1 > _rowCount)
160             _rowCount = row + 1;
161     }
162 }
163
164 - (void)recomputeWindowSize
165 {
166     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(recomputeWindowSize) object:nil];
167
168     NSWindow *window = [self window];
169     NSRect frame = [window frame];
170     NSRect contentRect = [window contentRectForFrameRect:frame];
171     contentRect.size = [self flexSize:frame.size];
172     NSRect newFrame = [window frameRectForContentRect:contentRect];
173     newFrame.origin.y -= newFrame.size.height - frame.size.height;
174     newFrame.origin.x -= (newFrame.size.width - frame.size.width) / 2;
175     [window setFrame:newFrame display:YES animate:YES];
176 }
177
178 - (NSSize)objectSizeToFit:(NSView *)view
179 {
180     if ([view isKindOfClass:[NSControl class]]) {
181         NSControl *control = (NSControl *)view;
182         return [[control cell] cellSize];
183     }
184     return [view frame].size;
185 }
186
187 - (CGFloat)marginX
188 {
189     return 16;
190 }
191 - (CGFloat)marginY
192 {
193     return 8;
194 }
195
196 - (CGFloat)constrainedHeightOfRow:(NSUInteger)targetRow
197 {
198     CGFloat height = 0;
199     for(NSDictionary *obj in _gridedViews) {
200         NSUInteger row = [[obj objectForKey:@"row"] intValue];
201         if (row != targetRow)
202             continue;
203         NSUInteger rowSpan = [[obj objectForKey:@"rowSpan"] intValue];
204         if (rowSpan != 1)
205             continue;
206         NSView *view = [obj objectForKey:@"view"];
207         if ([view autoresizingMask] & NSViewHeightSizable)
208             continue;
209         NSSize sizeToFit = [self objectSizeToFit:view];
210         if (height < sizeToFit.height)
211             height = sizeToFit.height;
212     }
213     return height;
214 }
215
216 - (CGFloat)remainingRowsHeight
217 {
218     NSUInteger height = [self marginY];
219     if (!_rowCount)
220         return 0;
221     NSUInteger autosizedRows = 0;
222     for (NSUInteger i = 0; i < _rowCount; i++) {
223         CGFloat constrainedHeight = [self constrainedHeightOfRow:i];
224         if (!constrainedHeight)
225             autosizedRows++;
226         height += constrainedHeight + [self marginY];
227     }
228     CGFloat remaining = 0;
229     if (height < self.bounds.size.height && autosizedRows)
230         remaining = (self.bounds.size.height - height) / autosizedRows;
231     if (remaining < 0)
232         remaining = 0;
233
234     return remaining;
235 }
236
237 - (CGFloat)heightOfRow:(NSUInteger)targetRow
238 {
239     NSAssert(targetRow < _rowCount, @"accessing a non existing row");
240     CGFloat height = [self constrainedHeightOfRow:targetRow];
241     if (!height)
242         height = [self remainingRowsHeight];
243     return height;
244 }
245
246
247 - (CGFloat)topOfRow:(NSUInteger)targetRow
248 {
249     CGFloat top = [self marginY];
250     for (NSUInteger i = 1; i < _rowCount - targetRow; i++)
251     {
252         top += [self heightOfRow:_rowCount - i] + [self marginY];
253     }
254     return top;
255 }
256
257 - (CGFloat)constrainedWidthOfColumn:(NSUInteger)targetColumn
258 {
259     CGFloat width = 0;
260     for(NSDictionary *obj in _gridedViews) {
261         NSUInteger col = [[obj objectForKey:@"col"] intValue];
262         if (col != targetColumn)
263             continue;
264         NSUInteger colSpan = [[obj objectForKey:@"colSpan"] intValue];
265         if (colSpan != 1)
266             continue;
267         NSView *view = [obj objectForKey:@"view"];
268         if ([view autoresizingMask] & NSViewWidthSizable)
269             return 0;
270         NSSize sizeToFit = [self objectSizeToFit:view];
271         if (width < sizeToFit.width)
272             width = sizeToFit.width;
273     }
274     return width;
275 }
276
277 - (CGFloat)remainingColumnWidth
278 {
279     NSUInteger width = [self marginX];
280     if (!_colCount)
281         return 0;
282     NSUInteger autosizedCol = 0;
283     for (NSUInteger i = 0; i < _colCount; i++) {
284         CGFloat constrainedWidth = [self constrainedWidthOfColumn:i];
285         if (!constrainedWidth)
286             autosizedCol++;
287         width += constrainedWidth + [self marginX];
288
289     }
290     CGFloat remaining = 0;
291     if (width < self.bounds.size.width && autosizedCol)
292         remaining = (self.bounds.size.width - width) / autosizedCol;
293     if (remaining < 0)
294         remaining = 0;
295     return remaining;
296 }
297
298 - (CGFloat)widthOfColumn:(NSUInteger)targetColumn
299 {
300     CGFloat width = [self constrainedWidthOfColumn:targetColumn];
301     if (!width)
302         width = [self remainingColumnWidth];
303     return width;
304 }
305
306
307 - (CGFloat)leftOfColumn:(NSUInteger)targetColumn
308 {
309     CGFloat left = [self marginX];
310     for (NSUInteger i = 0; i < targetColumn; i++)
311     {
312         left += [self widthOfColumn:i] + [self marginX];
313
314     }
315     return left;
316 }
317
318 - (void)relayout
319 {
320     for(NSDictionary *obj in _gridedViews) {
321         NSUInteger row = [[obj objectForKey:@"row"] intValue];
322         NSUInteger col = [[obj objectForKey:@"col"] intValue];
323         NSUInteger rowSpan = [[obj objectForKey:@"rowSpan"] intValue];
324         NSUInteger colSpan = [[obj objectForKey:@"colSpan"] intValue];
325         NSView *view = [obj objectForKey:@"view"];
326         NSRect rect;
327
328         // Get the height
329         if ([view autoresizingMask] & NSViewHeightSizable || rowSpan > 1) {
330             CGFloat height = 0;
331             for (NSUInteger r = 0; r < rowSpan; r++) {
332                 if (row + r >= _rowCount)
333                     break;
334                 height += [self heightOfRow:row + r] + [self marginY];
335             }
336             rect.size.height = height - [self marginY];
337         }
338         else
339             rect.size.height = [self objectSizeToFit:view].height;
340
341         // Get the width
342         if ([view autoresizingMask] & NSViewWidthSizable) {
343             CGFloat width = 0;
344             for (NSUInteger c = 0; c < colSpan; c++)
345                 width += [self widthOfColumn:col + c] + [self marginX];
346             rect.size.width = width - [self marginX];
347         }
348         else
349             rect.size.width = [self objectSizeToFit:view].width;
350
351         // Top corner
352         rect.origin.y = [self topOfRow:row] + ([self heightOfRow:row] - rect.size.height) / 2;
353         rect.origin.x = [self leftOfColumn:col];
354
355         [view setFrame:rect];
356         [view setNeedsDisplay:YES];
357     }
358 }
359
360 - (NSMutableDictionary *)objectForView:(NSView *)view
361 {
362     for (NSMutableDictionary *dict in _gridedViews)
363     {
364         if ([dict objectForKey:@"view"] == view)
365             return dict;
366     }
367     return nil;
368 }
369
370 - (void)addSubview:(NSView *)view atRow:(NSUInteger)row column:(NSUInteger)column rowSpan:(NSUInteger)rowSpan colSpan:(NSUInteger)colSpan
371 {
372     if (row + 1 > _rowCount)
373         _rowCount = row + 1;
374     if (column + 1 > _colCount)
375         _colCount = column + 1;
376
377     if (!_gridedViews)
378         _gridedViews = [[NSMutableArray alloc] init];
379
380     NSMutableDictionary *dict = [self objectForView:view];
381     if (!dict) {
382         dict = [NSMutableDictionary dictionary];
383         [dict setObject:view forKey:@"view"];
384         [_gridedViews addObject:dict];
385     }
386     [dict setObject:[NSNumber numberWithInt:rowSpan] forKey:@"rowSpan"];
387     [dict setObject:[NSNumber numberWithInt:colSpan] forKey:@"colSpan"];
388     [dict setObject:[NSNumber numberWithInt:row] forKey:@"row"];
389     [dict setObject:[NSNumber numberWithInt:column] forKey:@"col"];
390
391
392     [self addSubview:view];
393     [self relayout];
394
395     // Recompute the size of the window after making sure we won't see anymore update
396     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(recomputeWindowSize) object:nil];
397     [self performSelector:@selector(recomputeWindowSize) withObject:nil afterDelay:0.1];
398 }
399
400 - (void)removeSubview:(NSView *)view
401 {
402     NSDictionary *dict = [self objectForView:view];
403     if (dict)
404         [_gridedViews removeObject:dict];
405     [view removeFromSuperview];
406
407     [self recomputeCount];
408     [self recomputeWindowSize];
409
410     [self relayout];
411     [self setNeedsDisplay:YES];
412 }
413
414 - (void)setFrame:(NSRect)frameRect
415 {
416     [super setFrame:frameRect];
417     [self relayout];
418 }
419
420 - (NSSize)flexSize:(NSSize)size
421 {
422     if (!_rowCount || !_colCount)
423         return size;
424
425     CGFloat minHeight = [self marginY];
426     BOOL canFlexHeight = NO;
427     for (NSUInteger i = 0; i < _rowCount; i++) {
428         CGFloat constrained = [self constrainedHeightOfRow:i];
429         if (!constrained) {
430             canFlexHeight = YES;
431             constrained = 128;
432         }
433         minHeight += constrained + [self marginY];
434     }
435
436     CGFloat minWidth = [self marginX];
437     BOOL canFlexWidth = NO;
438     for (NSUInteger i = 0; i < _colCount; i++) {
439         CGFloat constrained = [self constrainedWidthOfColumn:i];
440         if (!constrained) {
441             canFlexWidth = YES;
442             constrained = 128;
443         }
444         minWidth += constrained + [self marginX];
445     }
446     if (size.width < minWidth)
447         size.width = minWidth;
448     if (size.height < minHeight)
449         size.height = minHeight;
450     if (!canFlexHeight)
451         size.height = minHeight;
452     if (!canFlexWidth)
453         size.width = minWidth;
454     return size;
455 }
456
457 @end
458
459
460 static inline NSDictionary *DictFromDialogFatal(dialog_fatal_t *dialog) {
461     return [VLCDialogDisplayer dictionaryForDialog:dialog->title :dialog->message :NULL :NULL :NULL];
462 }
463 static inline NSDictionary *DictFromDialogLogin(dialog_login_t *dialog) {
464     return [VLCDialogDisplayer dictionaryForDialog:dialog->title :dialog->message :NULL :NULL :NULL];
465 }
466 static inline NSDictionary *DictFromDialogQuestion(dialog_question_t *dialog) {
467     return [VLCDialogDisplayer dictionaryForDialog:dialog->title :dialog->message :dialog->yes :dialog->no :dialog->cancel];
468 }
469 static inline NSDictionary *DictFromDialogProgressBar(dialog_progress_bar_t *dialog) {
470     return [VLCDialogDisplayer dictionaryForDialog:dialog->title :dialog->message :NULL :NULL :dialog->cancel];
471 }
472
473 struct intf_sys_t
474 {
475     VLCDialogDisplayer *displayer;
476
477     vlc_mutex_t lock;
478     vlc_cond_t wait;
479     bool is_hidding_noaction_dialogs;
480 };
481
482
483 #define T_HIDE_NOACTION N_("Hide no user action dialogs")
484 #define LT_HIDE_NOACTION N_("Don't display dialogs that don't require user action (Critical and error panel).")
485
486 #define prefix "macosx-dialog-provider"
487 /*****************************************************************************
488  * Module descriptor
489  *****************************************************************************/
490
491 vlc_module_begin()
492     /* Minimal interface. see intf.m */
493     set_shortname("Mac OS X Dialogs")
494     add_shortcut("macosx_dialog_provider")
495     add_shortcut("miosx")
496     set_description("Minimal Mac OS X Dialog Provider")
497     set_capability("interface", 0)
498
499     /* This setting is interesting, because when used with a libvlc app
500      * it's almost certain that the client program will display error by
501      * itself. Moreover certain action might end up in an error, but
502      * the client wants to ignored them completely. */
503     add_bool(prefix "hide-no-user-action-dialogs", true, NULL, T_HIDE_NOACTION, LT_HIDE_NOACTION, false)
504
505     set_callbacks(OpenIntf, CloseIntf)
506     set_category(CAT_INTERFACE)
507     set_subcategory(SUBCAT_INTERFACE_MAIN)
508 vlc_module_end()
509
510 /*****************************************************************************
511  * OpenIntf: initialize interface
512  *****************************************************************************/
513 int OpenIntf(vlc_object_t *p_this)
514 {
515     intf_thread_t *p_intf = (intf_thread_t*) p_this;
516
517     p_intf->p_sys = malloc(sizeof(intf_sys_t));
518     if(!p_intf->p_sys)
519         return VLC_ENOMEM;
520
521     memset(p_intf->p_sys,0,sizeof(*p_intf->p_sys));
522
523     p_intf->p_sys->displayer = [[VLCDialogDisplayer alloc] init];
524     [p_intf->p_sys->displayer setIntf:p_intf];
525
526     bool hide = var_CreateGetBool(p_intf, prefix "hide-no-user-action-dialogs");
527     p_intf->p_sys->is_hidding_noaction_dialogs = hide;
528
529     /* subscribe to various interactive dialogues */
530
531     if (!hide)
532     {
533         var_Create(p_intf,"dialog-error",VLC_VAR_ADDRESS);
534         var_AddCallback(p_intf,"dialog-error",DisplayError,p_intf);
535         var_Create(p_intf,"dialog-critical",VLC_VAR_ADDRESS);
536         var_AddCallback(p_intf,"dialog-critical",DisplayCritical,p_intf);
537     }
538     var_Create(p_intf,"dialog-login",VLC_VAR_ADDRESS);
539     var_AddCallback(p_intf,"dialog-login",DisplayLogin,p_intf);
540     var_Create(p_intf,"dialog-question",VLC_VAR_ADDRESS);
541     var_AddCallback(p_intf,"dialog-question",DisplayQuestion,p_intf);
542     var_Create(p_intf,"dialog-progress-bar",VLC_VAR_ADDRESS);
543     var_AddCallback(p_intf,"dialog-progress-bar",DisplayProgressPanelAction,p_intf);
544     var_Create(p_intf,"dialog-extension",VLC_VAR_ADDRESS);
545     var_AddCallback(p_intf,"dialog-extension",DisplayExtension,p_intf);
546     dialog_Register(p_intf);
547
548     /* subscribe to our last.fm announcements */
549     [[NSDistributedNotificationCenter defaultCenter] addObserver:p_intf->p_sys->displayer
550                                                         selector:@selector(globalNotificationReceived:)
551                                                             name:NULL
552                                                           object:@"VLCLastFMSupport"
553                                               suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
554
555     msg_Dbg(p_intf,"Mac OS X dialog provider initialised");
556
557     return VLC_SUCCESS;
558 }
559
560 /*****************************************************************************
561  * CloseIntf: destroy interface
562  *****************************************************************************/
563 void CloseIntf(vlc_object_t *p_this)
564 {
565     intf_thread_t *p_intf = (intf_thread_t*) p_this;
566
567     /* unsubscribe from the interactive dialogues */
568     dialog_Unregister(p_intf);
569
570     if (!p_intf->p_sys->is_hidding_noaction_dialogs)
571     {
572         var_DelCallback(p_intf,"dialog-error",DisplayError,p_intf);
573         var_DelCallback(p_intf,"dialog-critical",DisplayCritical,p_intf);
574     }
575     var_DelCallback(p_intf,"dialog-login",DisplayLogin,p_intf);
576     var_DelCallback(p_intf,"dialog-question",DisplayQuestion,p_intf);
577     var_DelCallback(p_intf,"dialog-progress-bar",DisplayProgressPanelAction,p_intf);
578     var_DelCallback(p_intf,"dialog-extension",DisplayExtension,p_intf);
579
580     [p_intf->p_sys->displayer release];
581
582     msg_Dbg(p_intf,"Mac OS X dialog provider closed");
583     free(p_intf->p_sys);
584 }
585
586
587 /*****************************************************************************
588  * Callbacks triggered by the "dialog-*" variables
589  *****************************************************************************/
590 static int DisplayError(vlc_object_t *p_this, const char *type, vlc_value_t previous, vlc_value_t value, void *data)
591 {
592     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
593     dialog_fatal_t *dialog = value.p_address;
594     intf_thread_t *p_intf = (intf_thread_t*) p_this;
595     intf_sys_t *sys = p_intf->p_sys;
596     [sys->displayer performSelectorOnMainThread:@selector(displayError:) withObject:DictFromDialogFatal(dialog) waitUntilDone:NO];
597     [pool release];
598     return VLC_SUCCESS;
599 }
600
601 static int DisplayCritical(vlc_object_t *p_this, const char *type, vlc_value_t previous, vlc_value_t value, void *data)
602 {
603     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
604     dialog_fatal_t *dialog = value.p_address;
605     intf_thread_t *p_intf = (intf_thread_t*) p_this;
606     intf_sys_t *sys = p_intf->p_sys;
607     [sys->displayer performSelectorOnMainThread:@selector(displayCritical:) withObject:DictFromDialogFatal(dialog) waitUntilDone:NO];
608     [pool release];
609     return VLC_SUCCESS;
610 }
611
612 static int DisplayQuestion(vlc_object_t *p_this, const char *type, vlc_value_t previous, vlc_value_t value, void *data)
613 {
614     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
615     dialog_question_t *dialog = value.p_address;
616     intf_thread_t *p_intf = (intf_thread_t*) p_this;
617     intf_sys_t *sys = p_intf->p_sys;
618     dialog->answer = [[sys->displayer resultFromSelectorOnMainThread:@selector(displayQuestion:) withObject:DictFromDialogQuestion(dialog)] intValue];
619     [pool release];
620     return VLC_SUCCESS;
621 }
622
623 static int DisplayLogin(vlc_object_t *p_this, const char *type, vlc_value_t previous, vlc_value_t value, void *data)
624 {
625     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
626     dialog_login_t *dialog = value.p_address;
627     intf_thread_t *p_intf = (intf_thread_t*) p_this;
628     intf_sys_t *sys = p_intf->p_sys;
629     NSDictionary *dict = [sys->displayer resultFromSelectorOnMainThread:@selector(displayLogin:) withObject:DictFromDialogLogin(dialog)];
630     if (dict) {
631         *dialog->username = strdup([[dict objectForKey:@"username"] UTF8String]);
632         *dialog->password = strdup([[dict objectForKey:@"password"] UTF8String]);
633     }
634     [pool release];
635     return VLC_SUCCESS;
636 }
637
638 static int DisplayProgressPanelAction(vlc_object_t *p_this, const char *type, vlc_value_t previous, vlc_value_t value, void *data)
639 {
640     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
641     dialog_progress_bar_t *dialog = value.p_address;
642     intf_thread_t *p_intf = (intf_thread_t*) p_this;
643     intf_sys_t *sys = p_intf->p_sys;
644
645     [sys->displayer performSelectorOnMainThread:@selector(displayProgressBar:) withObject:DictFromDialogProgressBar(dialog) waitUntilDone:YES];
646
647     dialog->pf_update = updateProgressPanel;
648     dialog->pf_check = checkProgressPanel;
649     dialog->pf_destroy = destroyProgressPanel;
650     dialog->p_sys = p_intf->p_sys;
651
652     [pool release];
653     return VLC_SUCCESS;
654 }
655
656 static int DisplayExtension(vlc_object_t *p_this, const char *type, vlc_value_t previous, vlc_value_t value, void *data)
657 {
658     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
659     intf_thread_t *p_intf = (intf_thread_t*) p_this;
660     intf_sys_t *sys = p_intf->p_sys;
661     extension_dialog_t *dialog = value.p_address;
662
663     // -updateExtensionDialog: Open its own runloop, so be sure to run on DefaultRunLoop.
664     [sys->displayer performSelectorOnMainThread:@selector(updateExtensionDialog:) withObject:[NSValue valueWithPointer:dialog] waitUntilDone:YES];
665     [pool release];
666     return VLC_SUCCESS;
667 }
668
669
670 void updateProgressPanel (void *priv, const char *text, float value)
671 {
672     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
673     intf_sys_t *sys = (intf_sys_t *)priv;
674
675     NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
676                           [NSNumber numberWithFloat:value], @"value",
677                           text ? [NSString stringWithUTF8String:text] : nil, @"text",
678                           nil];
679
680     [sys->displayer performSelectorOnMainThread:@selector(updateProgressPanel:) withObject:dict waitUntilDone:YES];
681
682     [pool release];
683 }
684
685 void destroyProgressPanel (void *priv)
686 {
687     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
688     intf_sys_t *sys = (intf_sys_t *)priv;
689
690     [sys->displayer performSelectorOnMainThread:@selector(destroyProgressPanel) withObject:nil waitUntilDone:YES];
691
692     [pool release];
693 }
694
695 bool checkProgressPanel (void *priv)
696 {
697     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
698     intf_sys_t *sys = (intf_sys_t *)priv;
699     BOOL ret;
700
701     ret = [[sys->displayer resultFromSelectorOnMainThread:@selector(checkProgressPanel) withObject:nil] boolValue];
702
703     [pool release];
704     return ret;
705 }
706
707
708 @implementation VLCDialogDisplayer
709 - (void)dealloc
710 {
711     [[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
712     assert(!_currentProgressBarPanel); // This has to be closed on main thread.
713     [super dealloc];
714 }
715
716 - (void)setIntf: (intf_thread_t *)p_mainintf
717 {
718     p_intf = p_mainintf;
719 }
720
721 - (intf_thread_t *)intf
722 {
723     return p_intf;
724 }
725
726 + (NSDictionary *)dictionaryForDialog:(const char *)title :(const char *)message :(const char *)yes :(const char *)no :(const char *)cancel
727 {
728     NSMutableDictionary *dict = [NSMutableDictionary dictionary];
729     if (title)
730         [dict setObject:[NSString stringWithUTF8String:title] forKey:@"title"];
731     if (message)
732         [dict setObject:[NSString stringWithUTF8String:message] forKey:@"message"];
733     if (yes)
734         [dict setObject:[NSString stringWithUTF8String:yes] forKey:@"yes"];
735     if (no)
736         [dict setObject:[NSString stringWithUTF8String:no] forKey:@"no"];
737     if (cancel)
738         [dict setObject:[NSString stringWithUTF8String:cancel] forKey:@"cancel"];
739
740     return dict;
741 }
742 #define VLCAssertIsMainThread() assert([NSThread isMainThread])
743
744 - (void)displayError:(NSDictionary *)dialog
745 {
746     VLCAssertIsMainThread();
747
748     NSRunInformationalAlertPanel([dialog objectForKey:@"title"],
749                                  [dialog objectForKey:@"message"],
750                                  @"OK", nil, nil);
751 }
752
753 - (void)displayCritical:(NSDictionary *)dialog
754 {
755     VLCAssertIsMainThread();
756
757     NSRunCriticalAlertPanel([dialog objectForKey:@"title"],
758                                  [dialog objectForKey:@"message"],
759                                  @"OK", nil, nil);
760 }
761
762 - (NSNumber *)displayQuestion:(NSDictionary *)dialog
763 {
764     VLCAssertIsMainThread();
765
766     NSInteger alertRet = 0;
767
768     NSAlert *alert = [NSAlert alertWithMessageText:[dialog objectForKey:@"title"]
769                               defaultButton:[dialog objectForKey:@"yes"]
770                             alternateButton:[dialog objectForKey:@"no"]
771                                 otherButton:[dialog objectForKey:@"cancel"]
772                   informativeTextWithFormat:[dialog objectForKey:@"message"]];
773     [alert setAlertStyle:NSInformationalAlertStyle];
774     alertRet = [alert runModal];
775
776     int ret;
777     switch (alertRet) {
778         case NSAlertDefaultReturn:
779             ret = 1;
780             break;
781         case NSAlertAlternateReturn:
782             ret = 2;
783             break;
784         case NSAlertOtherReturn:
785             ret = 3;
786             break;
787         default:
788             assert(0);
789             ret = 0;
790             break;
791     }
792
793     return [NSNumber numberWithInt:ret];
794 }
795
796 - (NSDictionary *)displayLogin:(NSDictionary *)dialog
797 {
798     VLCAssertIsMainThread();
799
800     VLCLoginPanel *panel = [[VLCLoginPanel alloc] init];
801     [panel createContentView];
802     [panel setDialogTitle:[dialog objectForKey:@"title"]];
803     [panel setDialogMessage:[dialog objectForKey:@"message"]];
804     [panel center];
805     NSInteger ret = [NSApp runModalForWindow:panel];
806     [panel close];
807
808     if (!ret)
809         return nil;
810
811     return [NSDictionary dictionaryWithObjectsAndKeys:
812             [panel userName], @"username",
813             [panel password], @"password",
814             nil];
815 }
816
817 - (void)displayProgressBar:(NSDictionary *)dialog
818 {
819     VLCAssertIsMainThread();
820
821     if(_currentProgressBarPanel)
822         [self destroyProgressPanel];
823
824     assert(!_currentProgressBarPanel);
825     _currentProgressBarPanel = [[VLCProgressPanel alloc] init];
826     [_currentProgressBarPanel createContentView];
827     [_currentProgressBarPanel setDialogTitle:[dialog objectForKey:@"title"]];
828     [_currentProgressBarPanel setDialogMessage:[dialog objectForKey:@"message"] ?: @""];
829     [_currentProgressBarPanel setCancelButtonLabel:[dialog objectForKey:@"cancel"]];
830
831     [_currentProgressBarPanel center];
832     [_currentProgressBarPanel makeKeyAndOrderFront:nil];
833 }
834
835 - (void)updateProgressPanel:(NSDictionary *)dict
836 {
837     VLCAssertIsMainThread();
838
839     assert(_currentProgressBarPanel);
840     [_currentProgressBarPanel setDialogMessage:[dict objectForKey:@"text"] ?: @""];
841     [_currentProgressBarPanel setProgressAsDouble:[[dict objectForKey:@"value"] doubleValue] * 1000.];
842 }
843
844 - (void)destroyProgressPanel
845 {
846     VLCAssertIsMainThread();
847
848     [_currentProgressBarPanel close];
849     [_currentProgressBarPanel release];
850     _currentProgressBarPanel = nil;
851 }
852
853 - (NSNumber *)checkProgressPanel
854 {
855     VLCAssertIsMainThread();
856
857     return [NSNumber numberWithBool:[_currentProgressBarPanel isCancelled]];
858 }
859
860 #pragma mark -
861 #pragma mark Last.FM support
862 - (void)globalNotificationReceived: (NSNotification *)theNotification
863 {
864     NSLog(@"globalNotificationReceived");
865     NSDictionary *userData = [theNotification userInfo];
866     BOOL lastFMEnabled = [[userData objectForKey:@"enabled"] intValue];
867     NSString *lastFMUsername = [userData objectForKey:@"username"];
868     NSString *lastFMPassword = [userData objectForKey:@"password"];
869
870     if (module_exists("audioscrobbler")) {
871         if (lastFMEnabled)
872             config_AddIntf(p_intf, "audioscrobbler");
873         else
874             config_RemoveIntf(p_intf, "audioscrobbler");
875
876         config_PutPsz(p_intf, "lastfm-username", [lastFMUsername UTF8String]);
877         config_PutPsz(p_intf, "lastfm-password", [lastFMPassword UTF8String]);
878         config_SaveConfigFile( p_intf, "main" );
879         config_SaveConfigFile(p_intf, "audioscrobbler");
880     }
881     else
882         msg_Err(p_intf,"Last.FM module not found, no action");
883 }
884
885 #pragma mark -
886 #pragma mark Extensions Dialog
887
888 - (void)triggerClick:(id)sender
889 {
890     assert([sender isKindOfClass:[VLCDialogButton class]]);
891     VLCDialogButton *button = sender;
892     extension_widget_t *widget = [button widget];
893
894     NSLog(@"(triggerClick)");
895     vlc_mutex_lock(&widget->p_dialog->lock);
896     extension_WidgetClicked(widget->p_dialog, widget);
897     vlc_mutex_unlock(&widget->p_dialog->lock);
898 }
899
900 - (void)syncTextField:(NSNotification *)notifcation
901 {
902     id sender = [notifcation object];
903     assert([sender isKindOfClass:[VLCDialogTextField class]]);
904     VLCDialogTextField *field = sender;
905     extension_widget_t *widget = [field widget];
906
907     vlc_mutex_lock(&widget->p_dialog->lock);
908     free(widget->psz_text);
909     widget->psz_text = strdup([[field stringValue] UTF8String]);
910     vlc_mutex_unlock(&widget->p_dialog->lock);
911 }
912
913 - (void)tableViewSelectionDidChange:(NSNotification *)notifcation
914 {
915     id sender = [notifcation object];
916     assert(sender && [sender isKindOfClass:[VLCDialogList class]]);
917     VLCDialogList *list = sender;
918
919     struct extension_widget_value_t *value;
920     unsigned i = 0;
921     for(value = [list widget]->p_values; value != NULL; value = value->p_next, i++)
922         value->b_selected = (i == [list selectedRow]);
923 }
924
925 - (void)popUpSelectionChanged:(id)sender
926 {
927     assert([sender isKindOfClass:[VLCDialogPopUpButton class]]);
928     VLCDialogPopUpButton *popup = sender;
929     struct extension_widget_value_t *value;
930     unsigned i = 0;
931     for(value = [popup widget]->p_values; value != NULL; value = value->p_next, i++)
932         value->b_selected = (i == [popup indexOfSelectedItem]);
933
934 }
935
936 - (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize
937 {
938     NSView *contentView = [sender contentView];
939     assert([contentView isKindOfClass:[VLCDialogGridView class]]);
940     VLCDialogGridView *gridView = (VLCDialogGridView *)contentView;
941
942     NSRect rect = NSMakeRect(0, 0, 0, 0);
943     rect.size = frameSize;
944     rect = [sender contentRectForFrameRect:rect];
945     rect.size = [gridView flexSize:rect.size];
946     rect = [sender frameRectForContentRect:rect];
947     return rect.size;
948 }
949
950 - (BOOL)windowShouldClose:(id)sender
951 {
952     assert([sender isKindOfClass:[VLCDialogWindow class]]);
953     VLCDialogWindow *window = sender;
954     extension_dialog_t *dialog = [window dialog];
955     extension_DialogClosed(dialog);
956     dialog->p_sys_intf = NULL;
957     return YES;
958 }
959
960 static NSView *createControlFromWidget(extension_widget_t *widget, id self)
961 {
962     assert(!widget->p_sys_intf);
963
964     switch (widget->type)
965     {
966         case EXTENSION_WIDGET_HTML:
967         {
968 //            NSScrollView *scrollView = [[NSScrollView alloc] init];
969 //            [scrollView setHasVerticalScroller:YES];
970 //            NSTextView *field = [[NSTextView alloc] init];
971 //            [scrollView setDocumentView:field];
972 //            [scrollView setAutoresizesSubviews:YES];
973 //            [scrollView setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
974 //            [field release];
975 //            return scrollView;
976             NSTextView *field = [[NSTextView alloc] init];
977             [field setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
978             [field setDrawsBackground:NO];
979             return field;
980         }
981         case EXTENSION_WIDGET_LABEL:
982         {
983             NSTextField *field = [[NSTextField alloc] init];
984             [field setEditable:NO];
985             [field setBordered:NO];
986             [field setDrawsBackground:NO];
987             [field setFont:[NSFont systemFontOfSize:0]];
988             [[field cell] setControlSize:NSRegularControlSize];
989             [field setAutoresizingMask:NSViewNotSizable];
990             return field;
991         }
992         case EXTENSION_WIDGET_TEXT_FIELD:
993         {
994             VLCDialogTextField *field = [[VLCDialogTextField alloc] init];
995             [field setWidget:widget];
996             [field setAutoresizingMask:NSViewWidthSizable];
997             [field setFont:[NSFont systemFontOfSize:0]];
998             [[field cell] setControlSize:NSRegularControlSize];
999             [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(syncTextField:)  name:NSControlTextDidChangeNotification object:field];
1000             return field;
1001         }
1002         case EXTENSION_WIDGET_BUTTON:
1003         {
1004             VLCDialogButton *button = [[VLCDialogButton alloc] init];
1005             [button setBezelStyle:NSRoundedBezelStyle];
1006             [button setWidget:widget];
1007             [button setAction:@selector(triggerClick:)];
1008             [button setTarget:self];
1009             [[button cell] setControlSize:NSRegularControlSize];
1010             [button setAutoresizingMask:NSViewNotSizable];
1011             return button;
1012         }
1013         case EXTENSION_WIDGET_DROPDOWN:
1014         {
1015             VLCDialogPopUpButton *popup = [[VLCDialogPopUpButton alloc] init];
1016             [popup setAction:@selector(popUpSelectionChanged:)];
1017             [popup setTarget:self];
1018             [popup setWidget:widget];
1019             return popup;
1020         }
1021         case EXTENSION_WIDGET_LIST:
1022         {
1023             NSScrollView *scrollView = [[NSScrollView alloc] init];
1024             [scrollView setHasVerticalScroller:YES];
1025             VLCDialogList *list = [[VLCDialogList alloc] init];
1026             [list setUsesAlternatingRowBackgroundColors:YES];
1027             [list setHeaderView:nil];
1028             [scrollView setDocumentView:list];
1029             [scrollView setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
1030
1031             NSTableColumn *column = [[NSTableColumn alloc] init];
1032             [list addTableColumn:column];
1033             [column release];
1034             [list setDataSource:list];
1035             [list setDelegate:self];
1036             [list setWidget:widget];
1037             [list release];
1038             return scrollView;
1039         }
1040         case EXTENSION_WIDGET_IMAGE:
1041         {
1042             NSImageView *imageView = [[NSImageView alloc] init];
1043             [imageView setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
1044             [imageView setImageFrameStyle:NSImageFramePhoto];
1045             [imageView setImageScaling:NSImageScaleProportionallyUpOrDown];
1046             return imageView;
1047         }
1048         default:
1049             assert(0);
1050             return nil;
1051     }
1052
1053 }
1054
1055 static void updateControlFromWidget(NSView *control, extension_widget_t *widget, id self)
1056 {
1057     switch (widget->type)
1058     {
1059         case EXTENSION_WIDGET_HTML:
1060 //        {
1061 //            // Get the scroll view
1062 //            assert([control isKindOfClass:[NSScrollView class]]);
1063 //            NSScrollView *scrollView = (NSScrollView *)control;
1064 //            control = [scrollView documentView];
1065 //
1066 //            assert([control isKindOfClass:[NSTextView class]]);
1067 //            NSTextView *textView = (NSTextView *)control;
1068 //            NSString *string = [NSString stringWithUTF8String:widget->psz_text];
1069 //            NSAttributedString *attrString = [[NSAttributedString alloc] initWithHTML:[string dataUsingEncoding:NSUTF8StringEncoding] documentAttributes:NULL];
1070 //            [[textView textStorage] setAttributedString:[[NSAttributedString alloc] initWithString:@"Hello"]];
1071 //            NSLog(@"%@", string);
1072 //            [textView setNeedsDisplay:YES];
1073 //            [textView scrollRangeToVisible:NSMakeRange(0, 0)];
1074 //            [attrString release];
1075 //            break;
1076 //
1077 //        }
1078         {
1079             assert([control isKindOfClass:[NSTextView class]]);
1080             NSTextView *textView = (NSTextView *)control;
1081             NSString *string = [NSString stringWithUTF8String:widget->psz_text];
1082             NSAttributedString *attrString = [[NSAttributedString alloc] initWithHTML:[string dataUsingEncoding:NSUTF8StringEncoding] documentAttributes:NULL];
1083             [[textView textStorage] setAttributedString:attrString];
1084             [textView setNeedsDisplay:YES];
1085             [textView scrollRangeToVisible:NSMakeRange(0, 0)];
1086             [attrString release];
1087             break;
1088
1089         }
1090         case EXTENSION_WIDGET_LABEL:
1091         case EXTENSION_WIDGET_PASSWORD:
1092         case EXTENSION_WIDGET_TEXT_FIELD:
1093         {
1094             if (!widget->psz_text)
1095                 break;
1096             assert([control isKindOfClass:[NSControl class]]);
1097             NSControl *field = (NSControl *)control;
1098             NSString *string = [NSString stringWithUTF8String:widget->psz_text];
1099             NSAttributedString *attrString = [[NSAttributedString alloc] initWithHTML:[string dataUsingEncoding:NSUTF8StringEncoding] documentAttributes:NULL];
1100             [field setAttributedStringValue:attrString];
1101             [attrString release];
1102             break;
1103         }
1104         case EXTENSION_WIDGET_BUTTON:
1105         {
1106             assert([control isKindOfClass:[NSButton class]]);
1107             NSButton *button = (NSButton *)control;
1108             if (!widget->psz_text)
1109                 break;
1110             [button setTitle:[NSString stringWithUTF8String:widget->psz_text]];
1111             break;
1112         }
1113         case EXTENSION_WIDGET_DROPDOWN:
1114         {
1115             assert([control isKindOfClass:[NSPopUpButton class]]);
1116             NSPopUpButton *popup = (NSPopUpButton *)control;
1117             [popup removeAllItems];
1118             struct extension_widget_value_t *value;
1119             for(value = widget->p_values; value != NULL; value = value->p_next)
1120             {
1121                 [popup addItemWithTitle:[NSString stringWithUTF8String:value->psz_text]];
1122             }
1123             [popup synchronizeTitleAndSelectedItem];
1124             [self popUpSelectionChanged:popup];
1125             break;
1126         }
1127
1128         case EXTENSION_WIDGET_LIST:
1129         {
1130             assert([control isKindOfClass:[NSScrollView class]]);
1131             NSScrollView *scrollView = (NSScrollView *)control;
1132             assert([[scrollView documentView] isKindOfClass:[VLCDialogList class]]);
1133             VLCDialogList *list = (VLCDialogList *)[scrollView documentView];
1134
1135             NSMutableArray *contentArray = [NSMutableArray array];
1136             struct extension_widget_value_t *value;
1137             for(value = widget->p_values; value != NULL; value = value->p_next)
1138             {
1139                 NSDictionary *entry = [NSDictionary dictionaryWithObjectsAndKeys:
1140                                        [NSNumber numberWithInt:value->i_id], @"id",
1141                                        [NSString stringWithUTF8String:value->psz_text], @"text",
1142                                        nil];
1143                 [contentArray addObject:entry];
1144             }
1145             list.contentArray = contentArray;
1146             [list reloadData];
1147             break;
1148         }
1149         case EXTENSION_WIDGET_IMAGE:
1150         {
1151             assert([control isKindOfClass:[NSImageView class]]);
1152             NSImageView *imageView = (NSImageView *)control;
1153             NSString *string = widget->psz_text ? [NSString stringWithUTF8String:widget->psz_text] : nil;
1154             NSImage *image = nil;
1155             NSLog(@"Setting image to %@", string);
1156             if (string)
1157                 image = [[NSImage alloc] initWithContentsOfURL:[NSURL fileURLWithPath:string]];
1158             [imageView setImage:image];
1159             [image release];
1160             break;
1161         }
1162     }
1163
1164 }
1165
1166 - (void)updateWidgets:(extension_dialog_t *)dialog
1167 {
1168     extension_widget_t *widget;
1169     NSWindow *window = dialog->p_sys_intf;
1170     FOREACH_ARRAY(widget, dialog->widgets)
1171     {
1172         if (!widget)
1173             continue; /* Some widgets may be NULL at this point */
1174
1175         BOOL shouldDestroy = widget->b_kill;
1176         NSView *control = widget->p_sys_intf;
1177         BOOL update = widget->b_update;
1178
1179
1180         if (!control && !shouldDestroy)
1181         {
1182             control = createControlFromWidget(widget, self);
1183             updateControlFromWidget(control, widget, self);
1184             widget->p_sys_intf = control;
1185             update = YES; // Force update and repositionning
1186             [control setHidden:widget->b_hide];
1187         }
1188
1189         if (update && !shouldDestroy)
1190         {
1191             updateControlFromWidget(control, widget, self);
1192             [control setHidden:widget->b_hide];
1193
1194             int row = widget->i_row - 1;
1195             int col = widget->i_column - 1;
1196             int hsp = __MAX( 1, widget->i_horiz_span );
1197             int vsp = __MAX( 1, widget->i_vert_span );
1198             if( row < 0 )
1199             {
1200                 row = 4;
1201                 col = 0;
1202             }
1203
1204             VLCDialogGridView *gridView = (VLCDialogGridView *)[window contentView];
1205             [gridView addSubview:control atRow:row column:col rowSpan:vsp colSpan:hsp];
1206
1207             //this->resize( sizeHint() );
1208             widget->b_update = false;
1209         }
1210
1211         if (shouldDestroy)
1212         {
1213             VLCDialogGridView *gridView = (VLCDialogGridView *)[window contentView];
1214             [gridView removeSubview:control];
1215             [control release];
1216             widget->p_sys_intf = NULL;
1217         }
1218     }
1219     FOREACH_END()
1220 }
1221
1222 - (void)updateExtensionDialog:(NSValue *)extensionDialog
1223 {
1224     extension_dialog_t *dialog = [extensionDialog pointerValue];
1225
1226     vlc_mutex_lock(&dialog->lock);
1227
1228     NSSize size = NSMakeSize(dialog->i_width, dialog->i_height);
1229
1230     BOOL shouldDestroy = dialog->b_kill;
1231
1232     if (!dialog->i_width || !dialog->i_height)
1233         size = NSMakeSize(640, 480);
1234
1235     VLCDialogWindow *window = dialog->p_sys_intf;
1236     if (!window && !shouldDestroy)
1237     {
1238         NSRect content = NSMakeRect(0, 0, 1, 1);
1239         window = [[VLCDialogWindow alloc] initWithContentRect:content styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask backing:NSBackingStoreBuffered defer:NO];
1240         [window setDelegate:self];
1241         [window setDialog:dialog];
1242         [window setTitle:[NSString stringWithUTF8String:dialog->psz_title]];
1243         VLCDialogGridView *gridView = [[VLCDialogGridView alloc] init];
1244         [gridView setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
1245         [window setContentView:gridView];
1246         [gridView release];
1247         dialog->p_sys_intf = window;
1248     }
1249
1250     [self updateWidgets:dialog];
1251
1252     if (shouldDestroy)
1253     {
1254         [window setDelegate:nil];
1255         [window close];
1256         dialog->p_sys_intf = NULL;
1257         window = nil;
1258     }
1259
1260     if (![window isVisible]) {
1261         [window center];
1262         [window makeKeyAndOrderFront:self];
1263     }
1264
1265     vlc_cond_signal(&dialog->cond);
1266     vlc_mutex_unlock(&dialog->lock);
1267 }
1268
1269
1270 /**
1271  * Helper to execute a function on main thread and get its return value.
1272  */
1273 - (void)execute:(NSDictionary *)dict
1274 {
1275     SEL sel = [[dict objectForKey:@"sel"] pointerValue];
1276     id *result = [[dict objectForKey:@"result"] pointerValue];
1277     id object = [dict objectForKey:@"object"];
1278
1279     NSAssert(sel, @"Try to execute a NULL selector");
1280
1281     *result = [self performSelector:sel withObject:object];
1282     [*result retain]; // Balanced in -resultFromSelectorOnMainThread
1283 }
1284
1285 - (id)resultFromSelectorOnMainThread:(SEL)sel withObject:(id)object
1286 {
1287     id result = nil;
1288     NSAssert(sel, @"Try to execute a NULL selector");
1289     NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
1290      [NSValue valueWithPointer:sel], @"sel",
1291      [NSValue valueWithPointer:&result], @"result",
1292      object, @"object", nil];
1293     [self performSelectorOnMainThread:@selector(execute:) withObject:dict waitUntilDone:YES];
1294     return [result autorelease];
1295 }
1296 @end
1297
1298
1299