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