]> git.sesse.net Git - vlc/blob - modules/gui/macosx/controls.m
75536d4673548f23fdbb47ae53ceba91ba75ee04
[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_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                 playlist_t * p_playlist = pl_Yield( VLCIntf );
503                 /* Fullscreen state for next time will be saved here too */
504                 [o_vout_view toggleFullscreen];
505             }
506         }
507         vlc_object_release( (vlc_object_t *)p_vout );
508     }
509     else
510     {
511         playlist_t * p_playlist = pl_Yield( VLCIntf );
512
513         if( [o_title isEqualToString: _NS("Fullscreen")] ||
514             [sender isKindOfClass:[NSButton class]] )
515         {
516             vlc_value_t val;
517             var_Get( p_playlist, "fullscreen", &val );
518             var_Set( p_playlist, "fullscreen", (vlc_value_t)!val.b_bool );
519         }
520
521         pl_Release( VLCIntf );
522     }
523
524 }
525
526 - (void)scrollWheel:(NSEvent *)theEvent
527 {
528     intf_thread_t * p_intf = VLCIntf;
529     float f_yabsvalue = [theEvent deltaY] > 0.0f ? [theEvent deltaY] : -[theEvent deltaY];
530     float f_xabsvalue = [theEvent deltaX] > 0.0f ? [theEvent deltaX] : -[theEvent deltaX];
531     int i, i_yvlckey, i_xvlckey;
532
533     if ([theEvent deltaY] < 0.0f)
534         i_yvlckey = KEY_MOUSEWHEELDOWN;
535     else
536         i_yvlckey = KEY_MOUSEWHEELUP;
537
538     if ([theEvent deltaX] < 0.0f)
539         i_xvlckey = KEY_MOUSEWHEELRIGHT;
540     else
541         i_xvlckey = KEY_MOUSEWHEELLEFT;
542
543     /* Send multiple key event, depending on the intensity of the event */
544     for (i = 0; i < (int)(f_yabsvalue/4.+1.) && f_yabsvalue > 0.05 ; i++)
545         var_SetInteger( p_intf->p_libvlc, "key-pressed", i_yvlckey );
546
547     /* Prioritize Y event (sound volume) over X event */
548     if (f_yabsvalue < 0.05)
549     {
550         for (i = 0; i < (int)(f_xabsvalue/6.+1.) && f_xabsvalue > 0.05; i++)
551          var_SetInteger( p_intf->p_libvlc, "key-pressed", i_xvlckey );
552     }
553 }
554
555 - (BOOL)keyEvent:(NSEvent *)o_event
556 {
557     BOOL eventHandled = NO;
558     unichar key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
559
560     if( key )
561     {
562         vout_thread_t *p_vout = vlc_object_find( VLCIntf, VLC_OBJECT_VOUT,
563                                               FIND_ANYWHERE );
564         if( p_vout != NULL )
565         {
566             /* Escape */
567             if( key == (unichar) 0x1b )
568             {
569                 id o_vout_view = [self getVoutView];
570                 if( o_vout_view && [o_vout_view isFullscreen] )
571                 {
572                     [o_vout_view toggleFullscreen];
573                     eventHandled = YES;
574                 }
575             }
576             else if( key == ' ' )
577             {
578                 [self play:self];
579                 eventHandled = YES;
580             }
581             vlc_object_release( (vlc_object_t *)p_vout );
582         }
583     }
584     return eventHandled;
585 }
586
587 - (void)setupVarMenuItem:(NSMenuItem *)o_mi
588                     target:(vlc_object_t *)p_object
589                     var:(const char *)psz_variable
590                     selector:(SEL)pf_callback
591 {
592     vlc_value_t val, text;
593     int i_type = var_Type( p_object, psz_variable );
594
595     switch( i_type & VLC_VAR_TYPE )
596     {
597     case VLC_VAR_VOID:
598     case VLC_VAR_BOOL:
599     case VLC_VAR_VARIABLE:
600     case VLC_VAR_STRING:
601     case VLC_VAR_INTEGER:
602         break;
603     default:
604         /* Variable doesn't exist or isn't handled */
605         return;
606     }
607  
608     /* Make sure we want to display the variable */
609     if( i_type & VLC_VAR_HASCHOICE )
610     {
611         var_Change( p_object, psz_variable, VLC_VAR_CHOICESCOUNT, &val, NULL );
612         if( val.i_int == 0 ) return;
613         if( (i_type & VLC_VAR_TYPE) != VLC_VAR_VARIABLE && val.i_int == 1 )
614             return;
615     }
616  
617     /* Get the descriptive name of the variable */
618     var_Change( p_object, psz_variable, VLC_VAR_GETTEXT, &text, NULL );
619     [o_mi setTitle: [[VLCMain sharedInstance] localizedString: text.psz_string ?
620                                         text.psz_string : strdup( psz_variable ) ]];
621
622     var_Get( p_object, psz_variable, &val );
623     if( i_type & VLC_VAR_HASCHOICE )
624     {
625         NSMenu *o_menu = [o_mi submenu];
626
627         [self setupVarMenu: o_menu forMenuItem: o_mi target:p_object
628                         var:psz_variable selector:pf_callback];
629  
630         free( text.psz_string );
631         return;
632     }
633
634     VLCMenuExt *o_data;
635     switch( i_type & VLC_VAR_TYPE )
636     {
637     case VLC_VAR_VOID:
638         o_data = [[VLCMenuExt alloc] initWithVar: psz_variable Object: p_object->i_object_id
639                 Value: val ofType: i_type];
640         [o_mi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
641         break;
642
643     case VLC_VAR_BOOL:
644         o_data = [[VLCMenuExt alloc] initWithVar: psz_variable Object: p_object->i_object_id
645                 Value: val ofType: i_type];
646         [o_mi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
647         if( !( i_type & VLC_VAR_ISCOMMAND ) )
648             [o_mi setState: val.b_bool ? TRUE : FALSE ];
649         break;
650
651     default:
652         free( text.psz_string );
653         return;
654     }
655
656     if( ( i_type & VLC_VAR_TYPE ) == VLC_VAR_STRING ) free( val.psz_string );
657     free( text.psz_string );
658 }
659
660
661 - (void)setupVarMenu:(NSMenu *)o_menu
662                     forMenuItem: (NSMenuItem *)o_parent
663                     target:(vlc_object_t *)p_object
664                     var:(const char *)psz_variable
665                     selector:(SEL)pf_callback
666 {
667     vlc_value_t val, val_list, text_list;
668     int i_type, i, i_nb_items;
669
670     /* remove previous items */
671     i_nb_items = [o_menu numberOfItems];
672     for( i = 0; i < i_nb_items; i++ )
673     {
674         [o_menu removeItemAtIndex: 0];
675     }
676
677     /* Check the type of the object variable */
678     i_type = var_Type( p_object, psz_variable );
679
680     /* Make sure we want to display the variable */
681     if( i_type & VLC_VAR_HASCHOICE )
682     {
683         var_Change( p_object, psz_variable, VLC_VAR_CHOICESCOUNT, &val, NULL );
684         if( val.i_int == 0 ) return;
685         if( (i_type & VLC_VAR_TYPE) != VLC_VAR_VARIABLE && val.i_int == 1 )
686             return;
687     }
688     else
689     {
690         return;
691     }
692
693     switch( i_type & VLC_VAR_TYPE )
694     {
695     case VLC_VAR_VOID:
696     case VLC_VAR_BOOL:
697     case VLC_VAR_VARIABLE:
698     case VLC_VAR_STRING:
699     case VLC_VAR_INTEGER:
700         break;
701     default:
702         /* Variable doesn't exist or isn't handled */
703         return;
704     }
705
706     if( var_Get( p_object, psz_variable, &val ) < 0 )
707     {
708         return;
709     }
710
711     if( var_Change( p_object, psz_variable, VLC_VAR_GETLIST,
712                     &val_list, &text_list ) < 0 )
713     {
714         if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string );
715         return;
716     }
717
718     /* make (un)sensitive */
719     [o_parent setEnabled: ( val_list.p_list->i_count > 1 )];
720
721     for( i = 0; i < val_list.p_list->i_count; i++ )
722     {
723         vlc_value_t another_val;
724         NSMenuItem * o_lmi;
725         NSString *o_title = @"";
726         VLCMenuExt *o_data;
727
728         switch( i_type & VLC_VAR_TYPE )
729         {
730         case VLC_VAR_STRING:
731             another_val.psz_string =
732                 strdup(val_list.p_list->p_values[i].psz_string);
733
734             o_title = [[VLCMain sharedInstance] localizedString: text_list.p_list->p_values[i].psz_string ?
735                 text_list.p_list->p_values[i].psz_string : val_list.p_list->p_values[i].psz_string ];
736
737             o_lmi = [o_menu addItemWithTitle: o_title action: pf_callback keyEquivalent: @""];
738             o_data = [[VLCMenuExt alloc] initWithVar: strdup(psz_variable) Object: p_object->i_object_id
739                     Value: another_val ofType: i_type];
740             [o_lmi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
741             [o_lmi setTarget: self];
742
743             if( !strcmp( val.psz_string, val_list.p_list->p_values[i].psz_string ) && !( i_type & VLC_VAR_ISCOMMAND ) )
744                 [o_lmi setState: TRUE ];
745
746             break;
747
748         case VLC_VAR_INTEGER:
749
750              o_title = text_list.p_list->p_values[i].psz_string ?
751                                  [[VLCMain sharedInstance] localizedString: strdup( text_list.p_list->p_values[i].psz_string )] :
752                                  [NSString stringWithFormat: @"%d",
753                                  val_list.p_list->p_values[i].i_int];
754
755             o_lmi = [[o_menu addItemWithTitle: o_title action: pf_callback keyEquivalent: @""] retain ];
756             o_data = [[VLCMenuExt alloc] initWithVar: strdup(psz_variable) Object: p_object->i_object_id
757                     Value: val_list.p_list->p_values[i] ofType: i_type];
758             [o_lmi setRepresentedObject: [NSValue valueWithPointer:[ o_data retain]]];
759             [o_lmi setTarget: self];
760
761             if( val_list.p_list->p_values[i].i_int == val.i_int && !( i_type & VLC_VAR_ISCOMMAND ) )
762                 [o_lmi setState: TRUE ];
763             break;
764
765         default:
766           break;
767         }
768     }
769
770     /* clean up everything */
771     if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string );
772     var_Change( p_object, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list );
773 }
774
775 - (IBAction)toggleVar:(id)sender
776 {
777     NSMenuItem *o_mi = (NSMenuItem *)sender;
778     VLCMenuExt *o_data = [[o_mi representedObject] pointerValue];
779     [NSThread detachNewThreadSelector: @selector(toggleVarThread:)
780         toTarget: self withObject: o_data];
781
782     return;
783 }
784
785 - (int)toggleVarThread: (id)_o_data
786 {
787     vlc_object_t *p_object;
788     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
789     VLCMenuExt *o_data = (VLCMenuExt *)_o_data;
790
791     vlc_thread_set_priority( VLCIntf , VLC_THREAD_PRIORITY_LOW );
792
793     p_object = (vlc_object_t *)vlc_object_get( [o_data objectID] );
794
795     if( p_object != NULL )
796     {
797         var_Set( p_object, strdup([o_data name]), [o_data value] );
798         vlc_object_release( p_object );
799         [o_pool release];
800         return true;
801     }
802     [o_pool release];
803     return VLC_EGENERIC;
804 }
805
806 - (IBAction)goToSpecificTime:(id)sender
807 {
808     if( sender == o_specificTime_cancel_btn )
809     {
810         [NSApp endSheet: o_specificTime_win];
811         [o_specificTime_win close];
812     }
813     else if( sender == o_specificTime_ok_btn )
814     {
815         input_thread_t * p_input = (input_thread_t *)vlc_object_find( VLCIntf, \
816             VLC_OBJECT_INPUT, FIND_ANYWHERE );
817         if( p_input )
818         {
819             unsigned int timeInSec = 0;
820             NSString * fieldContent = [o_specificTime_enter_fld stringValue];
821             if( [[fieldContent componentsSeparatedByString: @":"] count] > 1 &&
822                 [[fieldContent componentsSeparatedByString: @":"] count] <= 3 )
823             {
824                 NSArray * ourTempArray = \
825                     [fieldContent componentsSeparatedByString: @":"];
826
827                 if( [[fieldContent componentsSeparatedByString: @":"] count] == 3 )
828                 {
829                     timeInSec += ([[ourTempArray objectAtIndex: 0] intValue] * 3600); //h
830                     timeInSec += ([[ourTempArray objectAtIndex: 1] intValue] * 60); //m
831                     timeInSec += [[ourTempArray objectAtIndex: 2] intValue];        //s
832                 }
833                 else
834                 {
835                     timeInSec += ([[ourTempArray objectAtIndex: 0] intValue] * 60); //m
836                     timeInSec += [[ourTempArray objectAtIndex: 1] intValue]; //s
837                 }
838             }
839             else
840                 timeInSec = [fieldContent intValue];
841
842             input_Control( p_input, INPUT_SET_TIME, (int64_t)(timeInSec * 1000000));
843             vlc_object_release( p_input );
844         }
845  
846         [NSApp endSheet: o_specificTime_win];
847         [o_specificTime_win close];
848     }
849     else
850     {
851         input_thread_t * p_input = (input_thread_t *)vlc_object_find( VLCIntf, \
852             VLC_OBJECT_INPUT, FIND_ANYWHERE );
853         if( p_input )
854         {
855             /* we can obviously only do that if an input is available */
856             vlc_value_t pos, length;
857             var_Get( p_input, "time", &pos );
858             [o_specificTime_enter_fld setIntValue: (pos.i_time / 1000000)];
859             var_Get( p_input, "length", &length );
860             [o_specificTime_stepper setMaxValue: (length.i_time / 1000000)];
861
862             [NSApp beginSheet: o_specificTime_win modalForWindow: \
863                 [NSApp mainWindow] modalDelegate: self didEndSelector: nil \
864                 contextInfo: nil];
865             [o_specificTime_win makeKeyWindow];
866             vlc_object_release( p_input );
867         }
868     }
869 }
870
871 - (id)getFSPanel
872 {
873     if( o_fs_panel )
874         return o_fs_panel;
875     else
876     {
877         msg_Err( VLCIntf, "FSPanel is nil" );
878         return NULL;
879     }
880 }
881
882 @end
883
884 @implementation VLCControls (NSMenuValidation)
885
886 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
887 {
888     BOOL bEnabled = TRUE;
889     vlc_value_t val;
890     intf_thread_t * p_intf = VLCIntf;
891     playlist_t * p_playlist = pl_Yield( p_intf );
892     input_thread_t * p_input = playlist_CurrentInput( p_playlist );
893
894     if( [[o_mi title] isEqualToString: _NS("Faster")] ||
895         [[o_mi title] isEqualToString: _NS("Slower")] )
896     {
897         if( p_input != NULL )
898         {
899             bEnabled = p_input->b_can_pace_control;
900         }
901         else
902         {
903             bEnabled = FALSE;
904         }
905     }
906     else if( [[o_mi title] isEqualToString: _NS("Stop")] )
907     {
908         if( p_input == NULL )
909         {
910             bEnabled = FALSE;
911         }
912         [o_main setupMenus]; /* Make sure input menu is up to date */
913     }
914     else if( [[o_mi title] isEqualToString: _NS("Previous")] ||
915              [[o_mi title] isEqualToString: _NS("Next")] )
916     {
917         /** \todo fix i_size use */
918         PL_LOCK;
919         bEnabled = p_playlist->items.i_size > 1;
920         PL_UNLOCK;
921     }
922     else if( [[o_mi title] isEqualToString: _NS("Random")] )
923     {
924         int i_state;
925         var_Get( p_playlist, "random", &val );
926         i_state = val.b_bool ? NSOnState : NSOffState;
927         [o_mi setState: i_state];
928     }
929     else if( [[o_mi title] isEqualToString: _NS("Repeat One")] )
930     {
931         int i_state;
932         var_Get( p_playlist, "repeat", &val );
933         i_state = val.b_bool ? NSOnState : NSOffState;
934         [o_mi setState: i_state];
935     }
936     else if( [[o_mi title] isEqualToString: _NS("Repeat All")] )
937     {
938         int i_state;
939         var_Get( p_playlist, "loop", &val );
940         i_state = val.b_bool ? NSOnState : NSOffState;
941         [o_mi setState: i_state];
942     }
943     else if( [[o_mi title] isEqualToString: _NS("Step Forward")] ||
944              [[o_mi title] isEqualToString: _NS("Step Backward")] ||
945              [[o_mi title] isEqualToString: _NS("Jump To Time")])
946     {
947         if( p_input != NULL )
948         {
949             var_Get( p_input, "seekable", &val);
950             bEnabled = val.b_bool;
951         }
952         else bEnabled = FALSE;
953     }
954     else if( [[o_mi title] isEqualToString: _NS("Mute")] )
955     {
956         [o_mi setState: p_intf->p_sys->b_mute ? NSOnState : NSOffState];
957         [o_main setupMenus]; /* Make sure audio menu is up to date */
958     }
959     else if( [[o_mi title] isEqualToString: _NS("Half Size")] ||
960                 [[o_mi title] isEqualToString: _NS("Normal Size")] ||
961                 [[o_mi title] isEqualToString: _NS("Double Size")] ||
962                 [[o_mi title] isEqualToString: _NS("Fit to Screen")] ||
963                 [[o_mi title] isEqualToString: _NS("Snapshot")] ||
964                 [[o_mi title] isEqualToString: _NS("Fullscreen")] ||
965                 [[o_mi title] isEqualToString: _NS("Float on Top")] )
966     {
967         id o_window;
968         NSArray *o_windows = [NSApp orderedWindows];
969         NSEnumerator *o_enumerator = [o_windows objectEnumerator];
970         bEnabled = FALSE;
971  
972         vout_thread_t   *p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
973                                               FIND_ANYWHERE );
974         if( p_vout != NULL )
975         {
976             if( [[o_mi title] isEqualToString: _NS("Float on Top")] )
977             {
978                 var_Get( p_vout, "video-on-top", &val );
979                 [o_mi setState: val.b_bool ?  NSOnState : NSOffState];
980             }
981
982             while( (o_window = [o_enumerator nextObject]))
983             {
984                 if( [[o_window className] isEqualToString: @"VLCVoutWindow"] ||
985                             [[[VLCMain sharedInstance] getEmbeddedList]
986                             windowContainsEmbedded: o_window])
987                 {
988                     bEnabled = TRUE;
989                     break;
990                 }
991             }
992
993             vlc_object_release( (vlc_object_t *)p_vout );
994         }
995         if( [[o_mi title] isEqualToString: _NS("Fullscreen")] )
996         {
997             var_Get( p_playlist, "fullscreen", &val );
998             [o_mi setState: val.b_bool];
999             bEnabled = TRUE;
1000         }
1001         [o_main setupMenus]; /* Make sure video menu is up to date */
1002     }
1003
1004     if( p_input ) vlc_object_release( p_input );
1005     vlc_object_release( p_playlist );
1006
1007     return( bEnabled );
1008 }
1009
1010 @end
1011
1012 /*****************************************************************************
1013  * VLCMenuExt implementation
1014  *****************************************************************************
1015  * Object connected to a playlistitem which remembers the data belonging to
1016  * the variable of the autogenerated menu
1017  *****************************************************************************/
1018 @implementation VLCMenuExt
1019
1020 - (id)initWithVar: (const char *)_psz_name Object: (int)i_id
1021         Value: (vlc_value_t)val ofType: (int)_i_type
1022 {
1023     self = [super init];
1024
1025     if( self != nil )
1026     {
1027         psz_name = strdup( _psz_name );
1028         i_object_id = i_id;
1029         value = val;
1030         i_type = _i_type;
1031     }
1032
1033     return( self );
1034 }
1035
1036 - (void)dealloc
1037 {
1038     free( psz_name );
1039     [super dealloc];
1040 }
1041
1042 - (char *)name
1043 {
1044     return psz_name;
1045 }
1046
1047 - (int)objectID
1048 {
1049     return i_object_id;
1050 }
1051
1052 - (vlc_value_t)value
1053 {
1054     return value;
1055 }
1056
1057 - (int)type
1058 {
1059     return i_type;
1060 }
1061
1062 @end
1063
1064
1065 /*****************************************************************************
1066  * VLCTimeField implementation
1067  *****************************************************************************
1068  * we need this to catch our click-event in the controller window
1069  *****************************************************************************/
1070
1071 @implementation VLCTimeField
1072 - (void)mouseDown: (NSEvent *)ourEvent
1073 {
1074     if( [ourEvent clickCount] > 1 )
1075         [[[VLCMain sharedInstance] getControls] goToSpecificTime: nil];
1076 }
1077 @end