1 /*****************************************************************************
2 * controls.m: MacOS X interface plugin
3 *****************************************************************************
4 * Copyright (C) 2002-2003 VideoLAN
5 * $Id: controls.m,v 1.49 2003/09/20 19:37:53 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 */
38 /*****************************************************************************
39 * VLCControls implementation
40 *****************************************************************************/
41 @implementation VLCControls
43 - (IBAction)play:(id)sender
45 intf_thread_t * p_intf = [NSApp getIntf];
47 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
49 if( p_playlist == NULL )
54 if( playlist_IsPlaying( p_playlist ) )
56 playlist_Pause( p_playlist );
57 vlc_object_release( p_playlist );
61 if( !playlist_IsEmpty( p_playlist ) )
63 playlist_Play( p_playlist );
64 vlc_object_release( p_playlist );
68 vlc_object_release( p_playlist );
69 [o_open openFileGeneric: nil];
74 - (IBAction)stop:(id)sender
76 intf_thread_t * p_intf = [NSApp getIntf];
77 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
79 if( p_playlist != NULL )
81 playlist_Stop( p_playlist );
82 vlc_object_release( p_playlist );
86 - (IBAction)faster:(id)sender
88 intf_thread_t * p_intf = [NSApp getIntf];
89 input_thread_t * p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
93 vlc_value_t val; val.b_bool = VLC_TRUE;
95 var_Set( p_input, "rate-faster", val );
96 vlc_object_release( p_input );
100 - (IBAction)slower:(id)sender
102 intf_thread_t * p_intf = [NSApp getIntf];
103 input_thread_t * p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
105 if( p_input != NULL )
107 vlc_value_t val; val.b_bool = VLC_TRUE;
109 var_Set( p_input, "rate-slower", val );
110 vlc_object_release( p_input );
114 - (IBAction)prev:(id)sender
117 intf_thread_t * p_intf = [NSApp getIntf];
119 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
121 if( p_playlist == NULL )
126 vlc_mutex_lock( &p_playlist->object_lock );
128 if( p_playlist->p_input == NULL )
130 vlc_mutex_unlock( &p_playlist->object_lock );
131 vlc_object_release( p_playlist );
135 vlc_mutex_lock( &p_playlist->p_input->stream.stream_lock );
136 val.b_bool = VLC_TRUE;
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)
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 );
145 else if( p_area->i_id > 1 )
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 );
153 vlc_mutex_unlock( &p_playlist->p_input->stream.stream_lock );
154 vlc_mutex_unlock( &p_playlist->object_lock );
155 playlist_Prev( p_playlist );
159 vlc_object_release( p_playlist );
162 - (IBAction)next:(id)sender
165 intf_thread_t * p_intf = [NSApp getIntf];
167 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
169 if( p_playlist == NULL )
174 vlc_mutex_lock( &p_playlist->object_lock );
176 if( p_playlist->p_input == NULL )
178 vlc_mutex_unlock( &p_playlist->object_lock );
179 vlc_object_release( p_playlist );
183 vlc_mutex_lock( &p_playlist->p_input->stream.stream_lock );
184 val.b_bool = VLC_TRUE;
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 )
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 );
193 else if( p_area->i_id < p_playlist->p_input->stream.i_area_nb && p_playlist->p_input->stream.i_area_nb > 1 )
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 );
201 vlc_mutex_unlock( &p_playlist->p_input->stream.stream_lock );
202 vlc_mutex_unlock( &p_playlist->object_lock );
203 playlist_Next( p_playlist );
207 vlc_object_release( p_playlist );
210 - (IBAction)random:(id)sender
212 intf_thread_t * p_intf = [NSApp getIntf];
214 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
216 if( p_playlist == NULL )
221 var_Get( p_playlist, "random", &val );
222 val.b_bool = !val.b_bool;
223 var_Set( p_playlist, "random", val );
225 vlc_object_release( p_playlist );
228 - (IBAction)repeat:(id)sender
230 intf_thread_t * p_intf = [NSApp getIntf];
232 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
234 if( p_playlist == NULL )
239 var_Get( p_playlist, "repeat", &val );
240 val.b_bool = !val.b_bool;
241 var_Set( p_playlist, "repeat", val );
243 vlc_object_release( p_playlist );
246 - (IBAction)loop:(id)sender
248 intf_thread_t * p_intf = [NSApp getIntf];
250 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
252 if( p_playlist == NULL )
257 var_Get( p_playlist, "loop", &val );
258 val.b_bool = !val.b_bool;
259 var_Set( p_playlist, "loop", val );
261 vlc_object_release( p_playlist );
264 - (IBAction)forward:(id)sender
266 intf_thread_t * p_intf = [NSApp getIntf];
267 input_thread_t * p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
269 if( p_input != NULL )
272 time.i_time = 5 * 1000000;
273 var_Set( p_input, "time-offset", time );
274 vlc_object_release( p_input );
278 - (IBAction)backward:(id)sender
280 intf_thread_t * p_intf = [NSApp getIntf];
281 input_thread_t * p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
283 if( p_input != NULL )
286 time.i_time = -5 * 1000000;
287 var_Set( p_input, "time-offset", time );
288 vlc_object_release( p_input );
292 - (IBAction)volumeUp:(id)sender
294 intf_thread_t * p_intf = [NSApp getIntf];
296 if( p_intf->p_sys->b_mute )
301 aout_VolumeUp( p_intf, 1, NULL );
303 [self updateVolumeSlider];
306 - (IBAction)volumeDown:(id)sender
308 intf_thread_t * p_intf = [NSApp getIntf];
310 if( p_intf->p_sys->b_mute )
315 aout_VolumeDown( p_intf, 1, NULL );
317 [self updateVolumeSlider];
320 - (IBAction)mute:(id)sender
322 intf_thread_t * p_intf = [NSApp getIntf];
323 audio_volume_t i_volume;
325 aout_VolumeMute( p_intf, &i_volume );
326 p_intf->p_sys->b_mute = ( i_volume == 0 );
328 [self updateVolumeSlider];
331 - (IBAction)volumeSliderUpdated:(id)sender
333 intf_thread_t * p_intf = [NSApp getIntf];
334 audio_volume_t i_volume = (audio_volume_t)[sender intValue];
336 aout_VolumeSet( p_intf, i_volume * AOUT_VOLUME_STEP );
339 - (void)updateVolumeSlider
341 intf_thread_t * p_intf = [NSApp getIntf];
342 audio_volume_t i_volume;
344 aout_VolumeGet( p_intf, &i_volume );
346 [o_volumeslider setFloatValue: (float)(i_volume / AOUT_VOLUME_STEP)];
349 - (IBAction)windowAction:(id)sender
351 id o_window = [NSApp keyWindow];
352 NSString *o_title = [sender title];
353 NSArray *o_windows = [NSApp windows];
354 NSEnumerator *o_enumerator = [o_windows objectEnumerator];
355 vout_thread_t *p_vout = vlc_object_find( [NSApp getIntf], VLC_OBJECT_VOUT,
360 while ((o_window = [o_enumerator nextObject]))
362 if( [[o_window className] isEqualToString: @"VLCWindow"] )
364 if( [o_title isEqualToString: _NS("Fullscreen") ] )
365 [o_window toggleFullscreen];
366 else if( [o_title isEqualToString: _NS("Half Size") ] )
367 [o_window scaleWindowWithFactor: 0.5];
368 else if( [o_title isEqualToString: _NS("Normal Size") ] )
369 [o_window scaleWindowWithFactor: 1.0];
370 else if( [o_title isEqualToString: _NS("Double Size") ] )
371 [o_window scaleWindowWithFactor: 2.0];
372 else if( [o_title isEqualToString: _NS("Float On Top") ] )
373 [o_window toggleFloatOnTop];
374 else if( [o_title isEqualToString: _NS("Fit To Screen") ] )
376 if( ![o_window isZoomed] )
377 [o_window performZoom:self];
381 vlc_object_release( (vlc_object_t *)p_vout );
385 - (void)setupVarMenuItem:(NSMenuItem *)o_mi
386 target:(vlc_object_t *)p_object
387 var:(const char *)psz_variable
388 selector:(SEL)pf_callback
390 vlc_value_t val, text;
391 int i_type = var_Type( p_object, psz_variable );
393 switch( i_type & VLC_VAR_TYPE )
397 case VLC_VAR_VARIABLE:
399 case VLC_VAR_INTEGER:
402 /* Variable doesn't exist or isn't handled */
406 /* Make sure we want to display the variable */
407 if( i_type & VLC_VAR_HASCHOICE )
409 var_Change( p_object, psz_variable, VLC_VAR_CHOICESCOUNT, &val, NULL );
410 if( val.i_int == 0 ) return;
411 if( (i_type & VLC_VAR_TYPE) != VLC_VAR_VARIABLE && val.i_int == 1 )
415 /* Get the descriptive name of the variable */
416 var_Change( p_object, psz_variable, VLC_VAR_GETTEXT, &text, NULL );
417 [o_mi setTitle: [NSApp localizedString: text.psz_string ?
418 text.psz_string : strdup( psz_variable ) ]];
420 var_Get( p_object, psz_variable, &val );
421 if( i_type & VLC_VAR_HASCHOICE )
423 NSMenu *o_menu = [o_mi submenu];
425 [self setupVarMenu: o_menu forMenuItem: o_mi target:p_object
426 var:psz_variable selector:pf_callback];
428 if( text.psz_string ) free( text.psz_string );
433 switch( i_type & VLC_VAR_TYPE )
436 o_data = [[VLCMenuExt alloc] initWithVar: psz_variable Object: p_object->i_object_id
437 Value: val ofType: i_type];
438 [o_mi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
442 o_data = [[VLCMenuExt alloc] initWithVar: psz_variable Object: p_object->i_object_id
443 Value: val ofType: i_type];
444 [o_mi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
445 [o_mi setState: val.b_bool ? TRUE : FALSE ];
449 if( text.psz_string ) free( text.psz_string );
453 if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string );
454 if( text.psz_string ) free( text.psz_string );
458 - (void)setupVarMenu:(NSMenu *)o_menu
459 forMenuItem: (NSMenuItem *)o_parent
460 target:(vlc_object_t *)p_object
461 var:(const char *)psz_variable
462 selector:(SEL)pf_callback
464 vlc_value_t val, val_list, text_list;
465 int i_type, i, i_nb_items;
467 /* remove previous items */
468 i_nb_items = [o_menu numberOfItems];
469 for( i = 0; i < i_nb_items; i++ )
471 [o_menu removeItemAtIndex: 0];
474 /* Check the type of the object variable */
475 i_type = var_Type( p_object, psz_variable );
477 /* Make sure we want to display the variable */
478 if( i_type & VLC_VAR_HASCHOICE )
480 var_Change( p_object, psz_variable, VLC_VAR_CHOICESCOUNT, &val, NULL );
481 if( val.i_int == 0 ) return;
482 if( (i_type & VLC_VAR_TYPE) != VLC_VAR_VARIABLE && val.i_int == 1 )
490 switch( i_type & VLC_VAR_TYPE )
494 case VLC_VAR_VARIABLE:
496 case VLC_VAR_INTEGER:
499 /* Variable doesn't exist or isn't handled */
503 if( var_Get( p_object, psz_variable, &val ) < 0 )
508 if( var_Change( p_object, psz_variable, VLC_VAR_GETLIST,
509 &val_list, &text_list ) < 0 )
511 if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string );
515 /* make (un)sensitive */
516 [o_parent setEnabled: ( val_list.p_list->i_count > 1 )];
518 for( i = 0; i < val_list.p_list->i_count; i++ )
520 vlc_value_t another_val;
522 NSString *o_title = @"";
525 switch( i_type & VLC_VAR_TYPE )
527 case VLC_VAR_VARIABLE:
529 /* This is causing crashes for the moment.
530 o_title = [NSApp localizedString: text_list.p_list->p_values[i].psz_string ?
531 text_list.p_list->p_values[i].psz_string : val_list.p_list->p_values[i].psz_string ];
533 o_data = [[VLCMenuExt alloc] initWithVar: strdup(psz_variable) Object: p_object->i_object_id
534 Value: val ofType: i_type];
535 [o_lmi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
538 NSMenu *o_menu = [o_lmi submenu];
540 [self setupVarMenu: o_menu forMenuItem: o_lmi target:p_object
541 var:psz_variable selector:pf_callback];
546 another_val.psz_string =
547 strdup(val_list.p_list->p_values[i].psz_string);
549 o_title = [NSApp localizedString: text_list.p_list->p_values[i].psz_string ?
550 text_list.p_list->p_values[i].psz_string : val_list.p_list->p_values[i].psz_string ];
552 o_lmi = [o_menu addItemWithTitle: o_title action: pf_callback keyEquivalent: @""];
553 o_data = [[VLCMenuExt alloc] initWithVar: strdup(psz_variable) Object: p_object->i_object_id
554 Value: another_val ofType: i_type];
555 [o_lmi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
556 [o_lmi setTarget: self];
558 if( !strcmp( val.psz_string, val_list.p_list->p_values[i].psz_string ) )
559 [o_lmi setState: TRUE ];
563 case VLC_VAR_INTEGER:
565 o_title = text_list.p_list->p_values[i].psz_string ?
566 [NSApp localizedString: strdup( text_list.p_list->p_values[i].psz_string )] :
567 [NSString stringWithFormat: @"%d",
568 val_list.p_list->p_values[i].i_int];
570 o_lmi = [[o_menu addItemWithTitle: o_title action: pf_callback keyEquivalent: @""] retain ];
571 o_data = [[VLCMenuExt alloc] initWithVar: strdup(psz_variable) Object: p_object->i_object_id
572 Value: val_list.p_list->p_values[i] ofType: i_type];
573 [o_lmi setRepresentedObject: [NSValue valueWithPointer:[ o_data retain]]];
574 [o_lmi setTarget: self];
576 if( val_list.p_list->p_values[i].i_int == val.i_int )
577 [o_lmi setState: TRUE ];
585 /* clean up everything */
586 if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string );
587 var_Change( p_object, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list );
590 - (IBAction)toggleVar:(id)sender
592 NSMenuItem *o_mi = (NSMenuItem *)sender;
593 VLCMenuExt *o_data = [[o_mi representedObject] pointerValue];
594 [NSThread detachNewThreadSelector: @selector(toggleVarThread:)
595 toTarget: self withObject: o_data];
600 - (int)toggleVarThread: (id)_o_data
602 vlc_object_t *p_object;
603 NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
604 VLCMenuExt *o_data = (VLCMenuExt *)_o_data;
606 vlc_thread_set_priority( [NSApp getIntf] , VLC_THREAD_PRIORITY_LOW );
608 p_object = (vlc_object_t *)vlc_object_get( [NSApp getIntf],
611 if( p_object != NULL )
613 var_Set( p_object, strdup([o_data name]), [o_data value] );
614 vlc_object_release( p_object );
624 @implementation VLCControls (NSMenuValidation)
626 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
628 BOOL bEnabled = TRUE;
630 intf_thread_t * p_intf = [NSApp getIntf];
631 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
634 if( p_playlist != NULL )
636 vlc_mutex_lock( &p_playlist->object_lock );
639 #define p_input p_playlist->p_input
641 if( [[o_mi title] isEqualToString: _NS("Faster")] ||
642 [[o_mi title] isEqualToString: _NS("Slower")] )
644 if( p_playlist != NULL && p_input != NULL )
646 vlc_mutex_lock( &p_input->stream.stream_lock );
647 bEnabled = p_input->stream.b_pace_control;
648 vlc_mutex_unlock( &p_input->stream.stream_lock );
655 else if( [[o_mi title] isEqualToString: _NS("Stop")] )
657 if( p_playlist == NULL || p_input == NULL )
662 else if( [[o_mi title] isEqualToString: _NS("Previous")] ||
663 [[o_mi title] isEqualToString: _NS("Next")] )
665 if( p_playlist == NULL )
671 bEnabled = p_playlist->i_size > 1;
673 if( p_input != NULL )
675 vlc_mutex_lock( &p_input->stream.stream_lock );
676 bEnabled |= p_input->stream.i_area_nb > 1;
677 vlc_mutex_unlock( &p_input->stream.stream_lock );
681 else if( [[o_mi title] isEqualToString: _NS("Shuffle")] )
684 var_Get( p_playlist, "random", &val );
685 i_state = val.b_bool ? NSOnState : NSOffState;
686 [o_mi setState: i_state];
688 else if( [[o_mi title] isEqualToString: _NS("Repeat Item")] )
691 var_Get( p_playlist, "repeat", &val );
692 i_state = val.b_bool ? NSOnState : NSOffState;
693 [o_mi setState: i_state];
695 else if( [[o_mi title] isEqualToString: _NS("Repeat Playlist")] )
698 var_Get( p_playlist, "loop", &val );
699 i_state = val.b_bool ? NSOnState : NSOffState;
700 [o_mi setState: i_state];
702 else if( [[o_mi title] isEqualToString: _NS("Step Forward")] ||
703 [[o_mi title] isEqualToString: _NS("Step Backward")] )
705 if( p_playlist != NULL && p_input != NULL )
707 vlc_mutex_lock( &p_input->stream.stream_lock );
708 bEnabled = p_input->stream.b_seekable;
709 vlc_mutex_unlock( &p_input->stream.stream_lock );
716 else if( [[o_mi title] isEqualToString: _NS("Mute")] )
718 [o_mi setState: p_intf->p_sys->b_mute ? NSOnState : NSOffState];
720 else if( [[o_mi title] isEqualToString: _NS("Fullscreen")] ||
721 [[o_mi title] isEqualToString: _NS("Half Size")] ||
722 [[o_mi title] isEqualToString: _NS("Normal Size")] ||
723 [[o_mi title] isEqualToString: _NS("Double Size")] ||
724 [[o_mi title] isEqualToString: _NS("Fit To Screen")] ||
725 [[o_mi title] isEqualToString: _NS("Float On Top")] )
728 NSArray *o_windows = [NSApp windows];
729 NSEnumerator *o_enumerator = [o_windows objectEnumerator];
732 if ( [[o_mi title] isEqualToString: _NS("Float On Top")] )
734 int i_state = config_GetInt( p_playlist, "macosx-float" ) ?
735 NSOnState : NSOffState;
736 [o_mi setState: i_state];
739 vout_thread_t *p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
743 while ((o_window = [o_enumerator nextObject]))
745 if( [[o_window className] isEqualToString: @"VLCWindow"] )
751 vlc_object_release( (vlc_object_t *)p_vout );
755 if( p_playlist != NULL )
757 vlc_mutex_unlock( &p_playlist->object_lock );
758 vlc_object_release( p_playlist );
766 /*****************************************************************************
767 * VLCMenuExt implementation
768 *****************************************************************************
769 * Object connected to a playlistitem which remembers the data belonging to
770 * the variable of the autogenerated menu
771 *****************************************************************************/
772 @implementation VLCMenuExt
774 - (id)initWithVar: (const char *)_psz_name Object: (int)i_id
775 Value: (vlc_value_t)val ofType: (int)_i_type
781 psz_name = strdup( _psz_name );