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