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