]> git.sesse.net Git - vlc/blob - modules/gui/macosx/open.m
47754df622557be62021ad3ed1e2515f62a19f57
[vlc] / modules / gui / macosx / open.m
1 /*****************************************************************************
2  * open.m: Open dialogues for VLC's MacOS X port
3  *****************************************************************************
4  * Copyright (C) 2002-2012 VLC authors and VideoLAN
5  * $Id$
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  *          Benjamin Pracht <bigben at videolan dot org>
11  *          Felix Paul Kühne <fkuehne at videolan dot org>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31 #import <stdlib.h>                                      /* malloc(), free() */
32 #import <sys/param.h>                                    /* for MAXPATHLEN */
33
34 #import "CompatibilityFixes.h"
35
36 #import <paths.h>
37 #import <IOKit/IOBSD.h>
38 #import <IOKit/storage/IOMedia.h>
39 #import <IOKit/storage/IOCDMedia.h>
40 #import <IOKit/storage/IODVDMedia.h>
41 #import <IOKit/storage/IOBDMedia.h>
42 #import <Cocoa/Cocoa.h>
43 #import <QTKit/QTKit.h>
44
45 #import "intf.h"
46 #import "playlist.h"
47 #import "open.h"
48 #import "output.h"
49 #import "eyetv.h"
50
51 #import <vlc_url.h>
52
53 NSArray               *qtkvideoDevices;
54 #define setEyeTVUnconnected \
55 [o_capture_lbl setStringValue: _NS("No device is selected")]; \
56 [o_capture_long_lbl setStringValue: _NS("No device is selected.\n\nChoose available device in above pull-down menu\n.")]; \
57 [o_capture_lbl displayIfNeeded]; \
58 [o_capture_long_lbl displayIfNeeded]; \
59 [self showCaptureView: o_capture_label_view]
60
61 #define kVLCMediaAudioCD "AudioCD"
62 #define kVLCMediaDVD "DVD"
63 #define kVLCMediaVCD "VCD"
64 #define kVLCMediaSVCD "SVCD"
65 #define kVLCMediaBD "Bluray"
66 #define kVLCMediaVideoTSFolder "VIDEO_TS"
67 #define kVLCMediaBDMVFolder "BDMV"
68 #define kVLCMediaUnknown "Unknown"
69
70
71 /*****************************************************************************
72  * VLCOpen implementation
73  *****************************************************************************/
74 @implementation VLCOpen
75
76 static VLCOpen *_o_sharedMainInstance = nil;
77
78 + (VLCOpen *)sharedInstance
79 {
80     return _o_sharedMainInstance ? _o_sharedMainInstance : [[self alloc] init];
81 }
82
83 - (id)init
84 {
85     if( _o_sharedMainInstance) {
86         [self dealloc];
87     } else {
88         _o_sharedMainInstance = [super init];
89         p_intf = VLCIntf;
90     }
91  
92     return _o_sharedMainInstance;
93 }
94
95 - (void)dealloc
96 {
97     [o_specialMediaFolders release];
98     [o_opticalDevices release];
99     if( o_file_slave_path )
100         [o_file_slave_path release];
101     [o_mrl release];
102     if (o_sub_path)
103         [o_sub_path release];
104     [o_currentOpticalMediaIconView release];
105     [o_currentOpticalMediaView release];
106     [super dealloc];
107 }
108
109 - (void)awakeFromNib
110 {
111     if (OSX_LION)
112         [o_panel setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary];
113
114     [o_panel setTitle: _NS("Open Source")];
115     [o_mrl_lbl setStringValue: _NS("Media Resource Locator (MRL)")];
116
117     [o_btn_ok setTitle: _NS("Open")];
118     [o_btn_cancel setTitle: _NS("Cancel")];
119
120     [[o_tabview tabViewItemAtIndex: 0] setLabel: _NS("File")];
121     [[o_tabview tabViewItemAtIndex: 1] setLabel: _NS("Disc")];
122     [[o_tabview tabViewItemAtIndex: 2] setLabel: _NS("Network")];
123     [[o_tabview tabViewItemAtIndex: 3] setLabel: _NS("Capture")];
124
125     [o_file_name setStringValue: @""];
126     [o_file_name_stub setStringValue: _NS("Choose a file")];
127     [o_file_icon_well setImage: [NSImage imageNamed:@"generic"]];
128     [o_file_btn_browse setTitle: _NS("Browse...")];
129     [o_file_stream setTitle: _NS("Treat as a pipe rather than as a file")];
130     [o_file_stream setHidden: NO];
131     [o_file_slave_ckbox setTitle: _NS("Play another media synchronously")];
132     [o_file_slave_select_btn setTitle: _NS("Choose...")];
133     [o_file_slave_filename_lbl setStringValue: @""];
134     [o_file_slave_icon_well setImage: NULL];
135     [o_file_subtitles_filename_lbl setStringValue: @""];
136     [o_file_subtitles_icon_well setImage: NULL];
137
138     [o_disc_selector_pop removeAllItems];
139     [o_disc_selector_pop setHidden: NO];
140     NSString *o_videots = _NS("Open VIDEO_TS folder");
141     NSString *o_bdmv = _NS("Open BDMV folder");
142     [o_disc_nodisc_lbl setStringValue: _NS("Insert Disc")];
143     [o_disc_nodisc_videots_btn setTitle: o_videots];
144     [o_disc_nodisc_bdmv_btn setTitle: o_bdmv];
145     [o_disc_audiocd_lbl setStringValue: _NS("Audio CD")];
146     [o_disc_audiocd_trackcount_lbl setStringValue: @""];
147     [o_disc_audiocd_videots_btn setTitle: o_videots];
148     [o_disc_audiocd_bdmv_btn setTitle: o_bdmv];
149     [o_disc_dvd_lbl setStringValue: @""];
150     [o_disc_dvd_disablemenus_btn setTitle: _NS("Disable DVD menus")];
151     [o_disc_dvd_videots_btn setTitle: o_videots];
152     [o_disc_dvd_bdmv_btn setTitle: o_bdmv];
153     [o_disc_dvdwomenus_lbl setStringValue: @""];
154     [o_disc_dvdwomenus_enablemenus_btn setTitle: _NS("Enable DVD menus")];
155     [o_disc_dvdwomenus_videots_btn setTitle: o_videots];
156     [o_disc_dvdwomenus_bdmv_btn setTitle: o_bdmv];
157     [o_disc_dvdwomenus_title_lbl setStringValue: _NS("Title")];
158     [o_disc_dvdwomenus_chapter_lbl setStringValue: _NS("Chapter")];
159     [o_disc_vcd_title_lbl setStringValue: _NS("Title")];
160     [o_disc_vcd_chapter_lbl setStringValue: _NS("Chapter")];
161     [o_disc_vcd_videots_btn setTitle: o_videots];
162     [o_disc_vcd_bdmv_btn setTitle: o_bdmv];
163     [o_disc_bd_videots_btn setTitle: o_videots];
164     [o_disc_bd_bdmv_btn setTitle: o_bdmv];
165
166     [o_net_udp_port_lbl setStringValue: _NS("Port")];
167     [o_net_udpm_addr_lbl setStringValue: _NS("IP Address")];
168     [o_net_udpm_port_lbl setStringValue: _NS("Port")];
169     [o_net_http_url_lbl setStringValue: _NS("URL")];
170     [o_net_help_lbl setStringValue: _NS("To Open a usual network stream (HTTP, RTSP, RTMP, MMS, FTP, etc.), just enter the URL in the field above. If you want to open a RTP or UDP stream, press the button below.")];
171     [o_net_help_udp_lbl setStringValue: _NS("If you want to open a multicast stream, enter the respective IP address given by the stream provider. In unicast mode, VLC will use your machine's IP automatically.\n\nTo open a stream using a different protocol, just press Cancel to close this sheet.")];
172     [o_net_udp_cancel_btn setTitle: _NS("Cancel")];
173     [o_net_udp_ok_btn setTitle: _NS("Open")];
174     [o_net_openUDP_btn setTitle: _NS("Open RTP/UDP Stream")];
175     [o_net_udp_mode_lbl setStringValue: _NS("Mode")];
176     [o_net_udp_protocol_lbl setStringValue: _NS("Protocol")];
177     [o_net_udp_address_lbl setStringValue: _NS("Address")];
178
179     [[o_net_mode cellAtRow:0 column:0] setTitle: _NS("Unicast")];
180     [[o_net_mode cellAtRow:1 column:0] setTitle: _NS("Multicast")];
181
182     [o_net_udp_port setIntValue: config_GetInt( p_intf, "server-port" )];
183     [o_net_udp_port_stp setIntValue: config_GetInt( p_intf, "server-port" )];
184
185     [o_eyetv_chn_bgbar setUsesThreadedAnimation: YES];
186
187     [o_capture_mode_pop removeAllItems];
188     [o_capture_mode_pop addItemWithTitle: _NS("Capture Device")];
189         // our screen capture module isn't Lion-compatible, so let's hide it from the user if needed (trac #4799)
190         if( NSAppKitVersionNumber < 1115.2 )
191                 [o_capture_mode_pop addItemWithTitle: _NS("Screen")];
192     [o_capture_mode_pop addItemWithTitle: @"EyeTV"];
193     [o_screen_long_lbl setStringValue: _NS("This input allows you to save, stream or display your current screen contents.")];
194     [o_screen_fps_lbl setStringValue: _NS("Frames per Second:")];
195     [o_screen_left_lbl setStringValue: _NS("Subscreen left:")];
196     [o_screen_top_lbl setStringValue: _NS("Subscreen top:")];
197     [o_screen_width_lbl setStringValue: _NS("Subscreen width:")];
198     [o_screen_height_lbl setStringValue: _NS("Subscreen height:")];
199     [o_screen_follow_mouse_ckb setTitle: _NS("Follow the mouse")];
200     [o_eyetv_currentChannel_lbl setStringValue: _NS("Current channel:")];
201     [o_eyetv_previousProgram_btn setTitle: _NS("Previous Channel")];
202     [o_eyetv_nextProgram_btn setTitle: _NS("Next Channel")];
203     [o_eyetv_chn_status_txt setStringValue: _NS("Retrieving Channel Info...")];
204     [o_eyetv_noInstance_lbl setStringValue: _NS("EyeTV is not launched")];
205     [o_eyetv_noInstanceLong_lbl setStringValue: _NS("VLC could not connect to EyeTV.\nMake sure that you installed VLC's EyeTV plugin.")];
206     [o_eyetv_launchEyeTV_btn setTitle: _NS("Launch EyeTV now")];
207     [o_eyetv_getPlugin_btn setTitle: _NS("Download Plugin")];
208     [o_qtk_long_lbl setStringValue: _NS("This input allows you to process input signals from QuickTime-compatible video devices.\nLive Audio input is not supported.")];
209     [o_capture_width_lbl setStringValue: _NS("Image width:")];
210     [o_capture_height_lbl setStringValue: _NS("Image height:")];
211
212     [self qtkvideoDevices];
213     [o_qtk_device_pop removeAllItems];
214     msg_Dbg( VLCIntf, "Found %lu capture devices", [qtkvideoDevices count] );
215         if([qtkvideoDevices count] >= 1)
216                 {
217         if (!qtk_currdevice_uid) {
218             qtk_currdevice_uid = [[[QTCaptureDevice defaultInputDeviceWithMediaType: QTMediaTypeVideo] uniqueID]
219                                                                 stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
220         }
221         NSUInteger deviceCount = [qtkvideoDevices count];
222         for(int ivideo = 0; ivideo < deviceCount; ivideo++){
223             QTCaptureDevice *qtk_device;
224             qtk_device = [qtkvideoDevices objectAtIndex:ivideo];
225             [o_qtk_device_pop addItemWithTitle: [qtk_device localizedDisplayName]];
226             if([[[qtk_device uniqueID]stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] isEqualToString:qtk_currdevice_uid]){
227                 [o_qtk_device_pop selectItemAtIndex:ivideo];
228             }
229         }
230     }
231         else
232         {
233                 [o_qtk_device_pop addItemWithTitle: _NS("None")];
234         [qtk_currdevice_uid release];
235         }
236
237
238     [self setSubPanel];
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     [[NSDistributedNotificationCenter defaultCenter] addObserver: self
258                                                         selector: @selector(eyetvChanged:)
259                                                             name: NULL
260                                                           object: @"VLCEyeTVSupport"
261                                               suspensionBehavior: NSNotificationSuspensionBehaviorDeliverImmediately];
262
263     [[NSNotificationCenter defaultCenter] addObserver: self
264                                              selector: @selector(screenFPSfieldChanged:)
265                                                  name: NSControlTextDidChangeNotification
266                                                object: o_screen_fps_fld];
267
268     /* register clicks on text fields */
269     [[NSNotificationCenter defaultCenter] addObserver: self
270                                              selector: @selector(textFieldWasClicked:)
271                                                  name: @"VLCOpenTextFieldWasClicked"
272                                                object: nil];
273
274     /* we want to be notified about removed or added media */
275     o_specialMediaFolders = [[NSMutableArray alloc] init];
276     o_opticalDevices = [[NSMutableArray alloc] init];
277     NSWorkspace *sharedWorkspace = [NSWorkspace sharedWorkspace];
278         [[sharedWorkspace notificationCenter] addObserver:self selector:@selector(scanOpticalMedia:) name:NSWorkspaceDidMountNotification object:nil];
279         [[sharedWorkspace notificationCenter] addObserver:self selector:@selector(scanOpticalMedia:) name:NSWorkspaceDidUnmountNotification object:nil];
280     [self scanOpticalMedia:nil];
281     [self qtkChanged:nil];
282
283     [self setMRL: @""];
284 }
285
286 - (void)setMRL:(NSString *)newMRL
287 {
288     [o_mrl release];
289     o_mrl = newMRL;
290     [o_mrl retain];
291     [o_mrl_fld setStringValue: newMRL];
292     if ([o_mrl length] > 0)
293         [o_btn_ok setEnabled: YES];
294     else
295         [o_btn_ok setEnabled: NO];
296 }
297
298 - (NSString *)MRL
299 {
300     return o_mrl;
301 }
302
303 - (void)setSubPanel
304 {
305     int i_index;
306     module_config_t * p_item;
307
308     [o_file_sub_ckbox setTitle: _NS("Load subtitles file:")];
309     [o_file_sub_path_lbl setStringValue: _NS("Choose a file")];
310     [o_file_sub_path_lbl setHidden: NO];
311     [o_file_sub_path_fld setStringValue: @""];
312     [o_file_sub_btn_settings setTitle: _NS("Choose...")];
313     [o_file_sub_btn_browse setTitle: _NS("Browse...")];
314     [o_file_sub_override setTitle: _NS("Override parameters")];
315     [o_file_sub_delay_lbl setStringValue: _NS("Delay")];
316     [o_file_sub_delay_stp setEnabled: NO];
317     [o_file_sub_fps_lbl setStringValue: _NS("FPS")];
318     [o_file_sub_fps_stp setEnabled: NO];
319     [o_file_sub_encoding_lbl setStringValue: _NS("Subtitles encoding")];
320     [o_file_sub_encoding_pop removeAllItems];
321     [o_file_sub_size_lbl setStringValue: _NS("Font size")];
322     [o_file_sub_size_pop removeAllItems];
323     [o_file_sub_align_lbl setStringValue: _NS("Subtitles alignment")];
324     [o_file_sub_align_pop removeAllItems];
325     [o_file_sub_ok_btn setStringValue: _NS("OK")];
326     [o_file_sub_font_box setTitle: _NS("Font Properties")];
327     [o_file_sub_file_box setTitle: _NS("Subtitle File")];
328
329     p_item = config_FindConfig( VLC_OBJECT(p_intf), "subsdec-encoding" );
330
331     if( p_item )
332     {
333         for( i_index = 0; p_item->ppsz_list && p_item->ppsz_list[i_index];
334              i_index++ )
335         {
336             [o_file_sub_encoding_pop addItemWithTitle:
337                 [NSString stringWithUTF8String: p_item->ppsz_list[i_index]]];
338         }
339         [o_file_sub_encoding_pop selectItemWithTitle:
340                 [NSString stringWithUTF8String: p_item->value.psz]];
341     }
342
343     p_item = config_FindConfig( VLC_OBJECT(p_intf), "subsdec-align" );
344
345     if ( p_item )
346     {
347         for ( i_index = 0; i_index < p_item->i_list; i_index++ )
348         {
349             [o_file_sub_align_pop addItemWithTitle:
350              _NS(p_item->ppsz_list_text[i_index])];
351         }
352         [o_file_sub_align_pop selectItemAtIndex: p_item->value.i];
353     }
354
355     p_item = config_FindConfig( VLC_OBJECT(p_intf), "freetype-rel-fontsize" );
356
357     if ( p_item )
358     {
359         for ( i_index = 0; i_index < p_item->i_list; i_index++ )
360         {
361             [o_file_sub_size_pop addItemWithTitle: _NS(p_item->ppsz_list_text[i_index])];
362             if ( p_item->value.i == p_item->pi_list[i_index] )
363             {
364                 [o_file_sub_size_pop selectItemAtIndex: i_index];
365             }
366         }
367     }
368 }
369
370 - (void)openTarget:(int)i_type
371 {
372     int i_result;
373
374     b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
375
376     [o_tabview selectTabViewItemAtIndex: i_type];
377     [o_file_sub_ckbox setState: NSOffState];
378  
379     i_result = [NSApp runModalForWindow: o_panel];
380     [o_panel close];
381
382     if( i_result )
383     {
384         NSMutableDictionary *o_dic;
385         NSMutableArray *o_options = [NSMutableArray array];
386
387         o_dic = [NSMutableDictionary dictionaryWithObject: [self MRL] forKey: @"ITEM_URL"];
388         if( [o_file_sub_ckbox state] == NSOnState )
389         {
390             module_config_t * p_item;
391
392             [o_options addObject: [NSString stringWithFormat: @"sub-file=%@", o_sub_path]];
393             if( [o_file_sub_override state] == NSOnState )
394             {
395                 [o_options addObject: [NSString stringWithFormat: @"sub-delay=%i", (int)( [o_file_sub_delay intValue] * 10 )]];
396                 [o_options addObject: [NSString stringWithFormat: @"sub-fps=%f", [o_file_sub_fps floatValue]]];
397             }
398             [o_options addObject: [NSString stringWithFormat:
399                     @"subsdec-encoding=%@",
400                     [o_file_sub_encoding_pop titleOfSelectedItem]]];
401             [o_options addObject: [NSString stringWithFormat:
402                     @"subsdec-align=%i",
403                     [o_file_sub_align_pop indexOfSelectedItem]]];
404
405             p_item = config_FindConfig( VLC_OBJECT(p_intf),
406                                             "freetype-rel-fontsize" );
407
408             if ( p_item )
409             {
410                 [o_options addObject: [NSString stringWithFormat:
411                     @"freetype-rel-fontsize=%i",
412                     p_item->pi_list[[o_file_sub_size_pop indexOfSelectedItem]]]];
413             }
414         }
415         if( [o_output_ckbox state] == NSOnState )
416         {
417             NSUInteger count = [[o_sout_options mrl] count];
418             for (NSUInteger i = 0 ; i < count ; i++)
419             {
420                 [o_options addObject: [NSString stringWithString: [[(VLCOutput *)o_sout_options mrl] objectAtIndex: i]]];
421             }
422         }
423         if( [o_file_slave_ckbox state] && o_file_slave_path )
424            [o_options addObject: [NSString stringWithFormat: @"input-slave=%@", o_file_slave_path]];
425         if( [[[o_tabview selectedTabViewItem] label] isEqualToString: _NS("Capture")] )
426         {
427             if( [[[o_capture_mode_pop selectedItem] title] isEqualToString: _NS("Screen")] )
428             {
429                 [o_options addObject: [NSString stringWithFormat: @"screen-fps=%f", [o_screen_fps_fld floatValue]]];
430                 [o_options addObject: [NSString stringWithFormat: @"screen-left=%i", [o_screen_left_fld intValue]]];
431                 [o_options addObject: [NSString stringWithFormat: @"screen-top=%i", [o_screen_top_fld intValue]]];
432                 [o_options addObject: [NSString stringWithFormat: @"screen-width=%i", [o_screen_width_fld intValue]]];
433                 [o_options addObject: [NSString stringWithFormat: @"screen-height=%i", [o_screen_height_fld intValue]]];
434                 if( [o_screen_follow_mouse_ckb intValue] == YES )
435                     [o_options addObject: @"screen-follow-mouse"];
436                 else
437                     [o_options addObject: @"no-screen-follow-mouse"];
438             }
439             else if( [[[o_capture_mode_pop selectedItem] title] isEqualToString: _NS("Capture Device")] )
440             {
441                 [o_options addObject: [NSString stringWithFormat: @"qtcapture-width=%i", [o_capture_width_fld intValue]]];
442                 [o_options addObject: [NSString stringWithFormat: @"qtcapture-height=%i", [o_capture_height_fld intValue]]];
443             }
444         }
445
446         /* apply the options to our item(s) */
447         [o_dic setObject: (NSArray *)[o_options copy] forKey: @"ITEM_OPTIONS"];
448         if( b_autoplay )
449             [[[VLCMain sharedInstance] playlist] appendArray: [NSArray arrayWithObject: o_dic] atPos: -1 enqueue:NO];
450         else
451             [[[VLCMain sharedInstance] playlist] appendArray: [NSArray arrayWithObject: o_dic] atPos: -1 enqueue:YES];
452     }
453 }
454
455 - (IBAction)qtkChanged:(id)sender
456 {
457         NSInteger i_selectedDevice = [o_qtk_device_pop indexOfSelectedItem];
458         if (i_selectedDevice >= ([qtkvideoDevices count] - 1))
459         {
460                 NSValue *sizes = [[[[qtkvideoDevices objectAtIndex:i_selectedDevice] formatDescriptions] objectAtIndex: 0] attributeForKey: QTFormatDescriptionVideoEncodedPixelsSizeAttribute];
461
462                 [o_capture_width_fld setIntValue: [sizes sizeValue].width];
463                 [o_capture_height_fld setIntValue: [sizes sizeValue].height];
464                 [o_capture_width_stp setIntValue: [o_capture_width_fld intValue]];
465                 [o_capture_height_stp setIntValue: [o_capture_height_fld intValue]];
466                 qtk_currdevice_uid = [[(QTCaptureDevice *)[qtkvideoDevices objectAtIndex:i_selectedDevice] uniqueID]
467                                                           stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
468                 [self setMRL:[NSString stringWithFormat:@"qtcapture://%@", qtk_currdevice_uid]];
469         }
470 }
471
472 - (void)tabView:(NSTabView *)o_tv didSelectTabViewItem:(NSTabViewItem *)o_tvi
473 {
474     NSString *o_label = [o_tvi label];
475
476     if( [o_label isEqualToString: _NS("File")] )
477     {
478         [self openFilePathChanged: nil];
479     }
480     else if( [o_label isEqualToString: _NS("Disc")] )
481     {
482         [self scanOpticalMedia: nil];
483     }
484     else if( [o_label isEqualToString: _NS("Network")] )
485     {
486         [self openNetInfoChanged: nil];
487     }
488     else if( [o_label isEqualToString: _NS("Capture")] )
489     {
490         [self openCaptureModeChanged: nil];
491     }
492 }
493
494 - (IBAction)expandMRLfieldAction:(id)sender
495 {
496     NSRect o_win_rect, o_view_rect;
497     o_win_rect = [o_panel frame];
498     o_view_rect = [o_mrl_view frame];
499
500     if( [o_mrl_btn state] == NSOffState )
501     {
502         /* we need to collaps, restore the panel size */
503         o_win_rect.size.height = o_win_rect.size.height - o_view_rect.size.height;
504         o_win_rect.origin.y = ( o_win_rect.origin.y + o_view_rect.size.height ) - o_view_rect.size.height;
505
506         /* remove the MRL view */
507         [o_mrl_view removeFromSuperview];
508     } else {
509         /* we need to expand */
510         [o_mrl_view setFrame: NSMakeRect( 0,
511                                          [o_mrl_btn frame].origin.y,
512                                          o_view_rect.size.width,
513                                          o_view_rect.size.height )];
514         [o_mrl_view setNeedsDisplay: NO];
515         [o_mrl_view setAutoresizesSubviews: YES];
516
517         /* enlarge panel size for MRL view */
518         o_win_rect.size.height = o_win_rect.size.height + o_view_rect.size.height;
519     }
520
521     [[o_panel animator] setFrame: o_win_rect display:YES];
522 //    [o_panel displayIfNeeded];
523     if( [o_mrl_btn state] == NSOnState )
524         [[o_panel contentView] addSubview: o_mrl_view];
525 }
526
527 - (IBAction)inputSlaveAction:(id)sender
528 {
529     if( sender == o_file_slave_ckbox )
530         [o_file_slave_select_btn setEnabled: [o_file_slave_ckbox state]];
531     else
532     {
533         NSOpenPanel *o_open_panel;
534         o_open_panel = [NSOpenPanel openPanel];
535         [o_open_panel setCanChooseFiles: YES];
536         [o_open_panel setCanChooseDirectories: NO];
537         if( [o_open_panel runModal] == NSOKButton )
538         {
539             if( o_file_slave_path )
540                 [o_file_slave_path release];
541             o_file_slave_path = [[[o_open_panel URLs] objectAtIndex: 0] path];
542             [o_file_slave_path retain];
543         }
544     }
545     if( o_file_slave_path && [o_file_slave_ckbox state] == NSOnState)
546     {
547         [o_file_slave_filename_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath:o_file_slave_path]];
548         [o_file_slave_icon_well setImage: [[NSWorkspace sharedWorkspace] iconForFile: o_file_slave_path]];
549     }
550     else
551     {
552         [o_file_slave_filename_lbl setStringValue: @""];
553         [o_file_slave_icon_well setImage: NULL];
554     }
555 }
556
557 - (void)openFileGeneric
558 {
559     [self openFilePathChanged: nil];
560     [self openTarget: 0];
561 }
562
563 - (void)openDisc
564 {
565     [o_specialMediaFolders removeAllObjects];
566     [o_opticalDevices removeAllObjects];
567     [self scanOpticalMedia: nil];
568     [self openTarget: 1];
569 }
570
571 - (void)openNet
572 {
573     [self openNetInfoChanged: nil];
574     [self openTarget: 2];
575 }
576
577 - (void)openCapture
578 {
579     [self openCaptureModeChanged: nil];
580     [self openTarget: 3];
581 }
582
583 - (void)openFilePathChanged:(NSNotification *)o_notification
584 {
585     if ( o_file_path && [o_file_path length] > 0 )
586     {
587         bool b_stream = [o_file_stream state];
588         BOOL b_dir = NO;
589
590         [[NSFileManager defaultManager] fileExistsAtPath:o_file_path isDirectory:&b_dir];
591
592         char *psz_uri = make_URI([o_file_path UTF8String], "file");
593         if( !psz_uri ) return;
594
595         NSMutableString *o_mrl_string = [NSMutableString stringWithUTF8String: psz_uri ];
596         NSRange offile = [o_mrl_string rangeOfString:@"file"];
597         free( psz_uri );
598
599         if( b_dir )
600             [o_mrl_string replaceCharactersInRange:offile withString: @"directory"];
601         else if( b_stream )
602             [o_mrl_string replaceCharactersInRange:offile withString: @"stream"];
603
604         [o_file_name setStringValue: [[NSFileManager defaultManager] displayNameAtPath:o_file_path]];
605         [o_file_name_stub setHidden: YES];
606         [o_file_stream setHidden: NO];
607         [o_file_icon_well setImage: [[NSWorkspace sharedWorkspace] iconForFile: o_file_path]];
608         [o_file_icon_well setHidden: NO];
609         [self setMRL: o_mrl_string];
610     }
611     else
612     {
613         [o_file_name setStringValue: @""];
614         [o_file_name_stub setHidden: NO];
615         [o_file_stream setHidden: YES];
616         [o_file_icon_well setImage: [NSImage imageNamed:@"generic"]];
617         [self setMRL: @""];
618     }
619 }
620
621 - (IBAction)openFileBrowse:(id)sender
622 {
623     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
624  
625     [o_open_panel setAllowsMultipleSelection: NO];
626     [o_open_panel setCanChooseDirectories: YES];
627     [o_open_panel setTitle: _NS("Open File")];
628     [o_open_panel setPrompt: _NS("Open")];
629
630     [o_open_panel beginSheetForDirectory:nil
631         file:nil
632         types:nil
633         modalForWindow:[sender window]
634         modalDelegate: self
635         didEndSelector: @selector(pathChosenInPanel:
636                         withReturn:
637                         contextInfo:)
638         contextInfo: nil];
639 }
640
641 - (void)pathChosenInPanel: (NSOpenPanel *) sheet withReturn:(int)returnCode contextInfo:(void  *)contextInfo
642 {
643     if (returnCode == NSFileHandlingPanelOKButton)
644     {
645         if( o_file_path )
646             [o_file_path release];
647         o_file_path = [[[sheet URLs] objectAtIndex: 0] path];
648         [o_file_path retain];
649         [self openFilePathChanged: nil];
650     }
651 }
652
653 - (IBAction)openFileStreamChanged:(id)sender
654 {
655     [self openFilePathChanged: nil];
656 }
657
658 - (void)showOpticalMediaView: theView withIcon:(NSImage *)icon
659 {
660     NSRect o_view_rect;
661     o_view_rect = [theView frame];
662     [theView setFrame: NSMakeRect( 233, 0, o_view_rect.size.width, o_view_rect.size.height)];
663     [theView setAutoresizesSubviews: YES];
664     if (o_currentOpticalMediaView)
665     {
666         [[[[o_tabview tabViewItemAtIndex: [o_tabview indexOfTabViewItemWithIdentifier:@"optical"]] view] animator] replaceSubview: o_currentOpticalMediaView with: theView];
667         [o_currentOpticalMediaView release];
668     }
669     else
670         [[[[o_tabview tabViewItemAtIndex: [o_tabview indexOfTabViewItemWithIdentifier:@"optical"]] view] animator] addSubview: theView];
671     o_currentOpticalMediaView = theView;
672     [o_currentOpticalMediaView retain];
673
674     NSImageView *imageView;
675     imageView = [[NSImageView alloc] init];
676     [imageView setFrame: NSMakeRect( 53, 61, 128, 128 )];
677     [icon setSize: NSMakeSize(128,128)];
678     [imageView setImage: icon];
679     if (o_currentOpticalMediaIconView)
680     {
681         [[[[o_tabview tabViewItemAtIndex: [o_tabview indexOfTabViewItemWithIdentifier:@"optical"]] view] animator] replaceSubview: o_currentOpticalMediaIconView with: imageView];
682         [o_currentOpticalMediaIconView release];
683     }
684     else
685          [[[[o_tabview tabViewItemAtIndex: [o_tabview indexOfTabViewItemWithIdentifier:@"optical"]] view] animator] addSubview: imageView];
686     o_currentOpticalMediaIconView = imageView;
687     [o_currentOpticalMediaIconView retain];
688     [o_currentOpticalMediaView setNeedsDisplay: YES];
689     [o_currentOpticalMediaIconView setNeedsDisplay: YES];
690     [[[o_tabview tabViewItemAtIndex: [o_tabview indexOfTabViewItemWithIdentifier:@"optical"]] view] setNeedsDisplay: YES];
691     [[[o_tabview tabViewItemAtIndex: [o_tabview indexOfTabViewItemWithIdentifier:@"optical"]] view] displayIfNeeded];
692 }
693
694 - (NSString *) getBSDNodeFromMountPath:(NSString *)mountPath
695 {
696     OSStatus err;
697     FSRef ref;
698     FSVolumeRefNum actualVolume;
699     err = FSPathMakeRef ( (const UInt8 *) [mountPath fileSystemRepresentation], &ref, NULL );
700
701     // get a FSVolumeRefNum from mountPath
702     if ( noErr == err ) {
703         FSCatalogInfo   catalogInfo;
704         err = FSGetCatalogInfo ( &ref,
705                                 kFSCatInfoVolume,
706                                 &catalogInfo,
707                                 NULL,
708                                 NULL,
709                                 NULL
710                                 );
711         if ( noErr == err ) {
712             actualVolume = catalogInfo.volume;
713         }
714     }
715
716     GetVolParmsInfoBuffer volumeParms;
717     err = FSGetVolumeParms( actualVolume, &volumeParms, sizeof(volumeParms) );
718     if ( noErr != err ) {
719         msg_Err( p_intf, "error retrieving volume params, bailing out" );
720         return @"";
721     }
722
723     NSString *bsdName = [NSString stringWithUTF8String:(char *)volumeParms.vMDeviceID];
724     return [NSString stringWithFormat:@"/dev/r%@", bsdName];
725 }
726
727 - (char *)getVolumeTypeFromMountPath:(NSString *)mountPath
728 {
729     OSStatus err;
730     FSRef ref;
731     FSVolumeRefNum actualVolume;
732     err = FSPathMakeRef ( (const UInt8 *) [mountPath fileSystemRepresentation], &ref, NULL );
733
734     // get a FSVolumeRefNum from mountPath
735     if ( noErr == err ) {
736         FSCatalogInfo   catalogInfo;
737         err = FSGetCatalogInfo ( &ref,
738                                 kFSCatInfoVolume,
739                                 &catalogInfo,
740                                 NULL,
741                                 NULL,
742                                 NULL
743                                 );
744         if ( noErr == err ) {
745             actualVolume = catalogInfo.volume;
746         }
747     }
748
749     GetVolParmsInfoBuffer volumeParms;
750     err = FSGetVolumeParms( actualVolume, &volumeParms, sizeof(volumeParms) );
751
752     CFMutableDictionaryRef      matchingDict;
753     io_service_t                        service;
754
755     matchingDict = IOBSDNameMatching(kIOMasterPortDefault, 0, volumeParms.vMDeviceID);
756     service = IOServiceGetMatchingService(kIOMasterPortDefault, matchingDict);
757
758     char *returnValue;
759     if (IO_OBJECT_NULL != service) {
760         if (IOObjectConformsTo(service, kIOCDMediaClass)) {
761             returnValue = kVLCMediaAudioCD;
762         }
763         else if(IOObjectConformsTo(service, kIODVDMediaClass))
764             returnValue = kVLCMediaDVD;
765         else if(IOObjectConformsTo(service, kIOBDMediaClass))
766             returnValue = kVLCMediaBD;
767         else
768         {
769             if ([mountPath rangeOfString:@"VIDEO_TS"].location != NSNotFound)
770                 returnValue = kVLCMediaVideoTSFolder;
771             else if ([mountPath rangeOfString:@"BDMV"].location != NSNotFound)
772                 returnValue = kVLCMediaBDMVFolder;
773             else
774             {
775                 NSArray * topLevelItems;
776                 topLevelItems = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath: mountPath error: NULL];
777                 NSUInteger itemCount = [topLevelItems count];
778                 for (int i = 0; i < itemCount; i++) {
779                     if([[topLevelItems objectAtIndex:i] rangeOfString:@"SVCD"].location != NSNotFound) {
780                         returnValue = kVLCMediaSVCD;
781                         break;
782                     }
783                     if([[topLevelItems objectAtIndex:i] rangeOfString:@"VCD"].location != NSNotFound) {
784                         returnValue = kVLCMediaVCD;
785                         break;
786                     }
787                 }
788                 if(!returnValue)
789                     returnValue = kVLCMediaUnknown;
790             }
791         }
792
793         IOObjectRelease(service);
794     }
795     return returnValue;
796 }
797
798 - (void)showOpticalAtIndex: (NSNumber *)n_index
799 {
800     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
801
802     if (!o_opticalDevices)
803         return;
804
805     if ([o_opticalDevices count] == 0)
806         return;
807
808     unsigned int index = [n_index intValue];
809         id o_currentOpticalDevice = [o_opticalDevices objectAtIndex: index];
810     char *diskType = [self getVolumeTypeFromMountPath:o_currentOpticalDevice];
811
812     if (diskType == kVLCMediaDVD || diskType == kVLCMediaVideoTSFolder)
813     {
814         [o_disc_dvd_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath:o_currentOpticalDevice]];
815         [o_disc_dvdwomenus_lbl setStringValue: [o_disc_dvd_lbl stringValue]];
816         NSString *pathToOpen;
817         if (diskType == kVLCMediaVideoTSFolder)
818             pathToOpen = o_currentOpticalDevice;
819         else
820             pathToOpen = [self getBSDNodeFromMountPath: o_currentOpticalDevice];
821         if (!b_nodvdmenus) {
822             [self setMRL: [NSString stringWithFormat: @"dvdnav://%@", pathToOpen]];
823             [self showOpticalMediaView: o_disc_dvd_view withIcon: [[NSWorkspace sharedWorkspace] iconForFile: o_currentOpticalDevice]];
824         } else {
825             [self setMRL: [NSString stringWithFormat: @"dvdread://%@@%i:%i-", pathToOpen, [o_disc_dvdwomenus_title intValue], [o_disc_dvdwomenus_chapter intValue]]];
826             [self showOpticalMediaView: o_disc_dvdwomenus_view withIcon: [[NSWorkspace sharedWorkspace] iconForFile: o_currentOpticalDevice]];
827         }
828     }
829     else if (diskType == kVLCMediaAudioCD)
830     {
831         [o_disc_audiocd_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath: o_currentOpticalDevice]];
832         [o_disc_audiocd_trackcount_lbl setStringValue: [NSString stringWithFormat:_NS("%i tracks"), [[[NSFileManager defaultManager] subpathsOfDirectoryAtPath: o_currentOpticalDevice error:NULL] count] - 1]]; // minus .TOC.plist
833         [self showOpticalMediaView: o_disc_audiocd_view withIcon: [[NSWorkspace sharedWorkspace] iconForFile: o_currentOpticalDevice]];
834         [self setMRL: [NSString stringWithFormat: @"cdda://%@", [self getBSDNodeFromMountPath: o_currentOpticalDevice]]];
835     }
836     else if (diskType == kVLCMediaVCD)
837     {
838         [o_disc_vcd_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath: o_currentOpticalDevice]];
839         [self showOpticalMediaView: o_disc_vcd_view withIcon: [[NSWorkspace sharedWorkspace] iconForFile: o_currentOpticalDevice]];
840         [self setMRL: [NSString stringWithFormat: @"vcd://%@#%i:%i", [self getBSDNodeFromMountPath: o_currentOpticalDevice], [o_disc_vcd_title intValue], [o_disc_vcd_chapter intValue]]];
841     }
842     else if (diskType == kVLCMediaSVCD)
843     {
844         [o_disc_vcd_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath: o_currentOpticalDevice]];
845         [self showOpticalMediaView: o_disc_vcd_view withIcon: [[NSWorkspace sharedWorkspace] iconForFile: o_currentOpticalDevice]];
846         [self setMRL: [NSString stringWithFormat: @"vcd://%@@%i:%i", [self getBSDNodeFromMountPath: o_currentOpticalDevice], [o_disc_vcd_title intValue], [o_disc_vcd_chapter intValue]]];
847     }
848     else if (diskType == kVLCMediaBD || diskType == kVLCMediaBDMVFolder)
849     {
850         [o_disc_bd_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath: o_currentOpticalDevice]];
851         [self showOpticalMediaView: o_disc_bd_view withIcon: [[NSWorkspace sharedWorkspace] iconForFile: o_currentOpticalDevice]];
852         [self setMRL: [NSString stringWithFormat: @"bluray://%@", o_currentOpticalDevice]];
853     }
854     else
855     {
856         msg_Warn( VLCIntf, "unknown disk type, no idea what to display" );
857         [self showOpticalMediaView: o_disc_nodisc_view withIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
858     }
859
860     [o_pool release];
861 }
862
863 - (void)scanOpticalMedia:(NSNotification *)o_notification
864 {
865     [o_opticalDevices removeAllObjects];
866     [o_disc_selector_pop removeAllItems];
867     [o_opticalDevices addObjectsFromArray: [[NSWorkspace sharedWorkspace] mountedRemovableMedia]];
868     if ([o_specialMediaFolders count] > 0)
869         [o_opticalDevices addObjectsFromArray: o_specialMediaFolders];
870     if ([o_opticalDevices count] > 0) {
871         NSUInteger deviceCount = [o_opticalDevices count];
872         for (NSUInteger i = 0; i < deviceCount ; i++)
873             [o_disc_selector_pop addItemWithTitle: [[NSFileManager defaultManager] displayNameAtPath:[o_opticalDevices objectAtIndex: i]]];
874
875         if ([o_disc_selector_pop numberOfItems] <= 1)
876             [o_disc_selector_pop setHidden: YES];
877         else
878             [o_disc_selector_pop setHidden: NO];
879
880         [NSThread detachNewThreadSelector:@selector(showOpticalAtIndex:) toTarget:self withObject:[NSNumber numberWithInt:[o_disc_selector_pop indexOfSelectedItem]]];
881     }
882     else
883     {
884         msg_Dbg( VLCIntf, "no optical media found" );
885         [o_disc_selector_pop setHidden: YES];
886         [self showOpticalMediaView: o_disc_nodisc_view withIcon: [NSImage imageNamed: @"NSApplicationIcon"]];
887     }
888 }
889
890 - (IBAction)discSelectorChanged:(id)sender
891 {
892     [NSThread detachNewThreadSelector:@selector(showOpticalAtIndex:) toTarget:self withObject:[NSNumber numberWithInt:[o_disc_selector_pop indexOfSelectedItem]]];
893 }
894
895 - (IBAction)openSpecialMediaFolder:(id)sender
896 {
897     /* this is currently for VIDEO_TS and BDMV folders */
898     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
899
900     [o_open_panel setAllowsMultipleSelection: NO];
901     [o_open_panel setCanChooseFiles: NO];
902     [o_open_panel setCanChooseDirectories: YES];
903     [o_open_panel setTitle: [sender title]];
904     [o_open_panel setPrompt: _NS("Open")];
905
906     if ([o_open_panel runModal] == NSOKButton)
907     {
908         NSString *o_path = [[[o_open_panel URLs] objectAtIndex: 0] path];
909         if ([o_path length] > 0 )
910         {
911             if ([o_path rangeOfString:@"VIDEO_TS"].location != NSNotFound || [o_path rangeOfString:@"BDMV"].location != NSNotFound)
912             {
913                 [o_specialMediaFolders addObject: o_path];
914                 [self scanOpticalMedia: nil];
915             }
916             else
917                 msg_Dbg( VLCIntf, "chosen directory is no suitable special media folder" );
918         }
919     }
920 }
921
922 - (IBAction)dvdreadOptionChanged:(id)sender
923 {
924     if (sender == o_disc_dvdwomenus_enablemenus_btn) {
925         b_nodvdmenus = NO;
926         [self setMRL: [NSString stringWithFormat: @"dvdnav://%@", [self getBSDNodeFromMountPath:[o_opticalDevices objectAtIndex: [o_disc_selector_pop indexOfSelectedItem]]]]];
927         [self showOpticalMediaView: o_disc_dvd_view withIcon: [o_currentOpticalMediaIconView image]];
928         return;
929     }
930     if (sender == o_disc_dvd_disablemenus_btn) {
931         b_nodvdmenus = YES;
932         [self showOpticalMediaView: o_disc_dvdwomenus_view withIcon: [o_currentOpticalMediaIconView image]];
933     }
934
935     if (sender == o_disc_dvdwomenus_title)
936         [o_disc_dvdwomenus_title_stp setIntValue: [o_disc_dvdwomenus_title intValue]];
937     if (sender == o_disc_dvdwomenus_title_stp)
938         [o_disc_dvdwomenus_title setIntValue: [o_disc_dvdwomenus_title_stp intValue]];
939     if (sender == o_disc_dvdwomenus_chapter)
940         [o_disc_dvdwomenus_chapter_stp setIntValue: [o_disc_dvdwomenus_chapter intValue]];
941     if (sender == o_disc_dvdwomenus_chapter_stp)
942         [o_disc_dvdwomenus_chapter setIntValue: [o_disc_dvdwomenus_chapter_stp intValue]];
943
944     [self setMRL: [NSString stringWithFormat: @"dvdread://%@@%i:%i-", [self getBSDNodeFromMountPath:[o_opticalDevices objectAtIndex: [o_disc_selector_pop indexOfSelectedItem]]], [o_disc_dvdwomenus_title intValue], [o_disc_dvdwomenus_chapter intValue]]];
945 }
946
947 - (IBAction)vcdOptionChanged:(id)sender
948 {
949     if (sender == o_disc_vcd_title)
950         [o_disc_vcd_title_stp setIntValue: [o_disc_vcd_title intValue]];
951     if (sender == o_disc_vcd_title_stp)
952         [o_disc_vcd_title setIntValue: [o_disc_vcd_title_stp intValue]];
953     if (sender == o_disc_vcd_chapter)
954         [o_disc_vcd_chapter_stp setIntValue: [o_disc_vcd_chapter intValue]];
955     if (sender == o_disc_vcd_chapter_stp)
956         [o_disc_vcd_chapter setIntValue: [o_disc_vcd_chapter_stp intValue]];
957
958     [self setMRL: [NSString stringWithFormat: @"vcd://%@@%i:%i", [self getBSDNodeFromMountPath:[o_opticalDevices objectAtIndex: [o_disc_selector_pop indexOfSelectedItem]]], [o_disc_vcd_title intValue], [o_disc_vcd_chapter intValue]]];
959 }
960
961 - (void)textFieldWasClicked:(NSNotification *)o_notification
962 {
963     if( [o_notification object] == o_net_udp_port )
964         [o_net_mode selectCellAtRow: 0 column: 0];
965     else if( [o_notification object] == o_net_udpm_addr ||
966              [o_notification object] == o_net_udpm_port )
967         [o_net_mode selectCellAtRow: 1 column: 0];
968     else
969         [o_net_mode selectCellAtRow: 2 column: 0];
970
971     [self openNetInfoChanged: nil];
972 }
973
974 - (IBAction)openNetModeChanged:(id)sender
975 {
976     if( sender == o_net_mode )
977     {
978         if( [[sender selectedCell] tag] == 0 )
979             [o_panel makeFirstResponder: o_net_udp_port];
980         else if ( [[sender selectedCell] tag] == 1 )
981             [o_panel makeFirstResponder: o_net_udpm_addr];
982         else
983             msg_Warn( p_intf, "Unknown sender tried to change UDP/RTP mode" );
984     }
985
986     [self openNetInfoChanged: nil];
987 }
988
989 - (IBAction)openNetStepperChanged:(id)sender
990 {
991     int i_tag = [sender tag];
992
993     if( i_tag == 0 )
994     {
995         [o_net_udp_port setIntValue: [o_net_udp_port_stp intValue]];
996         [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCOpenTextFieldWasClicked"
997                                                             object: o_net_udp_port];
998         [o_panel makeFirstResponder: o_net_udp_port];
999     }
1000     else if( i_tag == 1 )
1001     {
1002         [o_net_udpm_port setIntValue: [o_net_udpm_port_stp intValue]];
1003         [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCOpenTextFieldWasClicked"
1004                                                             object: o_net_udpm_port];
1005         [o_panel makeFirstResponder: o_net_udpm_port];
1006     }
1007
1008     [self openNetInfoChanged: nil];
1009 }
1010
1011 - (void)openNetInfoChanged:(NSNotification *)o_notification
1012 {
1013     NSString *o_mrl_string = [NSString string];
1014
1015     if( [o_net_udp_panel isVisible] )
1016     {
1017         NSString *o_mode;
1018         o_mode = [[o_net_mode selectedCell] title];
1019
1020         if( [o_mode isEqualToString: _NS("Unicast")] )
1021         {
1022             int i_port = [o_net_udp_port intValue];
1023
1024             if( [[o_net_udp_protocol_mat selectedCell] tag] == 0 )
1025                 o_mrl_string = [NSString stringWithString: @"udp://"];
1026             else
1027                 o_mrl_string = [NSString stringWithString: @"rtp://"];
1028
1029             if( i_port != config_GetInt( p_intf, "server-port" ) )
1030             {
1031                 o_mrl_string =
1032                     [o_mrl_string stringByAppendingFormat: @"@:%i", i_port];
1033             }
1034         }
1035         else if( [o_mode isEqualToString: _NS("Multicast")] )
1036         {
1037             NSString *o_addr = [o_net_udpm_addr stringValue];
1038             int i_port = [o_net_udpm_port intValue];
1039
1040             if( [[o_net_udp_protocol_mat selectedCell] tag] == 0 )
1041                 o_mrl_string = [NSString stringWithFormat: @"udp://@%@", o_addr];
1042             else
1043                 o_mrl_string = [NSString stringWithFormat: @"rtp://@%@", o_addr];
1044
1045             if( i_port != config_GetInt( p_intf, "server-port" ) )
1046             {
1047                 o_mrl_string =
1048                     [o_mrl_string stringByAppendingFormat: @":%i", i_port];
1049             }
1050         }
1051     }
1052     else
1053     {
1054         o_mrl_string = [o_net_http_url stringValue];
1055     }
1056     [self setMRL: o_mrl_string];
1057 }
1058
1059 - (IBAction)openNetUDPButtonAction:(id)sender
1060 {
1061     if( sender == o_net_openUDP_btn )
1062     {
1063         [NSApp beginSheet: o_net_udp_panel
1064            modalForWindow: o_panel
1065             modalDelegate: self
1066            didEndSelector: NULL
1067               contextInfo: nil];
1068         [self openNetInfoChanged: nil];
1069     }
1070     else if( sender == o_net_udp_cancel_btn )
1071     {
1072         [o_net_udp_panel orderOut: sender];
1073         [NSApp endSheet: o_net_udp_panel];
1074     }
1075     else if( sender == o_net_udp_ok_btn )
1076     {
1077         NSString *o_mrl_string = [NSString string];
1078         if( [[[o_net_mode selectedCell] title] isEqualToString: _NS("Unicast")] )
1079         {
1080             int i_port = [o_net_udp_port intValue];
1081             
1082             if( [[o_net_udp_protocol_mat selectedCell] tag] == 0 )
1083                 o_mrl_string = [NSString stringWithString: @"udp://"];
1084             else
1085                 o_mrl_string = [NSString stringWithString: @"rtp://"];
1086
1087             if( i_port != config_GetInt( p_intf, "server-port" ) )
1088             {
1089                 o_mrl_string =
1090                 [o_mrl_string stringByAppendingFormat: @"@:%i", i_port];
1091             }
1092         }
1093         else if( [[[o_net_mode selectedCell] title] isEqualToString: _NS("Multicast")] )
1094         {
1095             NSString *o_addr = [o_net_udpm_addr stringValue];
1096             int i_port = [o_net_udpm_port intValue];
1097             
1098             if( [[o_net_udp_protocol_mat selectedCell] tag] == 0 )
1099                 o_mrl_string = [NSString stringWithFormat: @"udp://@%@", o_addr];
1100             else
1101                 o_mrl_string = [NSString stringWithFormat: @"rtp://@%@", o_addr];
1102
1103             if( i_port != config_GetInt( p_intf, "server-port" ) )
1104             {
1105                 o_mrl_string =
1106                 [o_mrl_string stringByAppendingFormat: @":%i", i_port];
1107             }
1108         }
1109         [self setMRL: o_mrl_string];
1110         [o_net_http_url setStringValue: o_mrl_string];
1111         [o_net_udp_panel orderOut: sender];
1112         [NSApp endSheet: o_net_udp_panel];
1113     }
1114 }
1115     
1116 - (void)openFile
1117 {
1118     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
1119     b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
1120
1121     [o_open_panel setAllowsMultipleSelection: YES];
1122     [o_open_panel setCanChooseDirectories: YES];
1123     [o_open_panel setTitle: _NS("Open File")];
1124     [o_open_panel setPrompt: _NS("Open")];
1125  
1126     if( [o_open_panel runModal] == NSOKButton )
1127     {
1128         NSArray * o_urls = [o_open_panel URLs];
1129         NSUInteger count = [o_urls count];
1130         NSMutableArray *o_values = [NSMutableArray arrayWithCapacity:count];
1131         NSMutableArray *o_array = [NSMutableArray arrayWithCapacity:count];
1132         for( NSUInteger i = 0; i < count; i++ )
1133         {
1134             [o_values addObject: [[o_urls objectAtIndex: i] path]];
1135         }
1136         [o_values sortUsingSelector:@selector(caseInsensitiveCompare:)];
1137
1138         for( NSUInteger i = 0; i < count; i++ )
1139         {
1140             NSDictionary *o_dic;
1141             char *psz_uri = make_URI([[o_values objectAtIndex:i] UTF8String], "file");
1142             if( !psz_uri )
1143                 continue;
1144
1145             o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
1146
1147             free( psz_uri );
1148
1149             [o_array addObject: o_dic];
1150         }
1151         if( b_autoplay )
1152             [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO];
1153         else
1154             [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:YES];
1155     }
1156 }
1157
1158 - (void)showCaptureView: theView
1159 {
1160     NSRect o_view_rect;
1161     o_view_rect = [theView frame];
1162     [theView setFrame: NSMakeRect( 0, -10, o_view_rect.size.width, o_view_rect.size.height)];
1163     [theView setAutoresizesSubviews: YES];
1164     if (o_currentCaptureView)
1165     {
1166         [[[[o_tabview tabViewItemAtIndex: 3] view] animator] replaceSubview: o_currentCaptureView with: theView];
1167         [o_currentCaptureView release];
1168     }
1169     else
1170          [[[[o_tabview tabViewItemAtIndex: 3] view] animator] addSubview: theView];
1171     o_currentCaptureView = theView;
1172     [o_currentCaptureView retain];
1173 }
1174
1175 - (IBAction)openCaptureModeChanged:(id)sender
1176 {
1177     if( [[[o_capture_mode_pop selectedItem] title] isEqualToString: @"EyeTV"] )
1178     {
1179         if( [[[VLCMain sharedInstance] eyeTVController] isEyeTVrunning] == YES )
1180         {
1181             if( [[[VLCMain sharedInstance] eyeTVController] isDeviceConnected] == YES )
1182             {
1183                 [self showCaptureView: o_eyetv_running_view];
1184                 [self setupChannelInfo];
1185             }
1186             else
1187             {
1188                 setEyeTVUnconnected;
1189             }
1190         }
1191         else
1192             [self showCaptureView: o_eyetv_notLaunched_view];
1193         [self setMRL: @""];
1194     } 
1195     else if( [[[o_capture_mode_pop selectedItem] title] isEqualToString: _NS("Screen")] )
1196     {
1197         [self showCaptureView: o_screen_view];
1198         [self setMRL: @"screen://"];
1199         [o_screen_height_fld setIntValue: config_GetInt( p_intf, "screen-height" )];
1200         [o_screen_width_fld setIntValue: config_GetInt( p_intf, "screen-width" )];
1201         [o_screen_fps_fld setFloatValue: config_GetFloat( p_intf, "screen-fps" )];
1202         [o_screen_left_fld setIntValue: config_GetInt( p_intf, "screen-left" )];
1203         [o_screen_top_fld setIntValue: config_GetInt( p_intf, "screen-top" )];
1204         [o_screen_follow_mouse_ckb setIntValue: config_GetInt( p_intf, "screen-follow-mouse" )];
1205     }
1206     else if( [[[o_capture_mode_pop selectedItem] title] isEqualToString: _NS("Capture Device")] )
1207     {
1208         [self showCaptureView: o_qtk_view];
1209         if ([o_capture_width_fld intValue] <= 0)
1210             [self qtkChanged:nil];
1211
1212         if(!qtk_currdevice_uid)
1213             [self setMRL: @""];
1214         else
1215             [self setMRL:[NSString stringWithFormat:@"qtcapture://%@", qtk_currdevice_uid]];
1216     }
1217 }
1218
1219 - (IBAction)screenStepperChanged:(id)sender
1220 {
1221     [o_screen_fps_fld setFloatValue: [o_screen_fps_stp floatValue]];
1222     [o_panel makeFirstResponder: o_screen_fps_fld];
1223     [self setMRL: @"screen://"];
1224 }
1225
1226 - (void)screenFPSfieldChanged:(NSNotification *)o_notification
1227 {
1228     [o_screen_fps_stp setFloatValue: [o_screen_fps_fld floatValue]];
1229     if( [[o_screen_fps_fld stringValue] isEqualToString: @""] )
1230         [o_screen_fps_fld setFloatValue: 1.0];
1231     [self setMRL: @"screen://"];
1232 }
1233
1234 - (IBAction)eyetvSwitchChannel:(id)sender
1235 {
1236     if( sender == o_eyetv_nextProgram_btn )
1237     {
1238         int chanNum = [[[VLCMain sharedInstance] eyeTVController] switchChannelUp: YES];
1239         [o_eyetv_channels_pop selectItemWithTag:chanNum];
1240         [self setMRL: [NSString stringWithFormat:@"eyetv:// :eyetv-channel=%d", chanNum]];
1241     }
1242     else if( sender == o_eyetv_previousProgram_btn )
1243     {
1244         int chanNum = [[[VLCMain sharedInstance] eyeTVController] switchChannelUp: NO];
1245         [o_eyetv_channels_pop selectItemWithTag:chanNum];
1246         [self setMRL: [NSString stringWithFormat:@"eyetv:// :eyetv-channel=%d", chanNum]];
1247     }
1248     else if( sender == o_eyetv_channels_pop )
1249     {
1250         int chanNum = [[sender selectedItem] tag];
1251         [[[VLCMain sharedInstance] eyeTVController] selectChannel:chanNum];
1252         [self setMRL: [NSString stringWithFormat:@"eyetv:// :eyetv-channel=%d", chanNum]];
1253     }
1254     else
1255         msg_Err( VLCIntf, "eyetvSwitchChannel sent by unknown object" );
1256 }
1257
1258 - (IBAction)eyetvLaunch:(id)sender
1259 {
1260     [[[VLCMain sharedInstance] eyeTVController] launchEyeTV];
1261 }
1262
1263 - (IBAction)eyetvGetPlugin:(id)sender
1264 {
1265     [[NSWorkspace sharedWorkspace] openURL: [NSURL URLWithString: @"http://www.videolan.org/vlc/eyetv"]];
1266 }
1267
1268 - (void)eyetvChanged:(NSNotification *)o_notification
1269 {
1270     if( [[o_notification name] isEqualToString: @"DeviceAdded"] )
1271     {
1272         msg_Dbg( VLCIntf, "eyetv device was added" );
1273         [self showCaptureView: o_eyetv_running_view];
1274         [self setupChannelInfo];
1275     }
1276     else if( [[o_notification name] isEqualToString: @"DeviceRemoved"] )
1277     {
1278         /* leave the channel selection like that,
1279          * switch to our "no device" tab */
1280         msg_Dbg( VLCIntf, "eyetv device was removed" );
1281         setEyeTVUnconnected;
1282     }
1283     else if( [[o_notification name] isEqualToString: @"PluginQuit"] )
1284     {
1285         /* switch to the "launch eyetv" tab */
1286         msg_Dbg( VLCIntf, "eyetv was terminated" );
1287         [self showCaptureView: o_eyetv_notLaunched_view];
1288     }
1289     else if( [[o_notification name] isEqualToString: @"PluginInit"] )
1290     {
1291         /* we got no device yet */
1292         msg_Dbg( VLCIntf, "eyetv was launched, no device yet" );
1293         setEyeTVUnconnected;
1294     }
1295 }    
1296
1297 /* little helper method, since this code needs to be run by multiple objects */
1298 - (void)setupChannelInfo
1299 {
1300     /* set up channel selection */
1301     [o_eyetv_channels_pop removeAllItems];
1302     [o_eyetv_chn_bgbar setHidden: NO];
1303     [o_eyetv_chn_bgbar animate: self];
1304     [o_eyetv_chn_status_txt setStringValue: _NS("Retrieving Channel Info...")];
1305     [o_eyetv_chn_status_txt setHidden: NO];
1306  
1307     /* retrieve info */
1308     NSEnumerator *channels = [[[VLCMain sharedInstance] eyeTVController] allChannels];
1309     int x = -2;
1310     [[[o_eyetv_channels_pop menu] addItemWithTitle: _NS("Composite input")
1311                                                action: nil
1312                                         keyEquivalent: @""] setTag:x++];
1313     [[[o_eyetv_channels_pop menu] addItemWithTitle: _NS("S-Video input")
1314                                                action: nil
1315                                         keyEquivalent: @""] setTag:x++];
1316     if( channels ) 
1317     {
1318         NSString *channel;
1319         [[o_eyetv_channels_pop menu] addItem: [NSMenuItem separatorItem]];
1320         while( channel = [channels nextObject] )
1321         {
1322             /* we have to add items this way, because we accept duplicates
1323              * additionally, we save a bit of time */
1324             [[[o_eyetv_channels_pop menu] addItemWithTitle: channel
1325                                                    action: nil
1326                                             keyEquivalent: @""] setTag:++x];
1327         }
1328         /* make Tuner the default */
1329         [o_eyetv_channels_pop selectItemWithTag:[[[VLCMain sharedInstance] eyeTVController] currentChannel]];
1330     }
1331  
1332     /* clean up GUI */
1333     [o_eyetv_chn_bgbar setHidden: YES];
1334     [o_eyetv_chn_status_txt setHidden: YES];
1335 }
1336
1337 - (IBAction)subsChanged:(id)sender
1338 {
1339     if ([o_file_sub_ckbox state] == NSOnState)
1340     {
1341         [o_file_sub_btn_settings setEnabled:YES];
1342         if (o_sub_path) {
1343             [o_file_subtitles_filename_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath:o_sub_path]];
1344             [o_file_subtitles_icon_well setImage: [[NSWorkspace sharedWorkspace] iconForFile:o_sub_path]];
1345         }
1346     }
1347     else
1348     {
1349         [o_file_sub_btn_settings setEnabled:NO];
1350         [o_file_subtitles_filename_lbl setStringValue: @""];
1351         [o_file_subtitles_icon_well setImage: NULL];
1352     }
1353 }
1354
1355 - (IBAction)subSettings:(id)sender
1356 {
1357     [NSApp beginSheet: o_file_sub_sheet
1358         modalForWindow: [sender window]
1359         modalDelegate: self
1360         didEndSelector: NULL
1361         contextInfo: nil];
1362 }
1363
1364 - (IBAction)subCloseSheet:(id)sender
1365 {
1366     [self subsChanged: nil];
1367     [o_file_sub_sheet orderOut:sender];
1368     [NSApp endSheet: o_file_sub_sheet];
1369 }
1370     
1371 - (IBAction)subFileBrowse:(id)sender
1372 {
1373     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
1374  
1375     [o_open_panel setAllowsMultipleSelection: NO];
1376     [o_open_panel setTitle: _NS("Open File")];
1377     [o_open_panel setPrompt: _NS("Open")];
1378
1379     if( [o_open_panel runModal] == NSOKButton )
1380     {
1381         o_sub_path = [[[o_open_panel URLs] objectAtIndex: 0] path];
1382         [o_sub_path retain];
1383         [o_file_subtitles_filename_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath:o_sub_path]];
1384         [o_file_sub_path_fld setStringValue: [o_file_subtitles_filename_lbl stringValue]];
1385         [o_file_sub_path_lbl setHidden: YES];
1386         [o_file_subtitles_icon_well setImage: [[NSWorkspace sharedWorkspace] iconForFile:o_sub_path]];
1387         [o_file_sub_icon_view setImage: [o_file_subtitles_icon_well image]];
1388     }
1389     else
1390     {
1391         [o_file_sub_path_lbl setHidden: NO];
1392         [o_file_sub_path_fld setStringValue:@""];
1393         [o_file_subtitles_filename_lbl setStringValue:@""];
1394         [o_file_subtitles_icon_well setImage: nil];
1395         [o_file_sub_icon_view setImage: nil];
1396     }
1397 }
1398
1399 - (IBAction)subOverride:(id)sender
1400 {
1401     BOOL b_state = [o_file_sub_override state];
1402     [o_file_sub_delay setEnabled: b_state];
1403     [o_file_sub_delay_stp setEnabled: b_state];
1404     [o_file_sub_fps setEnabled: b_state];
1405     [o_file_sub_fps_stp setEnabled: b_state];
1406 }
1407
1408 - (IBAction)subDelayStepperChanged:(id)sender
1409 {
1410     [o_file_sub_delay setIntValue: [o_file_sub_delay_stp intValue]];
1411 }
1412
1413 - (IBAction)subFpsStepperChanged:(id)sender;
1414 {
1415     [o_file_sub_fps setFloatValue: [o_file_sub_fps_stp floatValue]];
1416 }
1417
1418 - (IBAction)panelCancel:(id)sender
1419 {
1420     [NSApp stopModalWithCode: 0];
1421 }
1422
1423 - (IBAction)panelOk:(id)sender
1424 {
1425     if( [[self MRL] length] )
1426         [NSApp stopModalWithCode: 1];
1427     else
1428         NSBeep();
1429 }
1430
1431 - (NSArray *)qtkvideoDevices
1432 {
1433     if (!qtkvideoDevices)
1434         [self qtkrefreshDevices];
1435     return qtkvideoDevices;
1436 }
1437
1438 - (void)qtkrefreshDevices
1439 {
1440     [qtkvideoDevices release];
1441     qtkvideoDevices = [[[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeVideo] arrayByAddingObjectsFromArray:[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeMuxed]] retain];
1442 }
1443
1444 @end
1445
1446 @implementation VLCOpenTextField
1447
1448 - (void)mouseDown:(NSEvent *)theEvent
1449 {
1450     [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCOpenTextFieldWasClicked"
1451                                                         object: self];
1452     [super mouseDown: theEvent];
1453 }
1454
1455 @end