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