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