]> git.sesse.net Git - vlc/blob - modules/gui/macosx/open.m
MacOS X: more EyeTV GUI improvements
[vlc] / modules / gui / macosx / open.m
1 /*****************************************************************************
2  * open.m: MacOS X module for vlc
3  *****************************************************************************
4  * Copyright (C) 2002-2007 the VideoLAN team
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 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 #include <stdlib.h>                                      /* malloc(), free() */
32 #include <sys/param.h>                                    /* for MAXPATHLEN */
33 #include <string.h>
34
35 #include <paths.h>
36 #include <IOKit/IOKitLib.h>
37 #include <IOKit/IOBSD.h>
38 #include <IOKit/storage/IOMedia.h>
39 #include <IOKit/storage/IOCDMedia.h>
40 #include <IOKit/storage/IODVDMedia.h>
41
42 #import "intf.h"
43 #import "playlist.h"
44 #import "open.h"
45 #import "output.h"
46 #import "eyetv.h"
47
48 /*****************************************************************************
49  * GetEjectableMediaOfClass
50  *****************************************************************************/
51 NSArray *GetEjectableMediaOfClass( const char *psz_class )
52 {
53     io_object_t next_media;
54     mach_port_t master_port;
55     kern_return_t kern_result;
56     NSArray *o_devices = nil;
57     NSMutableArray *p_list = nil;
58     io_iterator_t media_iterator;
59     CFMutableDictionaryRef classes_to_match;
60
61     kern_result = IOMasterPort( MACH_PORT_NULL, &master_port );
62     if( kern_result != KERN_SUCCESS )
63     {
64         return( nil );
65     }
66  
67     classes_to_match = IOServiceMatching( psz_class );
68     if( classes_to_match == NULL )
69     {
70         return( nil );
71     }
72  
73     CFDictionarySetValue( classes_to_match, CFSTR( kIOMediaEjectableKey ),
74                           kCFBooleanTrue );
75  
76     kern_result = IOServiceGetMatchingServices( master_port, classes_to_match,
77                                                 &media_iterator );
78     if( kern_result != KERN_SUCCESS )
79     {
80         return( nil );
81     }
82
83     p_list = [NSMutableArray arrayWithCapacity: 1];
84  
85     next_media = IOIteratorNext( media_iterator );
86     if( next_media != nil )
87     {
88         char psz_buf[0x32];
89         size_t dev_path_length;
90         CFTypeRef str_bsd_path;
91  
92         do
93         {
94             str_bsd_path = IORegistryEntryCreateCFProperty( next_media,
95                                                             CFSTR( kIOBSDNameKey ),
96                                                             kCFAllocatorDefault,
97                                                             0 );
98             if( str_bsd_path == NULL )
99             {
100                 IOObjectRelease( next_media );
101                 continue;
102             }
103  
104             snprintf( psz_buf, sizeof(psz_buf), "%s%c", _PATH_DEV, 'r' );
105             dev_path_length = strlen( psz_buf );
106  
107             if( CFStringGetCString( str_bsd_path,
108                                     (char*)&psz_buf + dev_path_length,
109                                     sizeof(psz_buf) - dev_path_length,
110                                     kCFStringEncodingASCII ) )
111             {
112                 [p_list addObject: [NSString stringWithCString: psz_buf]];
113             }
114  
115             CFRelease( str_bsd_path );
116  
117             IOObjectRelease( next_media );
118  
119         } while( ( next_media = IOIteratorNext( media_iterator ) ) != nil );
120     }
121  
122     IOObjectRelease( media_iterator );
123
124     o_devices = [NSArray arrayWithArray: p_list];
125
126     return( o_devices );
127 }
128
129 /*****************************************************************************
130  * VLCOpen implementation
131  *****************************************************************************/
132 @implementation VLCOpen
133
134 static VLCOpen *_o_sharedMainInstance = nil;
135
136 + (VLCOpen *)sharedInstance
137 {
138     return _o_sharedMainInstance ? _o_sharedMainInstance : [[self alloc] init];
139 }
140
141 - (id)init
142 {
143     if( _o_sharedMainInstance) {
144         [self dealloc];
145     } else {
146         _o_sharedMainInstance = [super init];
147     }
148  
149     return _o_sharedMainInstance;
150 }
151
152 - (void)awakeFromNib
153 {
154     intf_thread_t * p_intf = VLCIntf;
155
156     [o_panel setTitle: _NS("Open Source")];
157     [o_mrl_lbl setTitle: _NS("Media Resource Locator (MRL)")];
158
159     [o_btn_ok setTitle: _NS("OK")];
160     [o_btn_cancel setTitle: _NS("Cancel")];
161
162     [[o_tabview tabViewItemAtIndex: 0] setLabel: _NS("File")];
163     [[o_tabview tabViewItemAtIndex: 1] setLabel: _NS("Disc")];
164     [[o_tabview tabViewItemAtIndex: 2] setLabel: _NS("Network")];
165
166     [o_file_btn_browse setTitle: _NS("Browse...")];
167     [o_file_stream setTitle: _NS("Treat as a pipe rather than as a file")];
168
169     [o_disc_device_lbl setStringValue: _NS("Device name")];
170     [o_disc_title_lbl setStringValue: _NS("Title")];
171     [o_disc_chapter_lbl setStringValue: _NS("Chapter")];
172     [o_disc_videots_btn_browse setTitle: _NS("Browse...")];
173     [o_disc_dvd_menus setTitle: _NS("No DVD menus")];
174
175     [[o_disc_type cellAtRow:0 column:0] setTitle: _NS("VIDEO_TS directory")];
176     [[o_disc_type cellAtRow:1 column:0] setTitle: _NS("DVD")];
177     [[o_disc_type cellAtRow:2 column:0] setTitle: _NS("VCD")];
178     [[o_disc_type cellAtRow:3 column:0] setTitle: _NS("Audio CD")];
179
180     [o_net_udp_port_lbl setStringValue: _NS("Port")];
181     [o_net_udpm_addr_lbl setStringValue: _NS("Address")];
182     [o_net_udpm_port_lbl setStringValue: _NS("Port")];
183     [o_net_http_url_lbl setStringValue: _NS("URL")];
184
185     [[o_net_mode cellAtRow:0 column:0] setTitle: _NS("UDP/RTP")];
186     [[o_net_mode cellAtRow:1 column:0] setTitle: _NS("UDP/RTP Multicast")];
187     [[o_net_mode cellAtRow:2 column:0] setTitle: _NS("HTTP/FTP/MMS/RTSP")];
188     [o_net_timeshift_ckbox setTitle: _NS("Allow timeshifting")];
189
190     [o_net_udp_port setIntValue: config_GetInt( p_intf, "server-port" )];
191     [o_net_udp_port_stp setIntValue: config_GetInt( p_intf, "server-port" )];
192  
193     [o_eyetv_chn_bgbar setUsesThreadedAnimation: YES];
194     /* FIXME: implement EyeTV l10n here */
195  
196     [self setSubPanel];
197
198
199     [[NSNotificationCenter defaultCenter] addObserver: self
200         selector: @selector(openFilePathChanged:)
201         name: NSControlTextDidChangeNotification
202         object: o_file_path];
203
204     [[NSNotificationCenter defaultCenter] addObserver: self
205         selector: @selector(openDiscInfoChanged:)
206         name: NSControlTextDidChangeNotification
207         object: o_disc_device];
208     [[NSNotificationCenter defaultCenter] addObserver: self
209         selector: @selector(openDiscInfoChanged:)
210         name: NSControlTextDidChangeNotification
211         object: o_disc_title];
212     [[NSNotificationCenter defaultCenter] addObserver: self
213         selector: @selector(openDiscInfoChanged:)
214         name: NSControlTextDidChangeNotification
215         object: o_disc_chapter];
216     [[NSNotificationCenter defaultCenter] addObserver: self
217         selector: @selector(openDiscInfoChanged:)
218         name: NSControlTextDidChangeNotification
219         object: o_disc_videots_folder];
220
221     [[NSNotificationCenter defaultCenter] addObserver: self
222         selector: @selector(openNetInfoChanged:)
223         name: NSControlTextDidChangeNotification
224         object: o_net_udp_port];
225     [[NSNotificationCenter defaultCenter] addObserver: self
226         selector: @selector(openNetInfoChanged:)
227         name: NSControlTextDidChangeNotification
228         object: o_net_udpm_addr];
229     [[NSNotificationCenter defaultCenter] addObserver: self
230         selector: @selector(openNetInfoChanged:)
231         name: NSControlTextDidChangeNotification
232         object: o_net_udpm_port];
233     [[NSNotificationCenter defaultCenter] addObserver: self
234         selector: @selector(openNetInfoChanged:)
235         name: NSControlTextDidChangeNotification
236         object: o_net_http_url];
237
238     /* wake up with the correct EyeTV GUI */
239     if( [[[VLCMain sharedInstance] getEyeTVController] isEyeTVrunning] == YES )
240         [o_eyetv_tabView selectTabViewItemWithIdentifier:@"nodevice"];
241     else if( [[[VLCMain sharedInstance] getEyeTVController] isDeviceConnected] == YES )
242     {
243         [o_eyetv_tabView selectTabViewItemWithIdentifier:@"eyetvup"];
244         [self setupChannelInfo];
245     }
246     else
247         [o_eyetv_tabView selectTabViewItemWithIdentifier:@"noeyetv"];
248
249     [[NSDistributedNotificationCenter defaultCenter] addObserver: self
250                                                         selector: @selector(eyetvChanged:)
251                                                             name: NULL
252                                                           object: @"VLCEyeTVSupport"
253                                               suspensionBehavior: NSNotificationSuspensionBehaviorDeliverImmediately];
254  
255     /* register clicks on text fields */
256     [[NSNotificationCenter defaultCenter] addObserver: self
257                                              selector: @selector(textFieldWasClicked:)
258                                                  name: @"VLCOpenTextFieldWasClicked"
259                                                object: nil];
260 }
261
262 - (void)setSubPanel
263 {
264     intf_thread_t * p_intf = VLCIntf;
265     int i_index;
266     module_config_t * p_item;
267
268     [o_file_sub_ckbox setTitle: _NS("Load subtitles file:")];
269     [o_file_sub_btn_settings setTitle: _NS("Settings...")];
270     [o_file_sub_btn_browse setTitle: _NS("Browse...")];
271     [o_file_sub_override setTitle: _NS("Override parametters")];
272     [o_file_sub_delay_lbl setStringValue: _NS("Delay")];
273     [o_file_sub_delay_stp setEnabled: NO];
274     [o_file_sub_fps_lbl setStringValue: _NS("FPS")];
275     [o_file_sub_fps_stp setEnabled: NO];
276     [o_file_sub_encoding_lbl setStringValue: _NS("Subtitles encoding")];
277     [o_file_sub_encoding_pop removeAllItems];
278     [o_file_sub_size_lbl setStringValue: _NS("Font size")];
279     [o_file_sub_size_pop removeAllItems];
280     [o_file_sub_align_lbl setStringValue: _NS("Subtitles alignment")];
281     [o_file_sub_align_pop removeAllItems];
282     [o_file_sub_ok_btn setStringValue: _NS("OK")];
283     [o_file_sub_font_box setTitle: _NS("Font Properties")];
284     [o_file_sub_file_box setTitle: _NS("Subtitle File")];
285
286     p_item = config_FindConfig( VLC_OBJECT(p_intf), "subsdec-encoding" );
287
288     if( p_item )
289     {
290         for( i_index = 0; p_item->ppsz_list && p_item->ppsz_list[i_index];
291              i_index++ )
292         {
293             [o_file_sub_encoding_pop addItemWithTitle:
294                 [NSString stringWithCString:
295                 p_item->ppsz_list[i_index]]];
296         }
297         [o_file_sub_encoding_pop selectItemWithTitle:
298                 [NSString stringWithCString:
299                 p_item->value.psz]];
300     }
301
302     p_item = config_FindConfig( VLC_OBJECT(p_intf), "subsdec-align" );
303
304     if ( p_item )
305     {
306         for ( i_index = 0; i_index < p_item->i_list; i_index++ )
307         {
308             [o_file_sub_align_pop addItemWithTitle:
309                 [NSString stringWithUTF8String:
310                 p_item->ppsz_list_text[i_index]]];
311         }
312         [o_file_sub_align_pop selectItemAtIndex: p_item->value.i];
313     }
314
315     p_item = config_FindConfig( VLC_OBJECT(p_intf), "freetype-rel-fontsize" );
316
317     if ( p_item )
318     {
319         for ( i_index = 0; i_index < p_item->i_list; i_index++ )
320         {
321             [o_file_sub_size_pop addItemWithTitle:
322                 [NSString stringWithUTF8String:
323                 p_item->ppsz_list_text[i_index]]];
324             if ( p_item->value.i == p_item->pi_list[i_index] )
325             {
326                 [o_file_sub_size_pop selectItemAtIndex: i_index];
327             }
328         }
329     }
330 }
331
332 - (void)openTarget:(int)i_type
333 {
334     int i_result;
335     intf_thread_t * p_intf = VLCIntf;
336
337     b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
338
339     [o_tabview selectTabViewItemAtIndex: i_type];
340     [o_file_sub_ckbox setState: NSOffState];
341  
342     i_result = [NSApp runModalForWindow: o_panel];
343     [o_panel close];
344
345     if( i_result )
346     {
347         NSMutableDictionary *o_dic;
348         NSMutableArray *o_options = [NSMutableArray array];
349         unsigned int i;
350
351         o_dic = [NSMutableDictionary dictionaryWithObject: [o_mrl stringValue] forKey: @"ITEM_URL"];
352         if( [o_file_sub_ckbox state] == NSOnState )
353         {
354             module_config_t * p_item;
355
356             [o_options addObject: [NSString stringWithFormat: @"sub-file=%@", [o_file_sub_path stringValue]]];
357             if( [o_file_sub_override state] == NSOnState )
358             {
359                 [o_options addObject: [NSString stringWithFormat: @"sub-delay=%i", (int)( [o_file_sub_delay intValue] * 10 )]];
360                 [o_options addObject: [NSString stringWithFormat: @"sub-fps=%f", [o_file_sub_fps floatValue]]];
361             }
362             [o_options addObject: [NSString stringWithFormat:
363                     @"subsdec-encoding=%@",
364                     [o_file_sub_encoding_pop titleOfSelectedItem]]];
365             [o_options addObject: [NSString stringWithFormat:
366                     @"subsdec-align=%i",
367                     [o_file_sub_align_pop indexOfSelectedItem]]];
368
369             p_item = config_FindConfig( VLC_OBJECT(p_intf),
370                                             "freetype-rel-fontsize" );
371
372             if ( p_item )
373             {
374                 [o_options addObject: [NSString stringWithFormat:
375                     @"freetype-rel-fontsize=%i",
376                     p_item->pi_list[[o_file_sub_size_pop indexOfSelectedItem]]]];
377             }
378         }
379         if( [o_output_ckbox state] == NSOnState )
380         {
381             for (i = 0 ; i < [[o_sout_options getMRL] count] ; i++)
382             {
383                 [o_options addObject: [NSString stringWithString:
384                       [[(VLCOutput *)o_sout_options getMRL] objectAtIndex: i]]];
385             }
386         }
387         if( [o_net_timeshift_ckbox state] == NSOnState )
388         {
389             [o_options addObject: [NSString stringWithString:
390                                                 @"access-filter=timeshift"]];
391         }
392         [o_dic setObject: (NSArray *)[o_options copy] forKey: @"ITEM_OPTIONS"];
393         if( b_autoplay )
394             [o_playlist appendArray: [NSArray arrayWithObject: o_dic] atPos: -1 enqueue:NO];
395         else
396             [o_playlist appendArray: [NSArray arrayWithObject: o_dic] atPos: -1 enqueue:YES];
397     }
398 }
399
400 - (void)tabView:(NSTabView *)o_tv didSelectTabViewItem:(NSTabViewItem *)o_tvi
401 {
402     NSString *o_label = [o_tvi label];
403
404     if( [o_label isEqualToString: _NS("File")] )
405     {
406         [self openFilePathChanged: nil];
407     }
408     else if( [o_label isEqualToString: _NS("Disc")] )
409     {
410         [self openDiscTypeChanged: nil];
411     }
412     else if( [o_label isEqualToString: _NS("Network")] )
413     {
414         [self openNetInfoChanged: nil];
415     }
416 }
417
418 - (void)openFileGeneric
419 {
420     [self openFilePathChanged: nil];
421     [self openTarget: 0];
422 }
423
424 - (void)openDisc
425 {
426     [self openDiscTypeChanged: nil];
427     [self openTarget: 1];
428 }
429
430 - (void)openNet
431 {
432     [self openNetInfoChanged: nil];
433     [self openTarget: 2];
434 }
435
436 - (void)openFilePathChanged:(NSNotification *)o_notification
437 {
438     NSString *o_mrl_string;
439     NSString *o_filename = [o_file_path stringValue];
440     NSString *o_ext = [o_filename pathExtension];
441     vlc_bool_t b_stream = [o_file_stream state];
442     BOOL b_dir = NO;
443  
444     [[NSFileManager defaultManager] fileExistsAtPath:o_filename isDirectory:&b_dir];
445
446     if( b_dir )
447     {
448         o_mrl_string = [NSString stringWithFormat: @"dir:%@", o_filename];
449     }
450     else if( [o_ext isEqualToString: @"bin"] ||
451         [o_ext isEqualToString: @"cue"] ||
452         [o_ext isEqualToString: @"vob"] ||
453         [o_ext isEqualToString: @"iso"] )
454     {
455         o_mrl_string = o_filename;
456     }
457     else
458     {
459         o_mrl_string = [NSString stringWithFormat: @"%s://%@",
460                         b_stream ? "stream" : "file",
461                         o_filename];
462     }
463     [o_mrl setStringValue: o_mrl_string];
464 }
465
466 - (IBAction)openFileBrowse:(id)sender
467 {
468     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
469  
470     [o_open_panel setAllowsMultipleSelection: NO];
471     [o_open_panel setCanChooseDirectories: YES];
472     [o_open_panel setTitle: _NS("Open File")];
473     [o_open_panel setPrompt: _NS("Open")];
474
475     [o_open_panel beginSheetForDirectory:nil
476         file:nil
477         types:nil
478         modalForWindow:[sender window]
479         modalDelegate: self
480         didEndSelector: @selector(pathChosenInPanel:
481                         withReturn:
482                         contextInfo:)
483         contextInfo: nil];
484 }
485
486 - (void)pathChosenInPanel: (NSOpenPanel *) sheet withReturn:(int)returnCode contextInfo:(void  *)contextInfo
487 {
488     if (returnCode == NSFileHandlingPanelOKButton)
489     {
490         NSString *o_filename = [[sheet filenames] objectAtIndex: 0];
491         [o_file_path setStringValue: o_filename];
492         [self openFilePathChanged: nil];
493     }
494 }
495
496 - (IBAction)openFileStreamChanged:(id)sender
497 {
498     [self openFilePathChanged: nil];
499 }
500
501 - (IBAction)openDiscTypeChanged:(id)sender
502 {
503     NSString *o_type;
504     BOOL b_device, b_no_menus, b_title_chapter;
505  
506     [o_disc_device removeAllItems];
507     b_title_chapter = ![o_disc_dvd_menus state];
508  
509     o_type = [[o_disc_type selectedCell] title];
510
511     if ( [o_type isEqualToString: _NS("VIDEO_TS directory")] )
512     {
513         b_device = NO; b_no_menus = YES;
514     }
515     else
516     {
517         NSArray *o_devices;
518         NSString *o_disc;
519         const char *psz_class = NULL;
520         b_device = YES;
521
522         if ( [o_type isEqualToString: _NS("VCD")] )
523         {
524             psz_class = kIOCDMediaClass;
525             o_disc = o_type;
526             b_no_menus = NO; b_title_chapter = YES;
527                 }
528         else if ( [o_type isEqualToString: _NS("Audio CD")])
529         {
530             psz_class = kIOCDMediaClass;
531             o_disc = o_type;
532             b_no_menus = NO; b_title_chapter = NO;
533         }
534         else
535         {
536             psz_class = kIODVDMediaClass;
537             o_disc = o_type;
538             b_no_menus = YES;
539         }
540  
541         o_devices = GetEjectableMediaOfClass( psz_class );
542         if ( o_devices != nil )
543         {
544             int i_devices = [o_devices count];
545  
546             if ( i_devices )
547             {
548                                 for( int i = 0; i < i_devices; i++ )
549                 {
550                     [o_disc_device
551                         addItemWithObjectValue: [o_devices objectAtIndex: i]];
552                 }
553
554                 [o_disc_device selectItemAtIndex: 0];
555             }
556             else
557             {
558                 [o_disc_device setStringValue:
559                     [NSString stringWithFormat: _NS("No %@s found"), o_disc]];
560             }
561         }
562     }
563
564     [o_disc_device setEnabled: b_device];
565     [o_disc_title setEnabled: b_title_chapter];
566     [o_disc_title_stp setEnabled: b_title_chapter];
567     [o_disc_chapter setEnabled: b_title_chapter];
568     [o_disc_chapter_stp setEnabled: b_title_chapter];
569     [o_disc_videots_folder setEnabled: !b_device];
570     [o_disc_videots_btn_browse setEnabled: !b_device];
571     [o_disc_dvd_menus setEnabled: b_no_menus];
572
573     [self openDiscInfoChanged: nil];
574 }
575
576 - (IBAction)openDiscStepperChanged:(id)sender
577 {
578     int i_tag = [sender tag];
579
580     if( i_tag == 0 )
581     {
582         [o_disc_title setIntValue: [o_disc_title_stp intValue]];
583     }
584     else if( i_tag == 1 )
585     {
586         [o_disc_chapter setIntValue: [o_disc_chapter_stp intValue]];
587     }
588
589     [self openDiscInfoChanged: nil];
590 }
591
592 - (void)openDiscInfoChanged:(NSNotification *)o_notification
593 {
594     NSString *o_type;
595     NSString *o_device;
596     NSString *o_videots;
597     NSString *o_mrl_string;
598     int i_title, i_chapter;
599     BOOL b_no_menus;
600
601     o_type = [[o_disc_type selectedCell] title];
602     o_device = [o_disc_device stringValue];
603     i_title = [o_disc_title intValue];
604     i_chapter = [o_disc_chapter intValue];
605     o_videots = [o_disc_videots_folder stringValue];
606     b_no_menus = [o_disc_dvd_menus state];
607
608     if ( [o_type isEqualToString: _NS("VCD")] )
609     {
610         if ( [o_device isEqualToString:
611                 [NSString stringWithFormat: _NS("No %@s found"), o_type]] )
612             o_device = @"";
613         o_mrl_string = [NSString stringWithFormat: @"vcd://%@@%i:%i",
614                         o_device, i_title, i_chapter];
615     }
616     else if ( [o_type isEqualToString: _NS("Audio CD")] )
617     {
618         if ( [o_device isEqualToString:
619                 [NSString stringWithFormat: _NS("No %@s found"), o_type]] )
620             o_device = @"";
621         o_mrl_string = [NSString stringWithFormat: @"cdda://%@",
622                         o_device];
623     }
624     else if ( [o_type isEqualToString: _NS("DVD")] )
625     {
626         if ( [o_device isEqualToString:
627                 [NSString stringWithFormat: _NS("No %@s found"), o_type]] )
628             o_device = @"";
629         if ( b_no_menus )
630             o_mrl_string = [NSString stringWithFormat: @"dvdread://%@@%i:%i-",
631                             o_device, i_title, i_chapter];
632         else
633                         o_mrl_string = [NSString stringWithFormat: @"dvdnav://%@",
634                             o_device];
635             
636     }
637     else /* VIDEO_TS folder */
638     {
639         if ( b_no_menus )
640             o_mrl_string = [NSString stringWithFormat: @"dvdread://%@@%i:%i",
641                             o_videots, i_title, i_chapter];
642         else
643                         o_mrl_string = [NSString stringWithFormat: @"dvdnav://%@",
644                             o_videots];            
645     }
646
647     [o_mrl setStringValue: o_mrl_string];
648 }
649
650 - (IBAction)openDiscMenusChanged:(id)sender
651 {
652     [self openDiscInfoChanged: nil];
653     [self openDiscTypeChanged: nil];
654 }
655
656 - (IBAction)openVTSBrowse:(id)sender
657 {
658     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
659
660     [o_open_panel setAllowsMultipleSelection: NO];
661     [o_open_panel setCanChooseFiles: NO];
662     [o_open_panel setCanChooseDirectories: YES];
663     [o_open_panel setTitle: _NS("Open VIDEO_TS Directory")];
664     [o_open_panel setPrompt: _NS("Open")];
665
666     if( [o_open_panel runModalForDirectory: nil
667             file: nil types: nil] == NSOKButton )
668     {
669         NSString *o_dirname = [[o_open_panel filenames] objectAtIndex: 0];
670         [o_disc_videots_folder setStringValue: o_dirname];
671         [self openDiscInfoChanged: nil];
672     }
673 }
674
675 - (void)textFieldWasClicked:(NSNotification *)o_notification
676 {
677     if( [o_notification object] == o_net_udp_port )
678         [o_net_mode selectCellAtRow: 0 column: 0];
679     else if( [o_notification object] == o_net_udpm_addr ||
680              [o_notification object] == o_net_udpm_port )
681         [o_net_mode selectCellAtRow: 1 column: 0];
682     else
683         [o_net_mode selectCellAtRow: 2 column: 0];
684
685     [self openNetInfoChanged: nil];
686 }
687
688 - (IBAction)openNetModeChanged:(id)sender
689 {
690     if( [[sender selectedCell] tag] == 0 )
691         [o_panel makeFirstResponder: o_net_udp_port];
692     else if ( [[sender selectedCell] tag] == 1 )
693         [o_panel makeFirstResponder: o_net_udpm_addr];
694     else
695         [o_panel makeFirstResponder: o_net_http_url];
696
697     [self openNetInfoChanged: nil];
698 }
699
700 - (IBAction)openNetStepperChanged:(id)sender
701 {
702     int i_tag = [sender tag];
703
704     if( i_tag == 0 )
705     {
706         [o_net_udp_port setIntValue: [o_net_udp_port_stp intValue]];
707         [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCOpenTextFieldWasClicked"
708                                                             object: o_net_udp_port];
709         [o_panel makeFirstResponder: o_net_udp_port];
710     }
711     else if( i_tag == 1 )
712     {
713         [o_net_udpm_port setIntValue: [o_net_udpm_port_stp intValue]];
714         [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCOpenTextFieldWasClicked"
715                                                             object: o_net_udpm_port];
716         [o_panel makeFirstResponder: o_net_udpm_port];
717     }
718
719     [self openNetInfoChanged: nil];
720 }
721
722 - (void)openNetInfoChanged:(NSNotification *)o_notification
723 {
724     NSString *o_mode;
725     NSString *o_mrl_string = [NSString string];
726     intf_thread_t * p_intf = VLCIntf;
727
728     o_mode = [[o_net_mode selectedCell] title];
729
730     if( [o_mode isEqualToString: _NS("UDP/RTP")] )
731     {
732         int i_port = [o_net_udp_port intValue];
733
734         o_mrl_string = [NSString stringWithString: @"udp://"];
735
736         if( i_port != config_GetInt( p_intf, "server-port" ) )
737         {
738             o_mrl_string =
739                 [o_mrl_string stringByAppendingFormat: @"@:%i", i_port];
740         }
741     }
742     else if( [o_mode isEqualToString: _NS("UDP/RTP Multicast")] )
743     {
744         NSString *o_addr = [o_net_udpm_addr stringValue];
745         int i_port = [o_net_udpm_port intValue];
746
747         o_mrl_string = [NSString stringWithFormat: @"udp://@%@", o_addr];
748
749         if( i_port != config_GetInt( p_intf, "server-port" ) )
750         {
751             o_mrl_string =
752                 [o_mrl_string stringByAppendingFormat: @":%i", i_port];
753         }
754     }
755     else if( [o_mode isEqualToString: _NS("HTTP/FTP/MMS/RTSP")] )
756     {
757         NSString *o_url = [o_net_http_url stringValue];
758
759         if ( ![o_url hasPrefix:@"http:"] && ![o_url hasPrefix:@"ftp:"]
760               && ![o_url hasPrefix:@"mms"] && ![o_url hasPrefix:@"rtsp"] )
761             o_mrl_string = [NSString stringWithFormat: @"http://%@", o_url];
762         else
763             o_mrl_string = o_url;
764     }
765     [o_mrl setStringValue: o_mrl_string];
766 }
767
768 - (void)openFile
769 {
770     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
771     int i;
772     b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
773  
774     [o_open_panel setAllowsMultipleSelection: YES];
775     [o_open_panel setCanChooseDirectories: YES];
776     [o_open_panel setTitle: _NS("Open File")];
777     [o_open_panel setPrompt: _NS("Open")];
778  
779     if( [o_open_panel runModalForDirectory: nil
780             file: nil types: nil] == NSOKButton )
781     {
782         NSArray *o_array = [NSArray array];
783         NSArray *o_values = [[o_open_panel filenames]
784                 sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
785
786         for( i = 0; i < (int)[o_values count]; i++)
787         {
788             NSDictionary *o_dic;
789             o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"];
790             o_array = [o_array arrayByAddingObject: o_dic];
791         }
792         if( b_autoplay )
793             [o_playlist appendArray: o_array atPos: -1 enqueue:NO];
794         else
795             [o_playlist appendArray: o_array atPos: -1 enqueue:YES];
796     }
797 }
798
799 - (IBAction)eyetvSwitchChannel:(id)sender
800 {
801     if( sender == o_eyetv_nextProgram_btn )
802         [o_eyetv_channels_pop selectItemWithTag:[[[VLCMain sharedInstance] getEyeTVController] switchChannelUp: YES]];
803     else if( sender == o_eyetv_previousProgram_btn )
804         [o_eyetv_channels_pop selectItemWithTag:[[[VLCMain sharedInstance] getEyeTVController] switchChannelUp: NO]];
805     else if( sender == o_eyetv_channels_pop )
806         [[[VLCMain sharedInstance] getEyeTVController] selectChannel:
807             [[sender selectedItem] tag]];
808     else
809         msg_Err( VLCIntf, "eyetvSwitchChannel sent by unknown object" );
810 }
811
812 - (IBAction)eyetvLaunch:(id)sender
813 {
814     [[[VLCMain sharedInstance] getEyeTVController] launchEyeTV];
815 }
816
817 - (void)eyetvChanged:(NSNotification *)o_notification
818 {
819     if( [[o_notification name] isEqualToString: @"DeviceAdded"] )
820     {
821         msg_Dbg( VLCIntf, "eyetv device was added" );
822         [o_eyetv_tabView selectTabViewItemWithIdentifier:@"eyetvup"];
823         [self setupChannelInfo];
824     }
825     else if( [[o_notification name] isEqualToString: @"DeviceRemoved"] )
826     {
827         /* leave the channel selection like that,
828          * switch to our "no device" tab */
829         msg_Dbg( VLCIntf, "eyetv device was removed" );
830         [o_eyetv_tabView selectTabViewItemWithIdentifier:@"nodevice"];
831     }
832     else if( [[o_notification name] isEqualToString: @"PluginQuit"] )
833     {
834         /* switch to the "launch eyetv" tab */
835         msg_Dbg( VLCIntf, "eyetv was terminated" );
836         [o_eyetv_tabView selectTabViewItemWithIdentifier:@"noeyetv"];
837     }
838     else if( [[o_notification name] isEqualToString: @"PluginInit"] )
839     {
840         /* we got no device yet */
841         msg_Dbg( VLCIntf, "eyetv was launched, no device yet" );
842         [o_eyetv_tabView selectTabViewItemWithIdentifier:@"nodevice"];
843     }
844     else
845         msg_Warn( VLCIntf, "unknown external notify '%s' received", [[o_notification name] UTF8String] );
846 }
847
848 /* little helper method, since this code needs to be run by multiple objects */
849 - (void)setupChannelInfo
850 {
851     /* set up channel selection */
852     [o_eyetv_channels_pop removeAllItems];
853     [o_eyetv_chn_bgbar setHidden: NO];
854     [o_eyetv_chn_bgbar animate: self];
855     [o_eyetv_chn_status_txt setStringValue: _NS("Retrieving Channel Info...")];
856     [o_eyetv_chn_status_txt setHidden: NO];
857  
858     /* retrieve info */
859     NSEnumerator *channels = [[[VLCMain sharedInstance] getEyeTVController] allChannels];
860     int x = -2;
861     [[[o_eyetv_channels_pop menu] addItemWithTitle: _NS("Composite input")
862                                                action: nil
863                                         keyEquivalent: @""] setTag:x++];
864     [[[o_eyetv_channels_pop menu] addItemWithTitle: _NS("S-Video input")
865                                                action: nil
866                                         keyEquivalent: @""] setTag:x++];
867     if( channels ) 
868     {
869         NSString *channel;
870         [[[o_eyetv_channels_pop menu] addItemWithTitle: _NS("Tuner")
871                                                    action: nil
872                                             keyEquivalent: @""] setTag:x++];
873         [[o_eyetv_channels_pop menu] addItem: [NSMenuItem separatorItem]];
874         while( channel = [channels nextObject] )
875         {
876             /* we have to add items this way, because we accept duplicates
877              * additionally, we save a bit of time */
878             [[[o_eyetv_channels_pop menu] addItemWithTitle: channel
879                                                    action: nil
880                                             keyEquivalent: @""] setTag:x++];
881         }
882         /* make Tuner the default */
883         [o_eyetv_channels_pop selectItemWithTag:[[[VLCMain sharedInstance] getEyeTVController] currentChannel]];
884     }
885  
886     /* clean up GUI */
887     [o_eyetv_chn_bgbar setHidden: YES];
888     [o_eyetv_chn_status_txt setHidden: YES];
889
890     [o_mrl setStringValue: @"eyetv:"];
891 }
892
893 - (IBAction)subsChanged:(id)sender
894 {
895     if ([o_file_sub_ckbox state] == NSOnState)
896     {
897         [o_file_sub_btn_settings setEnabled:YES];
898     }
899     else
900     {
901         [o_file_sub_btn_settings setEnabled:NO];
902     }
903 }
904
905 - (IBAction)subSettings:(id)sender
906 {
907     [NSApp beginSheet: o_file_sub_sheet
908         modalForWindow: [sender window]
909         modalDelegate: self
910         didEndSelector: NULL
911         contextInfo: nil];
912 }
913
914 - (IBAction)subFileBrowse:(id)sender
915 {
916     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
917  
918     [o_open_panel setAllowsMultipleSelection: NO];
919     [o_open_panel setTitle: _NS("Open File")];
920     [o_open_panel setPrompt: _NS("Open")];
921
922     if( [o_open_panel runModalForDirectory: nil
923             file: nil types: nil] == NSOKButton )
924     {
925         NSString *o_filename = [[o_open_panel filenames] objectAtIndex: 0];
926         [o_file_sub_path setStringValue: o_filename];
927     }
928 }
929
930 - (IBAction)subOverride:(id)sender
931 {
932     BOOL b_state = [o_file_sub_override state];
933     [o_file_sub_delay setEnabled: b_state];
934     [o_file_sub_delay_stp setEnabled: b_state];
935     [o_file_sub_fps setEnabled: b_state];
936     [o_file_sub_fps_stp setEnabled: b_state];
937 }
938
939 - (IBAction)subDelayStepperChanged:(id)sender
940 {
941     [o_file_sub_delay setIntValue: [o_file_sub_delay_stp intValue]];
942 }
943
944 - (IBAction)subFpsStepperChanged:(id)sender;
945 {
946     [o_file_sub_fps setFloatValue: [o_file_sub_fps_stp floatValue]];
947 }
948
949 - (IBAction)subCloseSheet:(id)sender
950 {
951     [o_file_sub_sheet orderOut:sender];
952     [NSApp endSheet: o_file_sub_sheet];
953 }
954
955 - (IBAction)panelCancel:(id)sender
956 {
957     [NSApp stopModalWithCode: 0];
958 }
959
960 - (IBAction)panelOk:(id)sender
961 {
962     if( [[o_mrl stringValue] length] )
963     {
964         [NSApp stopModalWithCode: 1];
965     }
966     else
967     {
968         NSBeep();
969     }
970 }
971
972 @end
973
974 @implementation VLCOpenTextField
975
976 - (void)mouseDown:(NSEvent *)theEvent
977 {
978     [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCOpenTextFieldWasClicked"
979                                                         object: self];
980     [super mouseDown: theEvent];
981 }
982
983 @end