]> git.sesse.net Git - vlc/blob - modules/gui/macosx/open.m
* OSX lowlevel work
[vlc] / modules / gui / macosx / open.m
1 /*****************************************************************************
2  * open.m: MacOS X module for vlc
3  *****************************************************************************
4  * Copyright (C) 2002-2003 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  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <stdlib.h>                                      /* malloc(), free() */
30 #include <sys/param.h>                                    /* for MAXPATHLEN */
31 #include <string.h>
32
33 #include <paths.h>
34 #include <IOKit/IOKitLib.h>
35 #include <IOKit/IOBSD.h>
36 #include <IOKit/storage/IOMedia.h>
37 #include <IOKit/storage/IOCDMedia.h>
38 #include <IOKit/storage/IODVDMedia.h>
39
40 #include "intf.h"
41 #include "playlist.h"
42 #include "open.h"
43 #include "output.h"
44
45 /*****************************************************************************
46  * GetEjectableMediaOfClass 
47  *****************************************************************************/
48 NSArray *GetEjectableMediaOfClass( const char *psz_class )
49 {
50     io_object_t next_media;
51     mach_port_t master_port;
52     kern_return_t kern_result;
53     NSArray *o_devices = nil;
54     NSMutableArray *p_list = nil;
55     io_iterator_t media_iterator;
56     CFMutableDictionaryRef classes_to_match;
57
58     kern_result = IOMasterPort( MACH_PORT_NULL, &master_port );
59     if( kern_result != KERN_SUCCESS )
60     {
61         return( nil );
62     }
63     
64     classes_to_match = IOServiceMatching( psz_class );
65     if( classes_to_match == NULL )
66     {
67         return( nil );
68     }
69     
70     CFDictionarySetValue( classes_to_match, CFSTR( kIOMediaEjectableKey ), 
71                           kCFBooleanTrue );
72     
73     kern_result = IOServiceGetMatchingServices( master_port, classes_to_match, 
74                                                 &media_iterator );
75     if( kern_result != KERN_SUCCESS )
76     {
77         return( nil );
78     }
79
80     p_list = [NSMutableArray arrayWithCapacity: 1];
81     
82     next_media = IOIteratorNext( media_iterator );
83     if( next_media != NULL )
84     {
85         char psz_buf[0x32];
86         size_t dev_path_length;
87         CFTypeRef str_bsd_path;
88     
89         do
90         {
91             str_bsd_path = IORegistryEntryCreateCFProperty( next_media,
92                                                             CFSTR( kIOBSDNameKey ),
93                                                             kCFAllocatorDefault,
94                                                             0 );
95             if( str_bsd_path == NULL )
96             {
97                 IOObjectRelease( next_media );
98                 continue;
99             }
100             
101             snprintf( psz_buf, sizeof(psz_buf), "%s%c", _PATH_DEV, 'r' );
102             dev_path_length = strlen( psz_buf );
103             
104             if( CFStringGetCString( str_bsd_path,
105                                     (char*)&psz_buf + dev_path_length,
106                                     sizeof(psz_buf) - dev_path_length,
107                                     kCFStringEncodingASCII ) )
108             {
109                 [p_list addObject: [NSString stringWithCString: psz_buf]];
110             }
111             
112             CFRelease( str_bsd_path );
113             
114             IOObjectRelease( next_media );
115         
116         } while( ( next_media = IOIteratorNext( media_iterator ) ) != NULL );
117     }
118     
119     IOObjectRelease( media_iterator );
120
121     o_devices = [NSArray arrayWithArray: p_list];
122
123     return( o_devices );
124 }
125
126 /*****************************************************************************
127  * VLCOpen implementation 
128  *****************************************************************************/
129 @implementation VLCOpen
130
131 - (void)awakeFromNib
132 {
133     intf_thread_t * p_intf = VLCIntf;
134
135     [o_panel setTitle: _NS("Open Source")];
136     [o_mrl_lbl setTitle: _NS("Media Resource Locator (MRL)")];
137
138     [o_btn_ok setTitle: _NS("OK")];
139     [o_btn_cancel setTitle: _NS("Cancel")];
140
141     [[o_tabview tabViewItemAtIndex: 0] setLabel: _NS("File")];
142     [[o_tabview tabViewItemAtIndex: 1] setLabel: _NS("Disc")];
143     [[o_tabview tabViewItemAtIndex: 2] setLabel: _NS("Network")];
144
145     [o_file_btn_browse setTitle: _NS("Browse...")];
146     [o_file_stream setTitle: _NS("Treat as a pipe rather than as a file")];
147
148     [o_disc_device_lbl setStringValue: _NS("Device name")];
149     [o_disc_title_lbl setStringValue: _NS("Title")];
150     [o_disc_chapter_lbl setStringValue: _NS("Chapter")];
151     [o_disc_videots_btn_browse setTitle: _NS("Browse...")];
152     [o_disc_dvd_menus setTitle: _NS("Use DVD menus")];
153
154     [[o_disc_type cellAtRow:0 column:0] setTitle: _NS("VIDEO_TS folder")];
155     [[o_disc_type cellAtRow:1 column:0] setTitle: _NS("DVD")];
156     [[o_disc_type cellAtRow:2 column:0] setTitle: _NS("VCD")];
157     [[o_disc_type cellAtRow:3 column:0] setTitle: _NS("Audio CD")];
158
159     [o_net_udp_port_lbl setStringValue: _NS("Port")];
160     [o_net_udpm_addr_lbl setStringValue: _NS("Address")];
161     [o_net_udpm_port_lbl setStringValue: _NS("Port")];
162     [o_net_http_url_lbl setStringValue: _NS("URL")];
163
164     [[o_net_mode cellAtRow:0 column:0] setTitle: _NS("UDP/RTP")];
165     [[o_net_mode cellAtRow:1 column:0] setTitle: _NS("UDP/RTP Multicast")];
166     [[o_net_mode cellAtRow:2 column:0] setTitle: _NS("HTTP/FTP/MMS")];
167
168     [o_net_udp_port setIntValue: config_GetInt( p_intf, "server-port" )];
169     [o_net_udp_port_stp setIntValue: config_GetInt( p_intf, "server-port" )];
170
171     [o_file_sub_ckbox setTitle: _NS("Load subtitles file:")];
172     [o_file_sub_btn_settings setTitle: _NS("Settings...")];
173     [o_file_sub_btn_browse setTitle: _NS("Browse...")];
174     [o_file_sub_override setTitle: _NS("Override")];
175     [o_file_sub_delay_lbl setStringValue: _NS("delay")];
176     [o_file_sub_delay_stp setEnabled: NO];
177     [o_file_sub_fps_lbl setStringValue: _NS("fps")];
178     [o_file_sub_fps_stp setEnabled: NO];
179     [o_file_sub_ok_btn setStringValue: _NS("OK")];
180     
181     [[NSNotificationCenter defaultCenter] addObserver: self
182         selector: @selector(openFilePathChanged:)
183         name: NSControlTextDidChangeNotification
184         object: o_file_path];
185
186     [[NSNotificationCenter defaultCenter] addObserver: self
187         selector: @selector(openDiscInfoChanged:)
188         name: NSControlTextDidChangeNotification
189         object: o_disc_device];
190     [[NSNotificationCenter defaultCenter] addObserver: self
191         selector: @selector(openDiscInfoChanged:)
192         name: NSControlTextDidChangeNotification
193         object: o_disc_title];
194     [[NSNotificationCenter defaultCenter] addObserver: self
195         selector: @selector(openDiscInfoChanged:)
196         name: NSControlTextDidChangeNotification
197         object: o_disc_chapter];
198     [[NSNotificationCenter defaultCenter] addObserver: self
199         selector: @selector(openDiscInfoChanged:)
200         name: NSControlTextDidChangeNotification
201         object: o_disc_videots_folder];
202
203     [[NSNotificationCenter defaultCenter] addObserver: self
204         selector: @selector(openNetInfoChanged:)
205         name: NSControlTextDidChangeNotification
206         object: o_net_udp_port];
207     [[NSNotificationCenter defaultCenter] addObserver: self
208         selector: @selector(openNetInfoChanged:)
209         name: NSControlTextDidChangeNotification
210         object: o_net_udpm_addr];
211     [[NSNotificationCenter defaultCenter] addObserver: self
212         selector: @selector(openNetInfoChanged:)
213         name: NSControlTextDidChangeNotification
214         object: o_net_udpm_port];
215     [[NSNotificationCenter defaultCenter] addObserver: self
216         selector: @selector(openNetInfoChanged:)
217         name: NSControlTextDidChangeNotification
218         object: o_net_http_url];
219 }
220
221 - (void)openTarget:(int)i_type
222 {
223     int i_result;
224
225     [o_tabview selectTabViewItemAtIndex: i_type];
226     [o_file_sub_ckbox setState: NSOffState];
227     
228     i_result = [NSApp runModalForWindow: o_panel];
229     [o_panel close];
230
231     if( i_result )
232     {
233         NSMutableDictionary *o_dic;
234         NSMutableArray *o_options = [NSMutableArray array];
235         o_dic = [NSMutableDictionary dictionaryWithObject: [o_mrl stringValue] forKey: @"ITEM_URL"];
236         
237         if( [o_file_sub_ckbox state] == NSOnState )
238         {
239             [o_options addObject: [NSString stringWithFormat: @"sub-file=%s", [[o_file_sub_path stringValue] UTF8String]]];
240             if( [o_file_sub_override state] == NSOnState )
241             {
242                 [o_options addObject: [NSString stringWithFormat: @"sub-delay=%i", (int)( [o_file_sub_delay intValue] * 10 )]];
243                 [o_options addObject: [NSString stringWithFormat: @"sub-fps=%f", [o_file_sub_fps floatValue]]];
244             }
245         }
246         if( [o_output_ckbox state] == NSOnState )
247         {
248             [o_options addObject: [NSString stringWithString: [(VLCOutput *)o_sout_options getMRL]]];
249         }
250         [o_dic setObject: (NSArray *)[o_options copy] forKey: @"ITEM_OPTIONS"];
251         [o_playlist appendArray: [NSArray arrayWithObject: o_dic] atPos: -1 enqueue:NO];
252     }
253 }
254
255 - (void)tabView:(NSTabView *)o_tv didSelectTabViewItem:(NSTabViewItem *)o_tvi
256 {
257     NSString *o_label = [o_tvi label];
258
259     if( [o_label isEqualToString: _NS("File")] )
260     {
261         [self openFilePathChanged: nil];
262     }
263     else if( [o_label isEqualToString: _NS("Disc")] )
264     {
265         [self openDiscTypeChanged: nil];
266     }
267     else if( [o_label isEqualToString: _NS("Network")] )
268     {
269         [self openNetModeChanged: nil];
270     }  
271 }
272
273 - (IBAction)openFileGeneric:(id)sender
274 {
275     [self openFilePathChanged: nil];
276     [self openTarget: 0];
277 }
278
279 - (IBAction)openDisc:(id)sender
280 {
281     [self openDiscTypeChanged: nil];
282     [self openTarget: 1];
283 }
284
285 - (IBAction)openNet:(id)sender
286 {
287     [self openNetModeChanged: nil];
288     [self openTarget: 2];
289 }
290
291 - (void)openFilePathChanged:(NSNotification *)o_notification
292 {
293     NSString *o_mrl_string;
294     NSString *o_filename = [o_file_path stringValue];
295     NSString *o_ext = [o_filename pathExtension];
296     vlc_bool_t b_stream = [o_file_stream state];
297     BOOL b_dir = NO;
298     
299     [[NSFileManager defaultManager] fileExistsAtPath:o_filename isDirectory:&b_dir];
300
301     if( b_dir )
302     {
303         o_mrl_string = [NSString stringWithFormat: @"dir:%@", o_filename];
304     }
305     else if( [o_ext isEqualToString: @"bin"] ||
306         [o_ext isEqualToString: @"cue"] ||
307         [o_ext isEqualToString: @"vob"] ||
308         [o_ext isEqualToString: @"iso"] )
309     {
310         o_mrl_string = o_filename;
311     }
312     else
313     {
314         o_mrl_string = [NSString stringWithFormat: @"%s://%@",
315                         b_stream ? "stream" : "file",
316                         o_filename];
317     }
318     [o_mrl setStringValue: o_mrl_string]; 
319 }
320
321 - (IBAction)openFileBrowse:(id)sender
322 {
323     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
324     
325     [o_open_panel setAllowsMultipleSelection: NO];
326     [o_open_panel setCanChooseDirectories: YES];
327     [o_open_panel setTitle: _NS("Open File")];
328     [o_open_panel setPrompt: _NS("Open")];
329
330     [o_open_panel beginSheetForDirectory:nil
331         file:nil
332         types:nil
333         modalForWindow:[sender window]
334         modalDelegate: self
335         didEndSelector: @selector(pathChosenInPanel: 
336                         withReturn:
337                         contextInfo:)
338         contextInfo: nil];
339 }
340
341 - (void)pathChosenInPanel: (NSOpenPanel *) sheet withReturn:(int)returnCode contextInfo:(void  *)contextInfo
342 {
343     if (returnCode == NSFileHandlingPanelOKButton)
344     {
345         NSString *o_filename = [[sheet filenames] objectAtIndex: 0];
346         [o_file_path setStringValue: o_filename];
347         [self openFilePathChanged: nil];
348     }
349 }
350
351 - (IBAction)openFileStreamChanged:(id)sender
352 {
353     [self openFilePathChanged: nil];
354 }
355
356 - (IBAction)openDiscTypeChanged:(id)sender
357 {
358     NSString *o_type;
359     vlc_bool_t b_device, b_menus, b_title_chapter;
360     
361     [o_disc_device removeAllItems];
362     b_title_chapter = ![o_disc_dvd_menus state];
363     
364     o_type = [[o_disc_type selectedCell] title];
365
366     if ( [o_type isEqualToString: _NS("VIDEO_TS folder")] )
367     {
368         b_device = 0; b_menus = 1;
369     }
370     else
371     {
372         NSArray *o_devices;
373         NSString *o_disc;
374         const char *psz_class = NULL;
375         b_device = 1;
376
377         if ( [o_type isEqualToString: _NS("VCD")] )
378         {
379             psz_class = kIOCDMediaClass;
380             o_disc = o_type;
381             b_menus = 0; b_title_chapter = 1;
382             [o_disc_dvd_menus setState: FALSE];
383         }
384         else if ( [o_type isEqualToString: _NS("Audio CD")])
385         {
386             psz_class = kIOCDMediaClass;
387             o_disc = o_type;
388             b_menus = 0; b_title_chapter = 0;
389             [o_disc_dvd_menus setState: FALSE];
390         }
391         else
392         {
393             psz_class = kIODVDMediaClass;
394             o_disc = o_type;
395             b_menus = 1;
396         }
397     
398         o_devices = GetEjectableMediaOfClass( psz_class );
399         if ( o_devices != nil )
400         {
401             int i_devices = [o_devices count];
402         
403             if ( i_devices )
404             {
405                 int i;
406         
407                 for( i = 0; i < i_devices; i++ )
408                 {
409                     [o_disc_device 
410                         addItemWithObjectValue: [o_devices objectAtIndex: i]];
411                 }
412
413                 [o_disc_device selectItemAtIndex: 0];
414             }
415             else
416             {
417                 [o_disc_device setStringValue: 
418                     [NSString stringWithFormat: _NS("No %@s found"), o_disc]];
419             }
420         }
421     }
422
423     [o_disc_device setEnabled: b_device];
424     [o_disc_title setEnabled: b_title_chapter];
425     [o_disc_title_stp setEnabled: b_title_chapter];
426     [o_disc_chapter setEnabled: b_title_chapter];
427     [o_disc_chapter_stp setEnabled: b_title_chapter];
428     [o_disc_videots_folder setEnabled: !b_device];
429     [o_disc_videots_btn_browse setEnabled: !b_device];
430     [o_disc_dvd_menus setEnabled: b_menus];
431
432     [self openDiscInfoChanged: nil];
433 }
434
435 - (IBAction)openDiscStepperChanged:(id)sender
436 {
437     int i_tag = [sender tag];
438
439     if( i_tag == 0 )
440     {
441         [o_disc_title setIntValue: [o_disc_title_stp intValue]];
442     }
443     else if( i_tag == 1 )
444     {
445         [o_disc_chapter setIntValue: [o_disc_chapter_stp intValue]];
446     }
447
448     [self openDiscInfoChanged: nil];
449 }
450
451 - (void)openDiscInfoChanged:(NSNotification *)o_notification
452 {
453     NSString *o_type;
454     NSString *o_device;
455     NSString *o_videots;
456     NSString *o_mrl_string;
457     int i_title, i_chapter;
458     vlc_bool_t b_menus;
459
460     o_type = [[o_disc_type selectedCell] title];
461     o_device = [o_disc_device stringValue];
462     i_title = [o_disc_title intValue];
463     i_chapter = [o_disc_chapter intValue];
464     o_videots = [o_disc_videots_folder stringValue];
465     b_menus = [o_disc_dvd_menus state];
466
467     if ( [o_type isEqualToString: _NS("VCD")] )
468     {
469         if ( [o_device isEqualToString:
470                 [NSString stringWithFormat: _NS("No %@s found"), o_type]] )
471             o_device = @"";
472         o_mrl_string = [NSString stringWithFormat: @"vcd://%@@%i,%i",
473                         o_device, i_title, i_chapter]; 
474     }
475     else if ( [o_type isEqualToString: _NS("Audio CD")] )
476     {
477         if ( [o_device isEqualToString:
478                 [NSString stringWithFormat: _NS("No %@s found"), o_type]] )
479             o_device = @"";
480         o_mrl_string = [NSString stringWithFormat: @"cdda://%@",
481                         o_device]; 
482     }
483     else if ( [o_type isEqualToString: _NS("DVD")] )
484     {
485         if ( [o_device isEqualToString:
486                 [NSString stringWithFormat: _NS("No %@s found"), o_type]] )
487             o_device = @"";
488         if ( b_menus )
489             o_mrl_string = [NSString stringWithFormat: @"dvdplay://%@",
490                             o_device]; 
491         else
492             o_mrl_string = [NSString stringWithFormat: @"dvdold://%@@%i,%i",
493                             o_device, i_title, i_chapter]; 
494     }
495     else /* VIDEO_TS folder */
496     {
497         if ( b_menus )
498             o_mrl_string = [NSString stringWithFormat: @"dvdplay://%@",
499                             o_videots]; 
500         else
501             o_mrl_string = [NSString stringWithFormat: @"dvdread://%@@%i,%i",
502                             o_videots, i_title, i_chapter]; 
503     }
504
505     [o_mrl setStringValue: o_mrl_string]; 
506 }
507
508 - (IBAction)openDiscMenusChanged:(id)sender
509 {
510     [self openDiscInfoChanged: nil];
511     [self openDiscTypeChanged: nil];
512 }
513
514 - (IBAction)openVTSBrowse:(id)sender
515 {
516     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
517
518     [o_open_panel setAllowsMultipleSelection: NO];
519     [o_open_panel setCanChooseFiles: NO];
520     [o_open_panel setCanChooseDirectories: YES];
521     [o_open_panel setTitle: _NS("Open VIDEO_TS Directory")];
522     [o_open_panel setPrompt: _NS("Open")];
523
524     if( [o_open_panel runModalForDirectory: nil
525             file: nil types: nil] == NSOKButton )
526     {
527         NSString *o_dirname = [[o_open_panel filenames] objectAtIndex: 0];
528         [o_disc_videots_folder setStringValue: o_dirname];
529         [self openDiscInfoChanged: nil];
530     }
531 }
532
533 - (IBAction)openNetModeChanged:(id)sender
534 {
535     NSString *o_mode;
536     BOOL b_udp = FALSE;
537     BOOL b_udpm = FALSE;
538     BOOL b_http = FALSE;
539
540     o_mode = [[o_net_mode selectedCell] title];
541
542     if( [o_mode isEqualToString: _NS("UDP/RTP")] ) b_udp = TRUE;   
543     else if( [o_mode isEqualToString: _NS("UDP/RTP Multicast")] ) b_udpm = TRUE;
544     else if( [o_mode isEqualToString: _NS("HTTP/FTP/MMS")] ) b_http = TRUE;
545
546     [o_net_udp_port setEnabled: b_udp];
547     [o_net_udp_port_stp setEnabled: b_udp];
548     [o_net_udpm_addr setEnabled: b_udpm];
549     [o_net_udpm_port setEnabled: b_udpm];
550     [o_net_udpm_port_stp setEnabled: b_udpm];
551     [o_net_http_url setEnabled: b_http];
552
553     [self openNetInfoChanged: nil];
554 }
555
556 - (IBAction)openNetStepperChanged:(id)sender
557 {
558     int i_tag = [sender tag];
559
560     if( i_tag == 0 )
561     {
562         [o_net_udp_port setIntValue: [o_net_udp_port_stp intValue]];
563     }
564     else if( i_tag == 1 )
565     {
566         [o_net_udpm_port setIntValue: [o_net_udpm_port_stp intValue]];
567     }
568
569     [self openNetInfoChanged: nil];
570 }
571
572 - (void)openNetInfoChanged:(NSNotification *)o_notification
573 {
574     NSString *o_mode;
575     NSString *o_mrl_string = [NSString string];
576     intf_thread_t * p_intf = VLCIntf;
577
578     o_mode = [[o_net_mode selectedCell] title];
579
580     if( [o_mode isEqualToString: _NS("UDP/RTP")] )
581     {
582         int i_port = [o_net_udp_port intValue];
583
584         o_mrl_string = [NSString stringWithString: @"udp://"]; 
585
586         if( i_port != config_GetInt( p_intf, "server-port" ) )
587         {
588             o_mrl_string = 
589                 [o_mrl_string stringByAppendingFormat: @"@:%i", i_port]; 
590         } 
591     }
592     else if( [o_mode isEqualToString: _NS("UDP/RTP Multicast")] ) 
593     {
594         NSString *o_addr = [o_net_udpm_addr stringValue];
595         int i_port = [o_net_udpm_port intValue];
596
597         o_mrl_string = [NSString stringWithFormat: @"udp://@%@", o_addr]; 
598
599         if( i_port != config_GetInt( p_intf, "server-port" ) )
600         {
601             o_mrl_string = 
602                 [o_mrl_string stringByAppendingFormat: @":%i", i_port]; 
603         } 
604     }
605     else if( [o_mode isEqualToString: _NS("HTTP/FTP/MMS")] )
606     {
607         NSString *o_url = [o_net_http_url stringValue];
608
609         if ( ![o_url hasPrefix:@"http:"] && ![o_url hasPrefix:@"ftp:"]
610               && ![o_url hasPrefix:@"mms"] )
611             o_mrl_string = [NSString stringWithFormat: @"http://%@", o_url];
612         else
613             o_mrl_string = o_url;
614     }
615
616     [o_mrl setStringValue: o_mrl_string];
617 }
618
619 - (IBAction)openFile:(id)sender
620 {
621     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
622     int i;
623     
624     [o_open_panel setAllowsMultipleSelection: YES];
625     [o_open_panel setCanChooseDirectories: YES];
626     [o_open_panel setTitle: _NS("Open File")];
627     [o_open_panel setPrompt: _NS("Open")];
628     
629     if( [o_open_panel runModalForDirectory: nil
630             file: nil types: nil] == NSOKButton )
631     {
632         NSArray *o_array = [NSArray array];
633         NSArray *o_values = [[o_open_panel filenames]
634                 sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
635
636         for( i = 0; i < (int)[o_values count]; i++)
637         {
638             NSDictionary *o_dic;
639             o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"];
640             o_array = [o_array arrayByAddingObject: o_dic];
641         }
642         [o_playlist appendArray: o_array atPos: -1 enqueue:NO];
643     }
644 }
645
646 - (IBAction)subsChanged:(id)sender
647 {
648     if ([o_file_sub_ckbox state] == NSOnState)
649     {
650         [o_file_sub_btn_settings setEnabled:YES];
651     }
652     else
653     {
654         [o_file_sub_btn_settings setEnabled:NO];
655     }
656 }
657
658 - (IBAction)subSettings:(id)sender
659 {
660     [NSApp beginSheet: o_file_sub_sheet
661         modalForWindow: [sender window]
662         modalDelegate: self
663         didEndSelector: NULL
664         contextInfo: nil];
665 }
666
667 - (IBAction)subFileBrowse:(id)sender
668 {
669     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
670     
671     [o_open_panel setAllowsMultipleSelection: NO];
672     [o_open_panel setTitle: _NS("Open File")];
673     [o_open_panel setPrompt: _NS("Open")];
674
675     if( [o_open_panel runModalForDirectory: nil 
676             file: nil types: nil] == NSOKButton )
677     {
678         NSString *o_filename = [[o_open_panel filenames] objectAtIndex: 0];
679         [o_file_sub_path setStringValue: o_filename];
680     }
681 }
682
683 - (IBAction)subOverride:(id)sender
684 {
685     BOOL b_state = [o_file_sub_override state];
686     [o_file_sub_delay setEnabled: b_state];
687     [o_file_sub_delay_stp setEnabled: b_state];
688     [o_file_sub_fps setEnabled: b_state];
689     [o_file_sub_fps_stp setEnabled: b_state];
690 }
691
692 - (IBAction)subDelayStepperChanged:(id)sender
693 {
694     [o_file_sub_delay setIntValue: [o_file_sub_delay_stp intValue]];
695 }
696
697 - (IBAction)subFpsStepperChanged:(id)sender;
698 {
699     [o_file_sub_fps setFloatValue: [o_file_sub_fps_stp floatValue]];
700 }
701
702 - (IBAction)subCloseSheet:(id)sender
703 {
704     [o_file_sub_sheet orderOut:sender];
705     [NSApp endSheet: o_file_sub_sheet];
706 }
707
708 - (IBAction)panelCancel:(id)sender
709 {
710     [NSApp stopModalWithCode: 0];
711 }
712
713 - (IBAction)panelOk:(id)sender
714 {
715     if( [[o_mrl stringValue] length] )
716     {
717         [NSApp stopModalWithCode: 1];
718     }
719     else
720     {
721         NSBeep();
722     }
723 }
724
725 @end