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