0ffbe6607a28082ee86bcd4b18827a4c7dae327f
[vlc.git] / modules / gui / macosx / open.m
1 /*****************************************************************************
2  * open.m: MacOS X plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2002-2003 VideoLAN
5  * $Id: open.m,v 1.29 2003/04/15 14:05:13 hartman Exp $
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net> 
8  *          Christophe Massiot <massiot@via.ecp.fr>
9  *          Derk-Jan Hartman <thedj@users.sourceforge.net>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <stdlib.h>                                      /* malloc(), free() */
30 #include <sys/param.h>                                    /* for MAXPATHLEN */
31 #include <string.h>
32
33 #include <paths.h>
34 #include <IOKit/IOKitLib.h>
35 #include <IOKit/IOBSD.h>
36 #include <IOKit/storage/IOMedia.h>
37 #include <IOKit/storage/IOCDMedia.h>
38 #include <IOKit/storage/IODVDMedia.h>
39
40 #include "intf.h"
41 #include "playlist.h"
42 #include "open.h"
43
44 #include "netutils.h"
45
46 /*****************************************************************************
47  * GetEjectableMediaOfClass 
48  *****************************************************************************/
49 NSArray *GetEjectableMediaOfClass( const char *psz_class )
50 {
51     io_object_t next_media;
52     mach_port_t master_port;
53     kern_return_t kern_result;
54     NSArray *o_devices = nil;
55     NSMutableArray *p_list = nil;
56     io_iterator_t media_iterator;
57     CFMutableDictionaryRef classes_to_match;
58
59     kern_result = IOMasterPort( MACH_PORT_NULL, &master_port );
60     if( kern_result != KERN_SUCCESS )
61     {
62         return( nil );
63     }
64     
65     classes_to_match = IOServiceMatching( psz_class );
66     if( classes_to_match == NULL )
67     {
68         return( nil );
69     }
70     
71     CFDictionarySetValue( classes_to_match, CFSTR( kIOMediaEjectable ), 
72                           kCFBooleanTrue );
73     
74     kern_result = IOServiceGetMatchingServices( master_port, classes_to_match, 
75                                                 &media_iterator );
76     if( kern_result != KERN_SUCCESS )
77     {
78         return( nil );
79     }
80
81     p_list = [NSMutableArray arrayWithCapacity: 1];
82     
83     next_media = IOIteratorNext( media_iterator );
84     if( next_media != NULL )
85     {
86         char psz_buf[0x32];
87         size_t dev_path_length;
88         CFTypeRef str_bsd_path;
89     
90         do
91         {
92             str_bsd_path = IORegistryEntryCreateCFProperty( next_media,
93                                                             CFSTR( kIOBSDName ),
94                                                             kCFAllocatorDefault,
95                                                             0 );
96             if( str_bsd_path == NULL )
97             {
98                 IOObjectRelease( next_media );
99                 continue;
100             }
101             
102             snprintf( psz_buf, sizeof(psz_buf), "%s%c", _PATH_DEV, 'r' );
103             dev_path_length = strlen( psz_buf );
104             
105             if( CFStringGetCString( str_bsd_path,
106                                     (char*)&psz_buf + dev_path_length,
107                                     sizeof(psz_buf) - dev_path_length,
108                                     kCFStringEncodingASCII ) )
109             {
110                 [p_list addObject: [NSString stringWithCString: psz_buf]];
111             }
112             
113             CFRelease( str_bsd_path );
114             
115             IOObjectRelease( next_media );
116         
117         } while( ( next_media = IOIteratorNext( media_iterator ) ) != NULL );
118     }
119     
120     IOObjectRelease( media_iterator );
121
122     o_devices = [NSArray arrayWithArray: p_list];
123
124     return( o_devices );
125 }
126
127 /*****************************************************************************
128  * VLCOpen implementation 
129  *****************************************************************************/
130 @implementation VLCOpen
131
132 - (void)awakeFromNib
133 {
134     intf_thread_t * p_intf = [NSApp getIntf];
135     char * psz_sout = config_GetPsz( p_intf, "sout" );
136
137     if ( psz_sout != NULL && *psz_sout )
138     {
139         [o_sout_cbox setState: YES];
140     }
141     else
142     {
143         [o_sout_cbox setState: NO];
144     }
145     free(psz_sout);
146
147     [o_panel setTitle: _NS("Open Source")];
148     [o_mrl_lbl setTitle: _NS("Media Resource Locator (MRL)")];
149     [o_ckbox_enqueue setTitle: _NS("Only enqueue in playlist, do not play")];
150
151     [o_btn_ok setTitle: _NS("OK")];
152     [o_btn_cancel setTitle: _NS("Cancel")];
153
154     [[o_tabview tabViewItemAtIndex: 0] setLabel: _NS("File")];
155     [[o_tabview tabViewItemAtIndex: 1] setLabel: _NS("Disc")];
156     [[o_tabview tabViewItemAtIndex: 2] setLabel: _NS("Network")];
157
158     [o_file_btn_browse setTitle: _NS("Browse...")];
159     [o_file_stream setTitle: _NS("Treat as a pipe rather than as a file")];
160
161     [o_disc_device_lbl setStringValue: _NS("Device name")];
162     [o_disc_title_lbl setStringValue: _NS("Title")];
163     [o_disc_chapter_lbl setStringValue: _NS("Chapter")];
164     [o_disc_videots_btn_browse setTitle: _NS("Browse...")];
165     [o_disc_dvd_menus setTitle: _NS("Use DVD menus (EXPERIMENTAL)")];
166
167     [[o_disc_type cellAtRow:0 column:0] setTitle: _NS("VIDEO_TS folder")];
168     [[o_disc_type cellAtRow:1 column:0] setTitle: _NS("DVD")];
169     [[o_disc_type cellAtRow:2 column:0] setTitle: _NS("VCD")];
170
171     [o_net_udp_port_lbl setStringValue: _NS("Port")];
172     [o_net_udpm_addr_lbl setStringValue: _NS("Address")];
173     [o_net_udpm_port_lbl setStringValue: _NS("Port")];
174     [o_net_http_url_lbl setStringValue: _NS("URL")];
175
176     [[o_net_mode cellAtRow:0 column:0] setTitle: _NS("UDP/RTP")];
177     [[o_net_mode cellAtRow:1 column:0] setTitle: _NS("UDP/RTP Multicast")];
178     [[o_net_mode cellAtRow:2 column:0] setTitle: _NS("HTTP/FTP/MMS")];
179
180     [o_net_udp_port setIntValue: config_GetInt( p_intf, "server-port" )];
181     [o_net_udp_port_stp setIntValue: config_GetInt( p_intf, "server-port" )];
182
183     [o_sout_cbox setTitle: _NS("Stream output:")];
184     [o_sout_btn_ok setTitle: _NS("OK")];
185     [o_sout_settings setTitle: _NS("Settings...")];
186     [o_sout_mrl_lbl setTitle: _NS("Stream output MRL")];
187     
188     [o_sout_access_lbl setTitle: _NS("Output Method")];
189     [[o_sout_access cellAtRow:0 column:0] setTitle: _NS("File")];
190     [[o_sout_access cellAtRow:1 column:0] setTitle: _NS("HTTP")];
191     [[o_sout_access cellAtRow:2 column:0] setTitle: _NS("UDP")];
192     [[o_sout_access cellAtRow:3 column:0] setTitle: _NS("RTP")];
193
194     [o_sout_file_btn_browse setTitle: _NS("Browse...")];
195     [o_sout_udp_addr_lbl setStringValue: _NS("Address")];
196     [o_sout_udp_port_lbl setStringValue: _NS("Port")];
197
198     [o_sout_mux_lbl setTitle: _NS("Encapsulation Method")];
199     [[o_sout_mux cellAtRow:0 column:0] setTitle: _NS("MPEG TS")];
200     [[o_sout_mux cellAtRow:0 column:1] setTitle: _NS("MPEG PS")];
201     [[o_sout_mux cellAtRow:0 column:2] setTitle: _NS("AVI")];
202     [[o_sout_mux cellAtRow:0 column:3] setTitle: _NS("Ogg")];
203     
204     [o_file_sub_ckbox setTitle: _NS("Load subtitles file:")];
205     [o_file_sub_btn_settings setTitle: _NS("Settings...")];
206     [o_file_sub_btn_browse setTitle: _NS("Browse...")];
207     [o_file_sub_override setTitle: _NS("Override")];
208     [o_file_sub_delay_lbl setStringValue: _NS("delay")];
209     [o_file_sub_delay_stp setEnabled: NO];
210     [o_file_sub_fps_lbl setStringValue: _NS("fps")];
211     [o_file_sub_fps_stp setEnabled: NO];
212     [o_file_sub_ok_btn setStringValue: _NS("OK")];
213     
214     [[NSNotificationCenter defaultCenter] addObserver: self
215         selector: @selector(openFilePathChanged:)
216         name: NSControlTextDidChangeNotification
217         object: o_file_path];
218
219     [[NSNotificationCenter defaultCenter] addObserver: self
220         selector: @selector(openDiscInfoChanged:)
221         name: NSControlTextDidChangeNotification
222         object: o_disc_device];
223     [[NSNotificationCenter defaultCenter] addObserver: self
224         selector: @selector(openDiscInfoChanged:)
225         name: NSControlTextDidChangeNotification
226         object: o_disc_title];
227     [[NSNotificationCenter defaultCenter] addObserver: self
228         selector: @selector(openDiscInfoChanged:)
229         name: NSControlTextDidChangeNotification
230         object: o_disc_chapter];
231     [[NSNotificationCenter defaultCenter] addObserver: self
232         selector: @selector(openDiscInfoChanged:)
233         name: NSControlTextDidChangeNotification
234         object: o_disc_videots_folder];
235
236     [[NSNotificationCenter defaultCenter] addObserver: self
237         selector: @selector(openNetInfoChanged:)
238         name: NSControlTextDidChangeNotification
239         object: o_net_udp_port];
240     [[NSNotificationCenter defaultCenter] addObserver: self
241         selector: @selector(openNetInfoChanged:)
242         name: NSControlTextDidChangeNotification
243         object: o_net_udpm_addr];
244     [[NSNotificationCenter defaultCenter] addObserver: self
245         selector: @selector(openNetInfoChanged:)
246         name: NSControlTextDidChangeNotification
247         object: o_net_udpm_port];
248     [[NSNotificationCenter defaultCenter] addObserver: self
249         selector: @selector(openNetInfoChanged:)
250         name: NSControlTextDidChangeNotification
251         object: o_net_http_url];
252
253     [[NSNotificationCenter defaultCenter] addObserver: self
254         selector: @selector(soutInfoChanged:)
255         name: NSControlTextDidChangeNotification
256         object: o_sout_file_path];
257     [[NSNotificationCenter defaultCenter] addObserver: self
258         selector: @selector(soutInfoChanged:)
259         name: NSControlTextDidChangeNotification
260         object: o_sout_udp_addr];
261     [[NSNotificationCenter defaultCenter] addObserver: self
262         selector: @selector(soutInfoChanged:)
263         name: NSControlTextDidChangeNotification
264         object: o_sout_udp_port];
265 }
266
267 - (void)openTarget:(int)i_type
268 {
269     int i_result;
270
271     [o_tabview selectTabViewItemAtIndex: i_type];
272     [o_file_sub_ckbox setState: NSOffState];
273     
274     i_result = [NSApp runModalForWindow: o_panel];
275     [o_panel close];
276
277     if( i_result )
278     {
279         NSString *o_sout = [o_sout_mrl stringValue];
280         intf_thread_t * p_intf = [NSApp getIntf];
281         
282         if ( [o_sout_cbox state] )
283         {
284             config_PutPsz( p_intf, "sout", [o_sout lossyCString] );
285         }
286         else
287         {
288             config_PutPsz( p_intf, "sout", "" );
289         }
290
291         NSString *o_source = [o_mrl stringValue];
292         BOOL b_enq = [o_ckbox_enqueue state] == NSOnState ? YES : NO;
293         NSString *subPath = [o_file_sub_path stringValue];
294         
295         [o_playlist appendArray: 
296             [NSArray arrayWithObject: o_source] atPos: -1 enqueue:b_enq];
297         
298         if (([o_file_sub_ckbox state] == NSOnState) && !([subPath isEqualTo: @""]))
299         {
300             config_PutPsz( p_intf, "sub-file", strdup( [subPath cString] ) );
301             if ( [o_file_sub_override state] )
302             {
303                 config_PutInt( p_intf, "sub-delay", (int)( [o_file_sub_delay intValue] * 10 ) );
304                 config_PutFloat( p_intf, "sub-fps", [o_file_sub_fps floatValue] );
305             }
306         }
307         else
308         {
309             config_PutPsz( p_intf, "sub-file", "" );
310             config_PutInt( p_intf, "sub-delay", 0 );
311             config_PutFloat( p_intf, "sub-fps", 0.0 );
312         }
313     }
314     [self soutModeChanged: nil];
315 }
316
317 - (void)tabView:(NSTabView *)o_tv didSelectTabViewItem:(NSTabViewItem *)o_tvi
318 {
319     NSString *o_label = [o_tvi label];
320
321     if( [o_label isEqualToString: _NS("File")] )
322     {
323         [self openFilePathChanged: nil];
324     }
325     else if( [o_label isEqualToString: _NS("Disc")] )
326     {
327         [self openDiscTypeChanged: nil];
328     }
329     else if( [o_label isEqualToString: _NS("Network")] )
330     {
331         [self openNetModeChanged: nil];
332     }  
333 }
334
335 - (IBAction)openFileGeneric:(id)sender
336 {
337     [self openFilePathChanged: nil];
338     [self openTarget: 0];
339 }
340
341 - (IBAction)openDisc:(id)sender
342 {
343     [self openDiscTypeChanged: nil];
344     [self openTarget: 1];
345 }
346
347 - (IBAction)openNet:(id)sender
348 {
349     [self openNetModeChanged: nil];
350     [self openTarget: 2];
351 }
352
353 - (void)openFilePathChanged:(NSNotification *)o_notification
354 {
355     NSString *o_mrl_string;
356     NSString *o_filename = [o_file_path stringValue];
357     NSString *o_ext = [o_filename pathExtension];
358     vlc_bool_t b_stream = [o_file_stream state];
359     BOOL b_dir = NO;
360     
361     [[NSFileManager defaultManager] fileExistsAtPath:o_filename isDirectory:&b_dir];
362
363     if( b_dir )
364     {
365         o_mrl_string = [NSString stringWithFormat: @"dir:%@", o_filename];
366     }
367     else if( [o_ext isEqualToString: @"bin"] ||
368         [o_ext isEqualToString: @"cue"] ||
369         [o_ext isEqualToString: @"vob"] ||
370         [o_ext isEqualToString: @"iso"] )
371     {
372         o_mrl_string = o_filename;
373     }
374     else
375     {
376         o_mrl_string = [NSString stringWithFormat: @"%s://%@",
377                         b_stream ? "stream" : "file",
378                         o_filename];
379     }
380     [o_mrl setStringValue: o_mrl_string]; 
381 }
382
383 - (IBAction)openFileBrowse:(id)sender
384 {
385     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
386     
387     [o_open_panel setAllowsMultipleSelection: NO];
388     [o_open_panel setCanChooseDirectories: YES];
389     [o_open_panel setTitle: _NS("Open File")];
390     [o_open_panel setPrompt: _NS("Open")];
391
392     [o_open_panel beginSheetForDirectory:nil
393         file:nil
394         types:nil
395         modalForWindow:[sender window]
396         modalDelegate: self
397         didEndSelector: @selector(pathChosenInPanel: 
398                         withReturn:
399                         contextInfo:)
400         contextInfo: nil];
401 }
402
403 - (void)pathChosenInPanel: (NSOpenPanel *) sheet withReturn:(int)returnCode contextInfo:(void  *)contextInfo
404 {
405     if (returnCode == NSFileHandlingPanelOKButton)
406     {
407         NSString *o_filename = [[sheet filenames] objectAtIndex: 0];
408         [o_file_path setStringValue: o_filename];
409         [self openFilePathChanged: nil];
410     }
411 }
412
413 - (IBAction)openFileStreamChanged:(id)sender
414 {
415     [self openFilePathChanged: nil];
416 }
417
418 - (IBAction)openDiscTypeChanged:(id)sender
419 {
420     NSString *o_type;
421     vlc_bool_t b_device, b_menus, b_title_chapter;
422     
423     [o_disc_device removeAllItems];
424     b_title_chapter = ![o_disc_dvd_menus state];
425     
426     o_type = [[o_disc_type selectedCell] title];
427
428     if ( [o_type isEqualToString: _NS("VIDEO_TS folder")] )
429     {
430         b_device = 0; b_menus = 1;
431     }
432     else
433     {
434         NSArray *o_devices;
435         NSString *o_disc;
436         const char *psz_class = NULL;
437         b_device = 1;
438
439         if ( [o_type isEqualToString: _NS("VCD")] )
440         {
441             psz_class = kIOCDMediaClass;
442             o_disc = o_type;
443             b_menus = 0; b_title_chapter = 1;
444             [o_disc_dvd_menus setState: FALSE];
445         }
446         else
447         {
448             psz_class = kIODVDMediaClass;
449             o_disc = o_type;
450             b_menus = 1;
451         }
452     
453         o_devices = GetEjectableMediaOfClass( psz_class );
454         if ( o_devices != nil )
455         {
456             int i_devices = [o_devices count];
457         
458             if ( i_devices )
459             {
460                 int i;
461         
462                 for( i = 0; i < i_devices; i++ )
463                 {
464                     [o_disc_device 
465                         addItemWithObjectValue: [o_devices objectAtIndex: i]];
466                 }
467
468                 [o_disc_device selectItemAtIndex: 0];
469             }
470             else
471             {
472                 [o_disc_device setStringValue: 
473                     [NSString stringWithFormat: _NS("No %@s found"), o_disc]];
474             }
475         }
476     }
477
478     [o_disc_device setEnabled: b_device];
479     [o_disc_title setEnabled: b_title_chapter];
480     [o_disc_title_stp setEnabled: b_title_chapter];
481     [o_disc_chapter setEnabled: b_title_chapter];
482     [o_disc_chapter_stp setEnabled: b_title_chapter];
483     [o_disc_videots_folder setEnabled: !b_device];
484     [o_disc_videots_btn_browse setEnabled: !b_device];
485     [o_disc_dvd_menus setEnabled: b_menus];
486
487     [self openDiscInfoChanged: nil];
488 }
489
490 - (IBAction)openDiscStepperChanged:(id)sender
491 {
492     int i_tag = [sender tag];
493
494     if( i_tag == 0 )
495     {
496         [o_disc_title setIntValue: [o_disc_title_stp intValue]];
497     }
498     else if( i_tag == 1 )
499     {
500         [o_disc_chapter setIntValue: [o_disc_chapter_stp intValue]];
501     }
502
503     [self openDiscInfoChanged: nil];
504 }
505
506 - (void)openDiscInfoChanged:(NSNotification *)o_notification
507 {
508     NSString *o_type;
509     NSString *o_device;
510     NSString *o_videots;
511     NSString *o_mrl_string;
512     int i_title, i_chapter;
513     vlc_bool_t b_menus;
514
515     o_type = [[o_disc_type selectedCell] title];
516     o_device = [o_disc_device stringValue];
517     i_title = [o_disc_title intValue];
518     i_chapter = [o_disc_chapter intValue];
519     o_videots = [o_disc_videots_folder stringValue];
520     b_menus = [o_disc_dvd_menus state];
521
522     if ( [o_type isEqualToString: _NS("VCD")] )
523     {
524         if ( [o_device isEqualToString:
525                 [NSString stringWithFormat: _NS("No %@s found"), o_type]] )
526             o_device = @"";
527         o_mrl_string = [NSString stringWithFormat: @"vcd://%@@%i,%i",
528                         o_device, i_title, i_chapter]; 
529     }
530     else if ( [o_type isEqualToString: _NS("DVD")] )
531     {
532         if ( [o_device isEqualToString:
533                 [NSString stringWithFormat: _NS("No %@s found"), o_type]] )
534             o_device = @"";
535         if ( b_menus )
536             o_mrl_string = [NSString stringWithFormat: @"dvdplay://%@",
537                             o_device]; 
538         else
539             o_mrl_string = [NSString stringWithFormat: @"dvdold://%@@%i,%i",
540                             o_device, i_title, i_chapter]; 
541     }
542     else /* VIDEO_TS folder */
543     {
544         if ( b_menus )
545             o_mrl_string = [NSString stringWithFormat: @"dvdplay://%@",
546                             o_videots]; 
547         else
548             o_mrl_string = [NSString stringWithFormat: @"dvdread://%@@%i,%i",
549                             o_videots, i_title, i_chapter]; 
550     }
551
552     [o_mrl setStringValue: o_mrl_string]; 
553 }
554
555 - (IBAction)openDiscMenusChanged:(id)sender
556 {
557     [self openDiscInfoChanged: nil];
558     [self openDiscTypeChanged: nil];
559 }
560
561 - (IBAction)openVTSBrowse:(id)sender
562 {
563     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
564
565     [o_open_panel setAllowsMultipleSelection: NO];
566     [o_open_panel setCanChooseFiles: NO];
567     [o_open_panel setCanChooseDirectories: YES];
568     [o_open_panel setTitle: _NS("Open VIDEO_TS Directory")];
569     [o_open_panel setPrompt: _NS("Open")];
570
571     if( [o_open_panel runModalForDirectory: nil
572             file: nil types: nil] == NSOKButton )
573     {
574         NSString *o_dirname = [[o_open_panel filenames] objectAtIndex: 0];
575         [o_disc_videots_folder setStringValue: o_dirname];
576         [self openDiscInfoChanged: nil];
577     }
578 }
579
580 - (IBAction)openNetModeChanged:(id)sender
581 {
582     NSString *o_mode;
583     BOOL b_udp = FALSE;
584     BOOL b_udpm = FALSE;
585     BOOL b_http = FALSE;
586
587     o_mode = [[o_net_mode selectedCell] title];
588
589     if( [o_mode isEqualToString: _NS("UDP/RTP")] ) b_udp = TRUE;   
590     else if( [o_mode isEqualToString: _NS("UDP/RTP Multicast")] ) b_udpm = TRUE;
591     else if( [o_mode isEqualToString: _NS("HTTP/FTP/MMS")] ) b_http = TRUE;
592
593     [o_net_udp_port setEnabled: b_udp];
594     [o_net_udp_port_stp setEnabled: b_udp];
595     [o_net_udpm_addr setEnabled: b_udpm];
596     [o_net_udpm_port setEnabled: b_udpm];
597     [o_net_udpm_port_stp setEnabled: b_udpm];
598     [o_net_http_url setEnabled: b_http];
599
600     [self openNetInfoChanged: nil];
601 }
602
603 - (IBAction)openNetStepperChanged:(id)sender
604 {
605     int i_tag = [sender tag];
606
607     if( i_tag == 0 )
608     {
609         [o_net_udp_port setIntValue: [o_net_udp_port_stp intValue]];
610     }
611     else if( i_tag == 1 )
612     {
613         [o_net_udpm_port setIntValue: [o_net_udpm_port_stp intValue]];
614     }
615
616     [self openNetInfoChanged: nil];
617 }
618
619 - (void)openNetInfoChanged:(NSNotification *)o_notification
620 {
621     NSString *o_mode;
622     NSString *o_mrl_string = [NSString string];
623     intf_thread_t * p_intf = [NSApp getIntf];
624
625     o_mode = [[o_net_mode selectedCell] title];
626
627     if( [o_mode isEqualToString: _NS("UDP/RTP")] )
628     {
629         int i_port = [o_net_udp_port intValue];
630
631         o_mrl_string = [NSString stringWithString: @"udp://"]; 
632
633         if( i_port != config_GetInt( p_intf, "server-port" ) )
634         {
635             o_mrl_string = 
636                 [o_mrl_string stringByAppendingFormat: @"@:%i", i_port]; 
637         } 
638     }
639     else if( [o_mode isEqualToString: _NS("UDP/RTP Multicast")] ) 
640     {
641         NSString *o_addr = [o_net_udpm_addr stringValue];
642         int i_port = [o_net_udpm_port intValue];
643
644         o_mrl_string = [NSString stringWithFormat: @"udp://@%@", o_addr]; 
645
646         if( i_port != config_GetInt( p_intf, "server-port" ) )
647         {
648             o_mrl_string = 
649                 [o_mrl_string stringByAppendingFormat: @":%i", i_port]; 
650         } 
651     }
652     else if( [o_mode isEqualToString: _NS("HTTP/FTP/MMS")] )
653     {
654         NSString *o_url = [o_net_http_url stringValue];
655
656         if ( ![o_url hasPrefix:@"http:"] && ![o_url hasPrefix:@"ftp:"]
657               && ![o_url hasPrefix:@"mms"] )
658             o_mrl_string = [NSString stringWithFormat: @"http://%@", o_url];
659         else
660             o_mrl_string = o_url;
661     }
662
663     [o_mrl setStringValue: o_mrl_string];
664 }
665
666 - (IBAction)soutChanged:(id)sender;
667 {
668     if ([o_sout_cbox state] == NSOnState)
669     {
670         [o_sout_settings setEnabled:YES];
671     }
672     else
673     {
674         [o_sout_settings setEnabled:NO];
675     }
676 }
677
678 - (IBAction)soutSettings:(id)sender
679 {
680     [self soutModeChanged: nil];
681     [NSApp beginSheet: o_sout_sheet
682         modalForWindow: [sender window]
683         modalDelegate: self
684         didEndSelector: NULL
685         contextInfo: nil];
686 }
687
688 - (IBAction)soutFileBrowse:(id)sender
689 {
690     NSSavePanel *o_save_panel = [NSSavePanel savePanel];
691     NSString *o_mux_string;
692     if ( [[[o_sout_mux selectedCell] title] isEqualToString: _NS("MPEG PS")] )
693         o_mux_string = @"vob";
694     else if ( [[[o_sout_mux selectedCell] title] isEqualToString: _NS("AVI")] )
695         o_mux_string = @"avi";
696     else if ( [[[o_sout_mux selectedCell] title] isEqualToString: _NS("Ogg")] )
697         o_mux_string = @"ogm";
698     else
699         o_mux_string = @"ts";
700
701     NSString * o_name = [NSString stringWithFormat: @"vlc-output.%@",
702                          o_mux_string];
703
704     [o_save_panel setTitle: _NS("Save File")];
705     [o_save_panel setPrompt: _NS("Save")];
706
707     if( [o_save_panel runModalForDirectory: nil
708             file: o_name] == NSOKButton )
709     {
710         NSString *o_filename = [o_save_panel filename];
711         [o_sout_file_path setStringValue: o_filename];
712         [self soutInfoChanged: nil];
713     }
714 }
715
716 - (void)soutModeChanged:(NSNotification *)o_notification
717 {
718     NSString *o_mode;
719     BOOL b_file = FALSE;
720     BOOL b_net = FALSE;
721
722     o_mode = [[o_sout_access selectedCell] title];
723
724     if( [o_mode isEqualToString: _NS("File")] ) b_file = TRUE;   
725     else if( [o_mode isEqualToString: _NS("UDP")] ) b_net = TRUE;
726     else if( [o_mode isEqualToString: _NS("RTP")] ) b_net = TRUE;
727
728     [o_sout_file_path setEnabled: b_file];
729     [o_sout_file_btn_browse setEnabled: b_file];
730     [o_sout_udp_addr setEnabled: !b_file];
731     [o_sout_udp_port setEnabled: !b_file];
732     [o_sout_udp_port_stp setEnabled: !b_file ];
733     
734     [[o_sout_mux cellAtRow:0 column: 1] setEnabled: !b_net];
735     [[o_sout_mux cellAtRow:0 column: 2] setEnabled: !b_net];
736     [[o_sout_mux cellAtRow:0 column: 3] setEnabled: !b_net];
737     if ( b_net )
738     {
739         [o_sout_mux selectCellAtRow:0 column: 0];
740     }
741
742     [self soutInfoChanged: nil];
743 }
744
745 - (void)soutInfoChanged:(NSNotification *)o_notification
746 {
747     NSString *o_mode;
748     NSString *o_mux;
749     NSString *o_mrl_string;
750     NSString *o_mux_string;
751
752     o_mode = [[o_sout_access selectedCell] title];
753     o_mux = [[o_sout_mux selectedCell] title];
754
755     if ( [o_mux isEqualToString: _NS("AVI")] ) o_mux_string = @"avi";
756     else if ( [o_mux isEqualToString: _NS("Ogg")] ) o_mux_string = @"ogg";
757     else if ( [o_mux isEqualToString: _NS("MPEG PS")] ) o_mux_string = @"ps";
758     else o_mux_string = @"ts";
759
760     if ( [o_mode isEqualToString: _NS("File")] )
761     {
762         o_mrl_string = [NSString stringWithFormat: @"file/%@://%@",
763                         o_mux_string, [o_sout_file_path stringValue]];
764     }
765     else if ( [o_mode isEqualToString: _NS("HTTP")] )
766     {
767         o_mrl_string = [NSString stringWithFormat: @"http/%@://%@:%i",
768                         o_mux_string, [o_sout_udp_addr stringValue],
769                         [o_sout_udp_port intValue]];
770     }
771     else if ( [o_mode isEqualToString: _NS("UDP")] )
772     {
773         o_mrl_string = [NSString stringWithFormat: @"udp/%@://%@:%i",
774                         o_mux_string, [o_sout_udp_addr stringValue],
775                         [o_sout_udp_port intValue]];
776     }
777     else
778     {
779         o_mrl_string = [NSString stringWithFormat: @"rtp/%@://%@:%i",
780                         o_mux_string, [o_sout_udp_addr stringValue],
781                         [o_sout_udp_port intValue]];
782     }
783
784
785     [o_sout_mrl setStringValue: o_mrl_string];
786 }
787
788 - (IBAction)soutStepperChanged:(id)sender
789 {
790     [o_sout_udp_port setIntValue: [o_sout_udp_port_stp intValue]];
791     [self soutInfoChanged: nil];
792 }
793
794 - (IBAction)soutCloseSheet:(id)sender
795 {
796     [o_sout_sheet orderOut:sender];
797     [NSApp endSheet: o_sout_sheet];
798 }
799
800 - (IBAction)openFile:(id)sender
801 {
802     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
803     
804     [o_open_panel setAllowsMultipleSelection: YES];
805     [o_open_panel setCanChooseDirectories: YES];
806     [o_open_panel setTitle: _NS("Open File")];
807     [o_open_panel setPrompt: _NS("Open")];
808     
809     if( [o_open_panel runModalForDirectory: nil
810             file: nil types: nil] == NSOKButton )
811     {
812         intf_thread_t * p_intf = [NSApp getIntf];
813         config_PutPsz( p_intf, "sout", NULL );
814         
815         NSArray *o_values = [[o_open_panel filenames]
816                 sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
817         
818         [o_playlist appendArray: o_values atPos: -1 enqueue:NO];
819         config_PutPsz( p_intf, "sub-file", "" );
820         config_PutInt( p_intf, "sub-delay", 0 );
821         config_PutFloat( p_intf, "sub-fps", 0.0 );
822         config_PutPsz( p_intf, "sout", "" );
823     }
824 }
825
826 - (IBAction)subsChanged:(id)sender
827 {
828     if ([o_file_sub_ckbox state] == NSOnState)
829     {
830         [o_file_sub_btn_settings setEnabled:YES];
831     }
832     else
833     {
834         [o_file_sub_btn_settings setEnabled:NO];
835     }
836 }
837
838 - (IBAction)subSettings:(id)sender
839 {
840     [NSApp beginSheet: o_file_sub_sheet
841         modalForWindow: [sender window]
842         modalDelegate: self
843         didEndSelector: NULL
844         contextInfo: nil];
845 }
846
847 - (IBAction)subFileBrowse:(id)sender
848 {
849     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
850     
851     [o_open_panel setAllowsMultipleSelection: NO];
852     [o_open_panel setTitle: _NS("Open File")];
853     [o_open_panel setPrompt: _NS("Open")];
854
855     if( [o_open_panel runModalForDirectory: nil 
856             file: nil types: nil] == NSOKButton )
857     {
858         NSString *o_filename = [[o_open_panel filenames] objectAtIndex: 0];
859         [o_file_sub_path setStringValue: o_filename];
860     }
861 }
862
863 - (IBAction)subOverride:(id)sender
864 {
865     BOOL b_state = [o_file_sub_override state];
866     [o_file_sub_delay setEnabled: b_state];
867     [o_file_sub_delay_stp setEnabled: b_state];
868     [o_file_sub_fps setEnabled: b_state];
869     [o_file_sub_fps_stp setEnabled: b_state];
870 }
871
872 - (IBAction)subDelayStepperChanged:(id)sender
873 {
874     [o_file_sub_delay setIntValue: [o_file_sub_delay_stp intValue]];
875 }
876
877 - (IBAction)subFpsStepperChanged:(id)sender;
878 {
879     [o_file_sub_fps setFloatValue: [o_file_sub_fps_stp floatValue]];
880 }
881
882 - (IBAction)subCloseSheet:(id)sender
883 {
884     [o_file_sub_sheet orderOut:sender];
885     [NSApp endSheet: o_file_sub_sheet];
886 }
887
888 - (IBAction)panelCancel:(id)sender
889 {
890     [NSApp stopModalWithCode: 0];
891 }
892
893 - (IBAction)panelOk:(id)sender
894 {
895     if( [[o_mrl stringValue] length] )
896     {
897         [NSApp stopModalWithCode: 1];
898     }
899     else
900     {
901         NSBeep();
902     }
903 }
904
905 @end