]> git.sesse.net Git - vlc/blob - modules/gui/macosx/interaction.m
4dd7fda7dd25abb6af53b00fff093e2b3068f3bb
[vlc] / modules / gui / macosx / interaction.m
1 /*****************************************************************************
2  * interaction.h: Mac OS X interaction dialogs
3  *****************************************************************************
4  * Copyright (C) 2005-2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Derk-Jan Hartman <hartman at videolan dot org>
8  *          Felix Kühne <fkuehne at videolan dot org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #import "intf.h"
26 #import "interaction.h"
27
28 /* for the icons in our custom error panel */
29 #import <ApplicationServices/ApplicationServices.h>
30
31 /*****************************************************************************
32  * VLCInteractionList implementation
33  *****************************************************************************/
34 @implementation VLCInteractionList
35
36 -(id)init
37 {
38     [super init];
39     o_interaction_list = [[NSMutableArray alloc] initWithCapacity:1];
40     [[NSNotificationCenter defaultCenter] addObserver:self
41         selector:@selector(newInteractionEvent:)
42         name: @"VLCNewInteractionEventNotification"
43         object:self];
44
45     o_error_panel = [[VLCErrorInteractionPanel alloc] init];
46
47     return self;
48 }
49
50 -(void)newInteractionEvent: (NSNotification *)o_notification
51 {
52     VLCInteraction *o_interaction;
53     NSValue *o_value = [[o_notification userInfo] objectForKey:@"VLCDialogPointer"];
54     interaction_dialog_t *p_dialog = [o_value pointerValue];
55
56     switch( p_dialog->i_action )
57     {
58     case INTERACT_NEW:
59         [self addInteraction: p_dialog];
60         break;
61     case INTERACT_UPDATE:
62         o_interaction = (VLCInteraction *)p_dialog->p_private;
63         [o_interaction updateDialog];
64         break;
65     case INTERACT_HIDE:
66         o_interaction = (VLCInteraction *)p_dialog->p_private;
67         [o_interaction hideDialog];
68         break;
69     case INTERACT_DESTROY:
70         o_interaction = (VLCInteraction *)p_dialog->p_private;
71         [o_interaction destroyDialog];
72         [self removeInteraction:o_interaction];
73         p_dialog->i_status = DESTROYED_DIALOG;
74         break;
75     }
76 }
77
78 -(void)addInteraction: (interaction_dialog_t *)p_dialog
79 {
80     VLCInteraction *o_interaction = [[VLCInteraction alloc] initDialog: p_dialog];
81  
82     p_dialog->p_private = (void *)o_interaction;
83     [o_interaction_list addObject:[o_interaction autorelease]];
84     [o_interaction runDialog];
85 }
86
87 -(void)removeInteraction: (VLCInteraction *)o_interaction
88 {
89     [o_interaction_list removeObject:o_interaction];
90 }
91
92 -(id)getErrorPanel
93 {
94     return o_error_panel;
95 }
96
97 -(void)dealloc
98 {
99     [[NSNotificationCenter defaultCenter] removeObserver:self];
100     [o_interaction_list removeAllObjects];
101     [o_interaction_list release];
102     [super dealloc];
103 }
104 @end
105
106 /*****************************************************************************
107  * VLCInteraction implementation
108  *****************************************************************************/
109 @implementation VLCInteraction
110
111 -(id)initDialog: (interaction_dialog_t *)_p_dialog
112 {
113     p_intf = VLCIntf;
114     [super init];
115     p_dialog = _p_dialog;
116     return self;
117 }
118
119 -(void)runDialog
120 {
121     id o_window = NULL;
122     if( !p_dialog )
123         msg_Err( p_intf, "no available interaction framework" );
124
125     if( !nib_interact_loaded )
126     {
127         nib_interact_loaded = [NSBundle loadNibNamed:@"Interaction" owner:self];
128         [o_prog_cancel_btn setTitle: _NS("Cancel")];
129         [o_prog_bar setUsesThreadedAnimation: YES];
130         [o_auth_login_txt setStringValue: _NS("Login:")];
131         [o_auth_pw_txt setStringValue: _NS("Password:")];
132         [o_auth_cancel_btn setTitle: _NS("Cancel")];
133         [o_auth_ok_btn setTitle: _NS("OK")];
134         [o_input_ok_btn setTitle: _NS("OK")];
135         [o_input_cancel_btn setTitle: _NS("Cancel")];
136         o_mainIntfPgbar = [[VLCMain sharedInstance] getMainIntfPgbar];
137     }
138
139     NSString *o_title = [NSString stringWithUTF8String:p_dialog->psz_title ? p_dialog->psz_title : _("Error")];
140     NSString *o_description = [NSString stringWithUTF8String:p_dialog->psz_description ? p_dialog->psz_description : ""];
141     NSString *o_defaultButton = p_dialog->psz_default_button ? [NSString stringWithUTF8String:p_dialog->psz_default_button] : nil;
142     NSString *o_alternateButton = p_dialog->psz_alternate_button ? [NSString stringWithUTF8String:p_dialog->psz_alternate_button] : nil;
143     NSString *o_otherButton = p_dialog->psz_other_button ? [NSString stringWithUTF8String:p_dialog->psz_other_button] : nil;
144
145     vout_thread_t *p_vout = vlc_object_find( VLCIntf, VLC_OBJECT_VOUT, FIND_ANYWHERE );
146     if( p_vout != NULL )
147     {
148         NSEnumerator * o_enum = [[NSApp orderedWindows] objectEnumerator];
149
150         while( ( o_window = [o_enum nextObject] ) )
151         {
152             if( [[o_window className] isEqualToString: @"VLCVoutWindow"] )
153             {
154                 vlc_object_release( (vlc_object_t *)p_vout );
155                 break;
156             }
157         }
158         vlc_object_release( (vlc_object_t *)p_vout );
159     }
160     else
161     {
162         o_window = [NSApp mainWindow];
163     }
164
165 #if 0
166     msg_Dbg( p_intf, "Title: %s", [o_title UTF8String] );
167     msg_Dbg( p_intf, "Description: %s", [o_description UTF8String] );
168     msg_Dbg( p_intf, "Delivered flag: %i", p_dialog->i_flags );
169 #endif
170
171     if( p_dialog->i_flags & DIALOG_BLOCKING_ERROR )
172     {
173         msg_Dbg( p_intf, "error panel requested" );
174         NSBeginInformationalAlertSheet( o_title, _NS("OK"), nil, nil,
175             o_window, self, @selector(sheetDidEnd: returnCode: contextInfo:),
176             NULL, nil, o_description );
177     }
178     else if( p_dialog->i_flags & DIALOG_NONBLOCKING_ERROR )
179     {
180         msg_Dbg( p_intf, "addition to non-blocking error panel received" );
181         [[[[VLCMain sharedInstance] getInteractionList] getErrorPanel]
182         addError: o_title withMsg: o_description];
183     }
184     else if( p_dialog->i_flags & DIALOG_WARNING )
185     {
186         msg_Dbg( p_intf, "addition to non-blocking warning panel received" );
187         [[[[VLCMain sharedInstance] getInteractionList] getErrorPanel]
188             addWarning: o_title withMsg: o_description];
189     }
190     else if( p_dialog->i_flags & DIALOG_YES_NO_CANCEL )
191     {
192         msg_Dbg( p_intf, "yes-no-cancel-dialog requested" );
193         NSBeginInformationalAlertSheet( o_title, o_defaultButton,
194             o_alternateButton, o_otherButton, o_window, self,
195             @selector(sheetDidEnd: returnCode: contextInfo:), NULL, nil,
196             o_description );
197     }
198     else if( p_dialog->i_flags & DIALOG_LOGIN_PW_OK_CANCEL )
199     {
200         msg_Dbg( p_intf, "dialog for login and pw requested" );
201         [o_auth_title setStringValue: o_title];
202         [o_auth_description setStringValue: o_description];
203         [o_auth_login_fld setStringValue: @""];
204         [o_auth_pw_fld setStringValue: @""];
205         [NSApp beginSheet: o_auth_win modalForWindow: o_window
206             modalDelegate: self didEndSelector: nil contextInfo: nil];
207         [o_auth_win makeKeyWindow];
208     }
209     else if( p_dialog->i_flags & DIALOG_USER_PROGRESS )
210     {
211         msg_Dbg( p_intf, "user progress dialog requested" );
212         [o_prog_title setStringValue: o_title];
213         [o_prog_description setStringValue: o_description];
214         [o_prog_bar setDoubleValue: (double)p_dialog->val.f_float];
215         if( p_dialog->i_timeToGo < 1 )
216             [o_prog_timeToGo setStringValue: @""];
217         else
218             [o_prog_timeToGo setStringValue: [NSString stringWithFormat:
219                 _NS("Remaining time: %i seconds"), p_dialog->i_timeToGo]];
220         [NSApp beginSheet: o_prog_win modalForWindow: o_window
221             modalDelegate: self didEndSelector: nil contextInfo: nil];
222         [o_prog_win makeKeyWindow];
223     }
224     else if( p_dialog->i_flags & DIALOG_PSZ_INPUT_OK_CANCEL )
225     {
226         msg_Dbg( p_intf, "text input from user requested" );
227         [o_input_title setStringValue: o_title];
228         [o_input_description setStringValue: o_description];
229         [o_input_fld setStringValue: @""];
230         [NSApp beginSheet: o_input_win modalForWindow: o_window
231             modalDelegate: self didEndSelector: nil contextInfo: nil];
232         [o_input_win makeKeyWindow];
233     }
234     else if( p_dialog->i_flags & DIALOG_INTF_PROGRESS )
235     {
236         msg_Dbg( p_intf, "progress-bar in main intf requested" );
237         [[VLCMain sharedInstance] setScrollField: o_description stopAfter: -1];
238         [o_mainIntfPgbar setDoubleValue: (double)p_dialog->val.f_float];
239         [o_mainIntfPgbar setHidden: NO];
240         [[[VLCMain sharedInstance] getControllerWindow] makeKeyWindow];
241         [o_mainIntfPgbar setIndeterminate: NO];
242     }
243     else
244         msg_Err( p_intf, "requested dialog type unknown (%i)", p_dialog->i_flags );
245 }
246
247 - (void)sheetDidEnd:(NSWindow *)o_sheet returnCode:(int)i_return
248     contextInfo:(void *)o_context
249 {
250     vlc_object_lock( p_dialog->p_interaction );
251     if( i_return == NSAlertDefaultReturn )
252     {
253         p_dialog->i_return = DIALOG_OK_YES;
254     }
255     else if( i_return == NSAlertAlternateReturn )
256     {
257         p_dialog->i_return = DIALOG_NO;
258     }
259     else if( i_return == NSAlertOtherReturn )
260     {
261         p_dialog->i_return = DIALOG_CANCELLED;
262     }
263     p_dialog->i_status = ANSWERED_DIALOG;
264     vlc_object_unlock( p_dialog->p_interaction );
265 }
266
267 -(void)updateDialog
268 {
269     if( p_dialog->i_flags & DIALOG_USER_PROGRESS )
270     {
271         [o_prog_description setStringValue:
272             [NSString stringWithUTF8String: p_dialog->psz_description]];
273         [o_prog_bar setDoubleValue: (double)p_dialog->val.f_float];
274
275         if( [o_prog_bar doubleValue] == 100.0 )
276         {
277             /* we are done, let's hide */
278             [self hideDialog];
279         }
280
281         if( p_dialog->i_timeToGo < 1 )
282             [o_prog_timeToGo setStringValue: @""];
283         else
284             [o_prog_timeToGo setStringValue: [NSString stringWithFormat:
285                     _NS("Remaining time: %i seconds"), p_dialog->i_timeToGo]];
286
287         return;
288     }
289     if( p_dialog->i_flags & DIALOG_INTF_PROGRESS )
290     {
291         [[VLCMain sharedInstance] setScrollField:
292             [NSString stringWithUTF8String: p_dialog->psz_description]
293             stopAfter: -1];
294         [o_mainIntfPgbar setDoubleValue: (double)p_dialog->val.f_float];
295
296         if( [o_mainIntfPgbar doubleValue] == 100.0 )
297         {
298             /* we are done, let's hide */
299             [self hideDialog];
300         }
301         return;
302     }
303 }
304
305 -(void)hideDialog
306 {
307     msg_Dbg( p_intf, "hide event %p", self );
308     if( p_dialog->i_flags & DIALOG_USER_PROGRESS )
309     {
310         if([o_prog_win isVisible])
311         {
312             [NSApp endSheet: o_prog_win];
313             [o_prog_win close];
314         }
315     }
316     if( p_dialog->i_flags & DIALOG_LOGIN_PW_OK_CANCEL )
317     {
318         if([o_auth_win isVisible])
319         {
320             [NSApp endSheet: o_auth_win];
321             [o_auth_win close];
322         }
323     }
324     if( p_dialog->i_flags & DIALOG_PSZ_INPUT_OK_CANCEL )
325     {
326         if([o_input_win isVisible])
327         {
328             [NSApp endSheet: o_input_win];
329             [o_input_win close];
330         }
331     }
332     if( p_dialog->i_flags & DIALOG_INTF_PROGRESS )
333     {
334         [o_mainIntfPgbar setIndeterminate: YES];
335         [o_mainIntfPgbar setHidden: YES];
336         [[VLCMain sharedInstance] resetScrollField];
337     }
338 }
339
340 -(void)destroyDialog
341 {
342     msg_Dbg( p_intf, "destroy event" );
343     if( o_mainIntfPgbar )
344         [o_mainIntfPgbar release];
345 }
346
347 - (IBAction)cancelAndClose:(id)sender
348 {
349     /* tell the core that the dialog was cancelled in a yes/no-style dialogue */
350     vlc_object_lock( p_dialog->p_interaction );
351     p_dialog->i_return = DIALOG_CANCELLED;
352     p_dialog->i_status = ANSWERED_DIALOG;
353     vlc_object_unlock( p_dialog->p_interaction );
354     msg_Dbg( p_intf, "dialog cancelled" );
355 }
356
357 - (IBAction)cancelDialog:(id)sender
358 {
359     /* tell core that the user wishes to cancel the dialogue
360      * Use this function if cancelling is optionally like in the progress-dialogue */
361     vlc_object_lock( p_dialog->p_interaction );
362     p_dialog->b_cancelled = true;
363     vlc_object_unlock( p_dialog->p_interaction );
364     msg_Dbg( p_intf, "cancelling dialog, will close it later on" );
365 }
366
367 - (IBAction)okayAndClose:(id)sender
368 {
369     msg_Dbg( p_intf, "running okayAndClose" );
370     vlc_object_lock( p_dialog->p_interaction );
371     if( p_dialog->i_flags == DIALOG_LOGIN_PW_OK_CANCEL )
372     {
373         p_dialog->psz_returned[0] = strdup( [[o_auth_login_fld stringValue] UTF8String] );
374         p_dialog->psz_returned[1] = strdup( [[o_auth_pw_fld stringValue] UTF8String] );
375     }
376     else if( p_dialog->i_flags == DIALOG_PSZ_INPUT_OK_CANCEL )
377         p_dialog->psz_returned[0] = strdup( [[o_input_fld stringValue] UTF8String] );
378     p_dialog->i_return = DIALOG_OK_YES;
379     p_dialog->i_status = ANSWERED_DIALOG;
380     vlc_object_unlock( p_dialog->p_interaction );
381     msg_Dbg( p_intf, "dialog acknowledged" );
382 }
383
384 @end
385
386 /*****************************************************************************
387  * VLCErrorInteractionPanel implementation
388  *****************************************************************************/
389 @implementation VLCErrorInteractionPanel
390 -(id)init
391 {
392     [super init];
393
394     /* load the nib */
395     nib_interact_errpanel_loaded = [NSBundle loadNibNamed:@"InteractionErrorPanel" owner:self];
396
397     /* init strings */
398     [o_window setTitle: _NS("Errors and Warnings")];
399     [o_cleanup_button setTitle: _NS("Clean up")];
400     [o_messages_btn setTitle: _NS("Show Details")];
401
402     /* init data sources */
403     o_errors = [[NSMutableArray alloc] init];
404     o_icons = [[NSMutableArray alloc] init];
405
406     /* ugly Carbon stuff following...
407      * regrettably, you can't get the icons through clean Cocoa */
408
409     /* retrieve our error icon */
410     IconRef ourIconRef;
411     int returnValue;
412     returnValue = GetIconRef(kOnSystemDisk, 'macs', 'stop', &ourIconRef);
413     errorIcon = [[NSImage alloc] initWithSize:NSMakeSize(32,32)];
414     [errorIcon lockFocus];
415     CGRect rect = CGRectMake(0,0,32,32);
416     PlotIconRefInContext((CGContextRef)[[NSGraphicsContext currentContext]
417         graphicsPort],
418         &rect,
419         kAlignNone,
420         kTransformNone,
421         NULL /*inLabelColor*/,
422         kPlotIconRefNormalFlags,
423         (IconRef)ourIconRef);
424     [errorIcon unlockFocus];
425     returnValue = ReleaseIconRef(ourIconRef);
426
427     /* retrieve our caution icon */
428     returnValue = GetIconRef(kOnSystemDisk, 'macs', 'caut', &ourIconRef);
429     warnIcon = [[NSImage alloc] initWithSize:NSMakeSize(32,32)];
430     [warnIcon lockFocus];
431     PlotIconRefInContext((CGContextRef)[[NSGraphicsContext currentContext]
432         graphicsPort],
433         &rect,
434         kAlignNone,
435         kTransformNone,
436         NULL /*inLabelColor*/,
437         kPlotIconRefNormalFlags,
438         (IconRef)ourIconRef);
439     [warnIcon unlockFocus];
440     returnValue = ReleaseIconRef(ourIconRef);
441
442     return self;
443 }
444
445 -(void)dealloc
446 {
447     [errorIcon release];
448     [warnIcon release];
449     [o_errors release];
450     [o_icons release];
451     [super dealloc];
452 }
453
454 -(void)showPanel
455 {
456     [o_window makeKeyAndOrderFront: self];
457 }
458
459 -(void)addError: (NSString *)o_error withMsg:(NSString *)o_msg
460 {
461     /* format our string as desired */
462     NSMutableAttributedString * ourError;
463     ourError = [[NSMutableAttributedString alloc] initWithString:
464         [NSString stringWithFormat:@"%@\n%@", o_error, o_msg]
465         attributes:
466         [NSDictionary dictionaryWithObject: [NSFont systemFontOfSize:11] forKey: NSFontAttributeName]];
467     [ourError
468         addAttribute: NSFontAttributeName
469         value: [NSFont boldSystemFontOfSize:11]
470         range: NSMakeRange( 0, [o_error length])];
471     [o_errors addObject: ourError];
472     [ourError release];
473
474     [o_icons addObject: errorIcon];
475
476     [o_error_table reloadData];
477     [self showPanel];
478 }
479
480 -(void)addWarning: (NSString *)o_warning withMsg:(NSString *)o_msg
481 {
482     /* format our string as desired */
483     NSMutableAttributedString * ourWarning;
484     ourWarning = [[NSMutableAttributedString alloc] initWithString:
485         [NSString stringWithFormat:@"%@\n%@", o_warning, o_msg]
486         attributes:
487         [NSDictionary dictionaryWithObject: [NSFont systemFontOfSize:11] forKey: NSFontAttributeName]];
488     [ourWarning
489         addAttribute: NSFontAttributeName
490         value: [NSFont boldSystemFontOfSize:11]
491         range: NSMakeRange( 0, [o_warning length])];
492     [o_errors addObject: ourWarning];
493     [ourWarning release];
494
495     [o_icons addObject: warnIcon];
496  
497     [o_error_table reloadData];
498
499     [self showPanel];
500 }
501
502 -(IBAction)cleanupTable:(id)sender
503 {
504     [o_errors removeAllObjects];
505     [o_icons removeAllObjects];
506     [o_error_table reloadData];
507 }
508
509 -(IBAction)showMessages:(id)sender
510 {
511     [[VLCMain sharedInstance] showMessagesPanel: sender];
512 }
513
514 /*----------------------------------------------------------------------------
515  * data source methods
516  *---------------------------------------------------------------------------*/
517 - (int)numberOfRowsInTableView:(NSTableView *)theDataTable
518 {
519     return [o_errors count];
520 }
521
522 - (id)tableView:(NSTableView *)theDataTable objectValueForTableColumn:
523     (NSTableColumn *)theTableColumn row: (int)row
524 {
525     if( [[theTableColumn identifier] isEqualToString: @"error_msg"] )
526         return [o_errors objectAtIndex: row];
527
528     if( [[theTableColumn identifier] isEqualToString: @"icon"] )
529         return [o_icons objectAtIndex: row];
530
531     return @"unknown identifier";
532 }
533
534 @end