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