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