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