]> git.sesse.net Git - vlc/blob - modules/gui/macosx/controls.m
88a6b004ab61d164094a4b7e50b0eee64c671451
[vlc] / modules / gui / macosx / controls.m
1 /*****************************************************************************
2  * controls.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2002-2009 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 <hartman at videolan dot org>
10  *          Benjamin Pracht <bigben at videolan doit 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 #include <stdlib.h>                                      /* malloc(), free() */
32 #include <sys/param.h>                                    /* for MAXPATHLEN */
33 #include <string.h>
34
35 #import "intf.h"
36 #import "vout.h"
37 #import "open.h"
38 #import "controls.h"
39 #import "playlist.h"
40 #include <vlc_osd.h>
41 #include <vlc_keys.h>
42
43 /*****************************************************************************
44  * VLCAutoGeneratedMenuContent interface
45  *****************************************************************************
46  * This holds our data for autogenerated menus
47  *****************************************************************************/
48 @interface VLCAutoGeneratedMenuContent : NSObject
49 {
50     char *psz_name;
51     vlc_object_t * _vlc_object;
52     vlc_value_t value;
53     int i_type;
54 }
55
56 - (id)initWithVariableName: (const char *)name 
57            ofObject: (vlc_object_t *)object
58            andValue: (vlc_value_t)value 
59            ofType: (int)type;
60 - (const char *)name;
61 - (vlc_value_t)value;
62 - (vlc_object_t *)vlcObject;
63 - (int)type;
64
65 @end
66
67 #pragma mark -
68 /*****************************************************************************
69  * VLCControls implementation
70  *****************************************************************************/
71 @implementation VLCControls
72
73 - (id)init
74 {
75     [super init];
76     o_fs_panel = [[VLCFSPanel alloc] init];
77     return self;
78 }
79
80 - (void)awakeFromNib
81 {
82     [o_specificTime_mi setTitle: _NS("Jump To Time")];
83     [o_specificTime_cancel_btn setTitle: _NS("Cancel")];
84     [o_specificTime_ok_btn setTitle: _NS("OK")];
85     [o_specificTime_sec_lbl setStringValue: _NS("sec.")];
86     [o_specificTime_goTo_lbl setStringValue: _NS("Jump to time")];
87
88     o_repeat_off = [NSImage imageNamed:@"repeat_embedded"];
89
90     [self controlTintChanged];
91
92     [[NSNotificationCenter defaultCenter] addObserver: self
93                                              selector: @selector( controlTintChanged )
94                                                  name: NSControlTintDidChangeNotification
95                                                object: nil];
96 }
97
98 - (void)controlTintChanged
99 {
100     int i_repeat = 0;
101     if( [o_btn_repeat image] == o_repeat_single )
102         i_repeat = 1;
103     else if( [o_btn_repeat image] == o_repeat_all )
104         i_repeat = 2;
105
106     if( [NSColor currentControlTint] == NSGraphiteControlTint )
107     {
108         o_repeat_single = [NSImage imageNamed:@"repeat_single_embedded_graphite"];
109         o_repeat_all = [NSImage imageNamed:@"repeat_embedded_graphite"];
110         
111         [o_btn_shuffle setAlternateImage: [NSImage imageNamed: @"shuffle_embedded_graphite"]];
112         [o_btn_addNode setAlternateImage: [NSImage imageNamed: @"add_embedded_graphite"]];
113     }
114     else
115     {
116         o_repeat_single = [NSImage imageNamed:@"repeat_single_embedded_blue"];
117         o_repeat_all = [NSImage imageNamed:@"repeat_embedded_blue"];
118         
119         [o_btn_shuffle setAlternateImage: [NSImage imageNamed: @"shuffle_embedded_blue"]];
120         [o_btn_addNode setAlternateImage: [NSImage imageNamed: @"add_embedded_blue"]];
121     }
122     
123     /* update the repeat button, but keep its state */
124     if( i_repeat == 1 )
125         [self repeatOne];
126     else if( i_repeat == 2 )
127         [self repeatAll];
128     else
129         [self repeatOff];
130 }
131
132 - (void)dealloc
133 {
134     [[NSNotificationCenter defaultCenter] removeObserver: self];
135     
136     [o_repeat_single release];
137     [o_repeat_all release];
138     [o_repeat_off release];
139     
140     [super dealloc];
141 }
142
143 - (IBAction)play:(id)sender
144 {
145     intf_thread_t * p_intf = VLCIntf;
146     playlist_t * p_playlist = pl_Hold( p_intf );
147     bool empty;
148
149     PL_LOCK;
150     empty = playlist_IsEmpty( p_playlist );
151     PL_UNLOCK;
152
153     pl_Release( p_intf );
154
155     if( empty )
156         [o_main intfOpenFileGeneric: (id)sender];
157
158     var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_PLAY_PAUSE );
159 }
160
161 - (id)voutView
162 {
163     id window;
164     id voutView = nil;
165     id embeddedViewList = [[VLCMain sharedInstance] embeddedList];
166     NSEnumerator *enumerator = [[NSApp orderedWindows] objectEnumerator];
167     while( !voutView && ( window = [enumerator nextObject] ) )
168     {
169         /* We have an embedded vout */
170         if( [embeddedViewList windowContainsEmbedded: window] )
171         {
172             voutView = [embeddedViewList viewForWindow: window];
173         }
174         /* We have a detached vout */
175         else if( [[window className] isEqualToString: @"VLCVoutWindow"] )
176         {
177             voutView = [window voutView];
178         }
179     }
180     return [[voutView retain] autorelease];
181 }
182
183 - (IBAction)stop:(id)sender
184 {
185     intf_thread_t * p_intf = VLCIntf;
186     var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_STOP );
187     /* Close the window directly, because we do know that there
188      * won't be anymore video. It's currently waiting a bit. */
189     [[[self voutView] window] orderOut:self];
190 }
191
192 - (IBAction)faster:(id)sender
193 {
194     intf_thread_t * p_intf = VLCIntf;
195     var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_FASTER );
196 }
197
198 - (IBAction)slower:(id)sender
199 {
200     intf_thread_t * p_intf = VLCIntf;
201     var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_SLOWER );
202 }
203
204 - (IBAction)prev:(id)sender
205 {
206     intf_thread_t * p_intf = VLCIntf;
207     var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_PREV );
208 }
209
210 - (IBAction)next:(id)sender
211 {
212     intf_thread_t * p_intf = VLCIntf;
213     var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_NEXT );
214 }
215
216 - (IBAction)random:(id)sender
217 {
218     vlc_value_t val;
219     intf_thread_t * p_intf = VLCIntf;
220     playlist_t * p_playlist = pl_Hold( p_intf );
221
222     var_Get( p_playlist, "random", &val );
223     val.b_bool = !val.b_bool;
224     var_Set( p_playlist, "random", val );
225     if( val.b_bool )
226     {
227         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Random On" ) );
228         config_PutInt( p_playlist, "random", 1 );
229     }
230     else
231     {
232         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Random Off" ) );
233         config_PutInt( p_playlist, "random", 0 );
234     }
235
236     p_intf->p_sys->b_playmode_update = true;
237     p_intf->p_sys->b_intf_update = true;
238     pl_Release( p_intf );
239 }
240
241 /* three little ugly helpers */
242 - (void)repeatOne
243 {
244     [o_btn_repeat setImage: o_repeat_single];
245     [o_btn_repeat setAlternateImage: o_repeat_all];
246 }
247 - (void)repeatAll
248 {
249     [o_btn_repeat setImage: o_repeat_all];
250     [o_btn_repeat setAlternateImage: o_repeat_off];
251 }
252 - (void)repeatOff
253 {
254     [o_btn_repeat setImage: o_repeat_off];
255     [o_btn_repeat setAlternateImage: o_repeat_single];
256 }
257 - (void)shuffle
258 {
259     vlc_value_t val;
260     playlist_t *p_playlist = pl_Hold( VLCIntf );
261     var_Get( p_playlist, "random", &val );
262     [o_btn_shuffle setState: val.b_bool];
263     pl_Release( VLCIntf );
264 }
265
266 - (IBAction)repeatButtonAction:(id)sender
267 {
268     vlc_value_t looping,repeating;
269     intf_thread_t * p_intf = VLCIntf;
270     playlist_t * p_playlist = pl_Hold( p_intf );
271
272     var_Get( p_playlist, "repeat", &repeating );
273     var_Get( p_playlist, "loop", &looping );
274
275     if( !repeating.b_bool && !looping.b_bool )
276     {
277         /* was: no repeating at all, switching to Repeat One */
278  
279         /* set our button's look */
280         [self repeatOne];
281  
282         /* prepare core communication */
283         repeating.b_bool = true;
284         looping.b_bool = false;
285         config_PutInt( p_playlist, "repeat", 1 );
286         config_PutInt( p_playlist, "loop", 0 );
287  
288         /* show the change */
289         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat One" ) );
290     }
291     else if( repeating.b_bool && !looping.b_bool )
292     {
293         /* was: Repeat One, switching to Repeat All */
294  
295         /* set our button's look */
296         [self repeatAll];
297  
298         /* prepare core communication */
299         repeating.b_bool = false;
300         looping.b_bool = true;
301         config_PutInt( p_playlist, "repeat", 0 );
302         config_PutInt( p_playlist, "loop", 1 );
303  
304         /* show the change */
305         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat All" ) );
306     }
307     else
308     {
309         /* was: Repeat All or bug in VLC, switching to Repeat Off */
310  
311         /* set our button's look */
312         [self repeatOff];
313  
314         /* prepare core communication */
315         repeating.b_bool = false;
316         looping.b_bool = false;
317         config_PutInt( p_playlist, "repeat", 0 );
318         config_PutInt( p_playlist, "loop", 0 );
319  
320         /* show the change */
321         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
322     }
323
324     /* communicate with core and the main intf loop */
325     var_Set( p_playlist, "repeat", repeating );
326     var_Set( p_playlist, "loop", looping );
327     p_intf->p_sys->b_playmode_update = true;
328     p_intf->p_sys->b_intf_update = true;
329
330     pl_Release( p_intf );
331 }
332
333
334 - (IBAction)repeat:(id)sender
335 {
336     vlc_value_t val;
337     intf_thread_t * p_intf = VLCIntf;
338     playlist_t * p_playlist = pl_Hold( p_intf );
339
340     var_Get( p_playlist, "repeat", &val );
341     if (!val.b_bool)
342     {
343         var_Set( p_playlist, "loop", val );
344     }
345     val.b_bool = !val.b_bool;
346     var_Set( p_playlist, "repeat", val );
347     if( val.b_bool )
348     {
349         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat One" ) );
350         config_PutInt( p_playlist, "repeat", 1 );
351     }
352     else
353     {
354         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
355         config_PutInt( p_playlist, "repeat", 0 );
356     }
357  
358     p_intf->p_sys->b_playmode_update = true;
359     p_intf->p_sys->b_intf_update = true;
360     pl_Release( p_intf );
361 }
362
363 - (IBAction)loop:(id)sender
364 {
365     vlc_value_t val;
366     intf_thread_t * p_intf = VLCIntf;
367     playlist_t * p_playlist = pl_Hold( p_intf );
368
369     var_Get( p_playlist, "loop", &val );
370     if (!val.b_bool)
371     {
372         var_Set( p_playlist, "repeat", val );
373     }
374     val.b_bool = !val.b_bool;
375     var_Set( p_playlist, "loop", val );
376     if( val.b_bool )
377     {
378         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat All" ) );
379         config_PutInt( p_playlist, "loop", 1 );
380     }
381     else
382     {
383         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
384         config_PutInt( p_playlist, "loop", 0 );
385     }
386
387     p_intf->p_sys->b_playmode_update = true;
388     p_intf->p_sys->b_intf_update = true;
389     pl_Release( p_intf );
390 }
391
392 - (IBAction)forward:(id)sender
393 {
394     intf_thread_t * p_intf = VLCIntf;
395     var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_JUMP_FORWARD_SHORT );
396 }
397
398 - (IBAction)backward:(id)sender
399 {
400     vlc_value_t val;
401     intf_thread_t * p_intf = VLCIntf;
402     var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_JUMP_BACKWARD_SHORT );
403 }
404
405
406 - (IBAction)volumeUp:(id)sender
407 {
408     intf_thread_t * p_intf = VLCIntf;
409     var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_VOL_UP );
410     /* Manage volume status */
411     [o_main manageVolumeSlider];
412 }
413
414 - (IBAction)volumeDown:(id)sender
415 {
416     intf_thread_t * p_intf = VLCIntf;
417     var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_VOL_DOWN );
418     /* Manage volume status */
419     [o_main manageVolumeSlider];
420 }
421
422 - (IBAction)mute:(id)sender
423 {
424     intf_thread_t * p_intf = VLCIntf;
425     var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_VOL_MUTE );
426     /* Manage volume status */
427     [o_main manageVolumeSlider];
428 }
429
430 - (IBAction)volumeSliderUpdated:(id)sender
431 {
432     intf_thread_t * p_intf = VLCIntf;
433     audio_volume_t i_volume = (audio_volume_t)[sender intValue];
434     int i_volume_step = 0;
435     i_volume_step = config_GetInt( p_intf->p_libvlc, "volume-step" );
436     aout_VolumeSet( p_intf, i_volume * i_volume_step );
437     /* Manage volume status */
438     [o_main manageVolumeSlider];
439 }
440
441 - (IBAction)showPosition: (id)sender
442 {
443     vout_thread_t *p_vout = vlc_object_find( VLCIntf, VLC_OBJECT_VOUT,
444                                              FIND_ANYWHERE );
445     if( p_vout != NULL )
446     {
447         intf_thread_t * p_intf = VLCIntf;
448         var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_POSITION );
449         vlc_object_release( (vlc_object_t *)p_vout );
450     }
451 }
452
453 - (IBAction)toogleFullscreen:(id)sender {
454     NSMenuItem *o_mi = [[NSMenuItem alloc] initWithTitle: _NS("Fullscreen") action: nil keyEquivalent:@""];
455     [self windowAction: [o_mi autorelease]];
456 }
457
458 - (BOOL) isFullscreen {
459     id o_vout_view = [self voutView];
460     if( o_vout_view )
461     {
462         return [o_vout_view isFullscreen];
463     }
464     return NO;
465 }
466
467 - (IBAction)windowAction:(id)sender
468 {
469     NSString *o_title = [sender title];
470
471     vout_thread_t *p_vout = vlc_object_find( VLCIntf, VLC_OBJECT_VOUT,
472                                               FIND_ANYWHERE );
473     if( p_vout != NULL )
474     {
475         id o_vout_view = [self voutView];
476         if( o_vout_view )
477         {
478             if( [o_title isEqualToString: _NS("Half Size") ] )
479                 [o_vout_view scaleWindowWithFactor: 0.5 animate: YES];
480             else if( [o_title isEqualToString: _NS("Normal Size") ] )
481                 [o_vout_view scaleWindowWithFactor: 1.0 animate: YES];
482             else if( [o_title isEqualToString: _NS("Double Size") ] )
483                 [o_vout_view scaleWindowWithFactor: 2.0 animate: YES];
484             else if( [o_title isEqualToString: _NS("Float on Top") ] )
485                 [o_vout_view toggleFloatOnTop];
486             else if( [o_title isEqualToString: _NS("Fit to Screen") ] )
487             {
488                 id o_window = [o_vout_view voutWindow];
489                 if( ![o_window isZoomed] )
490                     [o_window performZoom:self];
491             }
492             else if( [o_title isEqualToString: _NS("Snapshot") ] )
493             {
494                 [o_vout_view snapshot];
495             }
496             else
497             {
498                 /* Fullscreen state for next time will be saved here too */
499                 [o_vout_view toggleFullscreen];
500             }
501         }
502         vlc_object_release( (vlc_object_t *)p_vout );
503     }
504     else
505     {
506         playlist_t * p_playlist = pl_Hold( VLCIntf );
507
508         if( [o_title isEqualToString: _NS("Fullscreen")] ||
509             [sender isKindOfClass:[NSButton class]] )
510         {
511             vlc_value_t val;
512             var_Get( p_playlist, "fullscreen", &val );
513             var_Set( p_playlist, "fullscreen", (vlc_value_t)!val.b_bool );
514         }
515
516         pl_Release( VLCIntf );
517     }
518
519 }
520
521 - (IBAction)telxTransparent:(id)sender
522 {
523     intf_thread_t * p_intf = VLCIntf;
524     vlc_object_t *p_vbi;
525     p_vbi = (vlc_object_t *) vlc_object_find_name( p_intf,
526                     "zvbi", FIND_ANYWHERE );
527     if( p_vbi )
528     {
529         var_SetBool( p_vbi, "vbi-opaque", [sender state] );
530         [sender setState: ![sender state]];
531         vlc_object_release( p_vbi );
532     }
533 }
534
535 - (IBAction)telxNavLink:(id)sender
536 {
537     intf_thread_t * p_intf = VLCIntf;
538     vlc_object_t *p_vbi;
539     int i_page = 0;
540
541     if( [[sender title] isEqualToString: _NS("Index")] )
542         i_page = 'i' << 16;
543     else if( [[sender title] isEqualToString: _NS("Red")] )
544         i_page = 'r' << 16;
545     else if( [[sender title] isEqualToString: _NS("Green")] )
546         i_page = 'g' << 16;
547     else if( [[sender title] isEqualToString: _NS("Yellow")] )
548         i_page = 'y' << 16;
549     else if( [[sender title] isEqualToString: _NS("Blue")] )
550         i_page = 'b' << 16;
551     if( i_page == 0 ) return;
552
553     p_vbi = (vlc_object_t *) vlc_object_find_name( p_intf,
554                 "zvbi", FIND_ANYWHERE );
555     if( p_vbi )
556     {
557         var_SetInteger( p_vbi, "vbi-page", i_page );
558         vlc_object_release( p_vbi );
559     }
560 }
561
562 - (IBAction)addSubtitleFile:(id)sender
563 {
564     NSInteger i_returnValue = 0;
565     input_thread_t * p_input = pl_CurrentInput( VLCIntf );
566     if( !p_input ) return;
567
568     input_item_t *p_item = input_GetItem( p_input );
569     if( !p_item ) return;
570
571     char *path = input_item_GetURI( p_item );
572     if( !path ) path = strdup( "" );
573
574     NSOpenPanel * openPanel = [NSOpenPanel openPanel];
575     [openPanel setCanChooseFiles: YES];
576     [openPanel setCanChooseDirectories: NO];
577     [openPanel setAllowsMultipleSelection: YES];
578     i_returnValue = [openPanel runModalForDirectory: [NSString stringWithUTF8String: path] file: nil types: [NSArray arrayWithObjects: @"cdg",@"@idx",@"srt",@"sub",@"utf",@"ass",@"ssa",@"aqt",@"jss",@"psb",@"rt",@"smi", nil]];
579     free( path );
580
581     if( i_returnValue == NSOKButton )
582     {
583         NSUInteger c = 0;
584         if( !p_input ) return;
585         
586         c = [[openPanel filenames] count];
587         NSLog( @"count: %i", c );
588         for (int i = 0; [[openPanel filenames] count] > i ; i++)
589         {
590             msg_Dbg( VLCIntf, "loading subs from %s", [[[openPanel filenames] objectAtIndex: i] UTF8String] );
591             if( input_AddSubtitle( p_input, [[[openPanel filenames] objectAtIndex: i] UTF8String], TRUE ) )
592                 msg_Warn( VLCIntf, "unable to load subtitles from '%s'",
593                          [[[openPanel filenames] objectAtIndex: i] UTF8String] );
594             i++;
595         }
596     }
597 }
598
599 - (void)scrollWheel:(NSEvent *)theEvent
600 {
601     intf_thread_t * p_intf = VLCIntf;
602     float f_yabsvalue = [theEvent deltaY] > 0.0f ? [theEvent deltaY] : -[theEvent deltaY];
603     float f_xabsvalue = [theEvent deltaX] > 0.0f ? [theEvent deltaX] : -[theEvent deltaX];
604     int i, i_yvlckey, i_xvlckey;
605
606     if ([theEvent deltaY] < 0.0f)
607         i_yvlckey = KEY_MOUSEWHEELDOWN;
608     else
609         i_yvlckey = KEY_MOUSEWHEELUP;
610
611     if ([theEvent deltaX] < 0.0f)
612         i_xvlckey = KEY_MOUSEWHEELRIGHT;
613     else
614         i_xvlckey = KEY_MOUSEWHEELLEFT;
615
616     /* Send multiple key event, depending on the intensity of the event */
617     for (i = 0; i < (int)(f_yabsvalue/4.+1.) && f_yabsvalue > 0.05 ; i++)
618         var_SetInteger( p_intf->p_libvlc, "key-pressed", i_yvlckey );
619
620     /* Prioritize Y event (sound volume) over X event */
621     if (f_yabsvalue < 0.05)
622     {
623         for (i = 0; i < (int)(f_xabsvalue/6.+1.) && f_xabsvalue > 0.05; i++)
624          var_SetInteger( p_intf->p_libvlc, "key-pressed", i_xvlckey );
625     }
626 }
627
628 - (BOOL)keyEvent:(NSEvent *)o_event
629 {
630     BOOL eventHandled = NO;
631     unichar key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
632
633     if( key )
634     {
635         vout_thread_t *p_vout = vlc_object_find( VLCIntf, VLC_OBJECT_VOUT,
636                                               FIND_ANYWHERE );
637         if( p_vout != NULL )
638         {
639             /* Escape */
640             if( key == (unichar) 0x1b )
641             {
642                 id o_vout_view = [self voutView];
643                 if( o_vout_view && [o_vout_view isFullscreen] )
644                 {
645                     [o_vout_view toggleFullscreen];
646                     eventHandled = YES;
647                 }
648             }
649             else if( key == ' ' )
650             {
651                 [self play:self];
652                 eventHandled = YES;
653             }
654             vlc_object_release( (vlc_object_t *)p_vout );
655         }
656     }
657     return eventHandled;
658 }
659
660 - (void)setupVarMenuItem:(NSMenuItem *)o_mi
661                     target:(vlc_object_t *)p_object
662                     var:(const char *)psz_variable
663                     selector:(SEL)pf_callback
664 {
665     vlc_value_t val, text;
666     int i_type = var_Type( p_object, psz_variable );
667
668     switch( i_type & VLC_VAR_TYPE )
669     {
670     case VLC_VAR_VOID:
671     case VLC_VAR_BOOL:
672     case VLC_VAR_VARIABLE:
673     case VLC_VAR_STRING:
674     case VLC_VAR_INTEGER:
675         break;
676     default:
677         /* Variable doesn't exist or isn't handled */
678         return;
679     }
680  
681     /* Make sure we want to display the variable */
682     if( i_type & VLC_VAR_HASCHOICE )
683     {
684         var_Change( p_object, psz_variable, VLC_VAR_CHOICESCOUNT, &val, NULL );
685         if( val.i_int == 0 ) return;
686         if( (i_type & VLC_VAR_TYPE) != VLC_VAR_VARIABLE && val.i_int == 1 )
687             return;
688     }
689  
690     /* Get the descriptive name of the variable */
691     var_Change( p_object, psz_variable, VLC_VAR_GETTEXT, &text, NULL );
692     [o_mi setTitle: [[VLCMain sharedInstance] localizedString: text.psz_string ?
693                                         text.psz_string : strdup( psz_variable ) ]];
694
695     var_Get( p_object, psz_variable, &val );
696     if( i_type & VLC_VAR_HASCHOICE )
697     {
698         NSMenu *o_menu = [o_mi submenu];
699
700         [self setupVarMenu: o_menu forMenuItem: o_mi target:p_object
701                         var:psz_variable selector:pf_callback];
702  
703         free( text.psz_string );
704         return;
705     }
706
707     VLCAutoGeneratedMenuContent *o_data;
708     switch( i_type & VLC_VAR_TYPE )
709     {
710     case VLC_VAR_VOID:
711         o_data = [[VLCAutoGeneratedMenuContent alloc] initWithVariableName: psz_variable ofObject: p_object
712                 andValue: val ofType: i_type];
713         [o_mi setRepresentedObject: [o_data autorelease]];
714         break;
715
716     case VLC_VAR_BOOL:
717         o_data = [[VLCAutoGeneratedMenuContent alloc] initWithVariableName: psz_variable ofObject: p_object
718                 andValue: val ofType: i_type];
719         [o_mi setRepresentedObject: [o_data autorelease]];
720         if( !( i_type & VLC_VAR_ISCOMMAND ) )
721             [o_mi setState: val.b_bool ? TRUE : FALSE ];
722         break;
723
724     default:
725         free( text.psz_string );
726         return;
727     }
728
729     if( ( i_type & VLC_VAR_TYPE ) == VLC_VAR_STRING ) free( val.psz_string );
730     free( text.psz_string );
731 }
732
733
734 - (void)setupVarMenu:(NSMenu *)o_menu
735                     forMenuItem: (NSMenuItem *)o_parent
736                     target:(vlc_object_t *)p_object
737                     var:(const char *)psz_variable
738                     selector:(SEL)pf_callback
739 {
740     vlc_value_t val, val_list, text_list;
741     int i_type, i, i_nb_items;
742
743     /* remove previous items */
744     i_nb_items = [o_menu numberOfItems];
745     for( i = 0; i < i_nb_items; i++ )
746     {
747         [o_menu removeItemAtIndex: 0];
748     }
749
750     /* Check the type of the object variable */
751     i_type = var_Type( p_object, psz_variable );
752
753     /* Make sure we want to display the variable */
754     if( i_type & VLC_VAR_HASCHOICE )
755     {
756         var_Change( p_object, psz_variable, VLC_VAR_CHOICESCOUNT, &val, NULL );
757         if( val.i_int == 0 ) return;
758         if( (i_type & VLC_VAR_TYPE) != VLC_VAR_VARIABLE && val.i_int == 1 )
759             return;
760     }
761     else
762     {
763         return;
764     }
765
766     switch( i_type & VLC_VAR_TYPE )
767     {
768     case VLC_VAR_VOID:
769     case VLC_VAR_BOOL:
770     case VLC_VAR_VARIABLE:
771     case VLC_VAR_STRING:
772     case VLC_VAR_INTEGER:
773         break;
774     default:
775         /* Variable doesn't exist or isn't handled */
776         return;
777     }
778
779     if( var_Get( p_object, psz_variable, &val ) < 0 )
780     {
781         return;
782     }
783
784     if( var_Change( p_object, psz_variable, VLC_VAR_GETLIST,
785                     &val_list, &text_list ) < 0 )
786     {
787         if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string );
788         return;
789     }
790
791     /* make (un)sensitive */
792     [o_parent setEnabled: ( val_list.p_list->i_count > 1 )];
793
794     /* special case for the subtitles items */
795     if( [[o_parent title] isEqualToString: _NS("Subtitles Track")] == YES )
796     {
797         NSMenuItem * o_lmi_tmp;
798         o_lmi_tmp = [o_menu addItemWithTitle: _NS("Open File...") action: @selector(addSubtitleFile:) keyEquivalent: @""];
799         [o_lmi_tmp setTarget: self];
800         [o_lmi_tmp setEnabled: YES];
801         [o_parent setEnabled: YES];
802         [o_menu addItem: [NSMenuItem separatorItem]];
803     }
804
805     for( i = 0; i < val_list.p_list->i_count; i++ )
806     {
807         vlc_value_t another_val;
808         NSMenuItem * o_lmi;
809         NSString *o_title = @"";
810         VLCAutoGeneratedMenuContent *o_data;
811
812         switch( i_type & VLC_VAR_TYPE )
813         {
814         case VLC_VAR_STRING:
815             another_val.psz_string =
816                 strdup(val_list.p_list->p_values[i].psz_string);
817
818             o_title = [[VLCMain sharedInstance] localizedString: text_list.p_list->p_values[i].psz_string ?
819                 text_list.p_list->p_values[i].psz_string : val_list.p_list->p_values[i].psz_string ];
820
821             o_lmi = [o_menu addItemWithTitle: o_title action: pf_callback keyEquivalent: @""];
822             o_data = [[VLCAutoGeneratedMenuContent alloc] initWithVariableName: strdup(psz_variable) ofObject: p_object
823                     andValue: another_val ofType: i_type];
824             [o_lmi setRepresentedObject: [o_data autorelease]];
825             [o_lmi setTarget: self];
826
827             if( !strcmp( val.psz_string, val_list.p_list->p_values[i].psz_string ) && !( i_type & VLC_VAR_ISCOMMAND ) )
828                 [o_lmi setState: TRUE ];
829
830             break;
831
832         case VLC_VAR_INTEGER:
833
834              o_title = text_list.p_list->p_values[i].psz_string ?
835                                  [[VLCMain sharedInstance] localizedString: strdup( text_list.p_list->p_values[i].psz_string )] :
836                                  [NSString stringWithFormat: @"%d",
837                                  val_list.p_list->p_values[i].i_int];
838
839             o_lmi = [o_menu addItemWithTitle: o_title action: pf_callback keyEquivalent: @""];
840             o_data = [[VLCAutoGeneratedMenuContent alloc] initWithVariableName: strdup(psz_variable) ofObject: p_object
841                     andValue: val_list.p_list->p_values[i] ofType: i_type];
842             [o_lmi setRepresentedObject: [o_data autorelease]];
843             [o_lmi setTarget: self];
844
845             if( val_list.p_list->p_values[i].i_int == val.i_int && !( i_type & VLC_VAR_ISCOMMAND ) )
846                 [o_lmi setState: TRUE ];
847             break;
848
849         default:
850           break;
851         }
852     }
853
854     /* special case for the subtitles sub-menu
855      * In case that we don't have any subs, we don't want a separator item at the end */
856     if( [[o_parent title] isEqualToString: _NS("Subtitles Track")] == YES )
857     {
858         if( [o_menu numberOfItems] == 2 )
859             [o_menu removeItemAtIndex: 1];
860     }
861
862     /* clean up everything */
863     if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string );
864     var_FreeList( &val_list, &text_list );
865 }
866
867 - (IBAction)toggleVar:(id)sender
868 {
869     NSMenuItem *o_mi = (NSMenuItem *)sender;
870     VLCAutoGeneratedMenuContent *o_data = [o_mi representedObject];
871     [NSThread detachNewThreadSelector: @selector(toggleVarThread:)
872         toTarget: self withObject: o_data];
873
874     return;
875 }
876
877 - (int)toggleVarThread: (id)data
878 {
879     vlc_object_t *p_object;
880     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
881
882     assert([data isKindOfClass:[VLCAutoGeneratedMenuContent class]]);
883     VLCAutoGeneratedMenuContent *menuContent = (VLCAutoGeneratedMenuContent *)data;
884
885     vlc_thread_set_priority( VLCIntf , VLC_THREAD_PRIORITY_LOW );
886
887     p_object = [menuContent vlcObject];
888
889     if( p_object != NULL )
890     {
891         var_Set( p_object, [menuContent name], [menuContent value] );
892         vlc_object_release( p_object );
893         [o_pool release];
894         return true;
895     }
896     [o_pool release];
897     return VLC_EGENERIC;
898 }
899
900 - (IBAction)goToSpecificTime:(id)sender
901 {
902     if( sender == o_specificTime_cancel_btn )
903     {
904         [NSApp endSheet: o_specificTime_win];
905         [o_specificTime_win close];
906     }
907     else if( sender == o_specificTime_ok_btn )
908     {
909         input_thread_t * p_input = pl_CurrentInput( VLCIntf );
910         if( p_input )
911         {
912             unsigned int timeInSec = 0;
913             NSString * fieldContent = [o_specificTime_enter_fld stringValue];
914             if( [[fieldContent componentsSeparatedByString: @":"] count] > 1 &&
915                 [[fieldContent componentsSeparatedByString: @":"] count] <= 3 )
916             {
917                 NSArray * ourTempArray = \
918                     [fieldContent componentsSeparatedByString: @":"];
919
920                 if( [[fieldContent componentsSeparatedByString: @":"] count] == 3 )
921                 {
922                     timeInSec += ([[ourTempArray objectAtIndex: 0] intValue] * 3600); //h
923                     timeInSec += ([[ourTempArray objectAtIndex: 1] intValue] * 60); //m
924                     timeInSec += [[ourTempArray objectAtIndex: 2] intValue];        //s
925                 }
926                 else
927                 {
928                     timeInSec += ([[ourTempArray objectAtIndex: 0] intValue] * 60); //m
929                     timeInSec += [[ourTempArray objectAtIndex: 1] intValue]; //s
930                 }
931             }
932             else
933                 timeInSec = [fieldContent intValue];
934
935             input_Control( p_input, INPUT_SET_TIME, (int64_t)(timeInSec * 1000000));
936             vlc_object_release( p_input );
937         }
938
939         [NSApp endSheet: o_specificTime_win];
940         [o_specificTime_win close];
941     }
942     else
943     {
944         input_thread_t * p_input = pl_CurrentInput( VLCIntf );
945         if( p_input )
946         {
947             /* we can obviously only do that if an input is available */
948             vlc_value_t pos, length;
949             var_Get( p_input, "time", &pos );
950             [o_specificTime_enter_fld setIntValue: (pos.i_time / 1000000)];
951             var_Get( p_input, "length", &length );
952             [o_specificTime_stepper setMaxValue: (length.i_time / 1000000)];
953
954             [NSApp beginSheet: o_specificTime_win modalForWindow: \
955                 [NSApp mainWindow] modalDelegate: self didEndSelector: nil \
956                 contextInfo: nil];
957             [o_specificTime_win makeKeyWindow];
958             vlc_object_release( p_input );
959         }
960     }
961 }
962
963 - (id)fspanel
964 {
965     if( o_fs_panel )
966         return o_fs_panel;
967     else
968     {
969         msg_Err( VLCIntf, "FSPanel is nil" );
970         return NULL;
971     }
972 }
973
974 @end
975
976 @implementation VLCControls (NSMenuValidation)
977
978 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
979 {
980     BOOL bEnabled = TRUE;
981     vlc_value_t val;
982     intf_thread_t * p_intf = VLCIntf;
983     playlist_t * p_playlist = pl_Hold( p_intf );
984     input_thread_t * p_input = playlist_CurrentInput( p_playlist );
985
986     if( [[o_mi title] isEqualToString: _NS("Faster")] ||
987         [[o_mi title] isEqualToString: _NS("Slower")] )
988     {
989         if( p_input != NULL )
990         {
991             bEnabled = var_GetBool( p_input, "can-rate" );
992         }
993         else
994         {
995             bEnabled = FALSE;
996         }
997     }
998     else if( [[o_mi title] isEqualToString: _NS("Stop")] )
999     {
1000         if( p_input == NULL )
1001         {
1002             bEnabled = FALSE;
1003         }
1004         [o_main setupMenus]; /* Make sure input menu is up to date */
1005     }
1006     else if( [[o_mi title] isEqualToString: _NS("Previous")] ||
1007              [[o_mi title] isEqualToString: _NS("Next")] )
1008     {
1009         /** \todo fix i_size use */
1010         PL_LOCK;
1011         bEnabled = p_playlist->items.i_size > 1;
1012         PL_UNLOCK;
1013     }
1014     else if( [[o_mi title] isEqualToString: _NS("Random")] )
1015     {
1016         int i_state;
1017         var_Get( p_playlist, "random", &val );
1018         i_state = val.b_bool ? NSOnState : NSOffState;
1019         [o_mi setState: i_state];
1020     }
1021     else if( [[o_mi title] isEqualToString: _NS("Repeat One")] )
1022     {
1023         int i_state;
1024         var_Get( p_playlist, "repeat", &val );
1025         i_state = val.b_bool ? NSOnState : NSOffState;
1026         [o_mi setState: i_state];
1027     }
1028     else if( [[o_mi title] isEqualToString: _NS("Repeat All")] )
1029     {
1030         int i_state;
1031         var_Get( p_playlist, "loop", &val );
1032         i_state = val.b_bool ? NSOnState : NSOffState;
1033         [o_mi setState: i_state];
1034     }
1035     else if( [[o_mi title] isEqualToString: _NS("Step Forward")] ||
1036              [[o_mi title] isEqualToString: _NS("Step Backward")] ||
1037              [[o_mi title] isEqualToString: _NS("Jump To Time")])
1038     {
1039         if( p_input != NULL )
1040         {
1041             var_Get( p_input, "can-seek", &val);
1042             bEnabled = val.b_bool;
1043         }
1044         else bEnabled = FALSE;
1045     }
1046     else if( [[o_mi title] isEqualToString: _NS("Mute")] )
1047     {
1048         [o_mi setState: p_intf->p_sys->b_mute ? NSOnState : NSOffState];
1049         [o_main setupMenus]; /* Make sure audio menu is up to date */
1050     }
1051     else if( [[o_mi title] isEqualToString: _NS("Half Size")] ||
1052                 [[o_mi title] isEqualToString: _NS("Normal Size")] ||
1053                 [[o_mi title] isEqualToString: _NS("Double Size")] ||
1054                 [[o_mi title] isEqualToString: _NS("Fit to Screen")] ||
1055                 [[o_mi title] isEqualToString: _NS("Snapshot")] ||
1056                 [[o_mi title] isEqualToString: _NS("Fullscreen")] ||
1057                 [[o_mi title] isEqualToString: _NS("Float on Top")] )
1058     {
1059         id o_window;
1060         NSArray *o_windows = [NSApp orderedWindows];
1061         NSEnumerator *o_enumerator = [o_windows objectEnumerator];
1062         bEnabled = FALSE;
1063  
1064         vout_thread_t   *p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
1065                                               FIND_ANYWHERE );
1066         if( p_vout != NULL )
1067         {
1068             if( [[o_mi title] isEqualToString: _NS("Float on Top")] )
1069             {
1070                 var_Get( p_vout, "video-on-top", &val );
1071                 [o_mi setState: val.b_bool ?  NSOnState : NSOffState];
1072             }
1073
1074             while( (o_window = [o_enumerator nextObject]))
1075             {
1076                 if( [[o_window className] isEqualToString: @"VLCVoutWindow"] ||
1077                             [[[VLCMain sharedInstance] embeddedList]
1078                             windowContainsEmbedded: o_window])
1079                 {
1080                     bEnabled = TRUE;
1081                     break;
1082                 }
1083             }
1084
1085             vlc_object_release( (vlc_object_t *)p_vout );
1086         }
1087         if( [[o_mi title] isEqualToString: _NS("Fullscreen")] )
1088         {
1089             var_Get( p_playlist, "fullscreen", &val );
1090             [o_mi setState: val.b_bool];
1091             bEnabled = TRUE;
1092         }
1093         [o_main setupMenus]; /* Make sure video menu is up to date */
1094     }
1095
1096     /* Special case for telx menu */
1097     if( [[o_mi title] isEqualToString: _NS("Normal Size")] )
1098     {
1099         NSMenuItem *item = [[o_mi menu] itemWithTitle:_NS("Teletext")];
1100                 bool b_telx = p_input && var_GetInteger( p_input, "teletext-es" ) >= 0;
1101
1102         [[item submenu] setAutoenablesItems:NO];
1103         for( int k=0; k < [[item submenu] numberOfItems]; k++ )
1104         {
1105             [[[item submenu] itemAtIndex:k] setEnabled: b_telx];
1106         }
1107     }
1108
1109     if( p_input ) vlc_object_release( p_input );
1110     pl_Release( p_intf );
1111
1112     return( bEnabled );
1113 }
1114
1115 @end
1116
1117 /*****************************************************************************
1118  * VLCAutoGeneratedMenuContent implementation
1119  *****************************************************************************
1120  * Object connected to a playlistitem which remembers the data belonging to
1121  * the variable of the autogenerated menu
1122  *****************************************************************************/
1123 @implementation VLCAutoGeneratedMenuContent
1124
1125 -(id) initWithVariableName:(const char *)name ofObject:(vlc_object_t *)object
1126         andValue:(vlc_value_t)val ofType:(int)type
1127 {
1128     self = [super init];
1129
1130     if( self != nil )
1131     {
1132         psz_name = strdup( name );
1133         _vlc_object = vlc_object_hold( object );
1134         value = val;
1135         i_type = type;
1136     }
1137
1138     return( self );
1139 }
1140
1141 - (void)dealloc
1142 {
1143     vlc_object_release( _vlc_object );
1144     free( psz_name );
1145     [super dealloc];
1146 }
1147
1148 - (const char *)name
1149 {
1150     return psz_name;
1151 }
1152
1153 - (vlc_value_t)value
1154 {
1155     return value;
1156 }
1157
1158 - (vlc_object_t *)vlcObject
1159 {
1160     return vlc_object_hold( _vlc_object );
1161 }
1162
1163
1164 - (int)type
1165 {
1166     return i_type;
1167 }
1168
1169 @end
1170
1171
1172 /*****************************************************************************
1173  * VLCTimeField implementation
1174  *****************************************************************************
1175  * we need this to catch our click-event in the controller window
1176  *****************************************************************************/
1177
1178 @implementation VLCTimeField
1179 - (void)mouseDown: (NSEvent *)ourEvent
1180 {
1181     if( [ourEvent clickCount] > 1 )
1182         [[[VLCMain sharedInstance] controls] goToSpecificTime: nil];
1183     else
1184         [[VLCMain sharedInstance] timeFieldWasClicked: self];
1185 }
1186 @end