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