]> git.sesse.net Git - vlc/blob - modules/gui/macosx/open.m
audiotrack: Fix deadlock with Android 4.4.2, 4.4.3 and 4.4.4
[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 @synthesize fileSubDelay, fileSubFps;
75
76 #pragma mark -
77 #pragma mark Init
78
79 static VLCOpen *_o_sharedMainInstance = nil;
80
81 + (VLCOpen *)sharedInstance
82 {
83     return _o_sharedMainInstance ? _o_sharedMainInstance : [[self alloc] init];
84 }
85
86 - (id)init
87 {
88     if (_o_sharedMainInstance)
89         [self dealloc];
90     else
91         _o_sharedMainInstance = [super init];
92
93     return _o_sharedMainInstance;
94 }
95
96 - (void)dealloc
97 {
98     [o_allMediaDevices release];
99     [o_specialMediaFolders release];
100     if (o_opticalDevices)
101         [o_opticalDevices release];
102     if (o_file_slave_path)
103         [o_file_slave_path release];
104     [o_mrl release];
105     if (o_sub_path)
106         [o_sub_path release];
107     [o_currentOpticalMediaIconView release];
108     [o_currentOpticalMediaView release];
109     for (int i = 0; i < [o_displayInfos count]; i ++) {
110         NSValue *v = [o_displayInfos objectAtIndex:i];
111         free([v pointerValue]);
112     }
113     [o_displayInfos removeAllObjects];
114     [o_displayInfos release];
115
116     [super dealloc];
117 }
118
119 - (void)awakeFromNib
120 {
121     if (!OSX_SNOW_LEOPARD)
122         [o_panel setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary];
123
124     [o_panel setTitle: _NS("Open Source")];
125     [o_mrl_lbl setStringValue: _NS("Media Resource Locator (MRL)")];
126
127     [o_btn_ok setTitle: _NS("Open")];
128     [o_btn_cancel setTitle: _NS("Cancel")];
129
130     [[o_tabview tabViewItemAtIndex: 0] setLabel: _NS("File")];
131     [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];
132     [[o_tabview tabViewItemAtIndex: 1] setLabel: _NS("Disc")];
133     [[o_tabview tabViewItemAtIndex: 2] setLabel: _NS("Network")];
134     [[o_tabview tabViewItemAtIndex: 3] setLabel: _NS("Capture")];
135     [o_file_name setStringValue: @""];
136     [o_file_name_stub setStringValue: _NS("Choose a file")];
137     [o_file_icon_well setImage: [NSImage imageNamed:@"generic"]];
138     [o_file_btn_browse setTitle: _NS("Browse...")];
139     [[o_file_btn_browse cell] accessibilitySetOverrideValue:_NS("Click to select a file for playback") forAttribute:NSAccessibilityDescriptionAttribute];
140     [o_file_stream setTitle: _NS("Treat as a pipe rather than as a file")];
141     [o_file_stream setHidden: NO];
142     [o_file_slave_ckbox setTitle: _NS("Play another media synchronously")];
143     [o_file_slave_select_btn setTitle: _NS("Choose...")];
144     [[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];
145     [o_file_slave_filename_lbl setStringValue: @""];
146     [o_file_slave_icon_well setImage: NULL];
147     [o_file_subtitles_filename_lbl setStringValue: @""];
148     [o_file_subtitles_icon_well setImage: NULL];
149     [o_file_custom_timing_ckb setTitle: _NS("Custom playback")];
150     [o_file_starttime_lbl setStringValue: _NS("Start time")];
151     [o_file_starttime_fld setStringValue: @""];
152     [o_file_stoptime_lbl setStringValue: _NS("Stop time")];
153     [o_file_stoptime_fld setStringValue: @""];
154
155     [o_disc_selector_pop removeAllItems];
156     [o_disc_selector_pop setHidden: NO];
157     NSString *o_videots = _NS("Open VIDEO_TS / BDMV folder");
158     [o_disc_nodisc_lbl setStringValue: _NS("Insert Disc")];
159     [o_disc_nodisc_videots_btn setTitle: o_videots];
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_dvd_lbl setStringValue: @""];
164     [o_disc_dvd_disablemenus_btn setTitle: _NS("Disable DVD menus")];
165     [o_disc_dvd_videots_btn setTitle: o_videots];
166     [o_disc_dvdwomenus_lbl setStringValue: @""];
167     [o_disc_dvdwomenus_enablemenus_btn setTitle: _NS("Enable DVD menus")];
168     [o_disc_dvdwomenus_videots_btn setTitle: o_videots];
169     [o_disc_dvdwomenus_title_lbl setStringValue: _NS("Title")];
170     [o_disc_dvdwomenus_chapter_lbl setStringValue: _NS("Chapter")];
171     [o_disc_vcd_title_lbl setStringValue: _NS("Title")];
172     [o_disc_vcd_chapter_lbl setStringValue: _NS("Chapter")];
173     [o_disc_vcd_videots_btn setTitle: o_videots];
174     [o_disc_bd_videots_btn setTitle: o_videots];
175
176     [o_net_udp_port_lbl setStringValue: _NS("Port")];
177     [o_net_udpm_addr_lbl setStringValue: _NS("IP Address")];
178     [o_net_udpm_port_lbl setStringValue: _NS("Port")];
179     [o_net_http_url_lbl setStringValue: _NS("URL")];
180     [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.")];
181     [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.")];
182     [[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];
183     [o_net_udp_cancel_btn setTitle: _NS("Cancel")];
184     [o_net_udp_ok_btn setTitle: _NS("Open")];
185     [o_net_openUDP_btn setTitle: _NS("Open RTP/UDP Stream")];
186     [o_net_udp_mode_lbl setStringValue: _NS("Mode")];
187     [o_net_udp_protocol_lbl setStringValue: _NS("Protocol")];
188     [o_net_udp_address_lbl setStringValue: _NS("Address")];
189
190     [[o_net_mode cellAtRow:0 column:0] setTitle: _NS("Unicast")];
191     [[o_net_mode cellAtRow:1 column:0] setTitle: _NS("Multicast")];
192
193     [o_net_udp_port setIntValue: config_GetInt(VLCIntf, "server-port")];
194     [o_net_udp_port_stp setIntValue: config_GetInt(VLCIntf, "server-port")];
195
196     [o_eyetv_chn_bgbar setUsesThreadedAnimation: YES];
197
198     [o_capture_mode_pop removeAllItems];
199     [o_capture_mode_pop addItemWithTitle: _NS("Input Devices")];
200     [o_capture_mode_pop addItemWithTitle: _NS("Screen")];
201     [o_capture_mode_pop addItemWithTitle: @"EyeTV"];
202     [o_screen_long_lbl setStringValue: _NS("This input allows you to save, stream or display your current screen contents.")];
203     [o_screen_fps_lbl setStringValue: [NSString stringWithFormat:@"%@:",_NS("Frames per Second")]];
204     [o_screen_screen_lbl setStringValue: [NSString stringWithFormat:@"%@:",_NS("Screen")]];
205     [o_screen_left_lbl setStringValue: [NSString stringWithFormat:@"%@:",_NS("Subscreen left")]];
206     [o_screen_top_lbl setStringValue: [NSString stringWithFormat:@"%@:",_NS("Subscreen top")]];
207     [o_screen_width_lbl setStringValue: [NSString stringWithFormat:@"%@:",_NS("Subscreen width")]];
208     [o_screen_height_lbl setStringValue: [NSString stringWithFormat:@"%@:",_NS("Subscreen height")]];
209     [o_screen_follow_mouse_ckb setTitle: _NS("Follow the mouse")];
210     [o_screen_qtk_audio_ckb setTitle: _NS("Capture Audio")];
211     [o_eyetv_currentChannel_lbl setStringValue: _NS("Current channel:")];
212     [o_eyetv_previousProgram_btn setTitle: _NS("Previous Channel")];
213     [o_eyetv_nextProgram_btn setTitle: _NS("Next Channel")];
214     [o_eyetv_chn_status_txt setStringValue: _NS("Retrieving Channel Info...")];
215     [o_eyetv_noInstance_lbl setStringValue: _NS("EyeTV is not launched")];
216     [o_eyetv_noInstanceLong_lbl setStringValue: _NS("VLC could not connect to EyeTV.\nMake sure that you installed VLC's EyeTV plugin.")];
217     [o_eyetv_launchEyeTV_btn setTitle: _NS("Launch EyeTV now")];
218     [o_eyetv_getPlugin_btn setTitle: _NS("Download Plugin")];
219     [o_capture_width_lbl setStringValue: [NSString stringWithFormat:@"%@:",_NS("Image width")]];
220     [o_capture_height_lbl setStringValue: [NSString stringWithFormat:@"%@:",_NS("Image height")]];
221
222     // setup start / stop time fields
223     [o_file_starttime_fld setFormatter:[[[PositionFormatter alloc] init] autorelease]];
224     [o_file_stoptime_fld setFormatter:[[[PositionFormatter alloc] init] autorelease]];
225
226     [self qtkvideoDevices];
227     [o_qtk_video_device_pop removeAllItems];
228     msg_Dbg(VLCIntf, "Found %lu video capture devices", [qtkvideoDevices count]);
229
230     if ([qtkvideoDevices count] >= 1) {
231         if (!qtk_currdevice_uid)
232             qtk_currdevice_uid = [[[QTCaptureDevice defaultInputDeviceWithMediaType: QTMediaTypeVideo] uniqueID]
233                                                                 stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
234
235         NSUInteger deviceCount = [qtkvideoDevices count];
236         for (int ivideo = 0; ivideo < deviceCount; ivideo++) {
237             QTCaptureDevice *qtk_device;
238             qtk_device = [qtkvideoDevices objectAtIndex:ivideo];
239             [o_qtk_video_device_pop addItemWithTitle: [qtk_device localizedDisplayName]];
240
241             if ([[[qtk_device uniqueID]stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] isEqualToString:qtk_currdevice_uid])
242                 [o_qtk_video_device_pop selectItemAtIndex:ivideo];
243         }
244     } else {
245         [o_qtk_video_device_pop addItemWithTitle: _NS("None")];
246         [qtk_currdevice_uid release];
247     }
248
249     [self qtkaudioDevices];
250     [o_qtk_audio_device_pop removeAllItems];
251     [o_screen_qtk_audio_pop removeAllItems];
252     msg_Dbg(VLCIntf, "Found %lu audio capture devices", [qtkaudioDevices count]);
253
254     if ([qtkaudioDevices count] >= 1) {
255         if (!qtkaudio_currdevice_uid)
256             qtkaudio_currdevice_uid = [[[QTCaptureDevice defaultInputDeviceWithMediaType: QTMediaTypeSound] uniqueID]
257                                   stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
258
259         NSUInteger deviceCount = [qtkaudioDevices count];
260         for (int iaudio = 0; iaudio < deviceCount; iaudio++) {
261             QTCaptureDevice *qtkaudio_device;
262             qtkaudio_device = [qtkaudioDevices objectAtIndex:iaudio];
263             [o_qtk_audio_device_pop addItemWithTitle: [qtkaudio_device localizedDisplayName]];
264             [o_screen_qtk_audio_pop addItemWithTitle: [qtkaudio_device localizedDisplayName]];
265             if ([[[qtkaudio_device uniqueID]stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] isEqualToString:qtkaudio_currdevice_uid]) {
266                 [o_qtk_audio_device_pop selectItemAtIndex:iaudio];
267                 [o_screen_qtk_audio_pop selectItemAtIndex:iaudio];
268             }
269         }
270     } else {
271         [o_qtk_audio_device_pop addItemWithTitle: _NS("None")];
272         [o_screen_qtk_audio_pop addItemWithTitle: _NS("None")];
273         [qtkaudio_currdevice_uid release];
274     }
275
276     [self setSubPanel];
277
278     [[NSNotificationCenter defaultCenter] addObserver: self
279         selector: @selector(openNetInfoChanged:)
280         name: NSControlTextDidChangeNotification
281         object: o_net_udp_port];
282     [[NSNotificationCenter defaultCenter] addObserver: self
283         selector: @selector(openNetInfoChanged:)
284         name: NSControlTextDidChangeNotification
285         object: o_net_udpm_addr];
286     [[NSNotificationCenter defaultCenter] addObserver: self
287         selector: @selector(openNetInfoChanged:)
288         name: NSControlTextDidChangeNotification
289         object: o_net_udpm_port];
290     [[NSNotificationCenter defaultCenter] addObserver: self
291         selector: @selector(openNetInfoChanged:)
292         name: NSControlTextDidChangeNotification
293         object: o_net_http_url];
294
295     [[NSDistributedNotificationCenter defaultCenter] addObserver: self
296                                                         selector: @selector(eyetvChanged:)
297                                                             name: NULL
298                                                           object: @"VLCEyeTVSupport"
299                                               suspensionBehavior: NSNotificationSuspensionBehaviorDeliverImmediately];
300
301     [[NSNotificationCenter defaultCenter] addObserver: self
302                                              selector: @selector(screenFPSfieldChanged:)
303                                                  name: NSControlTextDidChangeNotification
304                                                object: o_screen_fps_fld];
305
306     /* register clicks on text fields */
307     [[NSNotificationCenter defaultCenter] addObserver: self
308                                              selector: @selector(textFieldWasClicked:)
309                                                  name: @"VLCOpenTextFieldWasClicked"
310                                                object: nil];
311
312     /* we want to be notified about removed or added media */
313     o_allMediaDevices = [[NSMutableArray alloc] init];
314     o_specialMediaFolders = [[NSMutableArray alloc] init];
315     o_displayInfos = [[NSMutableArray alloc] init];
316     NSWorkspace *sharedWorkspace = [NSWorkspace sharedWorkspace];
317
318     [[sharedWorkspace notificationCenter] addObserver:self selector:@selector(scanOpticalMedia:) name:NSWorkspaceDidMountNotification object:nil];
319     [[sharedWorkspace notificationCenter] addObserver:self selector:@selector(scanOpticalMedia:) name:NSWorkspaceDidUnmountNotification object:nil];
320
321     [self qtkToggleUIElements:nil];
322     [self scanOpticalMedia:nil];
323
324     [self setMRL: @""];
325 }
326
327 - (void)setMRL:(NSString *)newMRL
328 {
329     if (!newMRL)
330         newMRL = @"";
331
332     if (o_mrl)
333         [o_mrl release];
334
335     o_mrl = newMRL;
336     [o_mrl retain];
337     [o_mrl_fld performSelectorOnMainThread:@selector(setStringValue:) withObject:o_mrl waitUntilDone:NO];
338     if ([o_mrl length] > 0)
339         [o_btn_ok setEnabled: YES];
340     else
341         [o_btn_ok setEnabled: NO];
342 }
343
344 - (NSString *)MRL
345 {
346     return o_mrl;
347 }
348
349 - (void)setSubPanel
350 {
351     int i_index;
352     module_config_t * p_item;
353     intf_thread_t * p_intf = VLCIntf;
354
355     [o_file_sub_ckbox setTitle: _NS("Add Subtitle File:")];
356     [o_file_sub_path_lbl setStringValue: _NS("Choose a file")];
357     [o_file_sub_path_lbl setHidden: NO];
358     [o_file_sub_path_fld setStringValue: @""];
359     [o_file_sub_btn_settings setTitle: _NS("Choose...")];
360     [[o_file_btn_browse cell] accessibilitySetOverrideValue:_NS("Click to setup subtitle playback in full detail.") forAttribute:NSAccessibilityDescriptionAttribute];
361     [o_file_sub_btn_browse setTitle: _NS("Browse...")];
362     [[o_file_sub_btn_browse cell] accessibilitySetOverrideValue:_NS("Click to select a subtitle file.") forAttribute:NSAccessibilityDescriptionAttribute];
363     [o_file_sub_override setTitle: _NS("Override parameters")];
364     [o_file_sub_delay_lbl setStringValue: _NS("Delay")];
365     [o_file_sub_delay_stp setEnabled: NO];
366     [o_file_sub_fps_lbl setStringValue: _NS("FPS")];
367     [o_file_sub_fps_stp setEnabled: NO];
368     [o_file_sub_encoding_lbl setStringValue: _NS("Subtitle encoding")];
369     [o_file_sub_encoding_pop removeAllItems];
370     [o_file_sub_size_lbl setStringValue: _NS("Font size")];
371     [o_file_sub_size_pop removeAllItems];
372     [o_file_sub_align_lbl setStringValue: _NS("Subtitle alignment")];
373     [o_file_sub_align_pop removeAllItems];
374     [o_file_sub_ok_btn setStringValue: _NS("OK")];
375     [[o_file_sub_ok_btn cell] accessibilitySetOverrideValue:_NS("Click to dismiss the subtitle setup dialog.") forAttribute:NSAccessibilityDescriptionAttribute];
376     [o_file_sub_font_box setTitle: _NS("Font Properties")];
377     [o_file_sub_file_box setTitle: _NS("Subtitle File")];
378
379     p_item = config_FindConfig(VLC_OBJECT(p_intf), "subsdec-encoding");
380
381     if (p_item) {
382         for (int i = 0; i < p_item->list_count; i++) {
383             [o_file_sub_encoding_pop addItemWithTitle: _NS(p_item->list_text[i])];
384             [[o_file_sub_encoding_pop lastItem] setRepresentedObject:[NSString stringWithFormat:@"%s", p_item->list.psz[i]]];
385             if (p_item->value.psz && !strcmp(p_item->value.psz, p_item->list.psz[i]))
386                 [o_file_sub_encoding_pop selectItem: [o_file_sub_encoding_pop lastItem]];
387         }
388
389         if ([o_file_sub_encoding_pop indexOfSelectedItem] < 0)
390             [o_file_sub_encoding_pop selectItemAtIndex:0];
391     }
392
393     p_item = config_FindConfig(VLC_OBJECT(p_intf), "subsdec-align");
394
395     if (p_item) {
396         for (i_index = 0; i_index < p_item->list_count; i_index++)
397             [o_file_sub_align_pop addItemWithTitle: _NS(p_item->list_text[i_index])];
398
399         [o_file_sub_align_pop selectItemAtIndex: p_item->value.i];
400     }
401
402     p_item = config_FindConfig(VLC_OBJECT(p_intf), "freetype-rel-fontsize");
403
404     if (p_item) {
405         for (i_index = 0; i_index < p_item->list_count; i_index++) {
406             [o_file_sub_size_pop addItemWithTitle: _NS(p_item->list_text[i_index])];
407
408             if (p_item->value.i == p_item->list.i[i_index])
409                 [o_file_sub_size_pop selectItemAtIndex: i_index];
410         }
411     }
412 }
413
414 - (void)openTarget:(int)i_type
415 {
416     /* check whether we already run a modal dialog */
417     if ([NSApp modalWindow] != nil)
418         return;
419
420     int i_result;
421
422     [o_tabview selectTabViewItemAtIndex: i_type];
423     [o_file_sub_ckbox setState: NSOffState];
424
425     i_result = [NSApp runModalForWindow: o_panel];
426     [o_panel close];
427
428     if (i_result) {
429         NSMutableDictionary *o_dic;
430         NSMutableArray *o_options = [NSMutableArray array];
431
432         o_dic = [NSMutableDictionary dictionaryWithObject: [self MRL] forKey: @"ITEM_URL"];
433         if ([o_file_sub_ckbox state] == NSOnState) {
434             module_config_t * p_item;
435
436             [o_options addObject: [NSString stringWithFormat: @"sub-file=%@", o_sub_path]];
437             if ([o_file_sub_override state] == NSOnState) {
438                 [o_options addObject: [NSString stringWithFormat: @"sub-delay=%f", ([self fileSubDelay] * 10)]];
439                 [o_options addObject: [NSString stringWithFormat: @"sub-fps=%f", [self fileSubFps]]];
440             }
441             [o_options addObject: [NSString stringWithFormat:
442                     @"subsdec-encoding=%@", [[o_file_sub_encoding_pop selectedItem] representedObject]]];
443             [o_options addObject: [NSString stringWithFormat:
444                     @"subsdec-align=%li", [o_file_sub_align_pop indexOfSelectedItem]]];
445
446             p_item = config_FindConfig(VLC_OBJECT(VLCIntf),
447                                             "freetype-rel-fontsize");
448
449             if (p_item) {
450                 [o_options addObject: [NSString stringWithFormat:
451                     @"freetype-rel-fontsize=%i",
452                     p_item->list.i[[o_file_sub_size_pop indexOfSelectedItem]]]];
453             }
454         }
455         if ([o_file_custom_timing_ckb state] == NSOnState) {
456             NSArray * components = [[o_file_starttime_fld stringValue] componentsSeparatedByString:@":"];
457             NSUInteger componentCount = [components count];
458             NSInteger tempValue = 0;
459             if (componentCount == 1)
460                 tempValue = [[components objectAtIndex:0] intValue];
461             else if (componentCount == 2)
462                 tempValue = [[components objectAtIndex:0] intValue] * 60 + [[components objectAtIndex:1] intValue];
463             else if (componentCount == 3)
464                 tempValue = [[components objectAtIndex:0] intValue] * 3600 + [[components objectAtIndex:1] intValue] * 60 + [[components objectAtIndex:2] intValue];
465             if (tempValue > 0)
466                 [o_options addObject: [NSString stringWithFormat:@"start-time=%li", tempValue]];
467             components = [[o_file_stoptime_fld stringValue] componentsSeparatedByString:@":"];
468             componentCount = [components count];
469             if (componentCount == 1)
470                 tempValue = [[components objectAtIndex:0] intValue];
471             else if (componentCount == 2)
472                 tempValue = [[components objectAtIndex:0] intValue] * 60 + [[components objectAtIndex:1] intValue];
473             else if (componentCount == 3)
474                 tempValue = [[components objectAtIndex:0] intValue] * 3600 + [[components objectAtIndex:1] intValue] * 60 + [[components objectAtIndex:2] intValue];
475             if (tempValue > 0)
476                 [o_options addObject: [NSString stringWithFormat:@"stop-time=%li", tempValue]];
477         }
478         if ([o_output_ckbox state] == NSOnState) {
479             NSArray * soutMRL = [o_sout_options soutMRL];
480             NSUInteger count = [soutMRL count];
481             for (NSUInteger i = 0 ; i < count ; i++)
482                 [o_options addObject: [NSString stringWithString: [soutMRL objectAtIndex:i]]];
483         }
484         if ([o_file_slave_ckbox state] && o_file_slave_path)
485            [o_options addObject: [NSString stringWithFormat: @"input-slave=%@", o_file_slave_path]];
486         if ([[[o_tabview selectedTabViewItem] label] isEqualToString: _NS("Capture")]) {
487             if ([[[o_capture_mode_pop selectedItem] title] isEqualToString: _NS("Screen")]) {
488                 int selected_index = [o_screen_screen_pop indexOfSelectedItem];
489                 NSValue *v = [o_displayInfos objectAtIndex:selected_index];
490                 struct display_info_t *item = (struct display_info_t *)[v pointerValue];
491
492                 [o_options addObject: [NSString stringWithFormat: @"screen-fps=%f", [o_screen_fps_fld floatValue]]];
493                 [o_options addObject: [NSString stringWithFormat: @"screen-display-id=%i", item->id]];
494                 [o_options addObject: [NSString stringWithFormat: @"screen-left=%i", [o_screen_left_fld intValue]]];
495                 [o_options addObject: [NSString stringWithFormat: @"screen-top=%i", [o_screen_top_fld intValue]]];
496                 [o_options addObject: [NSString stringWithFormat: @"screen-width=%i", [o_screen_width_fld intValue]]];
497                 [o_options addObject: [NSString stringWithFormat: @"screen-height=%i", [o_screen_height_fld intValue]]];
498                 if ([o_screen_follow_mouse_ckb intValue] == YES)
499                     [o_options addObject: @"screen-follow-mouse"];
500                 else
501                     [o_options addObject: @"no-screen-follow-mouse"];
502                 if ([o_screen_qtk_audio_ckb state] && qtkaudio_currdevice_uid)
503                    [o_options addObject: [NSString stringWithFormat: @"input-slave=qtsound://%@", qtkaudio_currdevice_uid]];
504             }
505             else if ([[[o_capture_mode_pop selectedItem] title] isEqualToString: _NS("Input Devices")]) {
506                 if ([o_qtk_video_ckb state]) {
507                     [o_options addObject: [NSString stringWithFormat: @"qtcapture-width=%i", [o_capture_width_fld intValue]]];
508                     [o_options addObject: [NSString stringWithFormat: @"qtcapture-height=%i", [o_capture_height_fld intValue]]];
509                     if ([o_qtk_audio_ckb state] && qtkaudio_currdevice_uid)
510                        [o_options addObject: [NSString stringWithFormat: @"input-slave=qtsound://%@", qtkaudio_currdevice_uid]];
511                 }
512             }
513         }
514
515         /* apply the options to our item(s) */
516         [o_dic setObject: (NSArray *)[o_options copy] forKey: @"ITEM_OPTIONS"];
517
518         [[[VLCMain sharedInstance] playlist] addPlaylistItems:[NSArray arrayWithObject:o_dic]];
519     }
520 }
521
522 - (IBAction)screenChanged:(id)sender
523 {
524     int selected_index = [o_screen_screen_pop indexOfSelectedItem];
525     if (selected_index >= [o_displayInfos count]) return;
526
527     NSValue *v = [o_displayInfos objectAtIndex:selected_index];
528     struct display_info_t *item = (struct display_info_t *)[v pointerValue];
529
530     [o_screen_left_stp setMaxValue: item->rect.size.width];
531     [o_screen_top_stp setMaxValue: item->rect.size.height];
532     [o_screen_width_stp setMaxValue: item->rect.size.width];
533     [o_screen_height_stp setMaxValue: item->rect.size.height];
534
535     [o_screen_qtk_audio_pop setEnabled: [o_screen_qtk_audio_ckb state]];
536 }
537
538 - (IBAction)qtkChanged:(id)sender
539 {
540     NSInteger i_selectedDevice = [o_qtk_video_device_pop indexOfSelectedItem];
541     if ([qtkvideoDevices count] >= 1) {
542         NSValue *sizes = [[[[qtkvideoDevices objectAtIndex:i_selectedDevice] formatDescriptions] objectAtIndex:0] attributeForKey: QTFormatDescriptionVideoEncodedPixelsSizeAttribute];
543
544         [o_capture_width_fld setIntValue: [sizes sizeValue].width];
545         [o_capture_height_fld setIntValue: [sizes sizeValue].height];
546         [o_capture_width_stp setIntValue: [o_capture_width_fld intValue]];
547         [o_capture_height_stp setIntValue: [o_capture_height_fld intValue]];
548         qtk_currdevice_uid = [[(QTCaptureDevice *)[qtkvideoDevices objectAtIndex:i_selectedDevice] uniqueID] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
549     }
550 }
551
552 - (IBAction)qtkAudioChanged:(id)sender
553 {
554     NSInteger i_selectedDevice = [o_qtk_audio_device_pop indexOfSelectedItem];
555     if ([qtkaudioDevices count] >= 1) {
556         qtkaudio_currdevice_uid = [[(QTCaptureDevice *)[qtkaudioDevices objectAtIndex:i_selectedDevice] uniqueID] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
557     }
558     [o_screen_qtk_audio_pop selectItemAtIndex: i_selectedDevice];
559     [o_qtk_audio_device_pop selectItemAtIndex: i_selectedDevice];
560 }
561
562 - (IBAction)qtkToggleUIElements:(id)sender
563 {
564     [o_qtk_audio_device_pop setEnabled:[o_qtk_audio_ckb state]];
565     BOOL b_state = [o_qtk_video_ckb state];
566     [o_qtk_video_device_pop setEnabled:b_state];
567     [o_capture_width_fld setEnabled:b_state];
568     [o_capture_width_stp setEnabled:b_state];
569     [o_capture_height_fld setEnabled:b_state];
570     [o_capture_height_stp setEnabled:b_state];
571     [self qtkAudioChanged:sender];
572     [self qtkChanged:sender];
573     [self openCaptureModeChanged:sender];
574 }
575
576 #pragma mark -
577 #pragma mark Main Actions
578
579 - (void)tabView:(NSTabView *)o_tv didSelectTabViewItem:(NSTabViewItem *)o_tvi
580 {
581     NSString *o_label = [o_tvi label];
582
583     if ([o_label isEqualToString: _NS("File")])
584         [self openFilePathChanged: nil];
585     else if ([o_label isEqualToString: _NS("Disc")])
586         [self scanOpticalMedia: nil];
587     else if ([o_label isEqualToString: _NS("Network")])
588         [self openNetInfoChanged: nil];
589     else if ([o_label isEqualToString: _NS("Capture")])
590         [self openCaptureModeChanged: nil];
591 }
592
593 - (IBAction)expandMRLfieldAction:(id)sender
594 {
595     NSRect o_win_rect, o_view_rect;
596     o_win_rect = [o_panel frame];
597     o_view_rect = [o_mrl_view frame];
598
599     if ([o_mrl_btn state] == NSOffState) {
600         /* we need to collaps, restore the panel size */
601         o_win_rect.size.height = o_win_rect.size.height - o_view_rect.size.height;
602         o_win_rect.origin.y = (o_win_rect.origin.y + o_view_rect.size.height) - o_view_rect.size.height;
603
604         /* remove the MRL view */
605         [o_mrl_view removeFromSuperview];
606     } else {
607         /* we need to expand */
608         [o_mrl_view setFrame: NSMakeRect(0,
609                                          [o_mrl_btn frame].origin.y,
610                                          o_view_rect.size.width,
611                                          o_view_rect.size.height)];
612         [o_mrl_view setNeedsDisplay: NO];
613         [o_mrl_view setAutoresizesSubviews: YES];
614
615         /* enlarge panel size for MRL view */
616         o_win_rect.size.height = o_win_rect.size.height + o_view_rect.size.height;
617     }
618
619     [[o_panel animator] setFrame: o_win_rect display:YES];
620
621     if ([o_mrl_btn state] == NSOnState)
622         [[o_panel contentView] addSubview: o_mrl_view];
623 }
624
625 - (void)openFileGeneric
626 {
627     [self openFilePathChanged: nil];
628     [self openTarget: 0];
629 }
630
631 - (void)openDisc
632 {
633     @synchronized (self) {
634         [o_specialMediaFolders removeAllObjects];
635     }
636
637     [self scanOpticalMedia: nil];
638     [self openTarget: 1];
639 }
640
641 - (void)openNet
642 {
643     [self openNetInfoChanged: nil];
644     [self openTarget: 2];
645 }
646
647 - (void)openCapture
648 {
649     [self openCaptureModeChanged: nil];
650     [self openTarget: 3];
651 }
652
653 - (void)openFile
654 {
655     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
656
657     [o_open_panel setAllowsMultipleSelection: YES];
658     [o_open_panel setCanChooseDirectories: YES];
659     [o_open_panel setTitle: _NS("Open File")];
660     [o_open_panel setPrompt: _NS("Open")];
661
662     if ([o_open_panel runModal] == NSOKButton) {
663         NSArray * o_urls = [o_open_panel URLs];
664         NSUInteger count = [o_urls count];
665         NSMutableArray *o_values = [NSMutableArray arrayWithCapacity:count];
666         NSMutableArray *o_array = [NSMutableArray arrayWithCapacity:count];
667         for (NSUInteger i = 0; i < count; i++)
668             [o_values addObject: [[o_urls objectAtIndex:i] path]];
669         [o_values sortUsingSelector:@selector(caseInsensitiveCompare:)];
670
671         for (NSUInteger i = 0; i < count; i++) {
672             NSDictionary *o_dic;
673             char *psz_uri = vlc_path2uri([[o_values objectAtIndex:i] UTF8String], "file");
674             if (!psz_uri)
675                 continue;
676
677             o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
678
679             free(psz_uri);
680
681             [o_array addObject: o_dic];
682         }
683
684         [[[VLCMain sharedInstance] playlist] addPlaylistItems:o_array];
685     }
686 }
687
688 #pragma mark -
689 #pragma mark File Panel
690
691 - (void)openFilePathChanged:(NSNotification *)o_notification
692 {
693     if (o_file_path && [o_file_path length] > 0) {
694         bool b_stream = [o_file_stream state];
695         BOOL b_dir = NO;
696
697         [[NSFileManager defaultManager] fileExistsAtPath:o_file_path isDirectory:&b_dir];
698
699         char *psz_uri = vlc_path2uri([o_file_path UTF8String], "file");
700         if (!psz_uri) return;
701
702         NSMutableString *o_mrl_string = [NSMutableString stringWithUTF8String: psz_uri ];
703         NSRange offile = [o_mrl_string rangeOfString:@"file"];
704         free(psz_uri);
705
706         if (b_dir)
707             [o_mrl_string replaceCharactersInRange:offile withString: @"directory"];
708         else if (b_stream)
709             [o_mrl_string replaceCharactersInRange:offile withString: @"stream"];
710
711         [o_file_name setStringValue: [[NSFileManager defaultManager] displayNameAtPath:o_file_path]];
712         [o_file_name_stub setHidden: YES];
713         [o_file_stream setHidden: NO];
714         [o_file_icon_well setImage: [[NSWorkspace sharedWorkspace] iconForFile: o_file_path]];
715         [o_file_icon_well setHidden: NO];
716         [self setMRL: o_mrl_string];
717     } else {
718         [o_file_name setStringValue: @""];
719         [o_file_name_stub setHidden: NO];
720         [o_file_stream setHidden: YES];
721         [o_file_icon_well setImage: [NSImage imageNamed:@"generic"]];
722         [self setMRL: @""];
723     }
724 }
725
726 - (IBAction)openFileBrowse:(id)sender
727 {
728     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
729
730     [o_open_panel setAllowsMultipleSelection: NO];
731     [o_open_panel setCanChooseDirectories: YES];
732     [o_open_panel setTitle: _NS("Open File")];
733     [o_open_panel setPrompt: _NS("Open")];
734     [o_open_panel beginSheetModalForWindow:[sender window] completionHandler:^(NSInteger returnCode) {
735         if (returnCode == NSFileHandlingPanelOKButton) {
736             if (o_file_path)
737                 [o_file_path release];
738             o_file_path = [[[o_open_panel URLs] objectAtIndex:0] path];
739             [o_file_path retain];
740             [self openFilePathChanged: nil];
741         }
742     }];
743 }
744
745 - (IBAction)openFileStreamChanged:(id)sender
746 {
747     [self openFilePathChanged: nil];
748 }
749
750 - (IBAction)inputSlaveAction:(id)sender
751 {
752     if (sender == o_file_slave_ckbox)
753         [o_file_slave_select_btn setEnabled: [o_file_slave_ckbox state]];
754     else {
755         NSOpenPanel *o_open_panel;
756         o_open_panel = [NSOpenPanel openPanel];
757         [o_open_panel setCanChooseFiles: YES];
758         [o_open_panel setCanChooseDirectories: NO];
759         if ([o_open_panel runModal] == NSOKButton) {
760             if (o_file_slave_path)
761                 [o_file_slave_path release];
762             o_file_slave_path = [[[o_open_panel URLs] objectAtIndex:0] path];
763             [o_file_slave_path retain];
764         }
765     }
766     if (o_file_slave_path && [o_file_slave_ckbox state] == NSOnState) {
767         [o_file_slave_filename_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath:o_file_slave_path]];
768         [o_file_slave_icon_well setImage: [[NSWorkspace sharedWorkspace] iconForFile: o_file_slave_path]];
769     } else {
770         [o_file_slave_filename_lbl setStringValue: @""];
771         [o_file_slave_icon_well setImage: NULL];
772     }
773 }
774
775 - (IBAction)fileTimeCustomization:(id)sender
776 {
777     BOOL b_value = [o_file_custom_timing_ckb state];
778     [o_file_starttime_fld setEnabled: b_value];
779     [o_file_starttime_lbl setEnabled: b_value];
780     [o_file_stoptime_fld setEnabled: b_value];
781     [o_file_stoptime_lbl setEnabled: b_value];
782 }
783
784 #pragma mark -
785 #pragma mark Optical Media Panel
786
787 - (void)showOpticalMediaView: theView withIcon:(NSImage *)icon
788 {
789     NSRect o_view_rect;
790     o_view_rect = [theView frame];
791     [theView setFrame: NSMakeRect(233, 0, o_view_rect.size.width, o_view_rect.size.height)];
792     [theView setAutoresizesSubviews: YES];
793     if (o_currentOpticalMediaView) {
794         [[[[o_tabview tabViewItemAtIndex: [o_tabview indexOfTabViewItemWithIdentifier:@"optical"]] view] animator] replaceSubview: o_currentOpticalMediaView with: theView];
795         [o_currentOpticalMediaView release];
796     }
797     else
798         [[[[o_tabview tabViewItemAtIndex: [o_tabview indexOfTabViewItemWithIdentifier:@"optical"]] view] animator] addSubview: theView];
799     o_currentOpticalMediaView = theView;
800     [o_currentOpticalMediaView retain];
801
802     NSImageView *imageView;
803     imageView = [[NSImageView alloc] init];
804     [imageView setFrame: NSMakeRect(53, 61, 128, 128)];
805     [icon setSize: NSMakeSize(128,128)];
806     [imageView setImage: icon];
807     if (o_currentOpticalMediaIconView) {
808         [[[[o_tabview tabViewItemAtIndex: [o_tabview indexOfTabViewItemWithIdentifier:@"optical"]] view] animator] replaceSubview: o_currentOpticalMediaIconView with: imageView];
809         [o_currentOpticalMediaIconView release];
810     }
811     else
812          [[[[o_tabview tabViewItemAtIndex: [o_tabview indexOfTabViewItemWithIdentifier:@"optical"]] view] animator] addSubview: imageView];
813     o_currentOpticalMediaIconView = imageView;
814     [o_currentOpticalMediaIconView retain];
815     [o_currentOpticalMediaView setNeedsDisplay: YES];
816     [o_currentOpticalMediaIconView setNeedsDisplay: YES];
817     [[[o_tabview tabViewItemAtIndex: [o_tabview indexOfTabViewItemWithIdentifier:@"optical"]] view] setNeedsDisplay: YES];
818     [[[o_tabview tabViewItemAtIndex: [o_tabview indexOfTabViewItemWithIdentifier:@"optical"]] view] displayIfNeeded];
819 }
820
821 + (NSString *) getBSDNodeFromMountPath:(NSString *)mountPath
822 {
823     OSStatus err;
824     FSRef ref;
825     FSVolumeRefNum actualVolume;
826     err = FSPathMakeRef ((const UInt8 *) [mountPath fileSystemRepresentation], &ref, NULL);
827
828     // get a FSVolumeRefNum from mountPath
829     if (noErr == err) {
830         FSCatalogInfo   catalogInfo;
831         err = FSGetCatalogInfo (&ref,
832                                 kFSCatInfoVolume,
833                                 &catalogInfo,
834                                 NULL,
835                                 NULL,
836                                 NULL
837                                );
838         if (noErr == err)
839             actualVolume = catalogInfo.volume;
840         else
841             return @"";
842     }
843     else
844         return @"";
845
846     GetVolParmsInfoBuffer volumeParms;
847     err = FSGetVolumeParms(actualVolume, &volumeParms, sizeof(volumeParms));
848     if (noErr == err) {
849         NSString *bsdName = [NSString stringWithUTF8String:(char *)volumeParms.vMDeviceID];
850         return [NSString stringWithFormat:@"/dev/r%@", bsdName];
851     }
852
853     return @"";
854 }
855
856 + (NSString *)getVolumeTypeFromMountPath:(NSString *)mountPath
857 {
858     OSStatus err;
859     FSRef ref;
860     FSVolumeRefNum actualVolume;
861     err = FSPathMakeRef ((const UInt8 *) [mountPath fileSystemRepresentation], &ref, NULL);
862
863     // get a FSVolumeRefNum from mountPath
864     if (noErr == err) {
865         FSCatalogInfo   catalogInfo;
866         err = FSGetCatalogInfo (&ref,
867                                 kFSCatInfoVolume,
868                                 &catalogInfo,
869                                 NULL,
870                                 NULL,
871                                 NULL
872                                );
873         if (noErr == err)
874             actualVolume = catalogInfo.volume;
875         else
876             goto out;
877     }
878     else
879         goto out;
880
881     GetVolParmsInfoBuffer volumeParms;
882     err = FSGetVolumeParms(actualVolume, &volumeParms, sizeof(volumeParms));
883
884     CFMutableDictionaryRef matchingDict;
885     io_service_t service;
886
887     if (!volumeParms.vMDeviceID) {
888         goto out;
889     }
890
891     matchingDict = IOBSDNameMatching(kIOMasterPortDefault, 0, volumeParms.vMDeviceID);
892     service = IOServiceGetMatchingService(kIOMasterPortDefault, matchingDict);
893
894
895     NSString *returnValue = nil;
896     if (IO_OBJECT_NULL != service) {
897         if (IOObjectConformsTo(service, kIOCDMediaClass))
898             returnValue = kVLCMediaAudioCD;
899         else if (IOObjectConformsTo(service, kIODVDMediaClass))
900             returnValue = kVLCMediaDVD;
901         else if (IOObjectConformsTo(service, kIOBDMediaClass))
902             returnValue = kVLCMediaBD;
903         IOObjectRelease(service);
904
905         if (returnValue)
906             return returnValue;
907     }
908
909 out:
910     if ([mountPath rangeOfString:@"VIDEO_TS" options:NSCaseInsensitiveSearch | NSBackwardsSearch].location != NSNotFound)
911         returnValue = kVLCMediaVideoTSFolder;
912     else if ([mountPath rangeOfString:@"BDMV" options:NSCaseInsensitiveSearch | NSBackwardsSearch].location != NSNotFound)
913         returnValue = kVLCMediaBDMVFolder;
914     else {
915         // NSFileManager is not thread-safe, don't use defaultManager outside of the main thread
916         NSFileManager * fm = [[NSFileManager alloc] init];
917
918         NSArray *dirContents = [fm contentsOfDirectoryAtPath:mountPath error:nil];
919         for (int i = 0; i < [dirContents count]; i++) {
920             NSString *currentFile = [dirContents objectAtIndex:i];
921             NSString *fullPath = [mountPath stringByAppendingPathComponent:currentFile];
922
923             BOOL isDir;
924             if ([fm fileExistsAtPath:fullPath isDirectory:&isDir] && isDir)
925             {
926                 if ([currentFile caseInsensitiveCompare:@"SVCD"] == NSOrderedSame) {
927                     returnValue = kVLCMediaSVCD;
928                     break;
929                 }
930                 if ([currentFile caseInsensitiveCompare:@"VCD"] == NSOrderedSame) {
931                     returnValue = kVLCMediaVCD;
932                     break;
933                 }
934                 if ([currentFile caseInsensitiveCompare:@"BDMV"] == NSOrderedSame) {
935                     returnValue = kVLCMediaBDMVFolder;
936                     break;
937                 }
938                 if ([currentFile caseInsensitiveCompare:@"VIDEO_TS"] == NSOrderedSame) {
939                     returnValue = kVLCMediaVideoTSFolder;
940                     break;
941                 }
942             }
943         }
944
945         [fm release];
946
947         if (!returnValue)
948             returnValue = kVLCMediaVideoTSFolder;
949     }
950
951     return returnValue;
952 }
953
954 - (void)showOpticalAtPath: (NSDictionary *)o_dict
955 {
956     NSString *diskType = [o_dict objectForKey:@"mediaType"];
957     NSString *o_opticalDevicePath = [o_dict objectForKey:@"path"];
958     NSString *o_device_path = [o_dict objectForKey:@"devicePath"];
959     NSImage *o_image = [o_dict objectForKey:@"image"];
960
961     if ([diskType isEqualToString: kVLCMediaDVD] || [diskType isEqualToString: kVLCMediaVideoTSFolder]) {
962         [o_disc_dvd_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath:o_opticalDevicePath]];
963         [o_disc_dvdwomenus_lbl setStringValue: [o_disc_dvd_lbl stringValue]];
964
965         if (!b_nodvdmenus) {
966             [self setMRL: [NSString stringWithFormat: @"dvdnav://%@", o_device_path]];
967             [self showOpticalMediaView: o_disc_dvd_view withIcon:o_image];
968         } else {
969             [self setMRL: [NSString stringWithFormat: @"dvdread://%@#%i:%i-", o_device_path, [o_disc_dvdwomenus_title intValue], [o_disc_dvdwomenus_chapter intValue]]];
970             [self showOpticalMediaView: o_disc_dvdwomenus_view withIcon: o_image];
971         }
972     } else if ([diskType isEqualToString: kVLCMediaAudioCD]) {
973         [o_disc_audiocd_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath: o_opticalDevicePath]];
974         [o_disc_audiocd_trackcount_lbl setStringValue: [NSString stringWithFormat:_NS("%i tracks"), [[[NSFileManager defaultManager] subpathsOfDirectoryAtPath: o_opticalDevicePath error:NULL] count] - 1]]; // minus .TOC.plist
975         [self showOpticalMediaView: o_disc_audiocd_view withIcon: o_image];
976         [self setMRL: [NSString stringWithFormat: @"cdda://%@", o_device_path]];
977     } else if ([diskType isEqualToString: kVLCMediaVCD]) {
978         [o_disc_vcd_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath: o_opticalDevicePath]];
979         [self showOpticalMediaView: o_disc_vcd_view withIcon: o_image];
980         [self setMRL: [NSString stringWithFormat: @"vcd://%@#%i:%i", o_device_path, [o_disc_vcd_title intValue], [o_disc_vcd_chapter intValue]]];
981     } else if ([diskType isEqualToString: kVLCMediaSVCD]) {
982         [o_disc_vcd_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath: o_opticalDevicePath]];
983         [self showOpticalMediaView: o_disc_vcd_view withIcon: o_image];
984         [self setMRL: [NSString stringWithFormat: @"vcd://%@@%i:%i", o_device_path, [o_disc_vcd_title intValue], [o_disc_vcd_chapter intValue]]];
985     } else if ([diskType isEqualToString: kVLCMediaBD] || [diskType isEqualToString: kVLCMediaBDMVFolder]) {
986         [o_disc_bd_lbl setStringValue: [[NSFileManager defaultManager] displayNameAtPath: o_opticalDevicePath]];
987         [self showOpticalMediaView: o_disc_bd_view withIcon: o_image];
988         [self setMRL: [NSString stringWithFormat: @"bluray://%@", o_opticalDevicePath]];
989     } else {
990         if (VLCIntf)
991             msg_Warn(VLCIntf, "unknown disk type, no idea what to display");
992
993         [self showOpticalMediaView: o_disc_nodisc_view withIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
994     }
995 }
996
997 - (NSDictionary *)scanPath:(NSString *)o_path
998 {
999     NSString *o_type = [VLCOpen getVolumeTypeFromMountPath:o_path];
1000     NSImage *o_image = [[NSWorkspace sharedWorkspace] iconForFile: o_path];
1001     NSString *o_device_path;
1002
1003     // BDMV path must not end with BDMV directory
1004     if([o_type isEqualToString: kVLCMediaBDMVFolder]) {
1005         if([[o_path lastPathComponent] isEqualToString: @"BDMV"]) {
1006             o_path = [o_path stringByDeletingLastPathComponent];
1007         }
1008     }
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 = [VLCOpen 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:[NSNumber numberWithBool: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:[NSArray arrayWithObject:@"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]) != nil)
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 #pragma mark -
1573 #pragma mark Miscellaneous
1574
1575 - (IBAction)panelCancel:(id)sender
1576 {
1577     [NSApp stopModalWithCode: 0];
1578 }
1579
1580 - (IBAction)panelOk:(id)sender
1581 {
1582     if ([[self MRL] length])
1583         [NSApp stopModalWithCode: 1];
1584     else
1585         NSBeep();
1586 }
1587
1588 - (NSArray *)qtkvideoDevices
1589 {
1590     if (!qtkvideoDevices)
1591         [self qtkrefreshVideoDevices];
1592     return qtkvideoDevices;
1593 }
1594
1595 - (void)qtkrefreshVideoDevices
1596 {
1597     [qtkvideoDevices release];
1598     qtkvideoDevices = [[[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeVideo] arrayByAddingObjectsFromArray:[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeMuxed]] retain];
1599 }
1600
1601 - (NSArray *)qtkaudioDevices
1602 {
1603     if (!qtkaudioDevices)
1604         [self qtkrefreshAudioDevices];
1605     return qtkaudioDevices;
1606 }
1607
1608 - (void)qtkrefreshAudioDevices
1609 {
1610     [qtkaudioDevices release];
1611     qtkaudioDevices = [[[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeSound] arrayByAddingObjectsFromArray:[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeMuxed]] retain];
1612 }
1613
1614 @end
1615
1616 @implementation VLCOpenTextField
1617
1618 - (void)mouseDown:(NSEvent *)theEvent
1619 {
1620     [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCOpenTextFieldWasClicked"
1621                                                         object: self];
1622     [super mouseDown: theEvent];
1623 }
1624
1625 @end