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