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