1 /*****************************************************************************
2 * controls.m: MacOS X interface plugin
3 *****************************************************************************
4 * Copyright (C) 2002 VideoLAN
5 * $Id: controls.m,v 1.11 2003/01/23 22:25:32 hartman Exp $
7 * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8 * Christophe Massiot <massiot@via.ecp.fr>
9 * Derk-Jan Hartman <thedj@users.sourceforge.net>
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.
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.
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 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
29 #include <stdlib.h> /* malloc(), free() */
30 #include <sys/param.h> /* for MAXPATHLEN */
37 #include <Cocoa/Cocoa.h>
38 #include <CoreAudio/AudioHardware.h>
43 /*****************************************************************************
44 * VLCControls interface
45 *****************************************************************************/
46 @interface VLCControls : NSObject
50 IBOutlet id o_mi_mute;
51 IBOutlet id o_volumeslider;
55 - (IBAction)play:(id)sender;
56 - (IBAction)stop:(id)sender;
57 - (IBAction)faster:(id)sender;
58 - (IBAction)slower:(id)sender;
59 - (IBAction)slowMotion:(id)sender;
60 - (IBAction)fastForward:(id)sender;
62 - (IBAction)prev:(id)sender;
63 - (IBAction)next:(id)sender;
64 - (IBAction)loop:(id)sender;
66 - (IBAction)volumeUp:(id)sender;
67 - (IBAction)volumeDown:(id)sender;
68 - (IBAction)mute:(id)sender;
69 - (IBAction)volumeSliderUpdate:(id)sender;
70 - (IBAction)fullscreen:(id)sender;
71 - (IBAction)deinterlace:(id)sender;
73 - (IBAction)toggleProgram:(id)sender;
74 - (IBAction)toggleTitle:(id)sender;
75 - (IBAction)toggleChapter:(id)sender;
76 - (IBAction)toggleLanguage:(id)sender;
77 - (IBAction)toggleVar:(id)sender;
79 - (void)setVolumeSlider;
83 /*****************************************************************************
84 * VLCControls implementation
85 *****************************************************************************/
86 @implementation VLCControls
88 - (IBAction)play:(id)sender
90 intf_thread_t * p_intf = [NSApp getIntf];
91 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
93 if( p_playlist == NULL )
98 if ( p_intf->p_sys->p_input != NULL && p_intf->p_sys->p_input->stream.control.i_status != PAUSE_S)
100 input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PAUSE );
101 vlc_object_release( p_playlist );
105 /* If the playlist is empty, open a file requester instead */
106 vlc_mutex_lock( &p_playlist->object_lock );
107 if( p_playlist->i_size )
109 vlc_mutex_unlock( &p_playlist->object_lock );
110 playlist_Play( p_playlist );
111 vlc_object_release( p_playlist );
115 vlc_mutex_unlock( &p_playlist->object_lock );
116 vlc_object_release( p_playlist );
118 [o_open openFile: nil];
123 - (IBAction)stop:(id)sender
125 intf_thread_t * p_intf = [NSApp getIntf];
126 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
128 if( p_playlist == NULL )
133 playlist_Stop( p_playlist );
134 vlc_object_release( p_playlist );
135 p_intf->p_sys->b_stopping = 1;
138 - (IBAction)faster:(id)sender
140 intf_thread_t * p_intf = [NSApp getIntf];
142 if( p_intf->p_sys->p_input == NULL )
147 input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_FASTER );
150 - (IBAction)slower:(id)sender
152 intf_thread_t * p_intf = [NSApp getIntf];
154 if( p_intf->p_sys->p_input == NULL )
159 input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_SLOWER );
162 - (IBAction)slowMotion:(id)sender
165 switch( [[NSApp currentEvent] type] )
170 [self slower:sender];
177 intf_thread_t * p_intf = [NSApp getIntf];
179 [self faster:sender];
180 if ( p_intf->p_sys->p_input != NULL &&
181 p_intf->p_sys->p_input->stream.control.i_status != PAUSE_S)
183 input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PAUSE );
194 - (IBAction)fastForward:(id)sender
196 playlist_t * p_playlist = vlc_object_find( [NSApp getIntf], VLC_OBJECT_PLAYLIST,
200 switch( [[NSApp currentEvent] type] )
202 /* A button does not send a NSLeftMouseDown unfortunately.
203 * Therefore we need to count. I know, it is ugly. We could have used
204 * a bool as well, but now we can also accellerate after a certain period.
205 * Currently this method is called every second if the button is pressed.
206 * You can set this value in intf.m (hartman)
213 else if ( i_ff == 5 )
217 else if ( i_ff == 15 )
225 vlc_mutex_lock( &p_playlist->object_lock );
226 if( p_playlist->i_size )
228 playlist_Play( p_playlist );
230 vlc_mutex_unlock( &p_playlist->object_lock );
236 vlc_object_release( p_playlist );
239 - (IBAction)prev:(id)sender
241 intf_thread_t * p_intf = [NSApp getIntf];
242 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
244 if( p_playlist == NULL )
249 playlist_Prev( p_playlist );
250 vlc_object_release( p_playlist );
253 - (IBAction)next:(id)sender
255 intf_thread_t * p_intf = [NSApp getIntf];
256 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
258 if( p_playlist == NULL )
263 playlist_Next( p_playlist );
264 vlc_object_release( p_playlist );
267 - (IBAction)loop:(id)sender
269 NSMenuItem * o_mi = (NSMenuItem *)sender;
270 intf_thread_t * p_intf = [NSApp getIntf];
271 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
273 if( p_playlist == NULL )
278 if( p_intf->p_sys->b_loop )
280 [o_mi setState: NSOffState];
281 config_PutInt( p_playlist, "loop", 0 );
285 [o_mi setState: NSOnState];
286 config_PutInt( p_playlist, "loop", 1 );
289 p_intf->p_sys->b_loop = !p_intf->p_sys->b_loop;
291 vlc_object_release( p_playlist );
294 - (IBAction)volumeUp:(id)sender
296 intf_thread_t * p_intf = [NSApp getIntf];
297 aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
299 if ( p_aout != NULL )
301 if (p_intf->p_sys->b_mute)
303 [self mute:o_mi_mute];
305 aout_VolumeUp( p_aout, 1, NULL );
306 vlc_object_release( (vlc_object_t *)p_aout );
308 [self setVolumeSlider];
311 - (IBAction)volumeDown:(id)sender
313 intf_thread_t * p_intf = [NSApp getIntf];
314 aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
316 if ( p_aout != NULL )
318 if (p_intf->p_sys->b_mute)
320 [self mute:o_mi_mute];
322 aout_VolumeDown( p_aout, 1, NULL );
323 vlc_object_release( (vlc_object_t *)p_aout );
325 [self setVolumeSlider];
328 - (IBAction)mute:(id)sender
330 intf_thread_t * p_intf = [NSApp getIntf];
331 aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
333 audio_volume_t i_volume;
335 if ( p_aout != NULL )
337 aout_VolumeMute( p_aout, &i_volume );
338 vlc_object_release( (vlc_object_t *)p_aout );
341 p_intf->p_sys->b_mute = (i_volume == 0);
342 [o_mi_mute setState: p_intf->p_sys->b_mute ? NSOnState : NSOffState];
343 [o_volumeslider setEnabled: p_intf->p_sys->b_mute ? FALSE : TRUE];
344 [self setVolumeSlider];
347 - (IBAction)volumeSliderUpdate:(id)sender
349 intf_thread_t * p_intf = [NSApp getIntf];
350 aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
352 audio_volume_t i_volume;
354 switch( [[NSApp currentEvent] type] )
356 case NSLeftMouseDragged:
357 if ( p_aout != NULL )
359 i_volume = (int) [sender floatValue];
360 aout_VolumeSet( p_aout, i_volume * AOUT_VOLUME_STEP);
361 vlc_object_release( (vlc_object_t *)p_aout );
366 if ( p_aout != NULL ) vlc_object_release( (vlc_object_t *)p_aout );
371 - (void)setVolumeSlider
373 intf_thread_t * p_intf = [NSApp getIntf];
374 aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
376 audio_volume_t i_volume;
378 if ( p_aout != NULL )
380 aout_VolumeGet( p_aout, &i_volume );
381 vlc_object_release( (vlc_object_t *)p_aout );
382 [o_volumeslider setFloatValue: (float) (i_volume / AOUT_VOLUME_STEP)];
386 [o_volumeslider setFloatValue: config_GetInt( p_intf, "volume" )];
390 - (IBAction)fullscreen:(id)sender
392 id o_window = [NSApp keyWindow];
393 NSArray *o_windows = [NSApp windows];
394 NSEnumerator *o_enumerator = [o_windows objectEnumerator];
396 while ((o_window = [o_enumerator nextObject]))
398 if( [[o_window className] isEqualToString: @"VLCWindow"] )
400 [o_window toggleFullscreen];
405 - (IBAction)deinterlace:(id)sender
407 intf_thread_t * p_intf = [NSApp getIntf];
408 BOOL bEnable = [sender state] == NSOffState;
412 config_PutPsz( p_intf, "filter", "deinterlace" );
413 config_PutPsz( p_intf, "deinterlace-mode",
414 [[sender title] lossyCString] );
418 config_PutPsz( p_intf, "filter", NULL );
422 - (IBAction)toggleProgram:(id)sender
424 NSMenuItem * o_mi = (NSMenuItem *)sender;
425 intf_thread_t * p_intf = [NSApp getIntf];
427 if( [o_mi state] == NSOffState )
429 u16 i_program_id = [o_mi tag];
431 input_ChangeProgram( p_intf->p_sys->p_input, i_program_id );
432 input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );
436 - (IBAction)toggleTitle:(id)sender
438 NSMenuItem * o_mi = (NSMenuItem *)sender;
439 intf_thread_t * p_intf = [NSApp getIntf];
441 if( [o_mi state] == NSOffState )
443 int i_title = [o_mi tag];
445 #define p_input p_intf->p_sys->p_input
446 input_ChangeArea( p_input, p_input->stream.pp_areas[i_title] );
447 input_SetStatus( p_input, INPUT_STATUS_PLAY );
452 - (IBAction)toggleChapter:(id)sender
454 NSMenuItem * o_mi = (NSMenuItem *)sender;
455 intf_thread_t * p_intf = [NSApp getIntf];
457 if( [o_mi state] == NSOffState )
459 int i_chapter = [o_mi tag];
461 #define p_input p_intf->p_sys->p_input
462 p_input->stream.p_selected_area->i_part = i_chapter;
463 input_ChangeArea( p_input, p_input->stream.p_selected_area );
464 input_SetStatus( p_input, INPUT_STATUS_PLAY );
469 - (IBAction)toggleLanguage:(id)sender
471 NSMenuItem * o_mi = (NSMenuItem *)sender;
472 intf_thread_t * p_intf = [NSApp getIntf];
474 #define p_input p_intf->p_sys->p_input
476 if( !p_intf->p_sys->b_audio_update )
478 NSValue * o_value = [o_mi representedObject];
479 es_descriptor_t * p_es = [o_value pointerValue];
481 if( [o_mi state] == NSOnState )
483 /* we just have one ES to disable */
484 input_ToggleES( p_input, p_es, 0 );
489 int i_cat = [o_mi tag];
491 vlc_mutex_lock( &p_input->stream.stream_lock );
493 #define ES p_input->stream.pp_selected_es[i]
495 /* unselect the selected ES in the same class */
496 for( i = 0; i < p_input->stream.i_selected_es_number; i++ )
498 if( ES->i_cat == i_cat )
500 vlc_mutex_unlock( &p_input->stream.stream_lock );
501 input_ToggleES( p_input, ES, 0 );
502 vlc_mutex_lock( &p_input->stream.stream_lock );
509 vlc_mutex_unlock( &p_input->stream.stream_lock );
511 input_ToggleES( p_input, p_es, 1 );
518 - (IBAction)toggleVar:(id)sender
520 NSMenuItem * o_mi = (NSMenuItem *)sender;
522 if( [o_mi state] == NSOffState )
524 const char * psz_variable = (const char *)[o_mi tag];
525 const char * psz_value = [[o_mi title] cString];
526 vlc_object_t * p_object = (vlc_object_t *)
527 [[o_mi representedObject] pointerValue];
529 /* psz_string sucks */
530 val.psz_string = (char *)psz_value;
532 if ( var_Set( p_object, psz_variable, val ) < 0 )
534 msg_Warn( p_object, "cannot set variable (%s)", psz_value );
541 @implementation VLCControls (NSMenuValidation)
543 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
545 BOOL bEnabled = TRUE;
546 NSMenu * o_menu = [o_mi menu];
547 intf_thread_t * p_intf = [NSApp getIntf];
549 if( [[o_mi title] isEqualToString: _NS("Faster")] ||
550 [[o_mi title] isEqualToString: _NS("Slower")] )
552 if( p_intf->p_sys->p_input != NULL )
554 #define p_input p_intf->p_sys->p_input
555 vlc_mutex_lock( &p_input->stream.stream_lock );
556 bEnabled = p_input->stream.b_pace_control;
557 vlc_mutex_unlock( &p_input->stream.stream_lock );
565 else if( [[o_mi title] isEqualToString: _NS("Stop")] )
567 bEnabled = p_intf->p_sys->p_input != NULL;
569 else if( [[o_mi title] isEqualToString: _NS("Previous")] ||
570 [[o_mi title] isEqualToString: _NS("Next")] )
572 playlist_t * p_playlist = vlc_object_find( p_intf,
575 if( p_playlist == NULL )
581 vlc_mutex_lock( &p_playlist->object_lock );
582 bEnabled = p_playlist->i_size > 1;
583 vlc_mutex_unlock( &p_playlist->object_lock );
584 vlc_object_release( p_playlist );
587 else if( [[o_mi title] isEqualToString: _NS("Fullscreen")] )
590 NSArray *o_windows = [NSApp windows];
591 NSEnumerator *o_enumerator = [o_windows objectEnumerator];
594 while ((o_window = [o_enumerator nextObject]))
596 if( [[o_window className] isEqualToString: @"VLCWindow"] )
603 else if( o_menu != nil &&
604 [[o_menu title] isEqualToString: _NS("Deinterlace")] )
606 char * psz_filter = config_GetPsz( p_intf, "filter" );
608 if( psz_filter != NULL )
612 psz_filter = config_GetPsz( p_intf, "deinterlace-mode" );
615 if( psz_filter != NULL )
617 if( strcmp( psz_filter, [[o_mi title] lossyCString] ) == 0 )
619 [o_mi setState: NSOnState];
623 [o_mi setState: NSOffState];
630 [o_mi setState: NSOffState];