macosx: Make sure input menu are up-to-date.
[vlc.git] / modules / gui / macosx / controls.m
1 /*****************************************************************************
2  * controls.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2002-2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8  *          Christophe Massiot <massiot@via.ecp.fr>
9  *          Derk-Jan Hartman <hartman at videolan dot org>
10  *          Benjamin Pracht <bigben at videolan doit org>
11  *          Felix K├╝hne <fkuehne at videolan dot org>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31 #include <stdlib.h>                                      /* malloc(), free() */
32 #include <sys/param.h>                                    /* for MAXPATHLEN */
33 #include <string.h>
34
35 #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  * VLCControls implementation
45  *****************************************************************************/
46 @implementation VLCControls
47
48 - (id)init
49 {
50     [super init];
51     o_fs_panel = [[VLCFSPanel alloc] init];
52     return self;
53 }
54
55 - (void)awakeFromNib
56 {
57     [o_specificTime_mi setTitle: _NS("Jump To Time")];
58     [o_specificTime_cancel_btn setTitle: _NS("Cancel")];
59     [o_specificTime_ok_btn setTitle: _NS("OK")];
60     [o_specificTime_sec_lbl setStringValue: _NS("sec.")];
61     [o_specificTime_goTo_lbl setStringValue: _NS("Jump to time")];
62
63     o_repeat_off = [NSImage imageNamed:@"repeat_embedded"];
64
65     [self controlTintChanged];
66
67     [[NSNotificationCenter defaultCenter] addObserver: self
68                                              selector: @selector( controlTintChanged )
69                                                  name: NSControlTintDidChangeNotification
70                                                object: nil];
71 }
72
73 - (void)controlTintChanged
74 {
75     int i_repeat = 0;
76     if( [o_btn_repeat image] == o_repeat_single )
77         i_repeat = 1;
78     else if( [o_btn_repeat image] == o_repeat_all )
79         i_repeat = 2;
80
81     if( [NSColor currentControlTint] == NSGraphiteControlTint )
82     {
83         o_repeat_single = [NSImage imageNamed:@"repeat_single_embedded_graphite"];
84         o_repeat_all = [NSImage imageNamed:@"repeat_embedded_graphite"];
85         
86         [o_btn_shuffle setAlternateImage: [NSImage imageNamed: @"shuffle_embedded_graphite"]];
87         [o_btn_addNode setAlternateImage: [NSImage imageNamed: @"add_embedded_graphite"]];
88     }
89     else
90     {
91         o_repeat_single = [NSImage imageNamed:@"repeat_single_embedded_blue"];
92         o_repeat_all = [NSImage imageNamed:@"repeat_embedded_blue"];
93         
94         [o_btn_shuffle setAlternateImage: [NSImage imageNamed: @"shuffle_embedded_blue"]];
95         [o_btn_addNode setAlternateImage: [NSImage imageNamed: @"add_embedded_blue"]];
96     }
97     
98     /* update the repeat button, but keep its state */
99     if( i_repeat == 1 )
100         [self repeatOne];
101     else if( i_repeat == 2 )
102         [self repeatAll];
103     else
104         [self repeatOff];
105 }
106
107 - (void)dealloc
108 {
109     [[NSNotificationCenter defaultCenter] removeObserver: self];
110     
111     [o_repeat_single release];
112     [o_repeat_all release];
113     [o_repeat_off release];
114     
115     [super dealloc];
116 }
117
118 - (IBAction)play:(id)sender
119 {
120     vlc_value_t val;
121     intf_thread_t * p_intf = VLCIntf;
122     playlist_t * p_playlist = pl_Yield( p_intf );
123
124     vlc_object_lock( p_playlist );
125     if( playlist_IsEmpty( p_playlist ) )
126     {
127         vlc_object_unlock( p_playlist );
128         vlc_object_release( p_playlist );
129         [o_main intfOpenFileGeneric: (id)sender];
130     }
131     else
132     {
133         vlc_object_unlock( p_playlist );
134         vlc_object_release( p_playlist );
135     }
136
137     val.i_int = config_GetInt( p_intf, "key-play-pause" );
138     var_Set( p_intf->p_libvlc, "key-pressed", val );
139 }
140
141 /* Small helper method */
142
143 -(id) getVoutView
144 {
145     id o_window;
146     id o_vout_view = nil;
147     id o_embedded_vout_list = [[VLCMain sharedInstance] getEmbeddedList];
148     NSEnumerator *o_enumerator = [[NSApp orderedWindows] objectEnumerator];
149     while( !o_vout_view && ( o_window = [o_enumerator nextObject] ) )
150     {
151         /* We have an embedded vout */
152         if( [o_embedded_vout_list windowContainsEmbedded: o_window] )
153         {
154             o_vout_view = [o_embedded_vout_list getViewForWindow: o_window];
155         }
156         /* We have a detached vout */
157         else if( [[o_window className] isEqualToString: @"VLCVoutWindow"] )
158         {
159             msg_Dbg( VLCIntf, "detached vout controls.m call getVoutView" );
160             o_vout_view = [o_window getVoutView];
161         }
162     }
163     return o_vout_view;
164 }
165
166 - (IBAction)stop:(id)sender
167 {
168     vlc_value_t val;
169     intf_thread_t * p_intf = VLCIntf;
170     val.i_int = config_GetInt( p_intf, "key-stop" );
171     var_Set( p_intf->p_libvlc, "key-pressed", val );
172     /* Close the window directly, because we do know that there
173      * won't be anymore video. It's currently waiting a bit. */
174     [[[self getVoutView] window] orderOut:self];
175 }
176
177 - (IBAction)faster:(id)sender
178 {
179     vlc_value_t val;
180     intf_thread_t * p_intf = VLCIntf;
181     val.i_int = config_GetInt( p_intf, "key-faster" );
182     var_Set( p_intf->p_libvlc, "key-pressed", val );
183 }
184
185 - (IBAction)slower:(id)sender
186 {
187     vlc_value_t val;
188     intf_thread_t * p_intf = VLCIntf;
189     val.i_int = config_GetInt( p_intf, "key-slower" );
190     var_Set( p_intf->p_libvlc, "key-pressed", val );
191 }
192
193 - (IBAction)prev:(id)sender
194 {
195     vlc_value_t val;
196     intf_thread_t * p_intf = VLCIntf;
197     val.i_int = config_GetInt( p_intf, "key-prev" );
198     var_Set( p_intf->p_libvlc, "key-pressed", val );
199 }
200
201 - (IBAction)next:(id)sender
202 {
203     vlc_value_t val;
204     intf_thread_t * p_intf = VLCIntf;
205     val.i_int = config_GetInt( p_intf, "key-next" );
206     var_Set( p_intf->p_libvlc, "key-pressed", val );
207 }
208
209 - (IBAction)random:(id)sender
210 {
211     vlc_value_t val;
212     intf_thread_t * p_intf = VLCIntf;
213     playlist_t * p_playlist = pl_Yield( p_intf );
214
215     var_Get( p_playlist, "random", &val );
216     val.b_bool = !val.b_bool;
217     var_Set( p_playlist, "random", val );
218     if( val.b_bool )
219     {
220         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Random On" ) );
221         config_PutInt( p_playlist, "random", 1 );
222     }
223     else
224     {
225         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Random Off" ) );
226         config_PutInt( p_playlist, "random", 0 );
227     }
228
229     p_intf->p_sys->b_playmode_update = true;
230     p_intf->p_sys->b_intf_update = true;
231     vlc_object_release( p_playlist );
232 }
233
234 /* three little ugly helpers */
235 - (void)repeatOne
236 {
237     [o_btn_repeat setImage: o_repeat_single];
238     [o_btn_repeat setAlternateImage: o_repeat_all];
239 }
240 - (void)repeatAll
241 {
242     [o_btn_repeat setImage: o_repeat_all];
243     [o_btn_repeat setAlternateImage: o_repeat_off];
244 }
245 - (void)repeatOff
246 {
247     [o_btn_repeat setImage: o_repeat_off];
248     [o_btn_repeat setAlternateImage: o_repeat_single];
249 }
250 - (void)shuffle
251 {
252     vlc_value_t val;
253     playlist_t *p_playlist = pl_Yield( VLCIntf );
254     var_Get( p_playlist, "random", &val );
255     [o_btn_shuffle setState: val.b_bool];
256     vlc_object_release( p_playlist );
257 }
258
259 - (IBAction)repeatButtonAction:(id)sender
260 {
261     vlc_value_t looping,repeating;
262     intf_thread_t * p_intf = VLCIntf;
263     playlist_t * p_playlist = pl_Yield( p_intf );
264
265     var_Get( p_playlist, "repeat", &repeating );
266     var_Get( p_playlist, "loop", &looping );
267
268     if( !repeating.b_bool && !looping.b_bool )
269     {
270         /* was: no repeating at all, switching to Repeat One */
271  
272         /* set our button's look */
273         [self repeatOne];
274  
275         /* prepare core communication */
276         repeating.b_bool = true;
277         looping.b_bool = false;
278         config_PutInt( p_playlist, "repeat", 1 );
279         config_PutInt( p_playlist, "loop", 0 );
280  
281         /* show the change */
282         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat One" ) );
283     }
284     else if( repeating.b_bool && !looping.b_bool )
285     {
286         /* was: Repeat One, switching to Repeat All */
287  
288         /* set our button's look */
289         [self repeatAll];
290  
291         /* prepare core communication */
292         repeating.b_bool = false;
293         looping.b_bool = true;
294         config_PutInt( p_playlist, "repeat", 0 );
295         config_PutInt( p_playlist, "loop", 1 );
296  
297         /* show the change */
298         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat All" ) );
299     }
300     else
301     {
302         /* was: Repeat All or bug in VLC, switching to Repeat Off */
303  
304         /* set our button's look */
305         [self repeatOff];
306  
307         /* prepare core communication */
308         repeating.b_bool = false;
309         looping.b_bool = false;
310         config_PutInt( p_playlist, "repeat", 0 );
311         config_PutInt( p_playlist, "loop", 0 );
312  
313         /* show the change */
314         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
315     }
316
317     /* communicate with core and the main intf loop */
318     var_Set( p_playlist, "repeat", repeating );
319     var_Set( p_playlist, "loop", looping );
320     p_intf->p_sys->b_playmode_update = true;
321     p_intf->p_sys->b_intf_update = true;
322
323     vlc_object_release( p_playlist );
324 }
325
326
327 - (IBAction)repeat:(id)sender
328 {
329     vlc_value_t val;
330     intf_thread_t * p_intf = VLCIntf;
331     playlist_t * p_playlist = pl_Yield( p_intf );
332
333     var_Get( p_playlist, "repeat", &val );
334     if (!val.b_bool)
335     {
336         var_Set( p_playlist, "loop", val );
337     }
338     val.b_bool = !val.b_bool;
339     var_Set( p_playlist, "repeat", val );
340     if( val.b_bool )
341     {
342         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat One" ) );
343         config_PutInt( p_playlist, "repeat", 1 );
344     }
345     else
346     {
347         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
348         config_PutInt( p_playlist, "repeat", 0 );
349     }
350  
351     p_intf->p_sys->b_playmode_update = true;
352     p_intf->p_sys->b_intf_update = true;
353     vlc_object_release( p_playlist );
354 }
355
356 - (IBAction)loop:(id)sender
357 {
358     vlc_value_t val;
359     intf_thread_t * p_intf = VLCIntf;
360     playlist_t * p_playlist = pl_Yield( p_intf );
361
362     var_Get( p_playlist, "loop", &val );
363     if (!val.b_bool)
364     {
365         var_Set( p_playlist, "repeat", val );
366     }
367     val.b_bool = !val.b_bool;
368     var_Set( p_playlist, "loop", val );
369     if( val.b_bool )
370     {
371         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat All" ) );
372         config_PutInt( p_playlist, "loop", 1 );
373     }
374     else
375     {
376         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
377         config_PutInt( p_playlist, "loop", 0 );
378     }
379
380     p_intf->p_sys->b_playmode_update = true;
381     p_intf->p_sys->b_intf_update = true;
382     vlc_object_release( p_playlist );
383 }
384
385 - (IBAction)forward:(id)sender
386 {
387     vlc_value_t val;
388     intf_thread_t * p_intf = VLCIntf;
389     val.i_int = config_GetInt( p_intf, "key-jump+short" );
390     var_Set( p_intf->p_libvlc, "key-pressed", val );
391 }
392
393 - (IBAction)backward:(id)sender
394 {
395     vlc_value_t val;
396     intf_thread_t * p_intf = VLCIntf;
397     val.i_int = config_GetInt( p_intf, "key-jump-short" );
398     var_Set( p_intf->p_libvlc, "key-pressed", val );
399 }
400
401
402 - (IBAction)volumeUp:(id)sender
403 {
404     vlc_value_t val;
405     intf_thread_t * p_intf = VLCIntf;
406     val.i_int = config_GetInt( p_intf, "key-vol-up" );
407     var_Set( p_intf->p_libvlc, "key-pressed", val );
408     /* Manage volume status */
409     [o_main manageVolumeSlider];
410 }
411
412 - (IBAction)volumeDown:(id)sender
413 {
414     vlc_value_t val;
415     intf_thread_t * p_intf = VLCIntf;
416     val.i_int = config_GetInt( p_intf, "key-vol-down" );
417     var_Set( p_intf->p_libvlc, "key-pressed", val );
418     /* Manage volume status */
419     [o_main manageVolumeSlider];
420 }
421
422 - (IBAction)mute:(id)sender
423 {
424     vlc_value_t val;
425     intf_thread_t * p_intf = VLCIntf;
426     val.i_int = config_GetInt( p_intf, "key-vol-mute" );
427     var_Set( p_intf->p_libvlc, "key-pressed", val );
428     /* Manage volume status */
429     [o_main manageVolumeSlider];
430 }
431
432 - (IBAction)volumeSliderUpdated:(id)sender
433 {
434     intf_thread_t * p_intf = VLCIntf;
435     audio_volume_t i_volume = (audio_volume_t)[sender intValue];
436     int i_volume_step = 0;
437     i_volume_step = config_GetInt( p_intf->p_libvlc, "volume-step" );
438     aout_VolumeSet( p_intf, i_volume * i_volume_step );
439     /* Manage volume status */
440     [o_main manageVolumeSlider];
441 }
442
443 - (IBAction)showPosition: (id)sender
444 {
445     vout_thread_t *p_vout = vlc_object_find( VLCIntf, VLC_OBJECT_VOUT,
446                                              FIND_ANYWHERE );
447     if( p_vout != NULL )
448     {
449         vlc_value_t val;
450         intf_thread_t * p_intf = VLCIntf;
451         val.i_int = config_GetInt( p_intf, "key-position" );
452         var_Set( p_intf, "key-pressed", val );
453         vlc_object_release( (vlc_object_t *)p_vout );
454     }
455 }
456
457 - (IBAction)toogleFullscreen:(id)sender {
458     NSMenuItem *o_mi = [[NSMenuItem alloc] initWithTitle: _NS("Fullscreen") action: nil keyEquivalent:@""];
459     [self windowAction: [o_mi autorelease]];
460 }
461
462 - (BOOL) isFullscreen {
463     id o_vout_view = [self getVoutView];
464     if( o_vout_view )
465     {
466         return [o_vout_view isFullscreen];
467     }
468     return NO;
469 }
470
471 - (IBAction)windowAction:(id)sender
472 {
473     NSString *o_title = [sender title];
474
475     vout_thread_t *p_vout = vlc_object_find( VLCIntf, VLC_OBJECT_VOUT,
476                                               FIND_ANYWHERE );
477     if( p_vout != NULL )
478     {
479         id o_vout_view = [self getVoutView];
480         if( o_vout_view )
481         {
482             if( [o_title isEqualToString: _NS("Half Size") ] )
483                 [o_vout_view scaleWindowWithFactor: 0.5 animate: YES];
484             else if( [o_title isEqualToString: _NS("Normal Size") ] )
485                 [o_vout_view scaleWindowWithFactor: 1.0 animate: YES];
486             else if( [o_title isEqualToString: _NS("Double Size") ] )
487                 [o_vout_view scaleWindowWithFactor: 2.0 animate: YES];
488             else if( [o_title isEqualToString: _NS("Float on Top") ] )
489                 [o_vout_view toggleFloatOnTop];
490             else if( [o_title isEqualToString: _NS("Fit to Screen") ] )
491             {
492                 id o_window = [o_vout_view getWindow];
493                 if( ![o_window isZoomed] )
494                     [o_window performZoom:self];
495             }
496             else if( [o_title isEqualToString: _NS("Snapshot") ] )
497             {
498                 [o_vout_view snapshot];
499             }
500             else
501             {
502                 [o_vout_view toggleFullscreen];
503             }
504         }
505         vlc_object_release( (vlc_object_t *)p_vout );
506     }
507     else
508     {
509         playlist_t * p_playlist = pl_Yield( VLCIntf );
510
511         if( [o_title isEqualToString: _NS("Fullscreen")] ||
512             [sender isKindOfClass:[NSButton class]] )
513         {
514             vlc_value_t val;
515             var_Get( p_playlist, "fullscreen", &val );
516             var_Set( p_playlist, "fullscreen", (vlc_value_t)!val.b_bool );
517         }
518
519         vlc_object_release( p_playlist );
520     }
521
522 }
523
524 - (void)scrollWheel:(NSEvent *)theEvent
525 {
526     intf_thread_t * p_intf = VLCIntf;
527     float f_yabsvalue = [theEvent deltaY] > 0.0f ? [theEvent deltaY] : -[theEvent deltaY];
528     float f_xabsvalue = [theEvent deltaX] > 0.0f ? [theEvent deltaX] : -[theEvent deltaX];
529     int i, i_yvlckey, i_xvlckey;
530
531     if ([theEvent deltaY] < 0.0f)
532         i_yvlckey = KEY_MOUSEWHEELDOWN;
533     else
534         i_yvlckey = KEY_MOUSEWHEELUP;
535
536     if ([theEvent deltaX] < 0.0f)
537         i_xvlckey = KEY_MOUSEWHEELRIGHT;
538     else
539         i_xvlckey = KEY_MOUSEWHEELLEFT;
540
541     /* Send multiple key event, depending on the intensity of the event */
542     for (i = 0; i < (int)(f_yabsvalue/4.+1.) && f_yabsvalue > 0.05 ; i++)
543         var_SetInteger( p_intf->p_libvlc, "key-pressed", i_yvlckey );
544
545     /* Prioritize Y event (sound volume) over X event */
546     if (f_yabsvalue < 0.05)
547     {
548         for (i = 0; i < (int)(f_xabsvalue/6.+1.) && f_xabsvalue > 0.05; i++)
549          var_SetInteger( p_intf->p_libvlc, "key-pressed", i_xvlckey );
550     }
551 }
552
553 - (BOOL)keyEvent:(NSEvent *)o_event
554 {
555     BOOL eventHandled = NO;
556     unichar key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
557
558     if( key )
559     {
560         vout_thread_t *p_vout = vlc_object_find( VLCIntf, VLC_OBJECT_VOUT,
561                                               FIND_ANYWHERE );
562         if( p_vout != NULL )
563         {
564             /* Escape */
565             if( key == (unichar) 0x1b )
566             {
567                 id o_vout_view = [self getVoutView];
568                 if( o_vout_view && [o_vout_view isFullscreen] )
569                 {
570                     [o_vout_view toggleFullscreen];
571                     eventHandled = YES;
572                 }
573             }
574             else if( key == ' ' )
575             {
576                 [self play:self];
577                 eventHandled = YES;
578             }
579             vlc_object_release( (vlc_object_t *)p_vout );
580         }
581     }
582     return eventHandled;
583 }
584
585 - (void)setupVarMenuItem:(NSMenuItem *)o_mi
586                     target:(vlc_object_t *)p_object
587                     var:(const char *)psz_variable
588                     selector:(SEL)pf_callback
589 {
590     vlc_value_t val, text;
591     int i_type = var_Type( p_object, psz_variable );
592
593     switch( i_type & VLC_VAR_TYPE )
594     {
595     case VLC_VAR_VOID:
596     case VLC_VAR_BOOL:
597     case VLC_VAR_VARIABLE:
598     case VLC_VAR_STRING:
599     case VLC_VAR_INTEGER:
600         break;
601     default:
602         /* Variable doesn't exist or isn't handled */
603         return;
604     }
605  
606     /* Make sure we want to display the variable */
607     if( i_type & VLC_VAR_HASCHOICE )
608     {
609         var_Change( p_object, psz_variable, VLC_VAR_CHOICESCOUNT, &val, NULL );
610         if( val.i_int == 0 ) return;
611         if( (i_type & VLC_VAR_TYPE) != VLC_VAR_VARIABLE && val.i_int == 1 )
612             return;
613     }
614  
615     /* Get the descriptive name of the variable */
616     var_Change( p_object, psz_variable, VLC_VAR_GETTEXT, &text, NULL );
617     [o_mi setTitle: [[VLCMain sharedInstance] localizedString: text.psz_string ?
618                                         text.psz_string : strdup( psz_variable ) ]];
619
620     var_Get( p_object, psz_variable, &val );
621     if( i_type & VLC_VAR_HASCHOICE )
622     {
623         NSMenu *o_menu = [o_mi submenu];
624
625         [self setupVarMenu: o_menu forMenuItem: o_mi target:p_object
626                         var:psz_variable selector:pf_callback];
627  
628         free( text.psz_string );
629         return;
630     }
631
632     VLCMenuExt *o_data;
633     switch( i_type & VLC_VAR_TYPE )
634     {
635     case VLC_VAR_VOID:
636         o_data = [[VLCMenuExt alloc] initWithVar: psz_variable Object: p_object->i_object_id
637                 Value: val ofType: i_type];
638         [o_mi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
639         break;
640
641     case VLC_VAR_BOOL:
642         o_data = [[VLCMenuExt alloc] initWithVar: psz_variable Object: p_object->i_object_id
643                 Value: val ofType: i_type];
644         [o_mi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
645         if( !( i_type & VLC_VAR_ISCOMMAND ) )
646             [o_mi setState: val.b_bool ? TRUE : FALSE ];
647         break;
648
649     default:
650         free( text.psz_string );
651         return;
652     }
653
654     if( ( i_type & VLC_VAR_TYPE ) == VLC_VAR_STRING ) free( val.psz_string );
655     free( text.psz_string );
656 }
657
658
659 - (void)setupVarMenu:(NSMenu *)o_menu
660                     forMenuItem: (NSMenuItem *)o_parent
661                     target:(vlc_object_t *)p_object
662                     var:(const char *)psz_variable
663                     selector:(SEL)pf_callback
664 {
665     vlc_value_t val, val_list, text_list;
666     int i_type, i, i_nb_items;
667
668     /* remove previous items */
669     i_nb_items = [o_menu numberOfItems];
670     for( i = 0; i < i_nb_items; i++ )
671     {
672         [o_menu removeItemAtIndex: 0];
673     }
674
675     /* Check the type of the object variable */
676     i_type = var_Type( p_object, psz_variable );
677
678     /* Make sure we want to display the variable */
679     if( i_type & VLC_VAR_HASCHOICE )
680     {
681         var_Change( p_object, psz_variable, VLC_VAR_CHOICESCOUNT, &val, NULL );
682         if( val.i_int == 0 ) return;
683         if( (i_type & VLC_VAR_TYPE) != VLC_VAR_VARIABLE && val.i_int == 1 )
684             return;
685     }
686     else
687     {
688         return;
689     }
690
691     switch( i_type & VLC_VAR_TYPE )
692     {
693     case VLC_VAR_VOID:
694     case VLC_VAR_BOOL:
695     case VLC_VAR_VARIABLE:
696     case VLC_VAR_STRING:
697     case VLC_VAR_INTEGER:
698         break;
699     default:
700         /* Variable doesn't exist or isn't handled */
701         return;
702     }
703
704     if( var_Get( p_object, psz_variable, &val ) < 0 )
705     {
706         return;
707     }
708
709     if( var_Change( p_object, psz_variable, VLC_VAR_GETLIST,
710                     &val_list, &text_list ) < 0 )
711     {
712         if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string );
713         return;
714     }
715
716     /* make (un)sensitive */
717     [o_parent setEnabled: ( val_list.p_list->i_count > 1 )];
718
719     for( i = 0; i < val_list.p_list->i_count; i++ )
720     {
721         vlc_value_t another_val;
722         NSMenuItem * o_lmi;
723         NSString *o_title = @"";
724         VLCMenuExt *o_data;
725
726         switch( i_type & VLC_VAR_TYPE )
727         {
728         case VLC_VAR_STRING:
729             another_val.psz_string =
730                 strdup(val_list.p_list->p_values[i].psz_string);
731
732             o_title = [[VLCMain sharedInstance] localizedString: text_list.p_list->p_values[i].psz_string ?
733                 text_list.p_list->p_values[i].psz_string : val_list.p_list->p_values[i].psz_string ];
734
735             o_lmi = [o_menu addItemWithTitle: o_title action: pf_callback keyEquivalent: @""];
736             o_data = [[VLCMenuExt alloc] initWithVar: strdup(psz_variable) Object: p_object->i_object_id
737                     Value: another_val ofType: i_type];
738             [o_lmi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
739             [o_lmi setTarget: self];
740
741             if( !strcmp( val.psz_string, val_list.p_list->p_values[i].psz_string ) && !( i_type & VLC_VAR_ISCOMMAND ) )
742                 [o_lmi setState: TRUE ];
743
744             break;
745
746         case VLC_VAR_INTEGER:
747
748              o_title = text_list.p_list->p_values[i].psz_string ?
749                                  [[VLCMain sharedInstance] localizedString: strdup( text_list.p_list->p_values[i].psz_string )] :
750                                  [NSString stringWithFormat: @"%d",
751                                  val_list.p_list->p_values[i].i_int];
752
753             o_lmi = [[o_menu addItemWithTitle: o_title action: pf_callback keyEquivalent: @""] retain ];
754             o_data = [[VLCMenuExt alloc] initWithVar: strdup(psz_variable) Object: p_object->i_object_id
755                     Value: val_list.p_list->p_values[i] ofType: i_type];
756             [o_lmi setRepresentedObject: [NSValue valueWithPointer:[ o_data retain]]];
757             [o_lmi setTarget: self];
758
759             if( val_list.p_list->p_values[i].i_int == val.i_int && !( i_type & VLC_VAR_ISCOMMAND ) )
760                 [o_lmi setState: TRUE ];
761             break;
762
763         default:
764           break;
765         }
766     }
767
768     /* clean up everything */
769     if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string );
770     var_Change( p_object, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list );
771 }
772
773 - (IBAction)toggleVar:(id)sender
774 {
775     NSMenuItem *o_mi = (NSMenuItem *)sender;
776     VLCMenuExt *o_data = [[o_mi representedObject] pointerValue];
777     [NSThread detachNewThreadSelector: @selector(toggleVarThread:)
778         toTarget: self withObject: o_data];
779
780     return;
781 }
782
783 - (int)toggleVarThread: (id)_o_data
784 {
785     vlc_object_t *p_object;
786     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
787     VLCMenuExt *o_data = (VLCMenuExt *)_o_data;
788
789     vlc_thread_set_priority( VLCIntf , VLC_THREAD_PRIORITY_LOW );
790
791     p_object = (vlc_object_t *)vlc_object_get( [o_data objectID] );
792
793     if( p_object != NULL )
794     {
795         var_Set( p_object, strdup([o_data name]), [o_data value] );
796         vlc_object_release( p_object );
797         [o_pool release];
798         return true;
799     }
800     [o_pool release];
801     return VLC_EGENERIC;
802 }
803
804 - (IBAction)goToSpecificTime:(id)sender
805 {
806     if( sender == o_specificTime_cancel_btn )
807     {
808         [NSApp endSheet: o_specificTime_win];
809         [o_specificTime_win close];
810     }
811     else if( sender == o_specificTime_ok_btn )
812     {
813         input_thread_t * p_input = (input_thread_t *)vlc_object_find( VLCIntf, \
814             VLC_OBJECT_INPUT, FIND_ANYWHERE );
815         if( p_input )
816         {
817             unsigned int timeInSec = 0;
818             NSString * fieldContent = [o_specificTime_enter_fld stringValue];
819             if( [[fieldContent componentsSeparatedByString: @":"] count] > 1 &&
820                 [[fieldContent componentsSeparatedByString: @":"] count] <= 3 )
821             {
822                 NSArray * ourTempArray = \
823                     [fieldContent componentsSeparatedByString: @":"];
824
825                 if( [[fieldContent componentsSeparatedByString: @":"] count] == 3 )
826                 {
827                     timeInSec += ([[ourTempArray objectAtIndex: 0] intValue] * 3600); //h
828                     timeInSec += ([[ourTempArray objectAtIndex: 1] intValue] * 60); //m
829                     timeInSec += [[ourTempArray objectAtIndex: 2] intValue];        //s
830                 }
831                 else
832                 {
833                     timeInSec += ([[ourTempArray objectAtIndex: 0] intValue] * 60); //m
834                     timeInSec += [[ourTempArray objectAtIndex: 1] intValue]; //s
835                 }
836             }
837             else
838                 timeInSec = [fieldContent intValue];
839
840             input_Control( p_input, INPUT_SET_TIME, (int64_t)(timeInSec * 1000000));
841             vlc_object_release( p_input );
842         }
843  
844         [NSApp endSheet: o_specificTime_win];
845         [o_specificTime_win close];
846     }
847     else
848     {
849         input_thread_t * p_input = (input_thread_t *)vlc_object_find( VLCIntf, \
850             VLC_OBJECT_INPUT, FIND_ANYWHERE );
851         if( p_input )
852         {
853             /* we can obviously only do that if an input is available */
854             vlc_value_t pos, length;
855             var_Get( p_input, "time", &pos );
856             [o_specificTime_enter_fld setIntValue: (pos.i_time / 1000000)];
857             var_Get( p_input, "length", &length );
858             [o_specificTime_stepper setMaxValue: (length.i_time / 1000000)];
859
860             [NSApp beginSheet: o_specificTime_win modalForWindow: \
861                 [NSApp mainWindow] modalDelegate: self didEndSelector: nil \
862                 contextInfo: nil];
863             [o_specificTime_win makeKeyWindow];
864             vlc_object_release( p_input );
865         }
866     }
867 }
868
869 - (id)getFSPanel
870 {
871     if( o_fs_panel )
872         return o_fs_panel;
873     else
874     {
875         msg_Err( VLCIntf, "FSPanel is nil" );
876         return NULL;
877     }
878 }
879
880 @end
881
882 @implementation VLCControls (NSMenuValidation)
883
884 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
885 {
886     BOOL bEnabled = TRUE;
887     vlc_value_t val;
888     intf_thread_t * p_intf = VLCIntf;
889     playlist_t * p_playlist = pl_Yield( p_intf );
890
891 #define p_input p_playlist->p_input
892
893     if( [[o_mi title] isEqualToString: _NS("Faster")] ||
894         [[o_mi title] isEqualToString: _NS("Slower")] )
895     {
896         if( p_input != NULL )
897         {
898             bEnabled = p_input->b_can_pace_control;
899         }
900         else
901         {
902             bEnabled = FALSE;
903         }
904     }
905     else if( [[o_mi title] isEqualToString: _NS("Stop")] )
906     {
907         if( p_input == NULL )
908         {
909             bEnabled = FALSE;
910         }
911     }
912     else if( [[o_mi title] isEqualToString: _NS("Previous")] ||
913              [[o_mi title] isEqualToString: _NS("Next")] )
914     {
915         /** \todo fix i_size use */
916         PL_LOCK;
917         bEnabled = p_playlist->items.i_size > 1;
918         PL_UNLOCK;
919     }
920     else if( [[o_mi title] isEqualToString: _NS("Random")] )
921     {
922         int i_state;
923         var_Get( p_playlist, "random", &val );
924         i_state = val.b_bool ? NSOnState : NSOffState;
925         [o_mi setState: i_state];
926     }
927     else if( [[o_mi title] isEqualToString: _NS("Repeat One")] )
928     {
929         int i_state;
930         var_Get( p_playlist, "repeat", &val );
931         i_state = val.b_bool ? NSOnState : NSOffState;
932         [o_mi setState: i_state];
933     }
934     else if( [[o_mi title] isEqualToString: _NS("Repeat All")] )
935     {
936         int i_state;
937         var_Get( p_playlist, "loop", &val );
938         i_state = val.b_bool ? NSOnState : NSOffState;
939         [o_mi setState: i_state];
940     }
941     else if( [[o_mi title] isEqualToString: _NS("Step Forward")] ||
942              [[o_mi title] isEqualToString: _NS("Step Backward")] ||
943              [[o_mi title] isEqualToString: _NS("Jump To Time")])
944     {
945         if( p_input != NULL )
946         {
947             var_Get( p_input, "seekable", &val);
948             bEnabled = val.b_bool;
949         }
950         else bEnabled = FALSE;
951     }
952     else if( [[o_mi title] isEqualToString: _NS("Mute")] )
953     {
954         [o_mi setState: p_intf->p_sys->b_mute ? NSOnState : NSOffState];
955         [o_main setupMenus]; /* Make sure audio menu is up to date */
956     }
957     else if( [[o_mi title] isEqualToString: _NS("Half Size")] ||
958                 [[o_mi title] isEqualToString: _NS("Normal Size")] ||
959                 [[o_mi title] isEqualToString: _NS("Double Size")] ||
960                 [[o_mi title] isEqualToString: _NS("Fit to Screen")] ||
961                 [[o_mi title] isEqualToString: _NS("Snapshot")] ||
962                 [[o_mi title] isEqualToString: _NS("Fullscreen")] ||
963                 [[o_mi title] isEqualToString: _NS("Float on Top")] )
964     {
965         id o_window;
966         NSArray *o_windows = [NSApp orderedWindows];
967         NSEnumerator *o_enumerator = [o_windows objectEnumerator];
968         bEnabled = FALSE;
969  
970         vout_thread_t   *p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
971                                               FIND_ANYWHERE );
972         if( p_vout != NULL )
973         {
974             if( [[o_mi title] isEqualToString: _NS("Float on Top")] )
975             {
976                 var_Get( p_vout, "video-on-top", &val );
977                 [o_mi setState: val.b_bool ?  NSOnState : NSOffState];
978             }
979
980             while( (o_window = [o_enumerator nextObject]))
981             {
982                 if( [[o_window className] isEqualToString: @"VLCVoutWindow"] ||
983                             [[[VLCMain sharedInstance] getEmbeddedList]
984                             windowContainsEmbedded: o_window])
985                 {
986                     bEnabled = TRUE;
987                     break;
988                 }
989             }
990             vlc_object_release( (vlc_object_t *)p_vout );
991         }
992         else if( [[o_mi title] isEqualToString: _NS("Fullscreen")] )
993         {
994             var_Get( p_playlist, "fullscreen", &val );
995             [o_mi setState: val.b_bool];
996             bEnabled = TRUE;
997         }
998         [o_main setupMenus]; /* Make sure video menu is up to date */
999     }
1000
1001     vlc_object_release( p_playlist );
1002
1003     return( bEnabled );
1004 }
1005
1006 @end
1007
1008 /*****************************************************************************
1009  * VLCMenuExt implementation
1010  *****************************************************************************
1011  * Object connected to a playlistitem which remembers the data belonging to
1012  * the variable of the autogenerated menu
1013  *****************************************************************************/
1014 @implementation VLCMenuExt
1015
1016 - (id)initWithVar: (const char *)_psz_name Object: (int)i_id
1017         Value: (vlc_value_t)val ofType: (int)_i_type
1018 {
1019     self = [super init];
1020
1021     if( self != nil )
1022     {
1023         psz_name = strdup( _psz_name );
1024         i_object_id = i_id;
1025         value = val;
1026         i_type = _i_type;
1027     }
1028
1029     return( self );
1030 }
1031
1032 - (void)dealloc
1033 {
1034     free( psz_name );
1035     [super dealloc];
1036 }
1037
1038 - (char *)name
1039 {
1040     return psz_name;
1041 }
1042
1043 - (int)objectID
1044 {
1045     return i_object_id;
1046 }
1047
1048 - (vlc_value_t)value
1049 {
1050     return value;
1051 }
1052
1053 - (int)type
1054 {
1055     return i_type;
1056 }
1057
1058 @end
1059
1060
1061 /*****************************************************************************
1062  * VLCTimeField implementation
1063  *****************************************************************************
1064  * we need this to catch our click-event in the controller window
1065  *****************************************************************************/
1066
1067 @implementation VLCTimeField
1068 - (void)mouseDown: (NSEvent *)ourEvent
1069 {
1070     if( [ourEvent clickCount] > 1 )
1071         [[[VLCMain sharedInstance] getControls] goToSpecificTime: nil];
1072 }
1073 @end