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