]> git.sesse.net Git - vlc/blob - modules/gui/macosx/open.m
* Bumped up revision number to 0.5.0-test2,
[vlc] / modules / gui / macosx / open.m
1 /*****************************************************************************
2  * open.m: MacOS X plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2002 VideoLAN
5  * $Id: open.m,v 1.8 2003/01/06 22:07:47 massiot Exp $
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net> 
8  *          Christophe Massiot <massiot@via.ecp.fr>
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>                                      /* malloc(), free() */
29 #include <sys/param.h>                                    /* for MAXPATHLEN */
30 #include <string.h>
31
32 #include <paths.h>
33 #include <IOKit/IOKitLib.h>
34 #include <IOKit/IOBSD.h>
35 #include <IOKit/storage/IOMedia.h>
36 #include <IOKit/storage/IOCDMedia.h>
37 #include <IOKit/storage/IODVDMedia.h>
38
39 #import <Cocoa/Cocoa.h>
40
41 #include <vlc/vlc.h>
42 #include <vlc/intf.h>
43
44 #include "netutils.h"
45
46 #import "intf.h"
47 #import "playlist.h"
48 #import "open.h"
49
50 /*****************************************************************************
51  * GetEjectableMediaOfClass 
52  *****************************************************************************/
53 NSArray *GetEjectableMediaOfClass( const char *psz_class )
54 {
55     io_object_t next_media;
56     mach_port_t master_port;
57     kern_return_t kern_result;
58     NSArray *o_devices = nil;
59     NSMutableArray *p_list = nil;
60     io_iterator_t media_iterator;
61     CFMutableDictionaryRef classes_to_match;
62
63     kern_result = IOMasterPort( MACH_PORT_NULL, &master_port );
64     if( kern_result != KERN_SUCCESS )
65     {
66         return( nil );
67     }
68     
69     classes_to_match = IOServiceMatching( psz_class );
70     if( classes_to_match == NULL )
71     {
72         return( nil );
73     }
74     
75     CFDictionarySetValue( classes_to_match, CFSTR( kIOMediaEjectable ), 
76                           kCFBooleanTrue );
77     
78     kern_result = IOServiceGetMatchingServices( master_port, classes_to_match, 
79                                                 &media_iterator );
80     if( kern_result != KERN_SUCCESS )
81     {
82         return( nil );
83     }
84
85     p_list = [NSMutableArray arrayWithCapacity: 1];
86     
87     next_media = IOIteratorNext( media_iterator );
88     if( next_media != NULL )
89     {
90         char psz_buf[0x32];
91         size_t dev_path_length;
92         CFTypeRef str_bsd_path;
93     
94         do
95         {
96             str_bsd_path = IORegistryEntryCreateCFProperty( next_media,
97                                                             CFSTR( kIOBSDName ),
98                                                             kCFAllocatorDefault,
99                                                             0 );
100             if( str_bsd_path == NULL )
101             {
102                 IOObjectRelease( next_media );
103                 continue;
104             }
105             
106             snprintf( psz_buf, sizeof(psz_buf), "%s%c", _PATH_DEV, 'r' );
107             dev_path_length = strlen( psz_buf );
108             
109             if( CFStringGetCString( str_bsd_path,
110                                     (char*)&psz_buf + dev_path_length,
111                                     sizeof(psz_buf) - dev_path_length,
112                                     kCFStringEncodingASCII ) )
113             {
114                 [p_list addObject: [NSString stringWithCString: psz_buf]];
115             }
116             
117             CFRelease( str_bsd_path );
118             
119             IOObjectRelease( next_media );
120         
121         } while( ( next_media = IOIteratorNext( media_iterator ) ) != NULL );
122     }
123     
124     IOObjectRelease( media_iterator );
125
126     o_devices = [NSArray arrayWithArray: p_list];
127
128     return( o_devices );
129 }
130
131 /*****************************************************************************
132  * VLCOpen implementation 
133  *****************************************************************************/
134 @implementation VLCOpen
135
136 - (void)awakeFromNib
137 {
138     intf_thread_t * p_intf = [NSApp getIntf];
139     char * psz_sout = config_GetPsz( p_intf, "sout" );
140
141     if ( psz_sout != NULL && *psz_sout )
142     {
143         [o_sout_cbox setState: YES];
144
145         NSRect s_rect = [o_panel frame];
146         s_rect.size.height = OPEN_PANEL_FULL_HEIGHT + WINDOW_TITLE_HEIGHT;
147         [o_panel setFrame: s_rect display: NO];
148
149         NSPoint s_point;
150         s_point.x = 0;
151         s_point.y = 0;
152         [[o_panel contentView] setBoundsOrigin: s_point];
153     }
154     else
155     {
156         [o_sout_cbox setState: NO];
157
158         NSRect s_rect = [o_panel frame];
159         s_rect.size.height = OPEN_PANEL_SHORT_HEIGHT + WINDOW_TITLE_HEIGHT;
160         [o_panel setFrame: s_rect display: NO];
161
162         NSPoint s_point;
163         s_point.x = 0;
164         s_point.y = OPEN_PANEL_FULL_HEIGHT - OPEN_PANEL_SHORT_HEIGHT;
165         [[o_panel contentView] setBoundsOrigin: s_point];
166
167         free(psz_sout);
168     }
169
170     [o_panel setTitle: _NS("Open Source")];
171     [o_mrl_lbl setTitle: _NS("Media Resource Locator (MRL)")];
172
173     [o_btn_ok setTitle: _NS("OK")];
174     [o_btn_cancel setTitle: _NS("Cancel")];
175
176     [[o_tabview tabViewItemAtIndex: 0] setLabel: _NS("File")];
177     [[o_tabview tabViewItemAtIndex: 1] setLabel: _NS("Disc")];
178     [[o_tabview tabViewItemAtIndex: 2] setLabel: _NS("Network")];
179
180     [o_file_btn_browse setTitle: _NS("Browse...")];
181     [o_file_stream setTitle: _NS("Treat as a pipe rather than as a file")];
182
183     [o_disc_device_lbl setStringValue: _NS("Device name")];
184     [o_disc_title_lbl setStringValue: _NS("Title")];
185     [o_disc_chapter_lbl setStringValue: _NS("Chapter")];
186     [o_disc_videots_btn_browse setStringValue: _NS("Browse...")];
187     [o_disc_dvd_menus setTitle: _NS("Use DVD menus")];
188
189     [[o_disc_type cellAtRow:0 column:0] setTitle: _NS("VIDEO_TS folder")];
190     [[o_disc_type cellAtRow:1 column:0] setTitle: _NS("DVD")];
191     [[o_disc_type cellAtRow:2 column:0] setTitle: _NS("VCD")];
192
193     [o_net_udp_port_lbl setStringValue: _NS("Port")];
194     [o_net_udpm_addr_lbl setStringValue: _NS("Address")];
195     [o_net_udpm_port_lbl setStringValue: _NS("Port")];
196     [o_net_cs_addr_lbl setStringValue: _NS("Address")];
197     [o_net_cs_port_lbl setStringValue: _NS("Port")];
198     [o_net_http_url_lbl setStringValue: _NS("URL")];
199
200     [[o_net_mode cellAtRow:0 column:0] setTitle: _NS("UDP/RTP")];
201     [[o_net_mode cellAtRow:1 column:0] setTitle: _NS("UDP/RTP Multicast")];
202     [[o_net_mode cellAtRow:2 column:0] setTitle: _NS("Channel server")];
203     [[o_net_mode cellAtRow:3 column:0] setTitle: _NS("HTTP/FTP/MMS")];
204
205     [o_net_udp_port setIntValue: config_GetInt( p_intf, "server-port" )];
206     [o_net_udp_port_stp setIntValue: config_GetInt( p_intf, "server-port" )];
207     [o_net_cs_port setIntValue: config_GetInt( p_intf, "channel-port" )];
208     [o_net_cs_port_stp setIntValue: config_GetInt( p_intf, "channel-port" )];
209
210     [o_sout_cbox setTitle: _NS("Stream output")];
211     [o_sout_mrl_lbl setTitle: _NS("Stream output MRL")];
212     [[o_sout_access cellAtRow:0 column:0] setTitle: _NS("File")];
213     [[o_sout_access cellAtRow:1 column:0] setTitle: _NS("UDP")];
214     [[o_sout_access cellAtRow:2 column:0] setTitle: _NS("RTP")];
215
216     [o_sout_file_btn_browse setStringValue: _NS("Browse...")];
217     [o_sout_udp_addr_lbl setStringValue: _NS("Address")];
218     [o_sout_udp_port_lbl setStringValue: _NS("Port")];
219
220     [[o_sout_mux cellAtRow:0 column:0] setTitle: _NS("PS")];
221     [[o_sout_mux cellAtRow:0 column:1] setTitle: _NS("TS")];
222
223     [[NSNotificationCenter defaultCenter] addObserver: self
224         selector: @selector(openFilePathChanged:)
225         name: NSControlTextDidChangeNotification
226         object: o_file_path];
227
228     [[NSNotificationCenter defaultCenter] addObserver: self
229         selector: @selector(openDiscInfoChanged:)
230         name: NSControlTextDidChangeNotification
231         object: o_disc_device];
232     [[NSNotificationCenter defaultCenter] addObserver: self
233         selector: @selector(openDiscInfoChanged:)
234         name: NSControlTextDidChangeNotification
235         object: o_disc_title];
236     [[NSNotificationCenter defaultCenter] addObserver: self
237         selector: @selector(openDiscInfoChanged:)
238         name: NSControlTextDidChangeNotification
239         object: o_disc_chapter];
240     [[NSNotificationCenter defaultCenter] addObserver: self
241         selector: @selector(openDiscInfoChanged:)
242         name: NSControlTextDidChangeNotification
243         object: o_disc_videots_folder];
244
245     [[NSNotificationCenter defaultCenter] addObserver: self
246         selector: @selector(openNetInfoChanged:)
247         name: NSControlTextDidChangeNotification
248         object: o_net_udp_port];
249     [[NSNotificationCenter defaultCenter] addObserver: self
250         selector: @selector(openNetInfoChanged:)
251         name: NSControlTextDidChangeNotification
252         object: o_net_udpm_addr];
253     [[NSNotificationCenter defaultCenter] addObserver: self
254         selector: @selector(openNetInfoChanged:)
255         name: NSControlTextDidChangeNotification
256         object: o_net_udpm_port];
257     [[NSNotificationCenter defaultCenter] addObserver: self
258         selector: @selector(openNetInfoChanged:)
259         name: NSControlTextDidChangeNotification
260         object: o_net_cs_addr];
261     [[NSNotificationCenter defaultCenter] addObserver: self
262         selector: @selector(openNetInfoChanged:)
263         name: NSControlTextDidChangeNotification
264         object: o_net_cs_port];
265     [[NSNotificationCenter defaultCenter] addObserver: self
266         selector: @selector(openNetInfoChanged:)
267         name: NSControlTextDidChangeNotification
268         object: o_net_http_url];
269
270     [[NSNotificationCenter defaultCenter] addObserver: self
271         selector: @selector(soutInfoChanged:)
272         name: NSControlTextDidChangeNotification
273         object: o_sout_file_path];
274     [[NSNotificationCenter defaultCenter] addObserver: self
275         selector: @selector(soutInfoChanged:)
276         name: NSControlTextDidChangeNotification
277         object: o_sout_udp_addr];
278     [[NSNotificationCenter defaultCenter] addObserver: self
279         selector: @selector(soutInfoChanged:)
280         name: NSControlTextDidChangeNotification
281         object: o_sout_udp_port];
282 }
283
284 - (void)openTarget:(int)i_type
285 {
286     int i_result;
287
288     [o_tabview selectTabViewItemAtIndex: i_type];
289
290     i_result = [NSApp runModalForWindow: o_panel];
291     [o_panel close];
292
293     if( i_result )
294     {
295         NSString *o_sout = [o_sout_mrl stringValue];
296
297         if ( [o_sout_cbox state] )
298         {
299             intf_thread_t * p_intf = [NSApp getIntf];
300             config_PutPsz( p_intf, "sout", [o_sout lossyCString] );
301         }
302
303         NSString *o_source = [o_mrl stringValue];
304
305         [o_playlist appendArray: 
306             [NSArray arrayWithObject: o_source] atPos: -1];
307     }
308
309     [self soutModeChanged: nil];
310 }
311
312 - (void)tabView:(NSTabView *)o_tv didSelectTabViewItem:(NSTabViewItem *)o_tvi
313 {
314     NSString *o_label = [o_tvi label];
315
316     if( [o_label isEqualToString: _NS("File")] )
317     {
318         [self openFilePathChanged: nil];
319     }
320     else if( [o_label isEqualToString: _NS("Disc")] )
321     {
322         [self openDiscTypeChanged: nil];
323     }
324     else if( [o_label isEqualToString: _NS("Network")] )
325     {
326         [self openNetModeChanged: nil];
327     }  
328 }
329
330 - (IBAction)openFileGeneric:(id)sender
331 {
332     [self openFilePathChanged: nil];
333     [self openTarget: 0];
334 }
335
336 - (IBAction)openDisc:(id)sender
337 {
338     [self openDiscTypeChanged: nil];
339     [self openTarget: 1];
340 }
341
342 - (IBAction)openNet:(id)sender
343 {
344     [self openNetModeChanged: nil];
345     [self openTarget: 2];
346 }
347
348 - (void)openFilePathChanged:(NSNotification *)o_notification
349 {
350     NSString *o_mrl_string;
351     NSString *o_filename = [o_file_path stringValue];
352     vlc_bool_t b_stream = [o_file_stream state];
353
354     o_mrl_string = [NSString stringWithFormat: @"%s://%@",
355                     b_stream ? "stream" : "file",
356                     o_filename];
357     [o_mrl setStringValue: o_mrl_string]; 
358 }
359
360 - (IBAction)openFileBrowse:(id)sender
361 {
362     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
363     
364     [o_open_panel setAllowsMultipleSelection: NO];
365     [o_open_panel setTitle: _NS("Open File")];
366     [o_open_panel setPrompt: _NS("Open")];
367
368     if( [o_open_panel runModalForDirectory: nil 
369             file: nil types: nil] == NSOKButton )
370     {
371         NSString *o_filename = [[o_open_panel filenames] objectAtIndex: 0];
372         [o_file_path setStringValue: o_filename];
373         [self openFilePathChanged: nil];
374     }
375 }
376
377 - (IBAction)openFileStreamChanged:(id)sender
378 {
379     [self openFilePathChanged: nil];
380 }
381
382 - (IBAction)openDiscTypeChanged:(id)sender
383 {
384     NSString *o_type;
385     vlc_bool_t b_device, b_menus, b_title_chapter;
386     
387     [o_disc_device removeAllItems];
388     b_title_chapter = ![o_disc_dvd_menus state];
389     
390     o_type = [[o_disc_type selectedCell] title];
391
392     if ( [o_type isEqualToString: _NS("VIDEO_TS folder")] )
393     {
394         b_device = 0; b_menus = 1;
395     }
396     else
397     {
398         NSArray *o_devices;
399         NSString *o_disc;
400         const char *psz_class = NULL;
401         b_device = 1;
402
403         if ( [o_type isEqualToString: _NS("VCD")] )
404         {
405             psz_class = kIOCDMediaClass;
406             o_disc = o_type;
407             b_menus = 0; b_title_chapter = 1;
408             [o_disc_dvd_menus setState: FALSE];
409         }
410         else
411         {
412             psz_class = kIODVDMediaClass;
413             o_disc = o_type;
414             b_menus = 1;
415         }
416     
417         o_devices = GetEjectableMediaOfClass( psz_class );
418         if ( o_devices != nil )
419         {
420             int i_devices = [o_devices count];
421         
422             if ( i_devices )
423             {
424                 int i;
425         
426                 for( i = 0; i < i_devices; i++ )
427                 {
428                     [o_disc_device 
429                         addItemWithObjectValue: [o_devices objectAtIndex: i]];
430                 }
431
432                 [o_disc_device selectItemAtIndex: 0];
433             }
434             else
435             {
436                 [o_disc_device setStringValue: 
437                     [NSString stringWithFormat: _NS("No %@s found"), o_disc]];
438             }
439         }
440     }
441
442     [o_disc_device setEnabled: b_device];
443     [o_disc_title setEnabled: b_title_chapter];
444     [o_disc_title_stp setEnabled: b_title_chapter];
445     [o_disc_chapter setEnabled: b_title_chapter];
446     [o_disc_chapter_stp setEnabled: b_title_chapter];
447     [o_disc_videots_folder setEnabled: !b_device];
448     [o_disc_videots_btn_browse setEnabled: !b_device];
449     [o_disc_dvd_menus setEnabled: b_menus];
450
451     [self openDiscInfoChanged: nil];
452 }
453
454 - (IBAction)openDiscStepperChanged:(id)sender
455 {
456     int i_tag = [sender tag];
457
458     if( i_tag == 0 )
459     {
460         [o_disc_title setIntValue: [o_disc_title_stp intValue]];
461     }
462     else if( i_tag == 1 )
463     {
464         [o_disc_chapter setIntValue: [o_disc_chapter_stp intValue]];
465     }
466
467     [self openDiscInfoChanged: nil];
468 }
469
470 - (void)openDiscInfoChanged:(NSNotification *)o_notification
471 {
472     NSString *o_type;
473     NSString *o_device;
474     NSString *o_videots;
475     NSString *o_mrl_string;
476     int i_title, i_chapter;
477     vlc_bool_t b_menus;
478
479     o_type = [[o_disc_type selectedCell] title];
480     o_device = [o_disc_device stringValue];
481     i_title = [o_disc_title intValue];
482     i_chapter = [o_disc_chapter intValue];
483     o_videots = [o_disc_videots_folder stringValue];
484     b_menus = [o_disc_dvd_menus state];
485
486     if ( [o_type isEqualToString: _NS("VCD")] )
487     {
488         if ( [o_device isEqualToString:
489                 [NSString stringWithFormat: _NS("No %@s found"), o_type]] )
490             o_device = @"";
491         o_mrl_string = [NSString stringWithFormat: @"vcd://%@@%i,%i",
492                         o_device, i_title, i_chapter]; 
493     }
494     else if ( [o_type isEqualToString: _NS("DVD")] )
495     {
496         if ( [o_device isEqualToString:
497                 [NSString stringWithFormat: _NS("No %@s found"), o_type]] )
498             o_device = @"";
499         if ( b_menus )
500             o_mrl_string = [NSString stringWithFormat: @"dvdplay://%@",
501                             o_device]; 
502         else
503             o_mrl_string = [NSString stringWithFormat: @"dvdold://%@@%i,%i",
504                             o_device, i_title, i_chapter]; 
505     }
506     else /* VIDEO_TS folder */
507     {
508         if ( b_menus )
509             o_mrl_string = [NSString stringWithFormat: @"dvdplay://%@",
510                             o_videots]; 
511         else
512             o_mrl_string = [NSString stringWithFormat: @"dvdread://%@@%i,%i",
513                             o_videots, i_title, i_chapter]; 
514     }
515
516     [o_mrl setStringValue: o_mrl_string]; 
517 }
518
519 - (IBAction)openDiscMenusChanged:(id)sender
520 {
521     [self openDiscInfoChanged: nil];
522     [self openDiscTypeChanged: nil];
523 }
524
525 - (IBAction)openVTSBrowse:(id)sender
526 {
527     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
528
529     [o_open_panel setAllowsMultipleSelection: NO];
530     [o_open_panel setCanChooseFiles: NO];
531     [o_open_panel setCanChooseDirectories: YES];
532     [o_open_panel setTitle: _NS("Open VIDEO_TS Directory")];
533     [o_open_panel setPrompt: _NS("Open")];
534
535     if( [o_open_panel runModalForDirectory: nil
536             file: nil types: nil] == NSOKButton )
537     {
538         NSString *o_dirname = [[o_open_panel filenames] objectAtIndex: 0];
539         [o_disc_videots_folder setStringValue: o_dirname];
540         [self openDiscInfoChanged: nil];
541     }
542 }
543
544 - (IBAction)openNetModeChanged:(id)sender
545 {
546     NSString *o_mode;
547     BOOL b_udp = FALSE;
548     BOOL b_udpm = FALSE;
549     BOOL b_cs = FALSE;
550     BOOL b_http = FALSE;
551
552     o_mode = [[o_net_mode selectedCell] title];
553
554     if( [o_mode isEqualToString: _NS("UDP/RTP")] ) b_udp = TRUE;   
555     else if( [o_mode isEqualToString: _NS("UDP/RTP Multicast")] ) b_udpm = TRUE;
556     else if( [o_mode isEqualToString: _NS("Channel server")] ) b_cs = TRUE;
557     else if( [o_mode isEqualToString: _NS("HTTP/FTP/MMS")] ) b_http = TRUE;
558
559     [o_net_udp_port setEnabled: b_udp];
560     [o_net_udp_port_stp setEnabled: b_udp];
561     [o_net_udpm_addr setEnabled: b_udpm];
562     [o_net_udpm_port setEnabled: b_udpm];
563     [o_net_udpm_port_stp setEnabled: b_udpm];
564     [o_net_cs_addr setEnabled: b_cs];
565     [o_net_cs_port setEnabled: b_cs]; 
566     [o_net_cs_port_stp setEnabled: b_cs]; 
567     [o_net_http_url setEnabled: b_http];
568
569     [self openNetInfoChanged: nil];
570 }
571
572 - (IBAction)openNetStepperChanged:(id)sender
573 {
574     int i_tag = [sender tag];
575
576     if( i_tag == 0 )
577     {
578         [o_net_udp_port setIntValue: [o_net_udp_port_stp intValue]];
579     }
580     else if( i_tag == 1 )
581     {
582         [o_net_udpm_port setIntValue: [o_net_udpm_port_stp intValue]];
583     }
584     else if( i_tag == 2 )
585     {
586         [o_net_cs_port setIntValue: [o_net_cs_port_stp intValue]];
587     }
588
589     [self openNetInfoChanged: nil];
590 }
591
592 - (void)openNetInfoChanged:(NSNotification *)o_notification
593 {
594     NSString *o_mode;
595     vlc_bool_t b_channel;
596     NSString *o_mrl_string = [NSString string];
597     intf_thread_t * p_intf = [NSApp getIntf];
598
599     o_mode = [[o_net_mode selectedCell] title];
600
601     b_channel = (vlc_bool_t)[o_mode isEqualToString: _NS("Channel server")]; 
602     config_PutInt( p_intf, "network-channel", b_channel );
603
604     if( [o_mode isEqualToString: _NS("UDP/RTP")] )
605     {
606         int i_port = [o_net_udp_port intValue];
607
608         o_mrl_string = [NSString stringWithString: @"udp://"]; 
609
610         if( i_port != config_GetInt( p_intf, "server-port" ) )
611         {
612             o_mrl_string = 
613                 [o_mrl_string stringByAppendingFormat: @"@:%i", i_port]; 
614         } 
615     }
616     else if( [o_mode isEqualToString: _NS("UDP/RTP Multicast")] ) 
617     {
618         NSString *o_addr = [o_net_udpm_addr stringValue];
619         int i_port = [o_net_udpm_port intValue];
620
621         o_mrl_string = [NSString stringWithFormat: @"udp://@%@", o_addr]; 
622
623         if( i_port != config_GetInt( p_intf, "server-port" ) )
624         {
625             o_mrl_string = 
626                 [o_mrl_string stringByAppendingFormat: @":%i", i_port]; 
627         } 
628     }
629     else if( [o_mode isEqualToString: _NS("Channel server")] )
630     {
631         NSString *o_addr = [o_net_cs_addr stringValue];
632         int i_port = [o_net_cs_port intValue];
633
634         if( p_intf->p_vlc->p_channel == NULL )
635         {
636             network_ChannelCreate( p_intf );
637         } 
638
639         config_PutPsz( p_intf, "channel-server", [o_addr lossyCString] ); 
640         if( i_port < 65536 )
641         {
642             config_PutInt( p_intf, "channel-port", i_port );
643         }
644
645         /* FIXME: we should use a playlist server instead */
646         o_mrl_string = [NSString stringWithString: @"udp://"];
647     }
648     else if( [o_mode isEqualToString: _NS("HTTP/FTP/MMS")] )
649     {
650         NSString *o_url = [o_net_http_url stringValue];
651
652         if ( ![o_url hasPrefix:@"http:"] && ![o_url hasPrefix:@"ftp:"]
653               && ![o_url hasPrefix:@"mms"] )
654             o_mrl_string = [NSString stringWithFormat: @"http://%@", o_url];
655         else
656             o_mrl_string = o_url;
657     }
658
659     [o_mrl setStringValue: o_mrl_string];
660 }
661
662 - (IBAction)soutChanged:(id)sender;
663 {
664     [self soutModeChanged: nil];
665
666     if ( [o_sout_cbox state] )
667     {
668         NSPoint s_point;
669         s_point.x = 0;
670         s_point.y = 0;
671         [[o_panel contentView] setBoundsOrigin: s_point];
672         [[o_panel contentView] setNeedsDisplay: YES];
673
674         NSRect s_rect = [o_panel frame];
675         s_rect.size.height = OPEN_PANEL_FULL_HEIGHT + WINDOW_TITLE_HEIGHT;
676         s_rect.origin.y -= OPEN_PANEL_FULL_HEIGHT - OPEN_PANEL_SHORT_HEIGHT;
677         [o_panel setFrame: s_rect display: YES animate: YES];
678     }
679     else
680     {
681         NSPoint s_point;
682         s_point.x = 0;
683         s_point.y = OPEN_PANEL_FULL_HEIGHT - OPEN_PANEL_SHORT_HEIGHT;
684         [[o_panel contentView] setBoundsOrigin: s_point];
685         [[o_panel contentView] setNeedsDisplay: YES];
686
687         NSRect s_rect = [o_panel frame];
688         s_rect.size.height = OPEN_PANEL_SHORT_HEIGHT + WINDOW_TITLE_HEIGHT;
689         s_rect.origin.y += OPEN_PANEL_FULL_HEIGHT - OPEN_PANEL_SHORT_HEIGHT;
690         [o_panel setFrame: s_rect display: YES animate:YES];
691     }
692 }
693
694 - (IBAction)soutFileBrowse:(id)sender
695 {
696     NSSavePanel *o_save_panel = [NSSavePanel savePanel];
697     NSString *o_mux_string;
698     if ( [[[o_sout_mux selectedCell] title] isEqualToString: _NS("PS")] )
699         o_mux_string = @"vob";
700     else
701         o_mux_string = @"ts";
702
703     NSString * o_name = [NSString stringWithFormat: @"vlc-output.%@",
704                          o_mux_string];
705
706     [o_save_panel setTitle: _NS("Save File")];
707     [o_save_panel setPrompt: _NS("Save")];
708
709     if( [o_save_panel runModalForDirectory: nil
710             file: o_name] == NSOKButton )
711     {
712         NSString *o_filename = [o_save_panel filename];
713         [o_sout_file_path setStringValue: o_filename];
714         [self soutInfoChanged: nil];
715     }
716 }
717
718 - (void)soutModeChanged:(NSNotification *)o_notification
719 {
720     NSString *o_mode;
721     BOOL b_file = FALSE;
722     BOOL b_udp = FALSE;
723     BOOL b_rtp = FALSE;
724
725     o_mode = [[o_sout_access selectedCell] title];
726
727     if( [o_mode isEqualToString: _NS("File")] ) b_file = TRUE;   
728     else if( [o_mode isEqualToString: _NS("UDP")] ) b_udp = TRUE;
729     else if( [o_mode isEqualToString: _NS("RTP")] ) b_rtp = TRUE;
730
731     [o_sout_file_path setEnabled: b_file];
732     [o_sout_file_btn_browse setEnabled: b_file];
733     [o_sout_udp_addr setEnabled: b_udp|b_rtp];
734     [o_sout_udp_port setEnabled: b_udp|b_rtp];
735     [o_sout_udp_port_stp setEnabled: b_udp|b_rtp];
736     [[o_sout_mux cellAtRow:0 column: 0] setEnabled: !b_rtp];
737
738     if ( b_rtp )
739     {
740         [[o_sout_mux cellAtRow: 0 column:1] setState: YES];
741     }
742
743     [self soutInfoChanged: nil];
744 }
745
746 - (void)soutInfoChanged:(NSNotification *)o_notification
747 {
748     NSString *o_mode;
749     NSString *o_mux;
750     NSString *o_mrl_string;
751     NSString *o_mux_string;
752
753     o_mode = [[o_sout_access selectedCell] title];
754     o_mux = [[o_sout_mux selectedCell] title];
755
756     if ( [o_mux isEqualToString: _NS("PS")] ) o_mux_string = @"ps";
757     else o_mux_string = @"ts";
758
759     if ( [o_mode isEqualToString: _NS("File")] )
760     {
761         o_mrl_string = [NSString stringWithFormat: @"file/%@://%@",
762                         o_mux_string, [o_sout_file_path stringValue]];
763     }
764     else if ( [o_mode isEqualToString: _NS("UDP")] )
765     {
766         o_mrl_string = [NSString stringWithFormat: @"udp/%@://%@:%i",
767                         o_mux_string, [o_sout_udp_addr stringValue],
768                         [o_sout_udp_port intValue]];
769     }
770     else
771     {
772         o_mrl_string = [NSString stringWithFormat: @"rtp/%@://%@:%i",
773                         o_mux_string, [o_sout_udp_addr stringValue],
774                         [o_sout_udp_port intValue]];
775     }
776
777
778     [o_sout_mrl setStringValue: o_mrl_string];
779 }
780
781 - (IBAction)soutStepperChanged:(id)sender
782 {
783     [o_sout_udp_port setIntValue: [o_net_udp_port_stp intValue]];
784
785     [self soutInfoChanged: nil];
786 }
787
788 - (IBAction)openFile:(id)sender
789 {
790     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
791
792     [o_open_panel setAllowsMultipleSelection: NO];
793     [o_open_panel setTitle: _NS("Open File")];
794     [o_open_panel setPrompt: _NS("Open")];
795
796     if( [o_open_panel runModalForDirectory: nil
797             file: nil types: nil] == NSOKButton )
798     {
799         intf_thread_t * p_intf = [NSApp getIntf];
800         config_PutPsz( p_intf, "sout", NULL );
801         [o_playlist appendArray: [o_open_panel filenames] atPos: -1];
802     }
803 }
804
805 - (IBAction)panelCancel:(id)sender
806 {
807     [NSApp stopModalWithCode: 0];
808 }
809
810 - (IBAction)panelOk:(id)sender
811 {
812     if( [[o_mrl stringValue] length] )
813     {
814         [NSApp stopModalWithCode: 1];
815     }
816     else
817     {
818         NSBeep();
819     }
820 }
821
822 @end