ce70a2c45ead7d3089c3f95d016a42b691d883be
[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.25 2003/03/24 15:20:26 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         NSRect s_rect = [o_panel frame];
142         s_rect.size.height = OPEN_PANEL_FULL_HEIGHT + WINDOW_TITLE_HEIGHT;
143         [o_panel setFrame: s_rect display: NO];
144
145         NSPoint s_point;
146         s_point.x = 0;
147         s_point.y = 0;
148         [[o_panel contentView] setBoundsOrigin: s_point];
149     }
150     else
151     {
152         [o_sout_cbox setState: NO];
153
154         NSRect s_rect = [o_panel frame];
155         s_rect.size.height = OPEN_PANEL_SHORT_HEIGHT + WINDOW_TITLE_HEIGHT;
156         [o_panel setFrame: s_rect display: NO];
157
158         NSPoint s_point;
159         s_point.x = 0;
160         s_point.y = OPEN_PANEL_FULL_HEIGHT - OPEN_PANEL_SHORT_HEIGHT;
161         [[o_panel contentView] setBoundsOrigin: s_point];
162     }
163     free(psz_sout);
164
165     [o_panel setTitle: _NS("Open Source")];
166     [o_mrl_lbl setTitle: _NS("Media Resource Locator (MRL)")];
167     [o_ckbox_enqueue setTitle: _NS("Only enqueue in playlist, do not play")];
168
169     [o_btn_ok setTitle: _NS("OK")];
170     [o_btn_cancel setTitle: _NS("Cancel")];
171
172     [[o_tabview tabViewItemAtIndex: 0] setLabel: _NS("File")];
173     [[o_tabview tabViewItemAtIndex: 1] setLabel: _NS("Disc")];
174     [[o_tabview tabViewItemAtIndex: 2] setLabel: _NS("Network")];
175
176     [o_file_btn_browse setTitle: _NS("Browse...")];
177     [o_file_stream setTitle: _NS("Treat as a pipe rather than as a file")];
178
179     [o_file_sub_btn_browse setTitle: _NS("Browse...")];
180     [o_file_sub_ckbox setTitle: _NS("Load subtitles file:")];
181
182     [o_disc_device_lbl setStringValue: _NS("Device name")];
183     [o_disc_title_lbl setStringValue: _NS("Title")];
184     [o_disc_chapter_lbl setStringValue: _NS("Chapter")];
185     [o_disc_videots_btn_browse setTitle: _NS("Browse...")];
186     [o_disc_dvd_menus setTitle: _NS("Use DVD menus (EXPERIMENTAL)")];
187
188     [[o_disc_type cellAtRow:0 column:0] setTitle: _NS("VIDEO_TS folder")];
189     [[o_disc_type cellAtRow:1 column:0] setTitle: _NS("DVD")];
190     [[o_disc_type cellAtRow:2 column:0] setTitle: _NS("VCD")];
191
192     [o_net_udp_port_lbl setStringValue: _NS("Port")];
193     [o_net_udpm_addr_lbl setStringValue: _NS("Address")];
194     [o_net_udpm_port_lbl setStringValue: _NS("Port")];
195     [o_net_http_url_lbl setStringValue: _NS("URL")];
196
197     [[o_net_mode cellAtRow:0 column:0] setTitle: _NS("UDP/RTP")];
198     [[o_net_mode cellAtRow:1 column:0] setTitle: _NS("UDP/RTP Multicast")];
199     [[o_net_mode cellAtRow:2 column:0] setTitle: _NS("HTTP/FTP/MMS")];
200
201     [o_net_udp_port setIntValue: config_GetInt( p_intf, "server-port" )];
202     [o_net_udp_port_stp setIntValue: config_GetInt( p_intf, "server-port" )];
203
204     [o_sout_cbox setTitle: _NS("Stream output")];
205     [o_sout_mrl_lbl setTitle: _NS("Stream output MRL")];
206     [[o_sout_access cellAtRow:0 column:0] setTitle: _NS("File")];
207     [[o_sout_access cellAtRow:1 column:0] setTitle: _NS("UDP")];
208     [[o_sout_access cellAtRow:2 column:0] setTitle: _NS("RTP")];
209
210     [o_sout_file_btn_browse setTitle: _NS("Browse...")];
211     [o_sout_udp_addr_lbl setStringValue: _NS("Address")];
212     [o_sout_udp_port_lbl setStringValue: _NS("Port")];
213
214     [[o_sout_mux cellAtRow:0 column:0] setTitle: _NS("AVI")];
215     [[o_sout_mux cellAtRow:0 column:1] setTitle: _NS("PS")];
216     [[o_sout_mux cellAtRow:0 column:2] setTitle: _NS("TS")];
217
218     [[NSNotificationCenter defaultCenter] addObserver: self
219         selector: @selector(openFilePathChanged:)
220         name: NSControlTextDidChangeNotification
221         object: o_file_path];
222
223     [[NSNotificationCenter defaultCenter] addObserver: self
224         selector: @selector(openDiscInfoChanged:)
225         name: NSControlTextDidChangeNotification
226         object: o_disc_device];
227     [[NSNotificationCenter defaultCenter] addObserver: self
228         selector: @selector(openDiscInfoChanged:)
229         name: NSControlTextDidChangeNotification
230         object: o_disc_title];
231     [[NSNotificationCenter defaultCenter] addObserver: self
232         selector: @selector(openDiscInfoChanged:)
233         name: NSControlTextDidChangeNotification
234         object: o_disc_chapter];
235     [[NSNotificationCenter defaultCenter] addObserver: self
236         selector: @selector(openDiscInfoChanged:)
237         name: NSControlTextDidChangeNotification
238         object: o_disc_videots_folder];
239
240     [[NSNotificationCenter defaultCenter] addObserver: self
241         selector: @selector(openNetInfoChanged:)
242         name: NSControlTextDidChangeNotification
243         object: o_net_udp_port];
244     [[NSNotificationCenter defaultCenter] addObserver: self
245         selector: @selector(openNetInfoChanged:)
246         name: NSControlTextDidChangeNotification
247         object: o_net_udpm_addr];
248     [[NSNotificationCenter defaultCenter] addObserver: self
249         selector: @selector(openNetInfoChanged:)
250         name: NSControlTextDidChangeNotification
251         object: o_net_udpm_port];
252     [[NSNotificationCenter defaultCenter] addObserver: self
253         selector: @selector(openNetInfoChanged:)
254         name: NSControlTextDidChangeNotification
255         object: o_net_http_url];
256
257     [[NSNotificationCenter defaultCenter] addObserver: self
258         selector: @selector(soutInfoChanged:)
259         name: NSControlTextDidChangeNotification
260         object: o_sout_file_path];
261     [[NSNotificationCenter defaultCenter] addObserver: self
262         selector: @selector(soutInfoChanged:)
263         name: NSControlTextDidChangeNotification
264         object: o_sout_udp_addr];
265     [[NSNotificationCenter defaultCenter] addObserver: self
266         selector: @selector(soutInfoChanged:)
267         name: NSControlTextDidChangeNotification
268         object: o_sout_udp_port];
269 }
270
271 - (void)openTarget:(int)i_type
272 {
273     int i_result;
274
275     [o_tabview selectTabViewItemAtIndex: i_type];
276     [o_ckbox_enqueue setState: NSOnState];
277     [o_file_sub_path setStringValue: @""];
278     [o_file_sub_ckbox setState: NSOffState];
279     [o_file_sub_path setEnabled: NO];
280     [o_file_sub_btn_browse setEnabled: NO];
281     
282     i_result = [NSApp runModalForWindow: o_panel];
283     [o_panel close];
284
285     if( i_result )
286     {
287         NSString *o_sout = [o_sout_mrl stringValue];
288         intf_thread_t * p_intf = [NSApp getIntf];
289         
290         if ( [o_sout_cbox state] )
291         {
292             config_PutPsz( p_intf, "sout", [o_sout lossyCString] );
293         }
294
295         NSString *o_source = [o_mrl stringValue];
296         BOOL b_enq = [o_ckbox_enqueue state] == NSOnState ? YES : NO;
297         NSString *subPath = [o_file_sub_path stringValue];
298         
299         [o_playlist appendArray: 
300             [NSArray arrayWithObject: o_source] atPos: -1 enqueue:b_enq];
301         
302         if (([o_file_sub_ckbox state] == NSOnState) && !([subPath isEqualTo: @""]))
303             config_PutPsz( p_intf, "sub-file", strdup( [subPath cString] ) );
304     }
305
306     [self soutModeChanged: nil];
307 }
308
309 - (void)tabView:(NSTabView *)o_tv didSelectTabViewItem:(NSTabViewItem *)o_tvi
310 {
311     NSString *o_label = [o_tvi label];
312
313     if( [o_label isEqualToString: _NS("File")] )
314     {
315         [self openFilePathChanged: nil];
316     }
317     else if( [o_label isEqualToString: _NS("Disc")] )
318     {
319         [self openDiscTypeChanged: nil];
320     }
321     else if( [o_label isEqualToString: _NS("Network")] )
322     {
323         [self openNetModeChanged: nil];
324     }  
325 }
326
327 - (IBAction)openFileGeneric:(id)sender
328 {
329     [self openFilePathChanged: nil];
330     [self openTarget: 0];
331 }
332
333 - (IBAction)openDisc:(id)sender
334 {
335     [self openDiscTypeChanged: nil];
336     [self openTarget: 1];
337 }
338
339 - (IBAction)openNet:(id)sender
340 {
341     [self openNetModeChanged: nil];
342     [self openTarget: 2];
343 }
344
345 - (void)openFilePathChanged:(NSNotification *)o_notification
346 {
347     NSString *o_mrl_string;
348     NSString *o_filename = [o_file_path stringValue];
349     NSString *o_ext = [o_filename pathExtension];
350     vlc_bool_t b_stream = [o_file_stream state];
351     BOOL b_dir = NO;
352     
353     [[NSFileManager defaultManager] fileExistsAtPath:o_filename isDirectory:&b_dir];
354
355     if( b_dir )
356     {
357         o_mrl_string = [NSString stringWithFormat: @"dir:%@", o_filename];
358     }
359     else if( [o_ext isEqualToString: @"bin"] ||
360         [o_ext isEqualToString: @"cue"] ||
361         [o_ext isEqualToString: @"vob"] ||
362         [o_ext isEqualToString: @"iso"] )
363     {
364         o_mrl_string = o_filename;
365     }
366     else
367     {
368         o_mrl_string = [NSString stringWithFormat: @"%s://%@",
369                         b_stream ? "stream" : "file",
370                         o_filename];
371     }
372     [o_mrl setStringValue: o_mrl_string]; 
373 }
374
375 - (IBAction)openFileBrowse:(id)sender
376 {
377     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
378     
379     [o_open_panel setAllowsMultipleSelection: NO];
380     [o_open_panel setCanChooseDirectories: YES];
381     [o_open_panel setTitle: _NS("Open File")];
382     [o_open_panel setPrompt: _NS("Open")];
383
384     if( [o_open_panel runModalForDirectory: nil 
385             file: nil types: nil] == NSOKButton )
386     {
387         NSString *o_filename = [[o_open_panel filenames] objectAtIndex: 0];
388         [o_file_path setStringValue: o_filename];
389         [self openFilePathChanged: nil];
390     }
391 }
392
393 - (IBAction)openFileStreamChanged:(id)sender
394 {
395     [self openFilePathChanged: nil];
396 }
397
398 - (IBAction)loadSubsChanged:(id)sender
399 {
400     if ([o_file_sub_ckbox state] == NSOnState)
401     {
402         [o_file_sub_path setEnabled:YES];
403         [o_file_sub_btn_browse setEnabled:YES];
404     }
405     else
406     {
407         [o_file_sub_path setEnabled:NO];
408         [o_file_sub_btn_browse setEnabled:NO];
409     }
410 }
411
412 - (IBAction)openSubBrowse:(id)sender
413 {
414     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
415     
416     [o_open_panel setAllowsMultipleSelection: NO];
417     [o_open_panel setTitle: _NS("Open File")];
418     [o_open_panel setPrompt: _NS("Open")];
419
420     if( [o_open_panel runModalForDirectory: nil 
421             file: nil types: nil] == NSOKButton )
422     {
423         NSString *o_filename = [[o_open_panel filenames] objectAtIndex: 0];
424         [o_file_sub_path setStringValue: o_filename];
425     }
426 }
427
428 - (IBAction)openDiscTypeChanged:(id)sender
429 {
430     NSString *o_type;
431     vlc_bool_t b_device, b_menus, b_title_chapter;
432     
433     [o_disc_device removeAllItems];
434     b_title_chapter = ![o_disc_dvd_menus state];
435     
436     o_type = [[o_disc_type selectedCell] title];
437
438     if ( [o_type isEqualToString: _NS("VIDEO_TS folder")] )
439     {
440         b_device = 0; b_menus = 1;
441     }
442     else
443     {
444         NSArray *o_devices;
445         NSString *o_disc;
446         const char *psz_class = NULL;
447         b_device = 1;
448
449         if ( [o_type isEqualToString: _NS("VCD")] )
450         {
451             psz_class = kIOCDMediaClass;
452             o_disc = o_type;
453             b_menus = 0; b_title_chapter = 1;
454             [o_disc_dvd_menus setState: FALSE];
455         }
456         else
457         {
458             psz_class = kIODVDMediaClass;
459             o_disc = o_type;
460             b_menus = 1;
461         }
462     
463         o_devices = GetEjectableMediaOfClass( psz_class );
464         if ( o_devices != nil )
465         {
466             int i_devices = [o_devices count];
467         
468             if ( i_devices )
469             {
470                 int i;
471         
472                 for( i = 0; i < i_devices; i++ )
473                 {
474                     [o_disc_device 
475                         addItemWithObjectValue: [o_devices objectAtIndex: i]];
476                 }
477
478                 [o_disc_device selectItemAtIndex: 0];
479             }
480             else
481             {
482                 [o_disc_device setStringValue: 
483                     [NSString stringWithFormat: _NS("No %@s found"), o_disc]];
484             }
485         }
486     }
487
488     [o_disc_device setEnabled: b_device];
489     [o_disc_title setEnabled: b_title_chapter];
490     [o_disc_title_stp setEnabled: b_title_chapter];
491     [o_disc_chapter setEnabled: b_title_chapter];
492     [o_disc_chapter_stp setEnabled: b_title_chapter];
493     [o_disc_videots_folder setEnabled: !b_device];
494     [o_disc_videots_btn_browse setEnabled: !b_device];
495     [o_disc_dvd_menus setEnabled: b_menus];
496
497     [self openDiscInfoChanged: nil];
498 }
499
500 - (IBAction)openDiscStepperChanged:(id)sender
501 {
502     int i_tag = [sender tag];
503
504     if( i_tag == 0 )
505     {
506         [o_disc_title setIntValue: [o_disc_title_stp intValue]];
507     }
508     else if( i_tag == 1 )
509     {
510         [o_disc_chapter setIntValue: [o_disc_chapter_stp intValue]];
511     }
512
513     [self openDiscInfoChanged: nil];
514 }
515
516 - (void)openDiscInfoChanged:(NSNotification *)o_notification
517 {
518     NSString *o_type;
519     NSString *o_device;
520     NSString *o_videots;
521     NSString *o_mrl_string;
522     int i_title, i_chapter;
523     vlc_bool_t b_menus;
524
525     o_type = [[o_disc_type selectedCell] title];
526     o_device = [o_disc_device stringValue];
527     i_title = [o_disc_title intValue];
528     i_chapter = [o_disc_chapter intValue];
529     o_videots = [o_disc_videots_folder stringValue];
530     b_menus = [o_disc_dvd_menus state];
531
532     if ( [o_type isEqualToString: _NS("VCD")] )
533     {
534         if ( [o_device isEqualToString:
535                 [NSString stringWithFormat: _NS("No %@s found"), o_type]] )
536             o_device = @"";
537         o_mrl_string = [NSString stringWithFormat: @"vcd://%@@%i,%i",
538                         o_device, i_title, i_chapter]; 
539     }
540     else if ( [o_type isEqualToString: _NS("DVD")] )
541     {
542         if ( [o_device isEqualToString:
543                 [NSString stringWithFormat: _NS("No %@s found"), o_type]] )
544             o_device = @"";
545         if ( b_menus )
546             o_mrl_string = [NSString stringWithFormat: @"dvdplay://%@",
547                             o_device]; 
548         else
549             o_mrl_string = [NSString stringWithFormat: @"dvdold://%@@%i,%i",
550                             o_device, i_title, i_chapter]; 
551     }
552     else /* VIDEO_TS folder */
553     {
554         if ( b_menus )
555             o_mrl_string = [NSString stringWithFormat: @"dvdplay://%@",
556                             o_videots]; 
557         else
558             o_mrl_string = [NSString stringWithFormat: @"dvdread://%@@%i,%i",
559                             o_videots, i_title, i_chapter]; 
560     }
561
562     [o_mrl setStringValue: o_mrl_string]; 
563 }
564
565 - (IBAction)openDiscMenusChanged:(id)sender
566 {
567     [self openDiscInfoChanged: nil];
568     [self openDiscTypeChanged: nil];
569 }
570
571 - (IBAction)openVTSBrowse:(id)sender
572 {
573     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
574
575     [o_open_panel setAllowsMultipleSelection: NO];
576     [o_open_panel setCanChooseFiles: NO];
577     [o_open_panel setCanChooseDirectories: YES];
578     [o_open_panel setTitle: _NS("Open VIDEO_TS Directory")];
579     [o_open_panel setPrompt: _NS("Open")];
580
581     if( [o_open_panel runModalForDirectory: nil
582             file: nil types: nil] == NSOKButton )
583     {
584         NSString *o_dirname = [[o_open_panel filenames] objectAtIndex: 0];
585         [o_disc_videots_folder setStringValue: o_dirname];
586         [self openDiscInfoChanged: nil];
587     }
588 }
589
590 - (IBAction)openNetModeChanged:(id)sender
591 {
592     NSString *o_mode;
593     BOOL b_udp = FALSE;
594     BOOL b_udpm = FALSE;
595     BOOL b_http = FALSE;
596
597     o_mode = [[o_net_mode selectedCell] title];
598
599     if( [o_mode isEqualToString: _NS("UDP/RTP")] ) b_udp = TRUE;   
600     else if( [o_mode isEqualToString: _NS("UDP/RTP Multicast")] ) b_udpm = TRUE;
601     else if( [o_mode isEqualToString: _NS("HTTP/FTP/MMS")] ) b_http = TRUE;
602
603     [o_net_udp_port setEnabled: b_udp];
604     [o_net_udp_port_stp setEnabled: b_udp];
605     [o_net_udpm_addr setEnabled: b_udpm];
606     [o_net_udpm_port setEnabled: b_udpm];
607     [o_net_udpm_port_stp setEnabled: b_udpm];
608     [o_net_http_url setEnabled: b_http];
609
610     [self openNetInfoChanged: nil];
611 }
612
613 - (IBAction)openNetStepperChanged:(id)sender
614 {
615     int i_tag = [sender tag];
616
617     if( i_tag == 0 )
618     {
619         [o_net_udp_port setIntValue: [o_net_udp_port_stp intValue]];
620     }
621     else if( i_tag == 1 )
622     {
623         [o_net_udpm_port setIntValue: [o_net_udpm_port_stp intValue]];
624     }
625
626     [self openNetInfoChanged: nil];
627 }
628
629 - (void)openNetInfoChanged:(NSNotification *)o_notification
630 {
631     NSString *o_mode;
632     NSString *o_mrl_string = [NSString string];
633     intf_thread_t * p_intf = [NSApp getIntf];
634
635     o_mode = [[o_net_mode selectedCell] title];
636
637     if( [o_mode isEqualToString: _NS("UDP/RTP")] )
638     {
639         int i_port = [o_net_udp_port intValue];
640
641         o_mrl_string = [NSString stringWithString: @"udp://"]; 
642
643         if( i_port != config_GetInt( p_intf, "server-port" ) )
644         {
645             o_mrl_string = 
646                 [o_mrl_string stringByAppendingFormat: @"@:%i", i_port]; 
647         } 
648     }
649     else if( [o_mode isEqualToString: _NS("UDP/RTP Multicast")] ) 
650     {
651         NSString *o_addr = [o_net_udpm_addr stringValue];
652         int i_port = [o_net_udpm_port intValue];
653
654         o_mrl_string = [NSString stringWithFormat: @"udp://@%@", o_addr]; 
655
656         if( i_port != config_GetInt( p_intf, "server-port" ) )
657         {
658             o_mrl_string = 
659                 [o_mrl_string stringByAppendingFormat: @":%i", i_port]; 
660         } 
661     }
662     else if( [o_mode isEqualToString: _NS("HTTP/FTP/MMS")] )
663     {
664         NSString *o_url = [o_net_http_url stringValue];
665
666         if ( ![o_url hasPrefix:@"http:"] && ![o_url hasPrefix:@"ftp:"]
667               && ![o_url hasPrefix:@"mms"] )
668             o_mrl_string = [NSString stringWithFormat: @"http://%@", o_url];
669         else
670             o_mrl_string = o_url;
671     }
672
673     [o_mrl setStringValue: o_mrl_string];
674 }
675
676 - (IBAction)soutChanged:(id)sender;
677 {
678     [self soutModeChanged: nil];
679
680     if ( [o_sout_cbox state] )
681     {
682         NSPoint s_point;
683         s_point.x = 0;
684         s_point.y = 0;
685         [[o_panel contentView] setBoundsOrigin: s_point];
686         [[o_panel contentView] setNeedsDisplay: YES];
687
688         NSRect s_rect = [o_panel frame];
689         s_rect.size.height = OPEN_PANEL_FULL_HEIGHT + WINDOW_TITLE_HEIGHT;
690         s_rect.origin.y -= OPEN_PANEL_FULL_HEIGHT - OPEN_PANEL_SHORT_HEIGHT;
691         [o_panel setFrame: s_rect display: YES animate: NO];
692     }
693     else
694     {
695         NSPoint s_point;
696         s_point.x = 0;
697         s_point.y = OPEN_PANEL_FULL_HEIGHT - OPEN_PANEL_SHORT_HEIGHT;
698         [[o_panel contentView] setBoundsOrigin: s_point];
699         [[o_panel contentView] setNeedsDisplay: YES];
700
701         NSRect s_rect = [o_panel frame];
702         s_rect.size.height = OPEN_PANEL_SHORT_HEIGHT + WINDOW_TITLE_HEIGHT;
703         s_rect.origin.y += OPEN_PANEL_FULL_HEIGHT - OPEN_PANEL_SHORT_HEIGHT;
704         [o_panel setFrame: s_rect display: YES animate:NO];
705     }
706 }
707
708 - (IBAction)soutFileBrowse:(id)sender
709 {
710     NSSavePanel *o_save_panel = [NSSavePanel savePanel];
711     NSString *o_mux_string;
712     if ( [[[o_sout_mux selectedCell] title] isEqualToString: _NS("PS")] )
713         o_mux_string = @"vob";
714     else
715         o_mux_string = @"ts";
716
717     NSString * o_name = [NSString stringWithFormat: @"vlc-output.%@",
718                          o_mux_string];
719
720     [o_save_panel setTitle: _NS("Save File")];
721     [o_save_panel setPrompt: _NS("Save")];
722
723     if( [o_save_panel runModalForDirectory: nil
724             file: o_name] == NSOKButton )
725     {
726         NSString *o_filename = [o_save_panel filename];
727         [o_sout_file_path setStringValue: o_filename];
728         [self soutInfoChanged: nil];
729     }
730 }
731
732 - (void)soutModeChanged:(NSNotification *)o_notification
733 {
734     NSString *o_mode;
735     BOOL b_file = FALSE;
736     BOOL b_net = FALSE;
737
738     o_mode = [[o_sout_access selectedCell] title];
739
740     if( [o_mode isEqualToString: _NS("File")] ) b_file = TRUE;   
741     else if( [o_mode isEqualToString: _NS("UDP")] ) b_net = TRUE;
742     else if( [o_mode isEqualToString: _NS("RTP")] ) b_net = TRUE;
743
744     [o_sout_file_path setEnabled: b_file];
745     [o_sout_file_btn_browse setEnabled: b_file];
746     [o_sout_udp_addr setEnabled: b_net];
747     [o_sout_udp_port setEnabled: b_net];
748     [o_sout_udp_port_stp setEnabled: b_net];
749     [[o_sout_mux cellAtRow:0 column: 0] setEnabled: !b_net];
750     [[o_sout_mux cellAtRow:0 column: 1] setEnabled: !b_net];
751
752     if ( b_net )
753     {
754         [[o_sout_mux cellAtRow: 0 column:2] setState: YES];
755     }
756
757     [self soutInfoChanged: nil];
758 }
759
760 - (void)soutInfoChanged:(NSNotification *)o_notification
761 {
762     NSString *o_mode;
763     NSString *o_mux;
764     NSString *o_mrl_string;
765     NSString *o_mux_string;
766
767     o_mode = [[o_sout_access selectedCell] title];
768     o_mux = [[o_sout_mux selectedCell] title];
769
770     if ( [o_mux isEqualToString: _NS("AVI")] ) o_mux_string = @"avi";
771     else if ( [o_mux isEqualToString: _NS("PS")] ) o_mux_string = @"ps";
772     else o_mux_string = @"ts";
773
774     if ( [o_mode isEqualToString: _NS("File")] )
775     {
776         o_mrl_string = [NSString stringWithFormat: @"file/%@://%@",
777                         o_mux_string, [o_sout_file_path stringValue]];
778     }
779     else if ( [o_mode isEqualToString: _NS("UDP")] )
780     {
781         o_mrl_string = [NSString stringWithFormat: @"udp/%@://%@:%i",
782                         o_mux_string, [o_sout_udp_addr stringValue],
783                         [o_sout_udp_port intValue]];
784     }
785     else
786     {
787         o_mrl_string = [NSString stringWithFormat: @"rtp/%@://%@:%i",
788                         o_mux_string, [o_sout_udp_addr stringValue],
789                         [o_sout_udp_port intValue]];
790     }
791
792
793     [o_sout_mrl setStringValue: o_mrl_string];
794 }
795
796 - (IBAction)soutStepperChanged:(id)sender
797 {
798     [o_sout_udp_port setIntValue: [o_net_udp_port_stp intValue]];
799
800     [self soutInfoChanged: nil];
801 }
802
803 - (IBAction)openFile:(id)sender
804 {
805     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
806     
807     [o_open_panel setAllowsMultipleSelection: YES];
808     [o_open_panel setCanChooseDirectories: YES];
809     [o_open_panel setTitle: _NS("Open File")];
810     [o_open_panel setPrompt: _NS("Open")];
811     
812     if( [o_open_panel runModalForDirectory: nil
813             file: nil types: nil] == NSOKButton )
814     {
815         intf_thread_t * p_intf = [NSApp getIntf];
816         config_PutPsz( p_intf, "sout", NULL );
817         [o_playlist appendArray: [o_open_panel filenames] atPos: -1 enqueue:NO];
818     }
819 }
820
821 - (IBAction)panelCancel:(id)sender
822 {
823     [NSApp stopModalWithCode: 0];
824 }
825
826 - (IBAction)panelOk:(id)sender
827 {
828     if( [[o_mrl stringValue] length] )
829     {
830         [NSApp stopModalWithCode: 1];
831     }
832     else
833     {
834         NSBeep();
835     }
836 }
837
838 @end