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