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