]> git.sesse.net Git - vlc/blob - modules/gui/macosx/controls.m
* src/input/input_clock.c: Fixed two long-standing (> 2 years) bugs
[vlc] / modules / gui / macosx / controls.m
1 /*****************************************************************************
2  * controls.m: MacOS X interface plugin
3  *****************************************************************************
4  * Copyright (C) 2002-2003 VideoLAN
5  * $Id: controls.m,v 1.46 2003/07/27 14:10:02 massiot Exp $
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8  *          Christophe Massiot <massiot@via.ecp.fr>
9  *          Derk-Jan Hartman <thedj@users.sourceforge.net>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <stdlib.h>                                      /* malloc(), free() */
30 #include <sys/param.h>                                    /* for MAXPATHLEN */
31 #include <string.h>
32
33 #include "intf.h"
34 #include "vout.h"
35 #include "open.h"
36 #include "controls.h"
37
38 /*****************************************************************************
39  * VLCControls implementation 
40  *****************************************************************************/
41 @implementation VLCControls
42
43 - (IBAction)play:(id)sender
44 {
45     intf_thread_t * p_intf = [NSApp getIntf];
46
47     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
48                                                        FIND_ANYWHERE );
49     if( p_playlist == NULL )
50     {
51         return;
52     }
53
54     if( playlist_IsPlaying( p_playlist ) )
55     {
56         playlist_Pause( p_playlist );
57         vlc_object_release( p_playlist );
58     }
59     else
60     {
61         if( !playlist_IsEmpty( p_playlist ) )
62         {
63             playlist_Play( p_playlist );
64             vlc_object_release( p_playlist );
65         }
66         else
67         {
68             vlc_object_release( p_playlist );
69             [o_open openFileGeneric: nil];
70         }
71     }
72 }
73
74 - (IBAction)stop:(id)sender
75 {
76     intf_thread_t * p_intf = [NSApp getIntf];
77
78     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
79                                                        FIND_ANYWHERE );
80     if( p_playlist == NULL )
81     {
82         return;
83     }
84
85     playlist_Stop( p_playlist );
86     vlc_object_release( p_playlist );
87 }
88
89 - (IBAction)faster:(id)sender
90 {
91     intf_thread_t * p_intf = [NSApp getIntf];
92
93     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
94                                                        FIND_ANYWHERE );
95     if( p_playlist == NULL )
96     {
97         return;
98     }
99
100     vlc_mutex_lock( &p_playlist->object_lock );
101     if( p_playlist->p_input != NULL )
102     {
103         input_SetStatus( p_playlist->p_input, INPUT_STATUS_FASTER );
104     } 
105     vlc_mutex_unlock( &p_playlist->object_lock );
106
107     vlc_object_release( p_playlist );
108 }
109
110 - (IBAction)slower:(id)sender
111 {
112     intf_thread_t * p_intf = [NSApp getIntf];
113
114     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
115                                                        FIND_ANYWHERE );
116     if( p_playlist == NULL )
117     {
118         return;
119     }
120
121     vlc_mutex_lock( &p_playlist->object_lock );
122     if( p_playlist->p_input != NULL )
123     {
124         input_SetStatus( p_playlist->p_input, INPUT_STATUS_SLOWER );
125     }
126     vlc_mutex_unlock( &p_playlist->object_lock );
127
128     vlc_object_release( p_playlist );
129 }
130
131 - (IBAction)prev:(id)sender
132 {
133     vlc_value_t val;
134     intf_thread_t * p_intf = [NSApp getIntf];
135
136     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
137                                                        FIND_ANYWHERE );
138     if( p_playlist == NULL )
139     {
140         return;
141     }
142
143     vlc_mutex_lock( &p_playlist->object_lock );
144
145     if( p_playlist->p_input == NULL )
146     {
147         vlc_mutex_unlock( &p_playlist->object_lock );
148         vlc_object_release( p_playlist );  
149         return;
150     }
151
152     vlc_mutex_lock( &p_playlist->p_input->stream.stream_lock );
153
154 #define p_area p_playlist->p_input->stream.p_selected_area
155     if( p_area->i_part > 0 && p_area->i_part_nb > 1)
156     {
157         vlc_mutex_unlock( &p_playlist->p_input->stream.stream_lock );
158         vlc_mutex_unlock( &p_playlist->object_lock );
159         var_Get( p_playlist->p_input, "prev-chapter", &val );
160         var_Set( p_playlist->p_input, "prev-chapter", val );
161     }
162     else if( p_area->i_id > 1 )
163     {
164         vlc_mutex_unlock( &p_playlist->p_input->stream.stream_lock );
165         vlc_mutex_unlock( &p_playlist->object_lock );
166         var_Get( p_playlist->p_input, "prev-title", &val );
167         var_Set( p_playlist->p_input, "prev-title", val );
168     }
169     else
170     {
171         vlc_mutex_unlock( &p_playlist->p_input->stream.stream_lock );
172         vlc_mutex_unlock( &p_playlist->object_lock );
173         playlist_Prev( p_playlist );
174     }
175 #undef p_area
176
177     vlc_object_release( p_playlist );
178 }
179
180 - (IBAction)next:(id)sender
181 {
182     vlc_value_t val;
183     intf_thread_t * p_intf = [NSApp getIntf];
184
185     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
186                                                        FIND_ANYWHERE );
187     if( p_playlist == NULL )
188     {
189         return;
190     }
191     
192     vlc_mutex_lock( &p_playlist->object_lock );
193
194     if( p_playlist->p_input == NULL )
195     {
196         vlc_mutex_unlock( &p_playlist->object_lock );
197         vlc_object_release( p_playlist );  
198         return;
199     }
200
201     vlc_mutex_lock( &p_playlist->p_input->stream.stream_lock );
202
203 #define p_area p_playlist->p_input->stream.p_selected_area
204     if( p_area->i_part < p_area->i_part_nb && p_area->i_part_nb > 1 )
205     {
206         vlc_mutex_unlock( &p_playlist->p_input->stream.stream_lock );
207         vlc_mutex_unlock( &p_playlist->object_lock );
208         var_Get( p_playlist->p_input, "next-chapter", &val );
209         var_Set( p_playlist->p_input, "next-chapter", val );
210     }
211     else if( p_area->i_id < p_playlist->p_input->stream.i_area_nb && p_playlist->p_input->stream.i_area_nb > 1 )
212     {
213         vlc_mutex_unlock( &p_playlist->p_input->stream.stream_lock );
214         vlc_mutex_unlock( &p_playlist->object_lock );
215         var_Get( p_playlist->p_input, "next-title", &val );
216         var_Set( p_playlist->p_input, "next-title", val );
217     }
218     else
219     {
220         vlc_mutex_unlock( &p_playlist->p_input->stream.stream_lock );
221         vlc_mutex_unlock( &p_playlist->object_lock );
222         playlist_Next( p_playlist );
223     }
224 #undef p_area
225
226     vlc_object_release( p_playlist );
227 }
228
229 - (IBAction)loop:(id)sender
230 {
231     intf_thread_t * p_intf = [NSApp getIntf];
232
233     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
234                                                        FIND_ANYWHERE );
235     if( p_playlist == NULL )
236     {
237         return;
238     }
239
240     config_PutInt( p_playlist, "loop",
241                    !config_GetInt( p_playlist, "loop" ) );
242
243     vlc_object_release( p_playlist );
244 }
245
246 - (IBAction)forward:(id)sender
247 {
248     intf_thread_t * p_intf = [NSApp getIntf];
249     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
250                                                        FIND_ANYWHERE );
251     if( p_playlist == NULL || p_playlist->p_input == NULL )
252     {
253         if ( p_playlist != NULL ) vlc_object_release( p_playlist );
254         return;
255     }
256
257     playlist_Play( p_playlist );
258     input_Seek( p_playlist->p_input, 5, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
259     vlc_object_release( p_playlist );
260 }
261
262 - (IBAction)backward:(id)sender
263 {
264     intf_thread_t * p_intf = [NSApp getIntf];
265     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
266                                                        FIND_ANYWHERE );
267     if( p_playlist == NULL || p_playlist->p_input == NULL )
268     {
269         if ( p_playlist != NULL ) vlc_object_release( p_playlist );
270         return;
271     }
272
273     playlist_Play( p_playlist );
274     input_Seek( p_playlist->p_input, -5, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
275     vlc_object_release( p_playlist );
276 }
277
278 - (IBAction)volumeUp:(id)sender
279 {
280     intf_thread_t * p_intf = [NSApp getIntf];
281
282     if( p_intf->p_sys->b_mute )
283     {
284         [self mute: nil];
285     }
286
287     aout_VolumeUp( p_intf, 1, NULL );
288
289     [self updateVolumeSlider];
290 }
291
292 - (IBAction)volumeDown:(id)sender
293 {
294     intf_thread_t * p_intf = [NSApp getIntf];
295
296     if( p_intf->p_sys->b_mute )
297     {
298         [self mute: nil];
299     }
300     
301     aout_VolumeDown( p_intf, 1, NULL );
302
303     [self updateVolumeSlider];
304 }
305
306 - (IBAction)mute:(id)sender
307 {
308     intf_thread_t * p_intf = [NSApp getIntf];
309     audio_volume_t i_volume;
310
311     aout_VolumeMute( p_intf, &i_volume );
312     p_intf->p_sys->b_mute = ( i_volume == 0 );
313
314     [self updateVolumeSlider];
315 }
316
317 - (IBAction)volumeSliderUpdated:(id)sender
318 {
319     intf_thread_t * p_intf = [NSApp getIntf];
320     audio_volume_t i_volume = (audio_volume_t)[sender intValue];
321
322     aout_VolumeSet( p_intf, i_volume * AOUT_VOLUME_STEP );
323 }
324
325 - (void)updateVolumeSlider
326 {
327     intf_thread_t * p_intf = [NSApp getIntf];
328     audio_volume_t i_volume;
329
330     aout_VolumeGet( p_intf, &i_volume );
331
332     [o_volumeslider setFloatValue: (float)(i_volume / AOUT_VOLUME_STEP)]; 
333 }
334
335 - (IBAction)windowAction:(id)sender
336 {
337     id o_window = [NSApp keyWindow];
338     NSString *o_title = [sender title];
339     NSArray *o_windows = [NSApp windows];
340     NSEnumerator *o_enumerator = [o_windows objectEnumerator];
341     vout_thread_t   *p_vout = vlc_object_find( [NSApp getIntf], VLC_OBJECT_VOUT,
342                                               FIND_ANYWHERE );
343
344     if( p_vout != NULL )
345     {
346         while ((o_window = [o_enumerator nextObject]))
347         {
348             if( [[o_window className] isEqualToString: @"VLCWindow"] )
349             {
350                 if( [o_title isEqualToString: _NS("Fullscreen") ] )
351                     [o_window toggleFullscreen];
352                 else if( [o_title isEqualToString: _NS("Half Size") ] )
353                     [o_window scaleWindowWithFactor: 0.5];
354                 else if( [o_title isEqualToString: _NS("Normal Size") ] )
355                     [o_window scaleWindowWithFactor: 1.0];
356                 else if( [o_title isEqualToString: _NS("Double Size") ] )
357                     [o_window scaleWindowWithFactor: 2.0];
358                 else if( [o_title isEqualToString: _NS("Float On Top") ] )
359                     [o_window toggleFloatOnTop];
360                 else if( [o_title isEqualToString: _NS("Fit To Screen") ] )
361                 {
362                     if( ![o_window isZoomed] )
363                         [o_window performZoom:self];
364                 }
365             }
366         }
367         vlc_object_release( (vlc_object_t *)p_vout );
368     }
369 }
370
371 - (void)setupVarMenuItem:(NSMenuItem *)o_mi
372                     target:(vlc_object_t *)p_object
373                     var:(const char *)psz_variable
374                     selector:(SEL)pf_callback
375 {
376     vlc_value_t val, text;
377     int i_type = var_Type( p_object, psz_variable );
378
379     switch( i_type & VLC_VAR_TYPE )
380     {
381     case VLC_VAR_VOID:
382     case VLC_VAR_BOOL:
383     case VLC_VAR_VARIABLE:
384     case VLC_VAR_STRING:
385     case VLC_VAR_INTEGER:
386         break;
387     default:
388         /* Variable doesn't exist or isn't handled */
389         return;
390     }
391     
392     /* Make sure we want to display the variable */
393     if( i_type & VLC_VAR_HASCHOICE )
394     {
395         var_Change( p_object, psz_variable, VLC_VAR_CHOICESCOUNT, &val, NULL );
396         if( val.i_int == 0 ) return;
397         if( (i_type & VLC_VAR_TYPE) != VLC_VAR_VARIABLE && val.i_int == 1 )
398             return;
399     }
400     
401     /* Get the descriptive name of the variable */
402     var_Change( p_object, psz_variable, VLC_VAR_GETTEXT, &text, NULL );
403     [o_mi setTitle: [NSApp localizedString: text.psz_string ?
404                                         text.psz_string : strdup( psz_variable ) ]];
405
406     var_Get( p_object, psz_variable, &val );
407     if( i_type & VLC_VAR_HASCHOICE )
408     {
409         NSMenu *o_menu = [o_mi submenu];
410
411         [self setupVarMenu: o_menu forMenuItem: o_mi target:p_object
412                         var:psz_variable selector:pf_callback];
413         
414         if( text.psz_string ) free( text.psz_string );
415         return;
416     }
417
418     VLCMenuExt *o_data;
419     switch( i_type & VLC_VAR_TYPE )
420     {
421     case VLC_VAR_VOID:
422         o_data = [[VLCMenuExt alloc] initWithVar: psz_variable Object: p_object->i_object_id
423                 Value: val ofType: i_type];
424         [o_mi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
425         break;
426
427     case VLC_VAR_BOOL:
428         o_data = [[VLCMenuExt alloc] initWithVar: psz_variable Object: p_object->i_object_id
429                 Value: val ofType: i_type];
430         [o_mi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
431         [o_mi setState: val.b_bool ? TRUE : FALSE ];
432         break;
433
434     default:
435         if( text.psz_string ) free( text.psz_string );
436         return;
437     }
438
439     if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string );
440     if( text.psz_string ) free( text.psz_string );
441 }
442
443
444 - (void)setupVarMenu:(NSMenu *)o_menu
445                     forMenuItem: (NSMenuItem *)o_parent
446                     target:(vlc_object_t *)p_object
447                     var:(const char *)psz_variable
448                     selector:(SEL)pf_callback
449 {
450     vlc_value_t val, val_list, text_list;
451     int i_type, i, i_nb_items;
452
453     /* remove previous items */
454     i_nb_items = [o_menu numberOfItems];
455     for( i = 0; i < i_nb_items; i++ )
456     {
457         [o_menu removeItemAtIndex: 0];
458     }
459
460     /* Check the type of the object variable */
461     i_type = var_Type( p_object, psz_variable );
462
463     /* Make sure we want to display the variable */
464     if( i_type & VLC_VAR_HASCHOICE )
465     {
466         var_Change( p_object, psz_variable, VLC_VAR_CHOICESCOUNT, &val, NULL );
467         if( val.i_int == 0 ) return;
468         if( (i_type & VLC_VAR_TYPE) != VLC_VAR_VARIABLE && val.i_int == 1 )
469             return;
470     }
471     else
472     {
473         return;
474     }
475
476     switch( i_type & VLC_VAR_TYPE )
477     {
478     case VLC_VAR_VOID:
479     case VLC_VAR_BOOL:
480     case VLC_VAR_VARIABLE:
481     case VLC_VAR_STRING:
482     case VLC_VAR_INTEGER:
483         break;
484     default:
485         /* Variable doesn't exist or isn't handled */
486         return;
487     }
488
489     if( var_Get( p_object, psz_variable, &val ) < 0 )
490     {
491         return;
492     }
493
494     if( var_Change( p_object, psz_variable, VLC_VAR_GETLIST,
495                     &val_list, &text_list ) < 0 )
496     {
497         if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string );
498         return;
499     }
500
501     /* make (un)sensitive */
502     [o_parent setEnabled: ( val_list.p_list->i_count > 1 )];
503
504     for( i = 0; i < val_list.p_list->i_count; i++ )
505     {
506         vlc_value_t another_val;
507         NSMenuItem * o_lmi;
508         NSString *o_title = @"";
509         VLCMenuExt *o_data;
510
511         switch( i_type & VLC_VAR_TYPE )
512         {
513         case VLC_VAR_VARIABLE:
514
515             /* This is causing crashes for the moment.
516             o_title = [NSApp localizedString: text_list.p_list->p_values[i].psz_string ?
517                 text_list.p_list->p_values[i].psz_string : val_list.p_list->p_values[i].psz_string ];
518             
519             o_data = [[VLCMenuExt alloc] initWithVar: strdup(psz_variable) Object: p_object->i_object_id
520                 Value: val ofType: i_type];
521             [o_lmi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
522
523             // Create a submenu
524             NSMenu *o_menu = [o_lmi submenu];
525
526             [self setupVarMenu: o_menu forMenuItem: o_lmi target:p_object
527                             var:psz_variable selector:pf_callback];
528 */
529             return;
530
531         case VLC_VAR_STRING:
532             another_val.psz_string =
533                 strdup(val_list.p_list->p_values[i].psz_string);
534
535             o_title = [NSApp localizedString: text_list.p_list->p_values[i].psz_string ?
536                 text_list.p_list->p_values[i].psz_string : val_list.p_list->p_values[i].psz_string ];
537
538             o_lmi = [o_menu addItemWithTitle: o_title action: pf_callback keyEquivalent: @""];
539             o_data = [[VLCMenuExt alloc] initWithVar: strdup(psz_variable) Object: p_object->i_object_id
540                     Value: another_val ofType: i_type];
541             [o_lmi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
542             [o_lmi setTarget: self];
543             
544             if( !strcmp( val.psz_string, val_list.p_list->p_values[i].psz_string ) )
545                 [o_lmi setState: TRUE ];
546
547             break;
548
549         case VLC_VAR_INTEGER:
550
551              o_title = text_list.p_list->p_values[i].psz_string ?
552                                  [NSApp localizedString: strdup( text_list.p_list->p_values[i].psz_string )] :
553                                  [NSString stringWithFormat: @"%d",
554                                  val_list.p_list->p_values[i].i_int];
555
556             o_lmi = [[o_menu addItemWithTitle: o_title action: pf_callback keyEquivalent: @""] retain ];
557             o_data = [[VLCMenuExt alloc] initWithVar: strdup(psz_variable) Object: p_object->i_object_id
558                     Value: val_list.p_list->p_values[i] ofType: i_type];
559             [o_lmi setRepresentedObject: [NSValue valueWithPointer:[ o_data retain]]];
560             [o_lmi setTarget: self];
561
562             if( val_list.p_list->p_values[i].i_int == val.i_int )
563                 [o_lmi setState: TRUE ];
564             break;
565
566         default:
567           break;
568         }
569     }
570     
571     /* clean up everything */
572     if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string );
573     var_Change( p_object, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list );
574 }
575
576 - (IBAction)toggleVar:(id)sender
577 {
578     NSMenuItem *o_mi = (NSMenuItem *)sender;
579     VLCMenuExt *o_data = [[o_mi representedObject] pointerValue];
580     [NSThread detachNewThreadSelector: @selector(toggleVarThread:)
581         toTarget: self withObject: o_data];
582
583     return;
584 }
585
586 - (int)toggleVarThread: (id)_o_data
587 {
588     vlc_object_t *p_object;
589     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
590     VLCMenuExt *o_data = (VLCMenuExt *)_o_data;
591
592     vlc_thread_set_priority( [NSApp getIntf] , VLC_THREAD_PRIORITY_LOW );
593
594     p_object = (vlc_object_t *)vlc_object_get( [NSApp getIntf],
595                                     [o_data objectID] );
596
597     if( p_object != NULL )
598     {
599         var_Set( p_object, strdup([o_data name]), [o_data value] );
600         vlc_object_release( p_object );
601         [o_pool release];
602         return VLC_TRUE;
603     }
604     [o_pool release];
605     return VLC_EGENERIC;
606 }
607
608 @end
609
610 @implementation VLCControls (NSMenuValidation)
611  
612 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
613 {
614     BOOL bEnabled = TRUE;
615     intf_thread_t * p_intf = [NSApp getIntf];
616
617     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
618                                                        FIND_ANYWHERE );
619
620     if( p_playlist != NULL )
621     {
622         vlc_mutex_lock( &p_playlist->object_lock );
623     }
624
625 #define p_input p_playlist->p_input
626
627     if( [[o_mi title] isEqualToString: _NS("Faster")] ||
628         [[o_mi title] isEqualToString: _NS("Slower")] )
629     {
630         if( p_playlist != NULL && p_input != NULL )
631         {
632             vlc_mutex_lock( &p_input->stream.stream_lock );
633             bEnabled = p_input->stream.b_pace_control;
634             vlc_mutex_unlock( &p_input->stream.stream_lock );
635         }
636         else
637         {
638             bEnabled = FALSE;
639         }
640     }
641     else if( [[o_mi title] isEqualToString: _NS("Stop")] )
642     {
643         if( p_playlist == NULL || p_input == NULL )
644         {
645             bEnabled = FALSE;
646         }
647     }
648     else if( [[o_mi title] isEqualToString: _NS("Previous")] ||
649              [[o_mi title] isEqualToString: _NS("Next")] )
650     {
651         if( p_playlist == NULL )
652         {
653             bEnabled = FALSE;
654         }
655         else
656         {
657             bEnabled = p_playlist->i_size > 1;
658
659             if( p_input != NULL )
660             {
661                 vlc_mutex_lock( &p_input->stream.stream_lock );
662                 bEnabled |= p_input->stream.i_area_nb > 1;
663                 vlc_mutex_unlock( &p_input->stream.stream_lock );
664             }
665         }
666     }
667     else if( [[o_mi title] isEqualToString: _NS("Loop")] )
668     {
669         int i_state = config_GetInt( p_playlist, "loop" ) ?
670                       NSOnState : NSOffState;
671
672         [o_mi setState: i_state];
673     }
674     else if( [[o_mi title] isEqualToString: _NS("Step Forward")] ||
675              [[o_mi title] isEqualToString: _NS("Step Backward")] )
676     {
677         if( p_playlist != NULL && p_input != NULL )
678         {
679             vlc_mutex_lock( &p_input->stream.stream_lock );
680             bEnabled = p_input->stream.b_seekable;
681             vlc_mutex_unlock( &p_input->stream.stream_lock );
682         }
683         else
684         {
685             bEnabled = FALSE;
686         }
687     }
688     else if( [[o_mi title] isEqualToString: _NS("Mute")] ) 
689     {
690         [o_mi setState: p_intf->p_sys->b_mute ? NSOnState : NSOffState];
691     }
692     else if( [[o_mi title] isEqualToString: _NS("Fullscreen")] ||
693                 [[o_mi title] isEqualToString: _NS("Half Size")] ||
694                 [[o_mi title] isEqualToString: _NS("Normal Size")] ||
695                 [[o_mi title] isEqualToString: _NS("Double Size")] ||
696                 [[o_mi title] isEqualToString: _NS("Fit To Screen")] ||
697                 [[o_mi title] isEqualToString: _NS("Float On Top")] )
698     {
699         id o_window;
700         NSArray *o_windows = [NSApp windows];
701         NSEnumerator *o_enumerator = [o_windows objectEnumerator];
702         bEnabled = FALSE;
703         
704         if ( [[o_mi title] isEqualToString: _NS("Float On Top")] )
705         {
706             int i_state = config_GetInt( p_playlist, "macosx-float" ) ?
707                       NSOnState : NSOffState;
708             [o_mi setState: i_state];
709         }
710         
711         vout_thread_t   *p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
712                                               FIND_ANYWHERE );
713         if( p_vout != NULL )
714         {
715             while ((o_window = [o_enumerator nextObject]))
716             {
717                 if( [[o_window className] isEqualToString: @"VLCWindow"] )
718                 {
719                     bEnabled = TRUE;
720                     break;
721                 }
722             }
723             vlc_object_release( (vlc_object_t *)p_vout );
724         }
725     }
726
727     if( p_playlist != NULL )
728     {
729         vlc_mutex_unlock( &p_playlist->object_lock );
730         vlc_object_release( p_playlist );
731     }
732
733     return( bEnabled );
734 }
735
736 @end
737
738 /*****************************************************************************
739  * VLCMenuExt implementation 
740  *****************************************************************************
741  * Object connected to a playlistitem which remembers the data belonging to
742  * the variable of the autogenerated menu
743  *****************************************************************************/
744 @implementation VLCMenuExt
745
746 - (id)initWithVar: (const char *)_psz_name Object: (int)i_id
747         Value: (vlc_value_t)val ofType: (int)_i_type
748 {
749     self = [super init];
750
751     if( self != nil )
752     {
753         psz_name = strdup( _psz_name );
754         i_object_id = i_id;
755         value = val;
756         i_type = _i_type;
757     }
758
759     return( self );
760 }
761
762 - (void)dealloc
763 {
764     free( psz_name );
765 }
766
767 - (char *)name
768 {
769     return psz_name;
770 }
771
772 - (int)objectID
773 {
774     return i_object_id;
775 }
776
777 - (vlc_value_t)value
778 {
779     return value;
780 }
781
782 - (int)type
783 {
784     return i_type;
785 }
786
787 @end