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