* removed the old panel which used to show msg_err completely (hopefully) and added...
[vlc.git] / modules / gui / macosx / interaction.m
1 /*****************************************************************************
2  * interaction.h: Mac OS X interaction dialogs
3  *****************************************************************************
4  * Copyright (C) 2005-2006 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_defaultButton ? [NSString stringWithUTF8String:p_dialog->psz_defaultButton] : nil;
142     NSString *o_alternateButton = p_dialog->psz_alternateButton ? [NSString stringWithUTF8String:p_dialog->psz_alternateButton] : nil;
143     NSString *o_otherButton = p_dialog->psz_otherButton ? [NSString stringWithUTF8String:p_dialog->psz_otherButton] : 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: @"VLCWindow"] )
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_mutex_lock( &p_dialog->p_interaction->object_lock );
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_mutex_unlock( &p_dialog->p_interaction->object_lock );
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" );
308     if( p_dialog->i_flags & DIALOG_USER_PROGRESS )
309     {
310         [NSApp endSheet: o_prog_win];
311         [o_prog_win close];
312     }
313     if( p_dialog->i_flags & DIALOG_LOGIN_PW_OK_CANCEL )
314     {
315         [NSApp endSheet: o_auth_win];
316         [o_auth_win close];
317     }
318     if( p_dialog->i_flags & DIALOG_PSZ_INPUT_OK_CANCEL )
319     {
320         [NSApp endSheet: o_input_win];
321         [o_input_win close];
322     }
323     if( p_dialog->i_flags & DIALOG_INTF_PROGRESS )
324     {
325         [o_mainIntfPgbar setIndeterminate: YES];
326         [o_mainIntfPgbar setHidden: YES];
327         [[VLCMain sharedInstance] resetScrollField];
328     }
329 }
330
331 -(void)destroyDialog
332 {
333     msg_Dbg( p_intf, "destroy event" );
334     if( o_mainIntfPgbar )
335         [o_mainIntfPgbar release];
336 }
337
338 - (IBAction)cancelAndClose:(id)sender
339 {
340     /* tell the core that the dialog was cancelled in a yes/no-style dialogue */
341     vlc_mutex_lock( &p_dialog->p_interaction->object_lock );
342     p_dialog->i_return = DIALOG_CANCELLED;
343     p_dialog->i_status = ANSWERED_DIALOG;
344     vlc_mutex_unlock( &p_dialog->p_interaction->object_lock );
345     msg_Dbg( p_intf, "dialog cancelled" );
346 }
347
348 - (IBAction)cancelDialog:(id)sender
349 {
350     /* tell core that the user wishes to cancel the dialogue
351      * Use this function if cancelling is optionally like in the progress-dialogue */
352     vlc_mutex_lock( &p_dialog->p_interaction->object_lock );
353     p_dialog->b_cancelled = VLC_TRUE;
354     vlc_mutex_unlock( &p_dialog->p_interaction->object_lock );
355     msg_Dbg( p_intf, "cancelling dialog, will close it later on" );
356 }
357
358 - (IBAction)okayAndClose:(id)sender
359 {
360     msg_Dbg( p_intf, "running okayAndClose" );
361     vlc_mutex_lock( &p_dialog->p_interaction->object_lock );
362     if( p_dialog->i_flags == DIALOG_LOGIN_PW_OK_CANCEL )
363     {
364         p_dialog->psz_returned[0] = strdup( [[o_auth_login_fld stringValue] UTF8String] );
365         p_dialog->psz_returned[1] = strdup( [[o_auth_pw_fld stringValue] UTF8String] );
366     }
367     else if( p_dialog->i_flags == DIALOG_PSZ_INPUT_OK_CANCEL )
368         p_dialog->psz_returned[0] = strdup( [[o_input_fld stringValue] UTF8String] );
369     p_dialog->i_return = DIALOG_OK_YES;
370     p_dialog->i_status = ANSWERED_DIALOG;
371     vlc_mutex_unlock( &p_dialog->p_interaction->object_lock );
372     msg_Dbg( p_intf, "dialog acknowledged" );
373 }
374
375 @end
376
377 /*****************************************************************************
378  * VLCErrorInteractionPanel implementation
379  *****************************************************************************/
380 @implementation VLCErrorInteractionPanel
381 -(id)init
382 {
383     [super init];
384
385     /* load the nib */
386     nib_interact_errpanel_loaded = [NSBundle loadNibNamed:@"InteractionErrorPanel" owner:self];
387
388     /* init strings */
389     [o_window setTitle: _NS("Errors and Warnings")];
390     [o_cleanup_button setTitle: _NS("Clean up")];
391     [o_messages_btn setTitle: _NS("Show Details")];
392
393     /* init data sources */
394     o_errors = [[NSMutableArray alloc] init];
395     o_icons = [[NSMutableArray alloc] init];
396
397     /* ugly Carbon stuff following...
398      * regrettably, you can't get the icons through clean Cocoa */
399
400     /* retrieve our error icon */
401     IconRef ourIconRef;
402     int returnValue;
403     returnValue = GetIconRef(kOnSystemDisk, 'macs', 'stop', &ourIconRef);
404     errorIcon = [[NSImage alloc] initWithSize:NSMakeSize(32,32)];
405     [errorIcon lockFocus];
406     CGRect rect = CGRectMake(0,0,32,32);
407     PlotIconRefInContext((CGContextRef)[[NSGraphicsContext currentContext] 
408         graphicsPort],
409         &rect,
410         kAlignNone,
411         kTransformNone,
412         NULL /*inLabelColor*/,
413         kPlotIconRefNormalFlags,
414         (IconRef)ourIconRef);
415     [errorIcon unlockFocus];
416     returnValue = ReleaseIconRef(ourIconRef);
417
418     /* retrieve our caution icon */
419     returnValue = GetIconRef(kOnSystemDisk, 'macs', 'caut', &ourIconRef);
420     warnIcon = [[NSImage alloc] initWithSize:NSMakeSize(32,32)];
421     [warnIcon lockFocus];
422     PlotIconRefInContext((CGContextRef)[[NSGraphicsContext currentContext] 
423         graphicsPort],
424         &rect,
425         kAlignNone,
426         kTransformNone,
427         NULL /*inLabelColor*/,
428         kPlotIconRefNormalFlags,
429         (IconRef)ourIconRef);
430     [warnIcon unlockFocus];
431     returnValue = ReleaseIconRef(ourIconRef);    
432
433     return self;
434 }
435
436 -(void)dealloc
437 {
438     [errorIcon release];
439     [warnIcon release];
440     [o_errors release];
441     [o_icons release];
442     [super dealloc];
443 }
444
445 -(void)showPanel
446 {
447     [o_window makeKeyAndOrderFront: self];
448 }
449
450 -(void)addError: (NSString *)o_error withMsg:(NSString *)o_msg
451 {
452     /* format our string as desired */
453     NSMutableAttributedString * ourError;
454     ourError = [[NSMutableAttributedString alloc] initWithString:
455         [NSString stringWithFormat:@"%@\n%@", o_error, o_msg]
456         attributes: 
457         [NSDictionary dictionaryWithObject: [NSFont systemFontOfSize:11] forKey: NSFontAttributeName]];
458     [ourError 
459         addAttribute: NSFontAttributeName
460         value: [NSFont boldSystemFontOfSize:11] 
461         range: NSMakeRange( 0, [o_error length])];
462     [o_errors addObject: ourError];
463     [ourError release];
464
465     [o_icons addObject: errorIcon];
466
467     [o_error_table reloadData];
468     [self showPanel];
469 }
470
471 -(void)addWarning: (NSString *)o_warning withMsg:(NSString *)o_msg
472 {
473     /* format our string as desired */
474     NSMutableAttributedString * ourWarning;
475     ourWarning = [[NSMutableAttributedString alloc] initWithString:
476         [NSString stringWithFormat:@"%@\n%@", o_warning, o_msg]
477         attributes: 
478         [NSDictionary dictionaryWithObject: [NSFont systemFontOfSize:11] forKey: NSFontAttributeName]];
479     [ourWarning 
480         addAttribute: NSFontAttributeName
481         value: [NSFont boldSystemFontOfSize:11] 
482         range: NSMakeRange( 0, [o_warning length])];
483     [o_errors addObject: ourWarning];
484     [ourWarning release];
485
486     [o_icons addObject: warnIcon];
487     
488     [o_error_table reloadData];
489
490     [self showPanel];
491 }
492
493 -(IBAction)cleanupTable:(id)sender
494 {
495     [o_errors removeAllObjects];
496     [o_icons removeAllObjects];
497     [o_error_table reloadData];
498 }
499
500 -(IBAction)showMessages:(id)sender
501 {
502     [[VLCMain sharedInstance] showMessagesPanel: sender];
503 }
504
505 /*----------------------------------------------------------------------------
506  * data source methods
507  *---------------------------------------------------------------------------*/
508 - (int)numberOfRowsInTableView:(NSTableView *)theDataTable
509 {
510     return [o_errors count];
511 }
512
513 - (id)tableView:(NSTableView *)theDataTable objectValueForTableColumn:
514     (NSTableColumn *)theTableColumn row: (int)row
515 {
516     if( [[theTableColumn identifier] isEqualToString: @"error_msg"] )
517         return [o_errors objectAtIndex: row];
518
519     if( [[theTableColumn identifier] isEqualToString: @"icon"] )
520         return [o_icons objectAtIndex: row];
521
522     return @"unknown identifier";
523 }
524
525 @end