]> git.sesse.net Git - vlc/blob - modules/gui/macosx/open.m
macosx: minor additions in optical media scanning code
[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 NSArray *qtkaudioDevices;
55 #define setEyeTVUnconnected \
56 [o_capture_lbl setStringValue: _NS("No device is selected")]; \
57 [o_capture_long_lbl setStringValue: _NS("No device is selected.\n\nChoose available device in above pull-down menu.\n")]; \
58 [o_capture_lbl displayIfNeeded]; \
59 [o_capture_long_lbl displayIfNeeded]; \
60 [self showCaptureView: o_capture_label_view]
61
62 struct display_info_t
63 {
64     CGRect rect;
65     CGDirectDisplayID id;
66 };
67
68 /*****************************************************************************
69  * VLCOpen implementation
70  *****************************************************************************/
71 @implementation VLCOpen
72
73 #pragma mark -
74 #pragma mark Init
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_allMediaDevices release];
98     [o_specialMediaFolders release];
99     if (o_opticalDevices)
100         [o_opticalDevices release];
101     if( o_file_slave_path )
102         [o_file_slave_path release];
103     [o_mrl release];
104     if (o_sub_path)
105         [o_sub_path release];
106     [o_currentOpticalMediaIconView release];
107     [o_currentOpticalMediaView release];
108     int i;
109     for( i = 0; i < [o_displayInfos count]; i ++ )
110     {
111         NSValue *v = [o_displayInfos objectAtIndex:i];
112         free( [v pointerValue] );
113     }
114     [o_displayInfos removeAllObjects];
115     [o_displayInfos release];
116
117     [super dealloc];
118 }
119
120 - (void)awakeFromNib
121 {
122     if (OSX_LION)
123         [o_panel setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary];
124
125     [o_panel setTitle: _NS("Open Source")];
126     [o_mrl_lbl setStringValue: _NS("Media Resource Locator (MRL)")];
127
128     [o_btn_ok setTitle: _NS("Open")];
129     [o_btn_cancel setTitle: _NS("Cancel")];
130
131     [[o_tabview tabViewItemAtIndex: 0] setLabel: _NS("File")];
132     [o_tabview accessibilitySetOverrideValue:_NS("4 Tabs to choose between media input. Select 'File' for files, 'Disc' for optical media such as DVDs, Audio CDs or BRs, 'Network' for network streams or 'Capture' for Input Devices such as microphones or cameras, the current screen or TV streams if the EyeTV application is installed.") forAttribute:NSAccessibilityDescriptionAttribute];
133     [[o_tabview tabViewItemAtIndex: 1] setLabel: _NS("Disc")];
134     [[o_tabview tabViewItemAtIndex: 2] setLabel: _NS("Network")];
135     [[o_tabview tabViewItemAtIndex: 3] setLabel: _NS("Capture")];
136     [o_file_name setStringValue: @""];
137     [o_file_name_stub setStringValue: _NS("Choose a file")];
138     [o_file_icon_well setImage: [NSImage imageNamed:@"generic"]];
139     [o_file_btn_browse setTitle: _NS("Browse...")];
140     [[o_file_btn_browse cell] accessibilitySetOverrideValue:_NS("Click to select a file for playback") forAttribute:NSAccessibilityDescriptionAttribute];
141     [o_file_stream setTitle: _NS("Treat as a pipe rather than as a file")];
142     [o_file_stream setHidden: NO];
143     [o_file_slave_ckbox setTitle: _NS("Play another media synchronously")];
144     [o_file_slave_select_btn setTitle: _NS("Choose...")];
145     [[o_file_btn_browse cell] accessibilitySetOverrideValue:_NS("Click to select a another file to play it in sync with the previously selected file.") forAttribute:NSAccessibilityDescriptionAttribute];
146     [o_file_slave_filename_lbl setStringValue: @""];
147     [o_file_slave_icon_well setImage: NULL];
148     [o_file_subtitles_filename_lbl setStringValue: @""];
149     [o_file_subtitles_icon_well setImage: NULL];
150     [o_file_custom_timing_ckb setTitle: _NS("Custom playback")];
151     [o_file_starttime_lbl setStringValue: _NS("Start time")];
152     [o_file_starttime_fld setStringValue: @""];
153     [o_file_stoptime_lbl setStringValue: _NS("Stop time")];
154     [o_file_stoptime_fld setStringValue: @""];
155
156     [o_disc_selector_pop removeAllItems];
157     [o_disc_selector_pop setHidden: NO];
158     NSString *o_videots = _NS("Open VIDEO_TS folder");
159     NSString *o_bdmv = _NS("Open BDMV folder");
160     [o_disc_nodisc_lbl setStringValue: _NS("Insert Disc")];
161     [o_disc_nodisc_videots_btn setTitle: o_videots];
162     [o_disc_nodisc_bdmv_btn setTitle: o_bdmv];
163     [o_disc_audiocd_lbl setStringValue: _NS("Audio CD")];
164     [o_disc_audiocd_trackcount_lbl setStringValue: @""];
165     [o_disc_audiocd_videots_btn setTitle: o_videots];
166     [o_disc_audiocd_bdmv_btn setTitle: o_bdmv];
167     [o_disc_dvd_lbl setStringValue: @""];
168     [o_disc_dvd_disablemenus_btn setTitle: _NS("Disable DVD menus")];
169     [o_disc_dvd_videots_btn setTitle: o_videots];
170     [o_disc_dvd_bdmv_btn setTitle: o_bdmv];
171     [o_disc_dvdwomenus_lbl setStringValue: @""];
172     [o_disc_dvdwomenus_enablemenus_btn setTitle: _NS("Enable DVD menus")];
173     [o_disc_dvdwomenus_videots_btn setTitle: o_videots];
174     [o_disc_dvdwomenus_bdmv_btn setTitle: o_bdmv];
175     [o_disc_dvdwomenus_title_lbl setStringValue: _NS("Title")];
176     [o_disc_dvdwomenus_chapter_lbl setStringValue: _NS("Chapter")];
177     [o_disc_vcd_title_lbl setStringValue: _NS("Title")];
178     [o_disc_vcd_chapter_lbl setStringValue: _NS("Chapter")];
179     [o_disc_vcd_videots_btn setTitle: o_videots];
180     [o_disc_vcd_bdmv_btn setTitle: o_bdmv];
181     [o_disc_bd_videots_btn setTitle: o_videots];
182     [o_disc_bd_bdmv_btn setTitle: o_bdmv];
183
184     [o_net_udp_port_lbl setStringValue: _NS("Port")];
185     [o_net_udpm_addr_lbl setStringValue: _NS("IP Address")];
186     [o_net_udpm_port_lbl setStringValue: _NS("Port")];
187     [o_net_http_url_lbl setStringValue: _NS("URL")];
188     [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.")];
189     [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.")];
190     [[o_net_http_url cell] accessibilitySetOverrideValue:_NS("Enter a URL here to open the network stream. To open RTP or UDP streams, click on the respective button below.") forAttribute:NSAccessibilityDescriptionAttribute];
191     [o_net_udp_cancel_btn setTitle: _NS("Cancel")];
192     [o_net_udp_ok_btn setTitle: _NS("Open")];
193     [o_net_openUDP_btn setTitle: _NS("Open RTP/UDP Stream")];
194     [o_net_udp_mode_lbl setStringValue: _NS("Mode")];
195     [o_net_udp_protocol_lbl setStringValue: _NS("Protocol")];
196     [o_net_udp_address_lbl setStringValue: _NS("Address")];
197
198     [[o_net_mode cellAtRow:0 column:0] setTitle: _NS("Unicast")];
199     [[o_net_mode cellAtRow:1 column:0] setTitle: _NS("Multicast")];
200
201     [o_net_udp_port setIntValue: config_GetInt( p_intf, "server-port" )];
202     [o_net_udp_port_stp setIntValue: config_GetInt( p_intf, "server-port" )];
203
204     [o_eyetv_chn_bgbar setUsesThreadedAnimation: YES];
205
206     [o_capture_mode_pop removeAllItems];
207     [o_capture_mode_pop addItemWithTitle: _NS("Input Devices")];
208     [o_capture_mode_pop addItemWithTitle: _NS("Screen")];
209     [o_capture_mode_pop addItemWithTitle: @"EyeTV"];
210     [o_screen_long_lbl setStringValue: _NS("This input allows you to save, stream or display your current screen contents.")];
211     [o_screen_fps_lbl setStringValue: _NS("Frames per Second:")];
212     [o_screen_screen_lbl setStringValue: _NS("Screen:")];
213     [o_screen_left_lbl setStringValue: _NS("Subscreen left:")];
214     [o_screen_top_lbl setStringValue: _NS("Subscreen top:")];
215     [o_screen_width_lbl setStringValue: _NS("Subscreen width:")];
216     [o_screen_height_lbl setStringValue: _NS("Subscreen height:")];
217     [o_screen_follow_mouse_ckb setTitle: _NS("Follow the mouse")];
218     [o_screen_qtk_audio_ckb setTitle: _NS("Capture Audio")];
219     [o_eyetv_currentChannel_lbl setStringValue: _NS("Current channel:")];
220     [o_eyetv_previousProgram_btn setTitle: _NS("Previous Channel")];
221     [o_eyetv_nextProgram_btn setTitle: _NS("Next Channel")];
222     [o_eyetv_chn_status_txt setStringValue: _NS("Retrieving Channel Info...")];
223     [o_eyetv_noInstance_lbl setStringValue: _NS("EyeTV is not launched")];
224     [o_eyetv_noInstanceLong_lbl setStringValue: _NS("VLC could not connect to EyeTV.\nMake sure that you installed VLC's EyeTV plugin.")];
225     [o_eyetv_launchEyeTV_btn setTitle: _NS("Launch EyeTV now")];
226     [o_eyetv_getPlugin_btn setTitle: _NS("Download Plugin")];
227     [o_capture_width_lbl setStringValue: _NS("Image width:")];
228     [o_capture_height_lbl setStringValue: _NS("Image height:")];
229
230     [self qtkvideoDevices];
231     [o_qtk_video_device_pop removeAllItems];
232     msg_Dbg( VLCIntf, "Found %lu video capture devices", [qtkvideoDevices count] );
233
234     if([qtkvideoDevices count] >= 1)
235     {
236         if (!qtk_currdevice_uid) {
237             qtk_currdevice_uid = [[[QTCaptureDevice defaultInputDeviceWithMediaType: QTMediaTypeVideo] uniqueID]
238                                                                 stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
239         }
240         NSUInteger deviceCount = [qtkvideoDevices count];
241         for(int ivideo = 0; ivideo < deviceCount; ivideo++){
242             QTCaptureDevice *qtk_device;
243             qtk_device = [qtkvideoDevices objectAtIndex:ivideo];
244             [o_qtk_video_device_pop addItemWithTitle: [qtk_device localizedDisplayName]];
245             if([[[qtk_device uniqueID]stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] isEqualToString:qtk_currdevice_uid]){
246                 [o_qtk_video_device_pop selectItemAtIndex:ivideo];
247             }
248         }
249     }
250     else
251     {
252         [o_qtk_video_device_pop addItemWithTitle: _NS("None")];
253         [qtk_currdevice_uid release];
254     }
255
256     [self qtkaudioDevices];
257     [o_qtk_audio_device_pop removeAllItems];
258     [o_screen_qtk_audio_pop removeAllItems];
259     msg_Dbg( VLCIntf, "Found %lu audio capture devices", [qtkaudioDevices count] );
260
261     if([qtkaudioDevices count] >= 1)
262     {
263         if (!qtkaudio_currdevice_uid) {
264             qtkaudio_currdevice_uid = [[[QTCaptureDevice defaultInputDeviceWithMediaType: QTMediaTypeSound] uniqueID]
265                                   stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
266         }
267         NSUInteger deviceCount = [qtkaudioDevices count];
268         for(int iaudio = 0; iaudio < deviceCount; iaudio++){
269             QTCaptureDevice *qtkaudio_device;
270             qtkaudio_device = [qtkaudioDevices objectAtIndex:iaudio];
271             [o_qtk_audio_device_pop addItemWithTitle: [qtkaudio_device localizedDisplayName]];
272             [o_screen_qtk_audio_pop addItemWithTitle: [qtkaudio_device localizedDisplayName]];
273             if([[[qtkaudio_device uniqueID]stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] isEqualToString:qtkaudio_currdevice_uid]){
274                 [o_qtk_audio_device_pop selectItemAtIndex:iaudio];
275                 [o_screen_qtk_audio_pop selectItemAtIndex:iaudio];
276             }
277         }
278     }
279     else
280     {
281         [o_qtk_audio_device_pop addItemWithTitle: _NS("None")];
282         [o_screen_qtk_audio_pop addItemWithTitle: _NS("None")];
283         [qtkaudio_currdevice_uid release];
284     }
285
286     [self setSubPanel];
287
288     [[NSNotificationCenter defaultCenter] addObserver: self
289         selector: @selector(openNetInfoChanged:)
290         name: NSControlTextDidChangeNotification
291         object: o_net_udp_port];
292     [[NSNotificationCenter defaultCenter] addObserver: self
293         selector: @selector(openNetInfoChanged:)
294         name: NSControlTextDidChangeNotification
295         object: o_net_udpm_addr];
296     [[NSNotificationCenter defaultCenter] addObserver: self
297         selector: @selector(openNetInfoChanged:)
298         name: NSControlTextDidChangeNotification
299         object: o_net_udpm_port];
300     [[NSNotificationCenter defaultCenter] addObserver: self
301         selector: @selector(openNetInfoChanged:)
302         name: NSControlTextDidChangeNotification
303         object: o_net_http_url];
304
305     [[NSDistributedNotificationCenter defaultCenter] addObserver: self
306                                                         selector: @selector(eyetvChanged:)
307                                                             name: NULL
308                                                           object: @"VLCEyeTVSupport"
309                                               suspensionBehavior: NSNotificationSuspensionBehaviorDeliverImmediately];
310
311     [[NSNotificationCenter defaultCenter] addObserver: self
312                                              selector: @selector(screenFPSfieldChanged:)
313                                                  name: NSControlTextDidChangeNotification
314                                                object: o_screen_fps_fld];
315
316     /* register clicks on text fields */
317     [[NSNotificationCenter defaultCenter] addObserver: self
318                                              selector: @selector(textFieldWasClicked:)
319                                                  name: @"VLCOpenTextFieldWasClicked"
320                                                object: nil];
321
322     /* we want to be notified about removed or added media */
323     o_allMediaDevices = [[NSMutableArray alloc] init];
324     o_specialMediaFolders = [[NSMutableArray alloc] init];
325     o_displayInfos = [[NSMutableArray alloc] init];
326     NSWorkspace *sharedWorkspace = [NSWorkspace sharedWorkspace];
327
328     [[sharedWorkspace notificationCenter] addObserver:self selector:@selector(scanOpticalMedia:) name:NSWorkspaceDidMountNotification object:nil];
329     [[sharedWorkspace notificationCenter] addObserver:self selector:@selector(scanOpticalMedia:) name:NSWorkspaceDidUnmountNotification object:nil];
330     [self performSelector:@selector(qtkToggleUIElements:) withObject:nil afterDelay:.3];
331     [self performSelector:@selector(scanOpticalMedia:) withObject:nil afterDelay:.5];
332
333     [self setMRL: @""];
334 }
335
336 - (void)setMRL:(NSString *)newMRL
337 {
338     if (o_mrl)
339         [o_mrl release];
340
341     o_mrl = newMRL;
342     [o_mrl retain];
343     [o_mrl_fld setStringValue: o_mrl];
344     if ([o_mrl length] > 0)
345         [o_btn_ok setEnabled: YES];
346     else
347         [o_btn_ok setEnabled: NO];
348 }
349
350 - (NSString *)MRL
351 {
352     return o_mrl;
353 }
354
355 - (void)setSubPanel
356 {
357     int i_index;
358     module_config_t * p_item;
359
360     [o_file_sub_ckbox setTitle: _NS("Load subtitles file:")];
361     [o_file_sub_path_lbl setStringValue: _NS("Choose a file")];
362     [o_file_sub_path_lbl setHidden: NO];
363     [o_file_sub_path_fld setStringValue: @""];
364     [o_file_sub_btn_settings setTitle: _NS("Choose...")];
365     [[o_file_btn_browse cell] accessibilitySetOverrideValue:_NS("Click to setup subtitle playback in full detail.") forAttribute:NSAccessibilityDescriptionAttribute];
366     [o_file_sub_btn_browse setTitle: _NS("Browse...")];
367     [[o_file_sub_btn_browse cell] accessibilitySetOverrideValue:_NS("Click to select a subtitle file.") forAttribute:NSAccessibilityDescriptionAttribute];
368     [o_file_sub_override setTitle: _NS("Override parameters")];
369     [o_file_sub_delay_lbl setStringValue: _NS("Delay")];
370     [o_file_sub_delay_stp setEnabled: NO];
371     [o_file_sub_fps_lbl setStringValue: _NS("FPS")];
372     [o_file_sub_fps_stp setEnabled: NO];
373     [o_file_sub_encoding_lbl setStringValue: _NS("Subtitles encoding")];
374     [o_file_sub_encoding_pop removeAllItems];
375     [o_file_sub_size_lbl setStringValue: _NS("Font size")];
376     [o_file_sub_size_pop removeAllItems];
377     [o_file_sub_align_lbl setStringValue: _NS("Subtitles alignment")];
378     [o_file_sub_align_pop removeAllItems];
379     [o_file_sub_ok_btn setStringValue: _NS("OK")];
380     [[o_file_sub_ok_btn cell] accessibilitySetOverrideValue:_NS("Click to dismiss the subtitle setup dialog.") forAttribute:NSAccessibilityDescriptionAttribute];
381     [o_file_sub_font_box setTitle: _NS("Font Properties")];
382     [o_file_sub_file_box setTitle: _NS("Subtitle File")];
383
384     p_item = config_FindConfig( VLC_OBJECT(p_intf), "subsdec-encoding" );
385
386     if( p_item )
387     {
388         for( i_index = 0; p_item->list.psz && p_item->list.psz[i_index];
389              i_index++ )
390         {
391             [o_file_sub_encoding_pop addItemWithTitle:
392                 [NSString stringWithUTF8String: p_item->list.psz[i_index]]];
393         }
394         [o_file_sub_encoding_pop selectItemWithTitle:
395                 [NSString stringWithUTF8String: p_item->value.psz]];
396     }
397
398     p_item = config_FindConfig( VLC_OBJECT(p_intf), "subsdec-align" );
399
400     if ( p_item )
401     {
402         for ( i_index = 0; i_index < p_item->list_count; i_index++ )
403         {
404             [o_file_sub_align_pop addItemWithTitle:
405              _NS(p_item->list_text[i_index])];
406         }
407         [o_file_sub_align_pop selectItemAtIndex: p_item->value.i];
408     }
409
410     p_item = config_FindConfig( VLC_OBJECT(p_intf), "freetype-rel-fontsize" );
411
412     if ( p_item )
413     {
414         for ( i_index = 0; i_index < p_item->list_count; i_index++ )
415         {
416             [o_file_sub_size_pop addItemWithTitle: _NS(p_item->list_text[i_index])];
417             if ( p_item->value.i == p_item->list.i[i_index] )
418             {
419                 [o_file_sub_size_pop selectItemAtIndex: i_index];
420             }
421         }
422     }
423 }
424
425 - (void)openTarget:(int)i_type
426 {
427     int i_result;
428
429     b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
430
431     [o_tabview selectTabViewItemAtIndex: i_type];
432     [o_file_sub_ckbox setState: NSOffState];
433
434     i_result = [NSApp runModalForWindow: o_panel];
435     [o_panel close];
436
437     if( i_result )
438     {
439         NSMutableDictionary *o_dic;
440         NSMutableArray *o_options = [NSMutableArray array];
441
442         o_dic = [NSMutableDictionary dictionaryWithObject: [self MRL] forKey: @"ITEM_URL"];
443         if( [o_file_sub_ckbox state] == NSOnState )
444         {
445             module_config_t * p_item;
446
447             [o_options addObject: [NSString stringWithFormat: @"sub-file=%@", o_sub_path]];
448             if( [o_file_sub_override state] == NSOnState )
449             {
450                 [o_options addObject: [NSString stringWithFormat: @"sub-delay=%i", (int)( [o_file_sub_delay intValue] * 10 )]];
451                 [o_options addObject: [NSString stringWithFormat: @"sub-fps=%f", [o_file_sub_fps floatValue]]];
452             }
453             [o_options addObject: [NSString stringWithFormat:
454                     @"subsdec-encoding=%@",
455                     [o_file_sub_encoding_pop titleOfSelectedItem]]];
456             [o_options addObject: [NSString stringWithFormat:
457                     @"subsdec-align=%li",
458                     [o_file_sub_align_pop indexOfSelectedItem]]];
459
460             p_item = config_FindConfig( VLC_OBJECT(p_intf),
461                                             "freetype-rel-fontsize" );
462
463             if ( p_item )
464             {
465                 [o_options addObject: [NSString stringWithFormat:
466                     @"freetype-rel-fontsize=%i",
467                     p_item->list.i[[o_file_sub_size_pop indexOfSelectedItem]]]];
468             }
469         }
470         NSArray * components = [[o_file_starttime_fld stringValue] componentsSeparatedByString:@":"];
471         NSUInteger componentCount = [components count];
472         NSInteger tempValue;
473         if( componentCount == 1 )
474             tempValue = 1000000 * ( [[components objectAtIndex:0] intValue] );
475         else if( componentCount == 2 )
476             tempValue = 1000000 * ( [[components objectAtIndex:0] intValue] * 60 + [[components objectAtIndex:1] intValue] );
477         else if( componentCount == 3 )
478             tempValue = 1000000 * ( [[components objectAtIndex:0] intValue] * 3600 + [[components objectAtIndex:1] intValue] * 60 + [[components objectAtIndex:2] intValue] );
479         if (tempValue > 0)
480             [o_options addObject: [NSString stringWithFormat:@"start-time=%li", tempValue]];
481         components = [[o_file_stoptime_fld stringValue] componentsSeparatedByString:@":"];
482         componentCount = [components count];
483         if( componentCount == 1 )
484             tempValue = 1000000 * ( [[components objectAtIndex:0] intValue] );
485         else if( componentCount == 2 )
486             tempValue = 1000000 * ( [[components objectAtIndex:0] intValue] * 60 + [[components objectAtIndex:1] intValue] );
487         else if( componentCount == 3 )
488             tempValue = 1000000 * ( [[components objectAtIndex:0] intValue] * 3600 + [[components objectAtIndex:1] intValue] * 60 + [[components objectAtIndex:2] intValue] );
489         if (tempValue > 0)
490             [o_options addObject: [NSString stringWithFormat:@"stop-time=%li", tempValue]];
491         if( [o_output_ckbox state] == NSOnState )
492         {
493             NSArray * soutMRL = [o_sout_options soutMRL];
494             NSUInteger count = [soutMRL count];
495             for (NSUInteger i = 0 ; i < count ; i++)
496                 [o_options addObject: [NSString stringWithString: [soutMRL objectAtIndex: i]]];
497         }
498         if( [o_file_slave_ckbox state] && o_file_slave_path )
499            [o_options addObject: [NSString stringWithFormat: @"input-slave=%@", o_file_slave_path]];
500         if( [[[o_tabview selectedTabViewItem] label] isEqualToString: _NS("Capture")] )
501         {
502             if( [[[o_capture_mode_pop selectedItem] title] isEqualToString: _NS("Screen")] )
503             {
504                 int selected_index = [o_screen_screen_pop indexOfSelectedItem];
505                 NSValue *v = [o_displayInfos objectAtIndex:selected_index];
506                 struct display_info_t *item = ( struct display_info_t * )[v pointerValue];
507
508                 [o_options addObject: [NSString stringWithFormat: @"screen-fps=%f", [o_screen_fps_fld floatValue]]];
509                 [o_options addObject: [NSString stringWithFormat: @"screen-display-id=%i", item->id]];
510                 [o_options addObject: [NSString stringWithFormat: @"screen-left=%i", [o_screen_left_fld intValue]]];
511                 [o_options addObject: [NSString stringWithFormat: @"screen-top=%i", [o_screen_top_fld intValue]]];
512                 [o_options addObject: [NSString stringWithFormat: @"screen-width=%i", [o_screen_width_fld intValue]]];
513                 [o_options addObject: [NSString stringWithFormat: @"screen-height=%i", [o_screen_height_fld intValue]]];
514                 if( [o_screen_follow_mouse_ckb intValue] == YES )
515                     [o_options addObject: @"screen-follow-mouse"];
516                 else
517                     [o_options addObject: @"no-screen-follow-mouse"];
518                 if ([o_screen_qtk_audio_ckb state] && qtkaudio_currdevice_uid)
519                    [o_options addObject: [NSString stringWithFormat: @"input-slave=qtsound://%@", qtkaudio_currdevice_uid]];
520             }
521             else if( [[[o_capture_mode_pop selectedItem] title] isEqualToString: _NS("Input Devices")] )
522             {
523                 if ([o_qtk_video_ckb state])
524                 {
525                     [o_options addObject: [NSString stringWithFormat: @"qtcapture-width=%i", [o_capture_width_fld intValue]]];
526                     [o_options addObject: [NSString stringWithFormat: @"qtcapture-height=%i", [o_capture_height_fld intValue]]];
527                     if ([o_qtk_audio_ckb state] && qtkaudio_currdevice_uid)
528                        [o_options addObject: [NSString stringWithFormat: @"input-slave=qtsound://%@", qtkaudio_currdevice_uid]];
529                 }
530             }
531         }
532
533         /* apply the options to our item(s) */
534         [o_dic setObject: (NSArray *)[o_options copy] forKey: @"ITEM_OPTIONS"];
535         if( b_autoplay )
536             [[[VLCMain sharedInstance] playlist] appendArray: [NSArray arrayWithObject: o_dic] atPos: -1 enqueue:NO];
537         else
538             [[[VLCMain sharedInstance] playlist] appendArray: [NSArray arrayWithObject: o_dic] atPos: -1 enqueue:YES];
539     }
540 }
541
542 - (IBAction)screenChanged:(id)sender
543 {
544     int selected_index = [o_screen_screen_pop indexOfSelectedItem];
545     if( selected_index >= [o_displayInfos count] ) return;
546
547     NSValue *v = [o_displayInfos objectAtIndex:selected_index];
548     struct display_info_t *item = ( struct display_info_t * )[v pointerValue];
549
550     [o_screen_left_stp setMaxValue: item->rect.size.width];
551     [o_screen_top_stp setMaxValue: item->rect.size.height];
552     [o_screen_width_stp setMaxValue: item->rect.size.width];
553     [o_screen_height_stp setMaxValue: item->rect.size.height];
554
555     [o_screen_qtk_audio_pop setEnabled: [o_screen_qtk_audio_ckb state]];
556 }
557
558 - (IBAction)qtkChanged:(id)sender
559 {
560     NSInteger i_selectedDevice = [o_qtk_video_device_pop indexOfSelectedItem];
561     if( [qtkvideoDevices count] >= 1 )
562     {
563         NSValue *sizes = [[[[qtkvideoDevices objectAtIndex:i_selectedDevice] formatDescriptions] objectAtIndex: 0] attributeForKey: QTFormatDescriptionVideoEncodedPixelsSizeAttribute];
564
565         [o_capture_width_fld setIntValue: [sizes sizeValue].width];
566         [o_capture_height_fld setIntValue: [sizes sizeValue].height];
567         [o_capture_width_stp setIntValue: [o_capture_width_fld intValue]];
568         [o_capture_height_stp setIntValue: [o_capture_height_fld intValue]];
569         qtk_currdevice_uid = [[(QTCaptureDevice *)[qtkvideoDevices objectAtIndex:i_selectedDevice] uniqueID] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
570     }
571 }
572
573 - (IBAction)qtkAudioChanged:(id)sender
574 {
575     NSInteger i_selectedDevice = [sender indexOfSelectedItem];
576     if( [qtkaudioDevices count] >= 1 )
577     {
578         qtkaudio_currdevice_uid = [[(QTCaptureDevice *)[qtkaudioDevices objectAtIndex:i_selectedDevice] uniqueID] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
579     }
580     [o_screen_qtk_audio_pop selectItemAtIndex: i_selectedDevice];
581     [o_qtk_audio_device_pop selectItemAtIndex: i_selectedDevice];
582 }
583
584 - (IBAction)qtkToggleUIElements:(id)sender
585 {
586     [o_qtk_audio_device_pop setEnabled:[o_qtk_audio_ckb state]];
587     BOOL b_state = [o_qtk_video_ckb state];
588     [o_qtk_video_device_pop setEnabled:b_state];
589     [o_capture_width_fld setEnabled:b_state];
590     [o_capture_width_stp setEnabled:b_state];
591     [o_capture_height_fld setEnabled:b_state];
592     [o_capture_height_stp setEnabled:b_state];
593     [self qtkAudioChanged:sender];
594     [self qtkChanged:sender];
595     [self openCaptureModeChanged:sender];
596 }
597
598 #pragma mark -
599 #pragma mark Main Actions
600
601 - (void)tabView:(NSTabView *)o_tv didSelectTabViewItem:(NSTabViewItem *)o_tvi
602 {
603     NSString *o_label = [o_tvi label];
604
605     if( [o_label isEqualToString: _NS("File")] )
606     {
607         [self openFilePathChanged: nil];
608     }
609     else if( [o_label isEqualToString: _NS("Disc")] )
610     {
611         [self scanOpticalMedia: nil];
612     }
613     else if( [o_label isEqualToString: _NS("Network")] )
614     {
615         [self openNetInfoChanged: nil];
616     }
617     else if( [o_label isEqualToString: _NS("Capture")] )
618     {
619         [self openCaptureModeChanged: nil];
620     }
621 }
622
623 - (IBAction)expandMRLfieldAction:(id)sender
624 {
625     NSRect o_win_rect, o_view_rect;
626     o_win_rect = [o_panel frame];
627     o_view_rect = [o_mrl_view frame];
628
629     if( [o_mrl_btn state] == NSOffState )
630     {
631         /* we need to collaps, restore the panel size */
632         o_win_rect.size.height = o_win_rect.size.height - o_view_rect.size.height;
633         o_win_rect.origin.y = ( o_win_rect.origin.y + o_view_rect.size.height ) - o_view_rect.size.height;
634
635         /* remove the MRL view */
636         [o_mrl_view removeFromSuperview];
637     } else {
638         /* we need to expand */
639         [o_mrl_view setFrame: NSMakeRect( 0,
640                                          [o_mrl_btn frame].origin.y,
641                                          o_view_rect.size.width,
642                                          o_view_rect.size.height )];
643         [o_mrl_view setNeedsDisplay: NO];
644         [o_mrl_view setAutoresizesSubviews: YES];
645
646         /* enlarge panel size for MRL view */
647         o_win_rect.size.height = o_win_rect.size.height + o_view_rect.size.height;
648     }
649
650     [[o_panel animator] setFrame: o_win_rect display:YES];
651 //    [o_panel displayIfNeeded];
652     if( [o_mrl_btn state] == NSOnState )
653         [[o_panel contentView] addSubview: o_mrl_view];
654 }
655
656 - (void)openFileGeneric
657 {
658     [self openFilePathChanged: nil];
659     [self openTarget: 0];
660 }
661
662 - (void)openDisc
663 {
664     @synchronized (self) {
665         [o_specialMediaFolders removeAllObjects];
666     }
667
668     [self scanOpticalMedia: nil];
669     [self openTarget: 1];
670 }
671
672 - (void)openNet
673 {
674     [self openNetInfoChanged: nil];
675     [self openTarget: 2];
676 }
677
678 - (void)openCapture
679 {
680     [self openCaptureModeChanged: nil];
681     [self openTarget: 3];
682 }
683
684 - (void)openFile
685 {
686     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
687     b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
688
689     [o_open_panel setAllowsMultipleSelection: YES];
690     [o_open_panel setCanChooseDirectories: YES];
691     [o_open_panel setTitle: _NS("Open File")];
692     [o_open_panel setPrompt: _NS("Open")];
693
694     if( [o_open_panel runModal] == NSOKButton )
695     {
696         NSArray * o_urls = [o_open_panel URLs];
697         NSUInteger count = [o_urls count];
698         NSMutableArray *o_values = [NSMutableArray arrayWithCapacity:count];
699         NSMutableArray *o_array = [NSMutableArray arrayWithCapacity:count];
700         for( NSUInteger i = 0; i < count; i++ )
701         {
702             [o_values addObject: [[o_urls objectAtIndex: i] path]];
703         }
704         [o_values sortUsingSelector:@selector(caseInsensitiveCompare:)];
705
706         for( NSUInteger i = 0; i < count; i++ )
707         {
708             NSDictionary *o_dic;
709             char *psz_uri = vlc_path2uri([[o_values objectAtIndex:i] UTF8String], "file");
710             if( !psz_uri )
711                 continue;
712
713             o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
714
715             free( psz_uri );
716
717             [o_array addObject: o_dic];
718         }
719         if( b_autoplay )
720             [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO];
721         else
722             [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:YES];
723     }
724 }
725
726 #pragma mark -
727 #pragma mark File Panel
728
729 - (void)openFilePathChanged:(NSNotification *)o_notification
730 {
731     if ( o_file_path && [o_file_path length] > 0 )
732     {
733         bool b_stream = [o_file_stream state];
734         BOOL b_dir = NO;
735
736         [[NSFileManager defaultManager] fileExistsAtPath:o_file_path isDirectory:&b_dir];
737
738         char *psz_uri = vlc_path2uri([o_file_path UTF8String], "file");
739         if( !psz_uri ) return;
740
741         NSMutableString *o_mrl_string = [NSMutableString stringWithUTF8String: psz_uri ];
742         NSRange offile = [o_mrl_string rangeOfString:@"file"];
743         free( psz_uri );
744
745         if( b_dir )
746             [o_mrl_string replaceCharactersInRange:offile withString: @"directory"];
747         else if( b_stream )
748             [o_mrl_string replaceCharactersInRange:offile withString: @"stream"];
749
750         [o_file_name setStringValue: [[NSFileManager defaultManager] displayNameAtPath:o_file_path]];
751         [o_file_name_stub setHidden: YES];
752         [o_file_stream setHidden: NO];
753         [o_file_icon_well setImage: [[NSWorkspace sharedWorkspace] iconForFile: o_file_path]];
754         [o_file_icon_well setHidden: NO];
755         [self setMRL: o_mrl_string];
756     }
757     else
758     {
759         [o_file_name setStringValue: @""];
760         [o_file_name_stub setHidden: NO];
761         [o_file_stream setHidden: YES];
762         [o_file_icon_well setImage: [NSImage imageNamed:@"generic"]];
763         [self setMRL: @""];
764     }
765 }
766
767 - (IBAction)openFileBrowse:(id)sender
768 {
769     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
770
771     [o_open_panel setAllowsMultipleSelection: NO];
772     [o_open_panel setCanChooseDirectories: YES];
773     [o_open_panel setTitle: _NS("Open File")];
774     [o_open_panel setPrompt: _NS("Open")];
775     [o_open_panel beginSheetModalForWindow:[sender window] completionHandler:^(NSInteger returnCode) {
776         if (returnCode == NSFileHandlingPanelOKButton)
777         {
778             if( o_file_path )
779                 [o_file_path release];
780             o_file_path = [[[o_open_panel URLs] objectAtIndex: 0] path];
781             [o_file_path retain];
782             [self openFilePathChanged: nil];
783         }
784     }];
785 }
786
787 - (IBAction)openFileStreamChanged:(id)sender
788 {
789     [self openFilePathChanged: nil];
790 }
791
792 - (IBAction)inputSlaveAction:(id)sender
793 {
794     if( sender == o_file_slave_ckbox )
795         [o_file_slave_select_btn setEnabled: [o_file_slave_ckbox state]];
796     else
797     {
798         NSOpenPanel *o_open_panel;
799         o_open_panel = [NSOpenPanel openPanel];
800         [o_open_panel setCanChooseFiles: YES];
801         [o_open_panel setCanChooseDirectories: NO];
802         if( [o_open_panel runModal] == NSOKButton )
803         {
804             if( o_file_slave_path )
805                 [o_file_slave_path release];
806             o_file_slave_path = [[[o_open_panel URLs] objectAtIndex: 0] path];
807             [o_file_slave_path retain];
808         }
809     }
810     if( o_file_slave_path && [o_file_slave_ckbox state] == NSOnState)
811     {
812         [o_file_slave_filename_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath:o_file_slave_path]];
813         [o_file_slave_icon_well setImage: [[NSWorkspace sharedWorkspace] iconForFile: o_file_slave_path]];
814     }
815     else
816     {
817         [o_file_slave_filename_lbl setStringValue: @""];
818         [o_file_slave_icon_well setImage: NULL];
819     }
820 }
821
822 - (IBAction)fileTimeCustomization:(id)sender
823 {
824     BOOL b_value = [o_file_custom_timing_ckb state];
825     [o_file_starttime_fld setEnabled: b_value];
826     [o_file_starttime_lbl setEnabled: b_value];
827     [o_file_stoptime_fld setEnabled: b_value];
828     [o_file_stoptime_lbl setEnabled: b_value];
829 }
830
831 #pragma mark -
832 #pragma mark Optical Media Panel
833
834 - (void)showOpticalMediaView: theView withIcon:(NSImage *)icon
835 {
836     NSRect o_view_rect;
837     o_view_rect = [theView frame];
838     [theView setFrame: NSMakeRect( 233, 0, o_view_rect.size.width, o_view_rect.size.height)];
839     [theView setAutoresizesSubviews: YES];
840     if (o_currentOpticalMediaView)
841     {
842         [[[[o_tabview tabViewItemAtIndex: [o_tabview indexOfTabViewItemWithIdentifier:@"optical"]] view] animator] replaceSubview: o_currentOpticalMediaView with: theView];
843         [o_currentOpticalMediaView release];
844     }
845     else
846         [[[[o_tabview tabViewItemAtIndex: [o_tabview indexOfTabViewItemWithIdentifier:@"optical"]] view] animator] addSubview: theView];
847     o_currentOpticalMediaView = theView;
848     [o_currentOpticalMediaView retain];
849
850     NSImageView *imageView;
851     imageView = [[NSImageView alloc] init];
852     [imageView setFrame: NSMakeRect( 53, 61, 128, 128 )];
853     [icon setSize: NSMakeSize(128,128)];
854     [imageView setImage: icon];
855     if (o_currentOpticalMediaIconView)
856     {
857         [[[[o_tabview tabViewItemAtIndex: [o_tabview indexOfTabViewItemWithIdentifier:@"optical"]] view] animator] replaceSubview: o_currentOpticalMediaIconView with: imageView];
858         [o_currentOpticalMediaIconView release];
859     }
860     else
861          [[[[o_tabview tabViewItemAtIndex: [o_tabview indexOfTabViewItemWithIdentifier:@"optical"]] view] animator] addSubview: imageView];
862     o_currentOpticalMediaIconView = imageView;
863     [o_currentOpticalMediaIconView retain];
864     [o_currentOpticalMediaView setNeedsDisplay: YES];
865     [o_currentOpticalMediaIconView setNeedsDisplay: YES];
866     [[[o_tabview tabViewItemAtIndex: [o_tabview indexOfTabViewItemWithIdentifier:@"optical"]] view] setNeedsDisplay: YES];
867     [[[o_tabview tabViewItemAtIndex: [o_tabview indexOfTabViewItemWithIdentifier:@"optical"]] view] displayIfNeeded];
868 }
869
870 - (NSString *) getBSDNodeFromMountPath:(NSString *)mountPath
871 {
872     OSStatus err;
873     FSRef ref;
874     FSVolumeRefNum actualVolume;
875     err = FSPathMakeRef ( (const UInt8 *) [mountPath fileSystemRepresentation], &ref, NULL );
876
877     // get a FSVolumeRefNum from mountPath
878     if ( noErr == err ) {
879         FSCatalogInfo   catalogInfo;
880         err = FSGetCatalogInfo ( &ref,
881                                 kFSCatInfoVolume,
882                                 &catalogInfo,
883                                 NULL,
884                                 NULL,
885                                 NULL
886                                 );
887         if ( noErr == err )
888             actualVolume = catalogInfo.volume;
889         else
890             return @"";
891     }
892     else
893         return @"";
894
895     GetVolParmsInfoBuffer volumeParms;
896     err = FSGetVolumeParms( actualVolume, &volumeParms, sizeof(volumeParms) );
897     if ( noErr == err )
898     {
899         NSString *bsdName = [NSString stringWithUTF8String:(char *)volumeParms.vMDeviceID];
900         return [NSString stringWithFormat:@"/dev/r%@", bsdName];
901     }
902
903     return @"";
904 }
905
906 - (NSString *)getVolumeTypeFromMountPath:(NSString *)mountPath
907 {
908     OSStatus err;
909     FSRef ref;
910     FSVolumeRefNum actualVolume;
911     err = FSPathMakeRef ( (const UInt8 *) [mountPath fileSystemRepresentation], &ref, NULL );
912
913     // get a FSVolumeRefNum from mountPath
914     if ( noErr == err ) {
915         FSCatalogInfo   catalogInfo;
916         err = FSGetCatalogInfo ( &ref,
917                                 kFSCatInfoVolume,
918                                 &catalogInfo,
919                                 NULL,
920                                 NULL,
921                                 NULL
922                                 );
923         if ( noErr == err )
924             actualVolume = catalogInfo.volume;
925         else
926             return NULL;
927     }
928     else
929         return NULL;
930
931     GetVolParmsInfoBuffer volumeParms;
932     err = FSGetVolumeParms( actualVolume, &volumeParms, sizeof(volumeParms) );
933
934     CFMutableDictionaryRef matchingDict;
935     io_service_t service;
936
937     if (!volumeParms.vMDeviceID)
938         return NULL;
939
940     matchingDict = IOBSDNameMatching(kIOMasterPortDefault, 0, volumeParms.vMDeviceID);
941     service = IOServiceGetMatchingService(kIOMasterPortDefault, matchingDict);
942
943     NSString *returnValue;
944     if (IO_OBJECT_NULL != service) {
945         if (IOObjectConformsTo(service, kIOCDMediaClass)) {
946             returnValue = kVLCMediaAudioCD;
947         }
948         else if(IOObjectConformsTo(service, kIODVDMediaClass))
949             returnValue = kVLCMediaDVD;
950         else if(IOObjectConformsTo(service, kIOBDMediaClass))
951             returnValue = kVLCMediaBD;
952         else
953         {
954             if ([mountPath rangeOfString:@"VIDEO_TS" options:NSCaseInsensitiveSearch | NSBackwardsSearch].location != NSNotFound)
955                 returnValue = kVLCMediaVideoTSFolder;
956             else if ([mountPath rangeOfString:@"BDMV" options:NSCaseInsensitiveSearch | NSBackwardsSearch].location != NSNotFound)
957                 returnValue = kVLCMediaBDMVFolder;
958             else
959             {
960                 // NSFileManager is not thread-safe, don't use defaultManager outside of the main thread
961                 NSFileManager * fm = [[NSFileManager alloc] init];
962
963                 NSArray *dirContents = [fm contentsOfDirectoryAtPath:mountPath error:nil];
964                 for (int i = 0; i < [dirContents count]; i++)
965                 {
966                     NSString *currentFile = [dirContents objectAtIndex:i];
967                     NSString *fullPath = [mountPath stringByAppendingPathComponent:currentFile];
968
969                     BOOL isDir;
970                     if ([fm fileExistsAtPath:fullPath isDirectory:&isDir] && isDir)
971                     {
972                         if ([currentFile caseInsensitiveCompare:@"SVCD"] == NSOrderedSame)
973                         {
974                             returnValue = kVLCMediaSVCD;
975                             break;
976                         }
977                         if ([currentFile caseInsensitiveCompare:@"VCD"] == NSOrderedSame)
978                         {
979                             returnValue = kVLCMediaVCD;
980                             break;
981                         }
982                         if ([currentFile caseInsensitiveCompare:@"BDMV"] == NSOrderedSame)
983                         {
984                             returnValue = kVLCMediaBDMVFolder;
985                             break;
986                         }
987                         if ([currentFile caseInsensitiveCompare:@"VIDEO_TS"] == NSOrderedSame)
988                         {
989                             returnValue = kVLCMediaVideoTSFolder;
990                             break;
991                         }
992                     }
993                 }
994
995                 [fm release];
996
997                 if(!returnValue)
998                     returnValue = kVLCMediaVideoTSFolder;
999             }
1000         }
1001
1002         IOObjectRelease(service);
1003     }
1004     return returnValue;
1005 }
1006
1007 - (void)showOpticalAtPath: (NSDictionary *)o_dict
1008 {
1009     NSString *diskType = [o_dict objectForKey:@"mediaType"];
1010     NSString *o_opticalDevicePath = [o_dict objectForKey:@"path"];
1011     NSString *o_device_path = [o_dict objectForKey:@"devicePath"];
1012     NSImage *o_image = [o_dict objectForKey:@"image"];
1013
1014     if ([diskType isEqualToString: kVLCMediaDVD] || [diskType isEqualToString: kVLCMediaVideoTSFolder])
1015     {
1016         [o_disc_dvd_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath:o_opticalDevicePath]];
1017         [o_disc_dvdwomenus_lbl setStringValue: [o_disc_dvd_lbl stringValue]];
1018
1019         if (!b_nodvdmenus) {
1020             [self setMRL: [NSString stringWithFormat: @"dvdnav://%@", o_device_path]];
1021             [self showOpticalMediaView: o_disc_dvd_view withIcon:o_image];
1022         } else {
1023             [self setMRL: [NSString stringWithFormat: @"dvdread://%@#%i:%i-", o_device_path, [o_disc_dvdwomenus_title intValue], [o_disc_dvdwomenus_chapter intValue]]];
1024             [self showOpticalMediaView: o_disc_dvdwomenus_view withIcon: o_image];
1025         }
1026     }
1027     else if ([diskType isEqualToString: kVLCMediaAudioCD])
1028     {
1029         [o_disc_audiocd_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath: o_opticalDevicePath]];
1030         [o_disc_audiocd_trackcount_lbl setStringValue: [NSString stringWithFormat:_NS("%i tracks"), [[[NSFileManager defaultManager] subpathsOfDirectoryAtPath: o_opticalDevicePath error:NULL] count] - 1]]; // minus .TOC.plist
1031         [self showOpticalMediaView: o_disc_audiocd_view withIcon: o_image];
1032         [self setMRL: [NSString stringWithFormat: @"cdda://%@", o_device_path]];
1033     }
1034     else if ([diskType isEqualToString: kVLCMediaVCD])
1035     {
1036         [o_disc_vcd_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath: o_opticalDevicePath]];
1037         [self showOpticalMediaView: o_disc_vcd_view withIcon: o_image];
1038         [self setMRL: [NSString stringWithFormat: @"vcd://%@#%i:%i", o_device_path, [o_disc_vcd_title intValue], [o_disc_vcd_chapter intValue]]];
1039     }
1040     else if ([diskType isEqualToString: kVLCMediaSVCD])
1041     {
1042         [o_disc_vcd_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath: o_opticalDevicePath]];
1043         [self showOpticalMediaView: o_disc_vcd_view withIcon: o_image];
1044         [self setMRL: [NSString stringWithFormat: @"vcd://%@@%i:%i", o_device_path, [o_disc_vcd_title intValue], [o_disc_vcd_chapter intValue]]];
1045     }
1046     else if ([diskType isEqualToString: kVLCMediaBD] || [diskType isEqualToString: kVLCMediaBDMVFolder])
1047     {
1048         [o_disc_bd_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath: o_opticalDevicePath]];
1049         [self showOpticalMediaView: o_disc_bd_view withIcon: o_image];
1050         [self setMRL: [NSString stringWithFormat: @"bluray://%@", o_opticalDevicePath]];
1051     }
1052     else
1053     {
1054         msg_Warn( VLCIntf, "unknown disk type, no idea what to display" );
1055         [self showOpticalMediaView: o_disc_nodisc_view withIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
1056     }
1057 }
1058
1059 - (NSDictionary *)scanPath:(NSString *)o_path
1060 {
1061     NSString *o_type = [self getVolumeTypeFromMountPath:o_path];
1062     NSImage *o_image = [[NSWorkspace sharedWorkspace] iconForFile: o_path];
1063     NSString *o_device_path;
1064
1065     if ([o_type isEqualToString: kVLCMediaVideoTSFolder] ||
1066         [o_type isEqualToString: kVLCMediaBD] ||
1067         [o_type isEqualToString: kVLCMediaBDMVFolder] ||
1068         [o_type isEqualToString: kVLCMediaUnknown])
1069         o_device_path = o_path;
1070     else
1071         o_device_path = [self getBSDNodeFromMountPath:o_path];
1072
1073     return [NSDictionary dictionaryWithObjectsAndKeys: o_path, @"path",
1074                                                 o_device_path, @"devicePath",
1075                                                        o_type, @"mediaType",
1076                                                       o_image, @"image", nil];
1077 }
1078
1079 - (void)scanDevicesWithPaths:(NSArray *)o_paths
1080 {
1081     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
1082
1083     NSUInteger count = [o_paths count];
1084     NSMutableArray *o_result = [NSMutableArray arrayWithCapacity:count];
1085     for (NSUInteger i = 0; i < count; i++)
1086         [o_result addObject: [self scanPath:[o_paths objectAtIndex:i]]];
1087
1088     @synchronized (self) {
1089         if (o_opticalDevices)
1090             [o_opticalDevices release];
1091         o_opticalDevices = [[NSArray alloc] initWithArray: o_result];
1092     }
1093
1094     [self performSelectorOnMainThread:@selector(updateMediaSelector:) withObject:nil waitUntilDone:NO];
1095     [o_pool release];
1096 }
1097
1098 - (void)scanSpecialPath:(NSString *)o_path
1099 {
1100     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
1101
1102     NSDictionary *o_dict = [self scanPath:o_path];
1103
1104     @synchronized (self) {
1105         [o_specialMediaFolders addObject:o_dict];
1106     }
1107
1108     [self performSelectorOnMainThread:@selector(updateMediaSelector:) withObject:[NSNumber numberWithBool:YES] waitUntilDone:NO];
1109     [o_pool release];
1110 }
1111
1112 - (void)scanOpticalMedia:(NSNotification *)o_notification
1113 {
1114     [NSThread detachNewThreadSelector:@selector(scanDevicesWithPaths:) toTarget:self withObject:[NSArray arrayWithArray:[[NSWorkspace sharedWorkspace] mountedRemovableMedia]]];
1115 }
1116
1117 - (void)updateMediaSelector:(NSNumber *)o_selection
1118 {
1119     [o_allMediaDevices removeAllObjects];
1120     [o_disc_selector_pop removeAllItems];
1121
1122     @synchronized (self) {
1123         [o_allMediaDevices addObjectsFromArray:o_opticalDevices];
1124         [o_allMediaDevices addObjectsFromArray:o_specialMediaFolders];
1125     }
1126
1127     NSUInteger count = [o_allMediaDevices count];
1128     if (count > 0)
1129     {
1130         for (NSUInteger i = 0; i < count ; i++)
1131         {
1132             NSDictionary *o_dict = [o_allMediaDevices objectAtIndex: i];
1133             [o_disc_selector_pop addItemWithTitle: [[NSFileManager defaultManager] displayNameAtPath:[o_dict objectForKey:@"path"]]];
1134         }
1135
1136         if ([o_disc_selector_pop numberOfItems] <= 1)
1137             [o_disc_selector_pop setHidden: YES];
1138         else
1139             [o_disc_selector_pop setHidden: NO];
1140
1141         // select newly added media folder
1142         if (o_selection && [o_selection boolValue])
1143             [o_disc_selector_pop selectItemAtIndex: [[o_disc_selector_pop itemArray] count] - 1];
1144
1145         [self discSelectorChanged:nil];
1146     }
1147     else
1148     {
1149         msg_Dbg( VLCIntf, "no optical media found" );
1150         [o_disc_selector_pop setHidden: YES];
1151         [self showOpticalMediaView: o_disc_nodisc_view withIcon: [NSImage imageNamed: @"NSApplicationIcon"]];
1152     }
1153
1154 }
1155
1156 - (IBAction)discSelectorChanged:(id)sender
1157 {
1158     NSDictionary *o_dict = [o_allMediaDevices objectAtIndex: [o_disc_selector_pop indexOfSelectedItem]];    
1159     [self showOpticalAtPath:o_dict];
1160 }
1161
1162 - (IBAction)openSpecialMediaFolder:(id)sender
1163 {
1164     /* this is currently for VIDEO_TS and BDMV folders */
1165     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
1166
1167     [o_open_panel setAllowsMultipleSelection: NO];
1168     [o_open_panel setCanChooseFiles: NO];
1169     [o_open_panel setCanChooseDirectories: YES];
1170     [o_open_panel setTitle: [sender title]];
1171     [o_open_panel setPrompt: _NS("Open")];
1172
1173     if ([o_open_panel runModal] == NSOKButton)
1174     {
1175         NSString *o_path = [[[o_open_panel URLs] objectAtIndex: 0] path];
1176         if ([o_path length] > 0 )
1177         {
1178             [NSThread detachNewThreadSelector:@selector(scanSpecialPath:) toTarget:self withObject:o_path];
1179         }
1180     }
1181 }
1182
1183 - (IBAction)dvdreadOptionChanged:(id)sender
1184 {
1185     NSDictionary *o_dict = [o_allMediaDevices objectAtIndex: [o_disc_selector_pop indexOfSelectedItem]];
1186     NSString *o_device_path = [o_dict objectForKey:@"devicePath"];
1187
1188     if (sender == o_disc_dvdwomenus_enablemenus_btn) {
1189         b_nodvdmenus = NO;
1190         [self setMRL: [NSString stringWithFormat: @"dvdnav://%@", o_device_path]];
1191         [self showOpticalMediaView: o_disc_dvd_view withIcon: [o_currentOpticalMediaIconView image]];
1192         return;
1193     }
1194     if (sender == o_disc_dvd_disablemenus_btn) {
1195         b_nodvdmenus = YES;
1196         [self showOpticalMediaView: o_disc_dvdwomenus_view withIcon: [o_currentOpticalMediaIconView image]];
1197     }
1198
1199     if (sender == o_disc_dvdwomenus_title)
1200         [o_disc_dvdwomenus_title_stp setIntValue: [o_disc_dvdwomenus_title intValue]];
1201     if (sender == o_disc_dvdwomenus_title_stp)
1202         [o_disc_dvdwomenus_title setIntValue: [o_disc_dvdwomenus_title_stp intValue]];
1203     if (sender == o_disc_dvdwomenus_chapter)
1204         [o_disc_dvdwomenus_chapter_stp setIntValue: [o_disc_dvdwomenus_chapter intValue]];
1205     if (sender == o_disc_dvdwomenus_chapter_stp)
1206         [o_disc_dvdwomenus_chapter setIntValue: [o_disc_dvdwomenus_chapter_stp intValue]];
1207
1208     [self setMRL: [NSString stringWithFormat: @"dvdread://%@#%i:%i-", o_device_path, [o_disc_dvdwomenus_title intValue], [o_disc_dvdwomenus_chapter intValue]]];
1209 }
1210
1211 - (IBAction)vcdOptionChanged:(id)sender
1212 {
1213     if (sender == o_disc_vcd_title)
1214         [o_disc_vcd_title_stp setIntValue: [o_disc_vcd_title intValue]];
1215     if (sender == o_disc_vcd_title_stp)
1216         [o_disc_vcd_title setIntValue: [o_disc_vcd_title_stp intValue]];
1217     if (sender == o_disc_vcd_chapter)
1218         [o_disc_vcd_chapter_stp setIntValue: [o_disc_vcd_chapter intValue]];
1219     if (sender == o_disc_vcd_chapter_stp)
1220         [o_disc_vcd_chapter setIntValue: [o_disc_vcd_chapter_stp intValue]];
1221
1222     NSString *o_device_path = [[o_allMediaDevices objectAtIndex: [o_disc_selector_pop indexOfSelectedItem]] objectForKey:@"devicePath"];
1223     [self setMRL: [NSString stringWithFormat: @"vcd://%@@%i:%i", o_device_path, [o_disc_vcd_title intValue], [o_disc_vcd_chapter intValue]]];
1224 }
1225
1226 #pragma mark -
1227 #pragma mark Network Panel
1228
1229 - (void)textFieldWasClicked:(NSNotification *)o_notification
1230 {
1231     if( [o_notification object] == o_net_udp_port )
1232         [o_net_mode selectCellAtRow: 0 column: 0];
1233     else if( [o_notification object] == o_net_udpm_addr ||
1234              [o_notification object] == o_net_udpm_port )
1235         [o_net_mode selectCellAtRow: 1 column: 0];
1236     else
1237         [o_net_mode selectCellAtRow: 2 column: 0];
1238
1239     [self openNetInfoChanged: nil];
1240 }
1241
1242 - (IBAction)openNetModeChanged:(id)sender
1243 {
1244     if( sender == o_net_mode )
1245     {
1246         if( [[sender selectedCell] tag] == 0 )
1247             [o_panel makeFirstResponder: o_net_udp_port];
1248         else if ( [[sender selectedCell] tag] == 1 )
1249             [o_panel makeFirstResponder: o_net_udpm_addr];
1250         else
1251             msg_Warn( p_intf, "Unknown sender tried to change UDP/RTP mode" );
1252     }
1253
1254     [self openNetInfoChanged: nil];
1255 }
1256
1257 - (IBAction)openNetStepperChanged:(id)sender
1258 {
1259     int i_tag = [sender tag];
1260
1261     if( i_tag == 0 )
1262     {
1263         [o_net_udp_port setIntValue: [o_net_udp_port_stp intValue]];
1264         [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCOpenTextFieldWasClicked"
1265                                                             object: o_net_udp_port];
1266         [o_panel makeFirstResponder: o_net_udp_port];
1267     }
1268     else if( i_tag == 1 )
1269     {
1270         [o_net_udpm_port setIntValue: [o_net_udpm_port_stp intValue]];
1271         [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCOpenTextFieldWasClicked"
1272                                                             object: o_net_udpm_port];
1273         [o_panel makeFirstResponder: o_net_udpm_port];
1274     }
1275
1276     [self openNetInfoChanged: nil];
1277 }
1278
1279 - (void)openNetInfoChanged:(NSNotification *)o_notification
1280 {
1281     NSString *o_mrl_string = [NSString string];
1282
1283     if( [o_net_udp_panel isVisible] )
1284     {
1285         NSString *o_mode;
1286         o_mode = [[o_net_mode selectedCell] title];
1287
1288         if( [o_mode isEqualToString: _NS("Unicast")] )
1289         {
1290             int i_port = [o_net_udp_port intValue];
1291
1292             if( [[o_net_udp_protocol_mat selectedCell] tag] == 0 )
1293                 o_mrl_string = @"udp://";
1294             else
1295                 o_mrl_string = @"rtp://";
1296
1297             if( i_port != config_GetInt( p_intf, "server-port" ) )
1298             {
1299                 o_mrl_string =
1300                     [o_mrl_string stringByAppendingFormat: @"@:%i", i_port];
1301             }
1302         }
1303         else if( [o_mode isEqualToString: _NS("Multicast")] )
1304         {
1305             NSString *o_addr = [o_net_udpm_addr stringValue];
1306             int i_port = [o_net_udpm_port intValue];
1307
1308             if( [[o_net_udp_protocol_mat selectedCell] tag] == 0 )
1309                 o_mrl_string = [NSString stringWithFormat: @"udp://@%@", o_addr];
1310             else
1311                 o_mrl_string = [NSString stringWithFormat: @"rtp://@%@", o_addr];
1312
1313             if( i_port != config_GetInt( p_intf, "server-port" ) )
1314             {
1315                 o_mrl_string =
1316                     [o_mrl_string stringByAppendingFormat: @":%i", i_port];
1317             }
1318         }
1319     }
1320     else
1321     {
1322         o_mrl_string = [o_net_http_url stringValue];
1323     }
1324     [self setMRL: o_mrl_string];
1325 }
1326
1327 - (IBAction)openNetUDPButtonAction:(id)sender
1328 {
1329     if( sender == o_net_openUDP_btn )
1330     {
1331         [NSApp beginSheet: o_net_udp_panel
1332            modalForWindow: o_panel
1333             modalDelegate: self
1334            didEndSelector: NULL
1335               contextInfo: nil];
1336         [self openNetInfoChanged: nil];
1337     }
1338     else if( sender == o_net_udp_cancel_btn )
1339     {
1340         [o_net_udp_panel orderOut: sender];
1341         [NSApp endSheet: o_net_udp_panel];
1342     }
1343     else if( sender == o_net_udp_ok_btn )
1344     {
1345         NSString *o_mrl_string = [NSString string];
1346         if( [[[o_net_mode selectedCell] title] isEqualToString: _NS("Unicast")] )
1347         {
1348             int i_port = [o_net_udp_port intValue];
1349
1350             if( [[o_net_udp_protocol_mat selectedCell] tag] == 0 )
1351                 o_mrl_string = @"udp://";
1352             else
1353                 o_mrl_string = @"rtp://";
1354
1355             if( i_port != config_GetInt( p_intf, "server-port" ) )
1356             {
1357                 o_mrl_string =
1358                 [o_mrl_string stringByAppendingFormat: @"@:%i", i_port];
1359             }
1360         }
1361         else if( [[[o_net_mode selectedCell] title] isEqualToString: _NS("Multicast")] )
1362         {
1363             NSString *o_addr = [o_net_udpm_addr stringValue];
1364             int i_port = [o_net_udpm_port intValue];
1365
1366             if( [[o_net_udp_protocol_mat selectedCell] tag] == 0 )
1367                 o_mrl_string = [NSString stringWithFormat: @"udp://@%@", o_addr];
1368             else
1369                 o_mrl_string = [NSString stringWithFormat: @"rtp://@%@", o_addr];
1370
1371             if( i_port != config_GetInt( p_intf, "server-port" ) )
1372             {
1373                 o_mrl_string =
1374                 [o_mrl_string stringByAppendingFormat: @":%i", i_port];
1375             }
1376         }
1377         [self setMRL: o_mrl_string];
1378         [o_net_http_url setStringValue: o_mrl_string];
1379         [o_net_udp_panel orderOut: sender];
1380         [NSApp endSheet: o_net_udp_panel];
1381     }
1382 }
1383
1384 #pragma mark -
1385 #pragma mark Capture Panel
1386
1387 - (void)showCaptureView: theView
1388 {
1389     NSRect o_view_rect;
1390     o_view_rect = [theView frame];
1391     [theView setFrame: NSMakeRect( 0, -10, o_view_rect.size.width, o_view_rect.size.height)];
1392     [theView setAutoresizesSubviews: YES];
1393     if (o_currentCaptureView)
1394     {
1395         [[[[o_tabview tabViewItemAtIndex: 3] view] animator] replaceSubview: o_currentCaptureView with: theView];
1396         [o_currentCaptureView release];
1397     }
1398     else
1399     {
1400         [[[[o_tabview tabViewItemAtIndex: 3] view] animator] addSubview: theView];
1401     }
1402     o_currentCaptureView = theView;
1403     [o_currentCaptureView retain];
1404 }
1405
1406 - (IBAction)openCaptureModeChanged:(id)sender
1407 {
1408     if( [[[o_capture_mode_pop selectedItem] title] isEqualToString: @"EyeTV"] )
1409     {
1410         if( [[[VLCMain sharedInstance] eyeTVController] eyeTVRunning] == YES )
1411         {
1412             if( [[[VLCMain sharedInstance] eyeTVController] deviceConnected] == YES )
1413             {
1414                 [self showCaptureView: o_eyetv_running_view];
1415                 [self setupChannelInfo];
1416             }
1417             else
1418             {
1419                 setEyeTVUnconnected;
1420             }
1421         }
1422         else
1423             [self showCaptureView: o_eyetv_notLaunched_view];
1424         [self setMRL: @""];
1425     }
1426     else if( [[[o_capture_mode_pop selectedItem] title] isEqualToString: _NS("Screen")] )
1427     {
1428         [self showCaptureView: o_screen_view];
1429         [self setMRL: @"screen://"];
1430         [o_screen_height_fld setIntValue: config_GetInt( p_intf, "screen-height" )];
1431         [o_screen_width_fld setIntValue: config_GetInt( p_intf, "screen-width" )];
1432         [o_screen_fps_fld setFloatValue: config_GetFloat( p_intf, "screen-fps" )];
1433         [o_screen_left_fld setIntValue: config_GetInt( p_intf, "screen-left" )];
1434         [o_screen_top_fld setIntValue: config_GetInt( p_intf, "screen-top" )];
1435         [o_screen_follow_mouse_ckb setIntValue: config_GetInt( p_intf, "screen-follow-mouse" )];
1436
1437         int screen_index = config_GetInt( p_intf, "screen-index" );
1438         int display_id = config_GetInt( p_intf, "screen-display-id" );
1439         unsigned int i, displayCount = 0;
1440         CGLError returnedError;
1441         struct display_info_t *item;
1442         NSValue *v;
1443
1444         returnedError = CGGetOnlineDisplayList( 0, NULL, &displayCount );
1445         if( !returnedError )
1446         {
1447             CGDirectDisplayID *ids;
1448             ids = ( CGDirectDisplayID * )malloc( displayCount * sizeof( CGDirectDisplayID ) );
1449             returnedError = CGGetOnlineDisplayList( displayCount, ids, &displayCount );
1450             if( !returnedError )
1451             {
1452                 for( i = 0; i < [o_displayInfos count]; i ++ )
1453                 {
1454                     v = [o_displayInfos objectAtIndex:i];
1455                     free( [v pointerValue] );
1456                 }
1457                 [o_displayInfos removeAllObjects];
1458                 [o_screen_screen_pop removeAllItems];
1459                 for( i = 0; i < displayCount; i ++ )
1460                 {
1461                     item = ( struct display_info_t * )malloc( sizeof( struct display_info_t ) );
1462                     item->id = ids[i];
1463                     item->rect = CGDisplayBounds( item->id );
1464                     [o_screen_screen_pop addItemWithTitle: [NSString stringWithFormat:@"Screen %d (%dx%d)", i + 1, (int)item->rect.size.width, (int)item->rect.size.height]];
1465                     v = [NSValue valueWithPointer:item];
1466                     [o_displayInfos addObject:v];
1467                     if( i == 0 || display_id == item->id || screen_index - 1 == i )
1468                     {
1469                         [o_screen_screen_pop selectItemAtIndex: i];
1470                         [o_screen_left_stp setMaxValue: item->rect.size.width];
1471                         [o_screen_top_stp setMaxValue: item->rect.size.height];
1472                         [o_screen_width_stp setMaxValue: item->rect.size.width];
1473                         [o_screen_height_stp setMaxValue: item->rect.size.height];
1474                     }
1475                 }
1476             }
1477             free( ids );
1478         }
1479     }
1480     else if( [[[o_capture_mode_pop selectedItem] title] isEqualToString: _NS("Input Devices")] )
1481     {
1482         [self showCaptureView: o_qtk_view];
1483         if ([o_capture_width_fld intValue] <= 0)
1484             [self qtkChanged:nil];
1485
1486         [self qtkAudioChanged:nil];
1487
1488         [self setMRL: @""];
1489
1490         if ([o_qtk_video_ckb state] && qtk_currdevice_uid)
1491             [self setMRL:[NSString stringWithFormat:@"qtcapture://%@", qtk_currdevice_uid]];
1492         else if([o_qtk_audio_ckb state] && qtkaudio_currdevice_uid)
1493             [self setMRL:[NSString stringWithFormat:@"qtsound://%@", qtkaudio_currdevice_uid]];
1494     }
1495 }
1496
1497 - (void)screenFPSfieldChanged:(NSNotification *)o_notification
1498 {
1499     [o_screen_fps_stp setFloatValue: [o_screen_fps_fld floatValue]];
1500     if( [[o_screen_fps_fld stringValue] isEqualToString: @""] )
1501         [o_screen_fps_fld setFloatValue: 1.0];
1502     [self setMRL: @"screen://"];
1503 }
1504
1505 - (IBAction)eyetvSwitchChannel:(id)sender
1506 {
1507     if( sender == o_eyetv_nextProgram_btn )
1508     {
1509         int chanNum = [[[VLCMain sharedInstance] eyeTVController] switchChannelUp: YES];
1510         [o_eyetv_channels_pop selectItemWithTag:chanNum];
1511         [self setMRL: [NSString stringWithFormat:@"eyetv:// :eyetv-channel=%d", chanNum]];
1512     }
1513     else if( sender == o_eyetv_previousProgram_btn )
1514     {
1515         int chanNum = [[[VLCMain sharedInstance] eyeTVController] switchChannelUp: NO];
1516         [o_eyetv_channels_pop selectItemWithTag:chanNum];
1517         [self setMRL: [NSString stringWithFormat:@"eyetv:// :eyetv-channel=%d", chanNum]];
1518     }
1519     else if( sender == o_eyetv_channels_pop )
1520     {
1521         int chanNum = [[sender selectedItem] tag];
1522         [[[VLCMain sharedInstance] eyeTVController] setChannel:chanNum];
1523         [self setMRL: [NSString stringWithFormat:@"eyetv:// :eyetv-channel=%d", chanNum]];
1524     }
1525     else
1526         msg_Err( VLCIntf, "eyetvSwitchChannel sent by unknown object" );
1527 }
1528
1529 - (IBAction)eyetvLaunch:(id)sender
1530 {
1531     [[[VLCMain sharedInstance] eyeTVController] launchEyeTV];
1532 }
1533
1534 - (IBAction)eyetvGetPlugin:(id)sender
1535 {
1536     [[NSWorkspace sharedWorkspace] openURL: [NSURL URLWithString: @"http://www.videolan.org/vlc/eyetv"]];
1537 }
1538
1539 - (void)eyetvChanged:(NSNotification *)o_notification
1540 {
1541     if( [[o_notification name] isEqualToString: @"DeviceAdded"] )
1542     {
1543         msg_Dbg( VLCIntf, "eyetv device was added" );
1544         [self showCaptureView: o_eyetv_running_view];
1545         [self setupChannelInfo];
1546     }
1547     else if( [[o_notification name] isEqualToString: @"DeviceRemoved"] )
1548     {
1549         /* leave the channel selection like that,
1550          * switch to our "no device" tab */
1551         msg_Dbg( VLCIntf, "eyetv device was removed" );
1552         setEyeTVUnconnected;
1553     }
1554     else if( [[o_notification name] isEqualToString: @"PluginQuit"] )
1555     {
1556         /* switch to the "launch eyetv" tab */
1557         msg_Dbg( VLCIntf, "eyetv was terminated" );
1558         [self showCaptureView: o_eyetv_notLaunched_view];
1559     }
1560     else if( [[o_notification name] isEqualToString: @"PluginInit"] )
1561     {
1562         /* we got no device yet */
1563         msg_Dbg( VLCIntf, "eyetv was launched, no device yet" );
1564         setEyeTVUnconnected;
1565     }
1566 }
1567
1568 /* little helper method, since this code needs to be run by multiple objects */
1569 - (void)setupChannelInfo
1570 {
1571     /* set up channel selection */
1572     [o_eyetv_channels_pop removeAllItems];
1573     [o_eyetv_chn_bgbar setHidden: NO];
1574     [o_eyetv_chn_bgbar animate: self];
1575     [o_eyetv_chn_status_txt setStringValue: _NS("Retrieving Channel Info...")];
1576     [o_eyetv_chn_status_txt setHidden: NO];
1577
1578     /* retrieve info */
1579     NSEnumerator *channels = [[[VLCMain sharedInstance] eyeTVController] allChannels];
1580     int x = -2;
1581     [[[o_eyetv_channels_pop menu] addItemWithTitle: _NS("Composite input")
1582                                                action: nil
1583                                         keyEquivalent: @""] setTag:x++];
1584     [[[o_eyetv_channels_pop menu] addItemWithTitle: _NS("S-Video input")
1585                                                action: nil
1586                                         keyEquivalent: @""] setTag:x++];
1587     if( channels )
1588     {
1589         NSString *channel;
1590         [[o_eyetv_channels_pop menu] addItem: [NSMenuItem separatorItem]];
1591         while( channel = [channels nextObject] )
1592         {
1593             /* we have to add items this way, because we accept duplicates
1594              * additionally, we save a bit of time */
1595             [[[o_eyetv_channels_pop menu] addItemWithTitle: channel
1596                                                    action: nil
1597                                             keyEquivalent: @""] setTag:++x];
1598         }
1599         /* make Tuner the default */
1600         [o_eyetv_channels_pop selectItemWithTag:[[[VLCMain sharedInstance] eyeTVController] channel]];
1601     }
1602
1603     /* clean up GUI */
1604     [o_eyetv_chn_bgbar setHidden: YES];
1605     [o_eyetv_chn_status_txt setHidden: YES];
1606 }
1607
1608 #pragma mark -
1609 #pragma mark Subtitle Settings
1610
1611 - (IBAction)subsChanged:(id)sender
1612 {
1613     if ([o_file_sub_ckbox state] == NSOnState)
1614     {
1615         [o_file_sub_btn_settings setEnabled:YES];
1616         if (o_sub_path) {
1617             [o_file_subtitles_filename_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath:o_sub_path]];
1618             [o_file_subtitles_icon_well setImage: [[NSWorkspace sharedWorkspace] iconForFile:o_sub_path]];
1619         }
1620     }
1621     else
1622     {
1623         [o_file_sub_btn_settings setEnabled:NO];
1624         [o_file_subtitles_filename_lbl setStringValue: @""];
1625         [o_file_subtitles_icon_well setImage: NULL];
1626     }
1627 }
1628
1629 - (IBAction)subSettings:(id)sender
1630 {
1631     [NSApp beginSheet: o_file_sub_sheet
1632         modalForWindow: [sender window]
1633         modalDelegate: self
1634         didEndSelector: NULL
1635         contextInfo: nil];
1636 }
1637
1638 - (IBAction)subCloseSheet:(id)sender
1639 {
1640     [self subsChanged: nil];
1641     [o_file_sub_sheet orderOut:sender];
1642     [NSApp endSheet: o_file_sub_sheet];
1643 }
1644
1645 - (IBAction)subFileBrowse:(id)sender
1646 {
1647     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
1648
1649     [o_open_panel setAllowsMultipleSelection: NO];
1650     [o_open_panel setTitle: _NS("Open File")];
1651     [o_open_panel setPrompt: _NS("Open")];
1652
1653     if( [o_open_panel runModal] == NSOKButton )
1654     {
1655         o_sub_path = [[[o_open_panel URLs] objectAtIndex: 0] path];
1656         [o_sub_path retain];
1657         [o_file_subtitles_filename_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath:o_sub_path]];
1658         [o_file_sub_path_fld setStringValue: [o_file_subtitles_filename_lbl stringValue]];
1659         [o_file_sub_path_lbl setHidden: YES];
1660         [o_file_subtitles_icon_well setImage: [[NSWorkspace sharedWorkspace] iconForFile:o_sub_path]];
1661         [o_file_sub_icon_view setImage: [o_file_subtitles_icon_well image]];
1662     }
1663     else
1664     {
1665         [o_file_sub_path_lbl setHidden: NO];
1666         [o_file_sub_path_fld setStringValue:@""];
1667         [o_file_subtitles_filename_lbl setStringValue:@""];
1668         [o_file_subtitles_icon_well setImage: nil];
1669         [o_file_sub_icon_view setImage: nil];
1670     }
1671 }
1672
1673 - (IBAction)subOverride:(id)sender
1674 {
1675     BOOL b_state = [o_file_sub_override state];
1676     [o_file_sub_delay setEnabled: b_state];
1677     [o_file_sub_delay_stp setEnabled: b_state];
1678     [o_file_sub_fps setEnabled: b_state];
1679     [o_file_sub_fps_stp setEnabled: b_state];
1680 }
1681
1682 - (IBAction)subDelayStepperChanged:(id)sender
1683 {
1684     [o_file_sub_delay setIntValue: [o_file_sub_delay_stp intValue]];
1685 }
1686
1687 - (IBAction)subFpsStepperChanged:(id)sender;
1688 {
1689     [o_file_sub_fps setFloatValue: [o_file_sub_fps_stp floatValue]];
1690 }
1691
1692 #pragma mark -
1693 #pragma mark Miscellaneous
1694
1695 - (IBAction)panelCancel:(id)sender
1696 {
1697     [NSApp stopModalWithCode: 0];
1698 }
1699
1700 - (IBAction)panelOk:(id)sender
1701 {
1702     if( [[self MRL] length] )
1703         [NSApp stopModalWithCode: 1];
1704     else
1705         NSBeep();
1706 }
1707
1708 - (NSArray *)qtkvideoDevices
1709 {
1710     if ( !qtkvideoDevices )
1711         [self qtkrefreshVideoDevices];
1712     return qtkvideoDevices;
1713 }
1714
1715 - (void)qtkrefreshVideoDevices
1716 {
1717     [qtkvideoDevices release];
1718     qtkvideoDevices = [[[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeVideo] arrayByAddingObjectsFromArray:[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeMuxed]] retain];
1719 }
1720
1721 - (NSArray *)qtkaudioDevices
1722 {
1723     if ( !qtkaudioDevices )
1724         [self qtkrefreshAudioDevices];
1725     return qtkaudioDevices;
1726 }
1727
1728 - (void)qtkrefreshAudioDevices
1729 {
1730     [qtkaudioDevices release];
1731     qtkaudioDevices = [[[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeSound] arrayByAddingObjectsFromArray:[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeMuxed]] retain];
1732 }
1733
1734 @end
1735
1736 @implementation VLCOpenTextField
1737
1738 - (void)mouseDown:(NSEvent *)theEvent
1739 {
1740     [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCOpenTextFieldWasClicked"
1741                                                         object: self];
1742     [super mouseDown: theEvent];
1743 }
1744
1745 @end