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