]> git.sesse.net Git - vlc/blob - modules/gui/macosx/controls.m
Merge branch 'master' of git@git.videolan.org:vlc
[vlc] / 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_mutex_lock( &p_playlist->object_lock );
125     if( playlist_IsEmpty( p_playlist ) )
126     {
127         vlc_mutex_unlock( &p_playlist->object_lock );
128         vlc_object_release( p_playlist );
129         [o_main intfOpenFileGeneric: (id)sender];
130     }
131     else
132     {
133         vlc_mutex_unlock( &p_playlist->object_lock );
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
167 - (IBAction)stop:(id)sender
168 {
169     vlc_value_t val;
170     intf_thread_t * p_intf = VLCIntf;
171     val.i_int = config_GetInt( p_intf, "key-stop" );
172     var_Set( p_intf->p_libvlc, "key-pressed", val );
173 }
174
175 - (IBAction)faster:(id)sender
176 {
177     vlc_value_t val;
178     intf_thread_t * p_intf = VLCIntf;
179     val.i_int = config_GetInt( p_intf, "key-faster" );
180     var_Set( p_intf->p_libvlc, "key-pressed", val );
181 }
182
183 - (IBAction)slower:(id)sender
184 {
185     vlc_value_t val;
186     intf_thread_t * p_intf = VLCIntf;
187     val.i_int = config_GetInt( p_intf, "key-slower" );
188     var_Set( p_intf->p_libvlc, "key-pressed", val );
189 }
190
191 - (IBAction)prev:(id)sender
192 {
193     vlc_value_t val;
194     intf_thread_t * p_intf = VLCIntf;
195     val.i_int = config_GetInt( p_intf, "key-prev" );
196     var_Set( p_intf->p_libvlc, "key-pressed", val );
197 }
198
199 - (IBAction)next:(id)sender
200 {
201     vlc_value_t val;
202     intf_thread_t * p_intf = VLCIntf;
203     val.i_int = config_GetInt( p_intf, "key-next" );
204     var_Set( p_intf->p_libvlc, "key-pressed", val );
205 }
206
207 - (IBAction)random:(id)sender
208 {
209     vlc_value_t val;
210     intf_thread_t * p_intf = VLCIntf;
211     playlist_t * p_playlist = pl_Yield( p_intf );
212
213     var_Get( p_playlist, "random", &val );
214     val.b_bool = !val.b_bool;
215     var_Set( p_playlist, "random", val );
216     if( val.b_bool )
217     {
218         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Random On" ) );
219         config_PutInt( p_playlist, "random", 1 );
220     }
221     else
222     {
223         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Random Off" ) );
224         config_PutInt( p_playlist, "random", 0 );
225     }
226
227     p_intf->p_sys->b_playmode_update = true;
228     p_intf->p_sys->b_intf_update = true;
229     vlc_object_release( p_playlist );
230 }
231
232 /* three little ugly helpers */
233 - (void)repeatOne
234 {
235     [o_btn_repeat setImage: o_repeat_single];
236     [o_btn_repeat setAlternateImage: o_repeat_all];
237 }
238 - (void)repeatAll
239 {
240     [o_btn_repeat setImage: o_repeat_all];
241     [o_btn_repeat setAlternateImage: o_repeat_off];
242 }
243 - (void)repeatOff
244 {
245     [o_btn_repeat setImage: o_repeat_off];
246     [o_btn_repeat setAlternateImage: o_repeat_single];
247 }
248 - (void)shuffle
249 {
250     vlc_value_t val;
251     playlist_t *p_playlist = pl_Yield( VLCIntf );
252     var_Get( p_playlist, "random", &val );
253     [o_btn_shuffle setState: val.b_bool];
254     vlc_object_release( p_playlist );
255 }
256
257 - (IBAction)repeatButtonAction:(id)sender
258 {
259     vlc_value_t looping,repeating;
260     intf_thread_t * p_intf = VLCIntf;
261     playlist_t * p_playlist = pl_Yield( p_intf );
262
263     var_Get( p_playlist, "repeat", &repeating );
264     var_Get( p_playlist, "loop", &looping );
265
266     if( !repeating.b_bool && !looping.b_bool )
267     {
268         /* was: no repeating at all, switching to Repeat One */
269  
270         /* set our button's look */
271         [self repeatOne];
272  
273         /* prepare core communication */
274         repeating.b_bool = true;
275         looping.b_bool = false;
276         config_PutInt( p_playlist, "repeat", 1 );
277         config_PutInt( p_playlist, "loop", 0 );
278  
279         /* show the change */
280         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat One" ) );
281     }
282     else if( repeating.b_bool && !looping.b_bool )
283     {
284         /* was: Repeat One, switching to Repeat All */
285  
286         /* set our button's look */
287         [self repeatAll];
288  
289         /* prepare core communication */
290         repeating.b_bool = false;
291         looping.b_bool = true;
292         config_PutInt( p_playlist, "repeat", 0 );
293         config_PutInt( p_playlist, "loop", 1 );
294  
295         /* show the change */
296         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat All" ) );
297     }
298     else
299     {
300         /* was: Repeat All or bug in VLC, switching to Repeat Off */
301  
302         /* set our button's look */
303         [self repeatOff];
304  
305         /* prepare core communication */
306         repeating.b_bool = false;
307         looping.b_bool = false;
308         config_PutInt( p_playlist, "repeat", 0 );
309         config_PutInt( p_playlist, "loop", 0 );
310  
311         /* show the change */
312         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
313     }
314
315     /* communicate with core and the main intf loop */
316     var_Set( p_playlist, "repeat", repeating );
317     var_Set( p_playlist, "loop", looping );
318     p_intf->p_sys->b_playmode_update = true;
319     p_intf->p_sys->b_intf_update = true;
320
321     vlc_object_release( p_playlist );
322 }
323
324
325 - (IBAction)repeat:(id)sender
326 {
327     vlc_value_t val;
328     intf_thread_t * p_intf = VLCIntf;
329     playlist_t * p_playlist = pl_Yield( p_intf );
330
331     var_Get( p_playlist, "repeat", &val );
332     if (!val.b_bool)
333     {
334         var_Set( p_playlist, "loop", val );
335     }
336     val.b_bool = !val.b_bool;
337     var_Set( p_playlist, "repeat", val );
338     if( val.b_bool )
339     {
340         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat One" ) );
341         config_PutInt( p_playlist, "repeat", 1 );
342     }
343     else
344     {
345         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
346         config_PutInt( p_playlist, "repeat", 0 );
347     }
348  
349     p_intf->p_sys->b_playmode_update = true;
350     p_intf->p_sys->b_intf_update = true;
351     vlc_object_release( p_playlist );
352 }
353
354 - (IBAction)loop:(id)sender
355 {
356     vlc_value_t val;
357     intf_thread_t * p_intf = VLCIntf;
358     playlist_t * p_playlist = pl_Yield( p_intf );
359
360     var_Get( p_playlist, "loop", &val );
361     if (!val.b_bool)
362     {
363         var_Set( p_playlist, "repeat", val );
364     }
365     val.b_bool = !val.b_bool;
366     var_Set( p_playlist, "loop", val );
367     if( val.b_bool )
368     {
369         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat All" ) );
370         config_PutInt( p_playlist, "loop", 1 );
371     }
372     else
373     {
374         vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
375         config_PutInt( p_playlist, "loop", 0 );
376     }
377
378     p_intf->p_sys->b_playmode_update = true;
379     p_intf->p_sys->b_intf_update = true;
380     vlc_object_release( p_playlist );
381 }
382
383 - (IBAction)forward:(id)sender
384 {
385     vlc_value_t val;
386     intf_thread_t * p_intf = VLCIntf;
387     val.i_int = config_GetInt( p_intf, "key-jump+short" );
388     var_Set( p_intf->p_libvlc, "key-pressed", val );
389 }
390
391 - (IBAction)backward:(id)sender
392 {
393     vlc_value_t val;
394     intf_thread_t * p_intf = VLCIntf;
395     val.i_int = config_GetInt( p_intf, "key-jump-short" );
396     var_Set( p_intf->p_libvlc, "key-pressed", val );
397 }
398
399
400 - (IBAction)volumeUp:(id)sender
401 {
402     vlc_value_t val;
403     intf_thread_t * p_intf = VLCIntf;
404     val.i_int = config_GetInt( p_intf, "key-vol-up" );
405     var_Set( p_intf->p_libvlc, "key-pressed", val );
406     /* Manage volume status */
407     [o_main manageVolumeSlider];
408 }
409
410 - (IBAction)volumeDown:(id)sender
411 {
412     vlc_value_t val;
413     intf_thread_t * p_intf = VLCIntf;
414     val.i_int = config_GetInt( p_intf, "key-vol-down" );
415     var_Set( p_intf->p_libvlc, "key-pressed", val );
416     /* Manage volume status */
417     [o_main manageVolumeSlider];
418 }
419
420 - (IBAction)mute:(id)sender
421 {
422     vlc_value_t val;
423     intf_thread_t * p_intf = VLCIntf;
424     val.i_int = config_GetInt( p_intf, "key-vol-mute" );
425     var_Set( p_intf->p_libvlc, "key-pressed", val );
426     /* Manage volume status */
427     [o_main manageVolumeSlider];
428 }
429
430 - (IBAction)volumeSliderUpdated:(id)sender
431 {
432     intf_thread_t * p_intf = VLCIntf;
433     audio_volume_t i_volume = (audio_volume_t)[sender intValue];
434     int i_volume_step = 0;
435     i_volume_step = config_GetInt( p_intf->p_libvlc, "volume-step" );
436     aout_VolumeSet( p_intf, i_volume * i_volume_step );
437     /* Manage volume status */
438     [o_main manageVolumeSlider];
439 }
440
441 - (IBAction)showPosition: (id)sender
442 {
443     vout_thread_t *p_vout = vlc_object_find( VLCIntf, VLC_OBJECT_VOUT,
444                                              FIND_ANYWHERE );
445     if( p_vout != NULL )
446     {
447         vlc_value_t val;
448         intf_thread_t * p_intf = VLCIntf;
449         val.i_int = config_GetInt( p_intf, "key-position" );
450         var_Set( p_intf, "key-pressed", val );
451         vlc_object_release( (vlc_object_t *)p_vout );
452     }
453 }
454
455 - (IBAction)toogleFullscreen:(id)sender {
456     NSMenuItem *o_mi = [[NSMenuItem alloc] initWithTitle: _NS("Fullscreen") action: nil keyEquivalent:@""];
457     [self windowAction: [o_mi autorelease]];
458 }
459
460 - (BOOL) isFullscreen {
461     id o_vout_view = [self getVoutView];
462     if( o_vout_view )
463     {
464         return [o_vout_view isFullscreen];
465     }
466     return NO;
467 }
468
469 - (IBAction)windowAction:(id)sender
470 {
471     NSString *o_title = [sender title];
472
473     vout_thread_t *p_vout = vlc_object_find( VLCIntf, VLC_OBJECT_VOUT,
474                                               FIND_ANYWHERE );
475     if( p_vout != NULL )
476     {
477         id o_vout_view = [self getVoutView];
478         if( o_vout_view )
479         {
480             if( [o_title isEqualToString: _NS("Half Size") ] )
481                 [o_vout_view scaleWindowWithFactor: 0.5 animate: YES];
482             else if( [o_title isEqualToString: _NS("Normal Size") ] )
483                 [o_vout_view scaleWindowWithFactor: 1.0 animate: YES];
484             else if( [o_title isEqualToString: _NS("Double Size") ] )
485                 [o_vout_view scaleWindowWithFactor: 2.0 animate: YES];
486             else if( [o_title isEqualToString: _NS("Float on Top") ] )
487                 [o_vout_view toggleFloatOnTop];
488             else if( [o_title isEqualToString: _NS("Fit to Screen") ] )
489             {
490                 id o_window = [o_vout_view getWindow];
491                 if( ![o_window isZoomed] )
492                     [o_window performZoom:self];
493             }
494             else if( [o_title isEqualToString: _NS("Snapshot") ] )
495             {
496                 [o_vout_view snapshot];
497             }
498             else
499             {
500                 [o_vout_view toggleFullscreen];
501             }
502         }
503         vlc_object_release( (vlc_object_t *)p_vout );
504     }
505     else
506     {
507         playlist_t * p_playlist = pl_Yield( VLCIntf );
508
509         if( [o_title isEqualToString: _NS("Fullscreen")] ||
510             [sender isKindOfClass:[NSButton class]] )
511         {
512             vlc_value_t val;
513             var_Get( p_playlist, "fullscreen", &val );
514             var_Set( p_playlist, "fullscreen", (vlc_value_t)!val.b_bool );
515         }
516
517         vlc_object_release( p_playlist );
518     }
519
520 }
521
522 - (void)scrollWheel:(NSEvent *)theEvent
523 {
524     intf_thread_t * p_intf = VLCIntf;
525     float f_yabsvalue = [theEvent deltaY] > 0.0f ? [theEvent deltaY] : -[theEvent deltaY];
526     float f_xabsvalue = [theEvent deltaX] > 0.0f ? [theEvent deltaX] : -[theEvent deltaX];
527     int i, i_yvlckey, i_xvlckey;
528
529     if ([theEvent deltaY] < 0.0f)
530         i_yvlckey = KEY_MOUSEWHEELDOWN;
531     else
532         i_yvlckey = KEY_MOUSEWHEELUP;
533
534     if ([theEvent deltaX] < 0.0f)
535         i_xvlckey = KEY_MOUSEWHEELRIGHT;
536     else
537         i_xvlckey = KEY_MOUSEWHEELLEFT;
538
539     /* Send multiple key event, depending on the intensity of the event */
540     for (i = 0; i < (int)(f_yabsvalue/4.+1.) && f_yabsvalue > 0.05 ; i++)
541         var_SetInteger( p_intf->p_libvlc, "key-pressed", i_yvlckey );
542
543     /* Prioritize Y event (sound volume) over X event */
544     if (f_yabsvalue < 0.05)
545     {
546         for (i = 0; i < (int)(f_xabsvalue/6.+1.) && f_xabsvalue > 0.05; i++)
547          var_SetInteger( p_intf->p_libvlc, "key-pressed", i_xvlckey );
548     }
549 }
550
551 - (BOOL)keyEvent:(NSEvent *)o_event
552 {
553     BOOL eventHandled = NO;
554     unichar key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
555
556     if( key )
557     {
558         vout_thread_t *p_vout = vlc_object_find( VLCIntf, VLC_OBJECT_VOUT,
559                                               FIND_ANYWHERE );
560         if( p_vout != NULL )
561         {
562             /* Escape */
563             if( key == (unichar) 0x1b )
564             {
565                 id o_vout_view = [self getVoutView];
566                 if( o_vout_view && [o_vout_view isFullscreen] )
567                 {
568                     [o_vout_view toggleFullscreen];
569                     eventHandled = YES;
570                 }
571             }
572             else if( key == ' ' )
573             {
574                 [self play:self];
575                 eventHandled = YES;
576             }
577             vlc_object_release( (vlc_object_t *)p_vout );
578         }
579     }
580     return eventHandled;
581 }
582
583 - (void)setupVarMenuItem:(NSMenuItem *)o_mi
584                     target:(vlc_object_t *)p_object
585                     var:(const char *)psz_variable
586                     selector:(SEL)pf_callback
587 {
588     vlc_value_t val, text;
589     int i_type = var_Type( p_object, psz_variable );
590
591     switch( i_type & VLC_VAR_TYPE )
592     {
593     case VLC_VAR_VOID:
594     case VLC_VAR_BOOL:
595     case VLC_VAR_VARIABLE:
596     case VLC_VAR_STRING:
597     case VLC_VAR_INTEGER:
598         break;
599     default:
600         /* Variable doesn't exist or isn't handled */
601         return;
602     }
603  
604     /* Make sure we want to display the variable */
605     if( i_type & VLC_VAR_HASCHOICE )
606     {
607         var_Change( p_object, psz_variable, VLC_VAR_CHOICESCOUNT, &val, NULL );
608         if( val.i_int == 0 ) return;
609         if( (i_type & VLC_VAR_TYPE) != VLC_VAR_VARIABLE && val.i_int == 1 )
610             return;
611     }
612  
613     /* Get the descriptive name of the variable */
614     var_Change( p_object, psz_variable, VLC_VAR_GETTEXT, &text, NULL );
615     [o_mi setTitle: [[VLCMain sharedInstance] localizedString: text.psz_string ?
616                                         text.psz_string : strdup( psz_variable ) ]];
617
618     var_Get( p_object, psz_variable, &val );
619     if( i_type & VLC_VAR_HASCHOICE )
620     {
621         NSMenu *o_menu = [o_mi submenu];
622
623         [self setupVarMenu: o_menu forMenuItem: o_mi target:p_object
624                         var:psz_variable selector:pf_callback];
625  
626         free( text.psz_string );
627         return;
628     }
629
630     VLCMenuExt *o_data;
631     switch( i_type & VLC_VAR_TYPE )
632     {
633     case VLC_VAR_VOID:
634         o_data = [[VLCMenuExt alloc] initWithVar: psz_variable Object: p_object->i_object_id
635                 Value: val ofType: i_type];
636         [o_mi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
637         break;
638
639     case VLC_VAR_BOOL:
640         o_data = [[VLCMenuExt alloc] initWithVar: psz_variable Object: p_object->i_object_id
641                 Value: val ofType: i_type];
642         [o_mi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
643         if( !( i_type & VLC_VAR_ISCOMMAND ) )
644             [o_mi setState: val.b_bool ? TRUE : FALSE ];
645         break;
646
647     default:
648         free( text.psz_string );
649         return;
650     }
651
652     if( ( i_type & VLC_VAR_TYPE ) == VLC_VAR_STRING ) free( val.psz_string );
653     free( text.psz_string );
654 }
655
656
657 - (void)setupVarMenu:(NSMenu *)o_menu
658                     forMenuItem: (NSMenuItem *)o_parent
659                     target:(vlc_object_t *)p_object
660                     var:(const char *)psz_variable
661                     selector:(SEL)pf_callback
662 {
663     vlc_value_t val, val_list, text_list;
664     int i_type, i, i_nb_items;
665
666     /* remove previous items */
667     i_nb_items = [o_menu numberOfItems];
668     for( i = 0; i < i_nb_items; i++ )
669     {
670         [o_menu removeItemAtIndex: 0];
671     }
672
673     /* Check the type of the object variable */
674     i_type = var_Type( p_object, psz_variable );
675
676     /* Make sure we want to display the variable */
677     if( i_type & VLC_VAR_HASCHOICE )
678     {
679         var_Change( p_object, psz_variable, VLC_VAR_CHOICESCOUNT, &val, NULL );
680         if( val.i_int == 0 ) return;
681         if( (i_type & VLC_VAR_TYPE) != VLC_VAR_VARIABLE && val.i_int == 1 )
682             return;
683     }
684     else
685     {
686         return;
687     }
688
689     switch( i_type & VLC_VAR_TYPE )
690     {
691     case VLC_VAR_VOID:
692     case VLC_VAR_BOOL:
693     case VLC_VAR_VARIABLE:
694     case VLC_VAR_STRING:
695     case VLC_VAR_INTEGER:
696         break;
697     default:
698         /* Variable doesn't exist or isn't handled */
699         return;
700     }
701
702     if( var_Get( p_object, psz_variable, &val ) < 0 )
703     {
704         return;
705     }
706
707     if( var_Change( p_object, psz_variable, VLC_VAR_GETLIST,
708                     &val_list, &text_list ) < 0 )
709     {
710         if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string );
711         return;
712     }
713
714     /* make (un)sensitive */
715     [o_parent setEnabled: ( val_list.p_list->i_count > 1 )];
716
717     for( i = 0; i < val_list.p_list->i_count; i++ )
718     {
719         vlc_value_t another_val;
720         NSMenuItem * o_lmi;
721         NSString *o_title = @"";
722         VLCMenuExt *o_data;
723
724         switch( i_type & VLC_VAR_TYPE )
725         {
726         case VLC_VAR_STRING:
727             another_val.psz_string =
728                 strdup(val_list.p_list->p_values[i].psz_string);
729
730             o_title = [[VLCMain sharedInstance] localizedString: text_list.p_list->p_values[i].psz_string ?
731                 text_list.p_list->p_values[i].psz_string : val_list.p_list->p_values[i].psz_string ];
732
733             o_lmi = [o_menu addItemWithTitle: o_title action: pf_callback keyEquivalent: @""];
734             o_data = [[VLCMenuExt alloc] initWithVar: strdup(psz_variable) Object: p_object->i_object_id
735                     Value: another_val ofType: i_type];
736             [o_lmi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
737             [o_lmi setTarget: self];
738
739             if( !strcmp( val.psz_string, val_list.p_list->p_values[i].psz_string ) && !( i_type & VLC_VAR_ISCOMMAND ) )
740                 [o_lmi setState: TRUE ];
741
742             break;
743
744         case VLC_VAR_INTEGER:
745
746              o_title = text_list.p_list->p_values[i].psz_string ?
747                                  [[VLCMain sharedInstance] localizedString: strdup( text_list.p_list->p_values[i].psz_string )] :
748                                  [NSString stringWithFormat: @"%d",
749                                  val_list.p_list->p_values[i].i_int];
750
751             o_lmi = [[o_menu addItemWithTitle: o_title action: pf_callback keyEquivalent: @""] retain ];
752             o_data = [[VLCMenuExt alloc] initWithVar: strdup(psz_variable) Object: p_object->i_object_id
753                     Value: val_list.p_list->p_values[i] ofType: i_type];
754             [o_lmi setRepresentedObject: [NSValue valueWithPointer:[ o_data retain]]];
755             [o_lmi setTarget: self];
756
757             if( val_list.p_list->p_values[i].i_int == val.i_int && !( i_type & VLC_VAR_ISCOMMAND ) )
758                 [o_lmi setState: TRUE ];
759             break;
760
761         default:
762           break;
763         }
764     }
765
766     /* clean up everything */
767     if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string );
768     var_Change( p_object, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list );
769 }
770
771 - (IBAction)toggleVar:(id)sender
772 {
773     NSMenuItem *o_mi = (NSMenuItem *)sender;
774     VLCMenuExt *o_data = [[o_mi representedObject] pointerValue];
775     [NSThread detachNewThreadSelector: @selector(toggleVarThread:)
776         toTarget: self withObject: o_data];
777
778     return;
779 }
780
781 - (int)toggleVarThread: (id)_o_data
782 {
783     vlc_object_t *p_object;
784     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
785     VLCMenuExt *o_data = (VLCMenuExt *)_o_data;
786
787     vlc_thread_set_priority( VLCIntf , VLC_THREAD_PRIORITY_LOW );
788
789     p_object = (vlc_object_t *)vlc_object_get( [o_data objectID] );
790
791     if( p_object != NULL )
792     {
793         var_Set( p_object, strdup([o_data name]), [o_data value] );
794         vlc_object_release( p_object );
795         [o_pool release];
796         return true;
797     }
798     [o_pool release];
799     return VLC_EGENERIC;
800 }
801
802 - (IBAction)goToSpecificTime:(id)sender
803 {
804     if( sender == o_specificTime_cancel_btn )
805     {
806         [NSApp endSheet: o_specificTime_win];
807         [o_specificTime_win close];
808     }
809     else if( sender == o_specificTime_ok_btn )
810     {
811         input_thread_t * p_input = (input_thread_t *)vlc_object_find( VLCIntf, \
812             VLC_OBJECT_INPUT, FIND_ANYWHERE );
813         if( p_input )
814         {
815             unsigned int timeInSec = 0;
816             NSString * fieldContent = [o_specificTime_enter_fld stringValue];
817             if( [[fieldContent componentsSeparatedByString: @":"] count] > 1 &&
818                 [[fieldContent componentsSeparatedByString: @":"] count] <= 3 )
819             {
820                 NSArray * ourTempArray = \
821                     [fieldContent componentsSeparatedByString: @":"];
822
823                 if( [[fieldContent componentsSeparatedByString: @":"] count] == 3 )
824                 {
825                     timeInSec += ([[ourTempArray objectAtIndex: 0] intValue] * 3600); //h
826                     timeInSec += ([[ourTempArray objectAtIndex: 1] intValue] * 60); //m
827                     timeInSec += [[ourTempArray objectAtIndex: 2] intValue];        //s
828                 }
829                 else
830                 {
831                     timeInSec += ([[ourTempArray objectAtIndex: 0] intValue] * 60); //m
832                     timeInSec += [[ourTempArray objectAtIndex: 1] intValue]; //s
833                 }
834             }
835             else
836                 timeInSec = [fieldContent intValue];
837
838             input_Control( p_input, INPUT_SET_TIME, (int64_t)(timeInSec * 1000000));
839             vlc_object_release( p_input );
840         }
841  
842         [NSApp endSheet: o_specificTime_win];
843         [o_specificTime_win close];
844     }
845     else
846     {
847         input_thread_t * p_input = (input_thread_t *)vlc_object_find( VLCIntf, \
848             VLC_OBJECT_INPUT, FIND_ANYWHERE );
849         if( p_input )
850         {
851             /* we can obviously only do that if an input is available */
852             vlc_value_t pos, length;
853             var_Get( p_input, "time", &pos );
854             [o_specificTime_enter_fld setIntValue: (pos.i_time / 1000000)];
855             var_Get( p_input, "length", &length );
856             [o_specificTime_stepper setMaxValue: (length.i_time / 1000000)];
857
858             [NSApp beginSheet: o_specificTime_win modalForWindow: \
859                 [NSApp mainWindow] modalDelegate: self didEndSelector: nil \
860                 contextInfo: nil];
861             [o_specificTime_win makeKeyWindow];
862             vlc_object_release( p_input );
863         }
864     }
865 }
866
867 - (id)getFSPanel
868 {
869     if( o_fs_panel )
870         return o_fs_panel;
871     else
872     {
873         msg_Err( VLCIntf, "FSPanel is nil" );
874         return NULL;
875     }
876 }
877
878 @end
879
880 @implementation VLCControls (NSMenuValidation)
881
882 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
883 {
884     BOOL bEnabled = TRUE;
885     vlc_value_t val;
886     intf_thread_t * p_intf = VLCIntf;
887     playlist_t * p_playlist = pl_Yield( p_intf );
888
889     vlc_mutex_lock( &p_playlist->object_lock );
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         [o_main setupMenus]; /* Make sure input menu is up to date */
912     }
913     else if( [[o_mi title] isEqualToString: _NS("Previous")] ||
914              [[o_mi title] isEqualToString: _NS("Next")] )
915     {
916         /** \todo fix i_size use */
917         bEnabled = p_playlist->items.i_size > 1;
918     }
919     else if( [[o_mi title] isEqualToString: _NS("Random")] )
920     {
921         int i_state;
922         var_Get( p_playlist, "random", &val );
923         i_state = val.b_bool ? NSOnState : NSOffState;
924         [o_mi setState: i_state];
925     }
926     else if( [[o_mi title] isEqualToString: _NS("Repeat One")] )
927     {
928         int i_state;
929         var_Get( p_playlist, "repeat", &val );
930         i_state = val.b_bool ? NSOnState : NSOffState;
931         [o_mi setState: i_state];
932     }
933     else if( [[o_mi title] isEqualToString: _NS("Repeat All")] )
934     {
935         int i_state;
936         var_Get( p_playlist, "loop", &val );
937         i_state = val.b_bool ? NSOnState : NSOffState;
938         [o_mi setState: i_state];
939     }
940     else if( [[o_mi title] isEqualToString: _NS("Step Forward")] ||
941              [[o_mi title] isEqualToString: _NS("Step Backward")] ||
942              [[o_mi title] isEqualToString: _NS("Jump To Time")])
943     {
944         if( p_input != NULL )
945         {
946             var_Get( p_input, "seekable", &val);
947             bEnabled = val.b_bool;
948         }
949         else bEnabled = FALSE;
950     }
951     else if( [[o_mi title] isEqualToString: _NS("Mute")] )
952     {
953         [o_mi setState: p_intf->p_sys->b_mute ? NSOnState : NSOffState];
954         [o_main setupMenus]; /* Make sure audio menu is up to date */
955     }
956     else if( [[o_mi title] isEqualToString: _NS("Half Size")] ||
957                 [[o_mi title] isEqualToString: _NS("Normal Size")] ||
958                 [[o_mi title] isEqualToString: _NS("Double Size")] ||
959                 [[o_mi title] isEqualToString: _NS("Fit to Screen")] ||
960                 [[o_mi title] isEqualToString: _NS("Snapshot")] ||
961                 [[o_mi title] isEqualToString: _NS("Fullscreen")] ||
962                 [[o_mi title] isEqualToString: _NS("Float on Top")] )
963     {
964         id o_window;
965         NSArray *o_windows = [NSApp orderedWindows];
966         NSEnumerator *o_enumerator = [o_windows objectEnumerator];
967         bEnabled = FALSE;
968  
969         vout_thread_t   *p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
970                                               FIND_ANYWHERE );
971         if( p_vout != NULL )
972         {
973             if( [[o_mi title] isEqualToString: _NS("Float on Top")] )
974             {
975                 var_Get( p_vout, "video-on-top", &val );
976                 [o_mi setState: val.b_bool ?  NSOnState : NSOffState];
977             }
978
979             while( (o_window = [o_enumerator nextObject]))
980             {
981                 if( [[o_window className] isEqualToString: @"VLCVoutWindow"] ||
982                             [[[VLCMain sharedInstance] getEmbeddedList]
983                             windowContainsEmbedded: o_window])
984                 {
985                     bEnabled = TRUE;
986                     break;
987                 }
988             }
989             vlc_object_release( (vlc_object_t *)p_vout );
990         }
991         else if( [[o_mi title] isEqualToString: _NS("Fullscreen")] )
992         {
993             var_Get( p_playlist, "fullscreen", &val );
994             [o_mi setState: val.b_bool];
995             bEnabled = TRUE;
996         }
997         [o_main setupMenus]; /* Make sure video menu is up to date */
998     }
999
1000     vlc_mutex_unlock( &p_playlist->object_lock );
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