1 /*****************************************************************************
2 * controls.m: MacOS X interface module
3 *****************************************************************************
4 * Copyright (C) 2002-2003 VideoLAN
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>
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 */
39 /*****************************************************************************
40 * VLCControls implementation
41 *****************************************************************************/
42 @implementation VLCControls
44 - (IBAction)play:(id)sender
47 playlist_t * p_playlist;
48 intf_thread_t * p_intf = [NSApp getIntf];
49 input_thread_t * p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
52 val.i_int = PLAYING_S;
55 var_Get( p_input, "state", &val );
57 if( p_input && val.i_int != PAUSE_S )
59 vout_OSDMessage( p_intf, _( "Pause" ) );
61 var_Set( p_input, "state", val );
65 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
69 vlc_mutex_lock( &p_playlist->object_lock );
70 if( p_playlist->i_size )
72 vlc_mutex_unlock( &p_playlist->object_lock );
73 vout_OSDMessage( p_intf, _( "Play" ) );
74 playlist_Play( p_playlist );
75 vlc_object_release( p_playlist );
79 vlc_mutex_unlock( &p_playlist->object_lock );
80 vlc_object_release( p_playlist );
81 [o_open openFileGeneric: nil];
85 if( p_input ) vlc_object_release( p_input );
88 - (IBAction)stop:(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 )
95 vout_OSDMessage( p_intf, _( "Stop" ) );
96 playlist_Stop( p_playlist );
97 vlc_object_release( p_playlist );
101 - (IBAction)faster:(id)sender
103 intf_thread_t * p_intf = [NSApp getIntf];
104 input_thread_t * p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
106 if( p_input != NULL )
108 vlc_value_t val; val.b_bool = VLC_TRUE;
110 var_Set( p_input, "rate-faster", val );
111 vout_OSDMessage( p_intf, _( "Faster" ) );
112 vlc_object_release( p_input );
116 - (IBAction)slower:(id)sender
118 intf_thread_t * p_intf = [NSApp getIntf];
119 input_thread_t * p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
121 if( p_input != NULL )
123 vlc_value_t val; val.b_bool = VLC_TRUE;
125 var_Set( p_input, "rate-slower", val );
126 vout_OSDMessage( p_intf, _( "Slower" ) );
127 vlc_object_release( p_input );
131 - (IBAction)prev:(id)sender
133 intf_thread_t * p_intf = [NSApp getIntf];
134 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
138 playlist_Prev( p_playlist );
139 vlc_object_release( p_playlist );
140 vout_OSDMessage( p_intf, _( "Previous" ) );
144 - (IBAction)next:(id)sender
146 intf_thread_t * p_intf = [NSApp getIntf];
147 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
151 playlist_Next( p_playlist );
152 vlc_object_release( p_playlist );
153 vout_OSDMessage( p_intf, _( "Next" ) );
157 - (IBAction)random:(id)sender
159 intf_thread_t * p_intf = [NSApp getIntf];
161 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
163 if( p_playlist == NULL )
168 var_Get( p_playlist, "random", &val );
169 val.b_bool = !val.b_bool;
170 var_Set( p_playlist, "random", val );
173 vout_OSDMessage( p_intf, _( "Random On" ) );
177 vout_OSDMessage( p_intf, _( "Random Off" ) );
180 p_intf->p_sys->b_playlist_update = VLC_TRUE;
181 p_intf->p_sys->b_intf_update = VLC_TRUE;
182 vlc_object_release( p_playlist );
185 - (IBAction)repeat:(id)sender
187 intf_thread_t * p_intf = [NSApp getIntf];
189 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
191 if( p_playlist == NULL )
196 var_Get( p_playlist, "repeat", &val );
199 var_Set( p_playlist, "loop", val );
201 val.b_bool = !val.b_bool;
202 var_Set( p_playlist, "repeat", val );
205 vout_OSDMessage( p_intf, _( "Repeat All" ) );
209 vout_OSDMessage( p_intf, _( "Repeat Off" ) );
212 p_intf->p_sys->b_playlist_update = VLC_TRUE;
213 p_intf->p_sys->b_intf_update = VLC_TRUE;
214 vlc_object_release( p_playlist );
217 - (IBAction)loop:(id)sender
219 intf_thread_t * p_intf = [NSApp getIntf];
221 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
223 if( p_playlist == NULL )
228 var_Get( p_playlist, "loop", &val );
231 var_Set( p_playlist, "repeat", val );
233 val.b_bool = !val.b_bool;
234 var_Set( p_playlist, "loop", val );
237 vout_OSDMessage( p_intf, _( "Repeat One" ) );
241 vout_OSDMessage( p_intf, _( "Repeat Off" ) );
244 p_intf->p_sys->b_playlist_update = VLC_TRUE;
245 p_intf->p_sys->b_intf_update = VLC_TRUE;
246 vlc_object_release( p_playlist );
249 - (IBAction)forward:(id)sender
251 intf_thread_t * p_intf = [NSApp getIntf];
252 input_thread_t * p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
254 if( p_input != NULL )
257 time.i_time = 10 * 1000000;
258 var_Set( p_input, "time-offset", time );
259 vout_OSDMessage( p_intf, _( "Jump +10 Seconds" ) );
260 vlc_object_release( p_input );
264 - (IBAction)forward1Min:(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 = 60 * 1000000;
273 var_Set( p_input, "time-offset", time );
274 vout_OSDMessage( p_intf, _( "Jump +1 Minute" ) );
275 vlc_object_release( p_input );
279 - (IBAction)forward5Min:(id)sender
281 intf_thread_t * p_intf = [NSApp getIntf];
282 input_thread_t * p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
284 if( p_input != NULL )
287 time.i_time = 300 * 1000000;
288 var_Set( p_input, "time-offset", time );
289 vout_OSDMessage( p_intf, _( "Jump +5 Minutes" ) );
290 vlc_object_release( p_input );
294 - (IBAction)backward:(id)sender
296 intf_thread_t * p_intf = [NSApp getIntf];
297 input_thread_t * p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
299 if( p_input != NULL )
302 time.i_time = -10 * 1000000;
303 var_Set( p_input, "time-offset", time );
304 vout_OSDMessage( p_intf, _( "Jump -10 Seconds" ) );
305 vlc_object_release( p_input );
309 - (IBAction)backward1Min:(id)sender
311 intf_thread_t * p_intf = [NSApp getIntf];
312 input_thread_t * p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
314 if( p_input != NULL )
317 time.i_time = -60 * 1000000;
318 var_Set( p_input, "time-offset", time );
319 vout_OSDMessage( p_intf, _( "Jump -1 Minute" ) );
320 vlc_object_release( p_input );
324 - (IBAction)backward5Min:(id)sender
326 intf_thread_t * p_intf = [NSApp getIntf];
327 input_thread_t * p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
329 if( p_input != NULL )
332 time.i_time = -300 * 1000000;
333 var_Set( p_input, "time-offset", time );
334 vout_OSDMessage( p_intf, _( "Jump -5 Minutes" ) );
335 vlc_object_release( p_input );
339 - (IBAction)volumeUp:(id)sender
341 intf_thread_t * p_intf = [NSApp getIntf];
343 if( p_intf->p_sys->b_mute )
348 aout_VolumeUp( p_intf, 1, NULL );
350 [self updateVolumeSlider];
353 - (IBAction)volumeDown:(id)sender
355 intf_thread_t * p_intf = [NSApp getIntf];
357 if( p_intf->p_sys->b_mute )
362 aout_VolumeDown( p_intf, 1, NULL );
364 [self updateVolumeSlider];
367 - (IBAction)mute:(id)sender
369 intf_thread_t * p_intf = [NSApp getIntf];
370 audio_volume_t i_volume;
372 aout_VolumeMute( p_intf, &i_volume );
373 p_intf->p_sys->b_mute = ( i_volume == 0 );
375 [self updateVolumeSlider];
378 - (IBAction)volumeSliderUpdated:(id)sender
380 intf_thread_t * p_intf = [NSApp getIntf];
381 audio_volume_t i_volume = (audio_volume_t)[sender intValue];
383 aout_VolumeSet( p_intf, i_volume * AOUT_VOLUME_STEP );
386 - (void)updateVolumeSlider
388 intf_thread_t * p_intf = [NSApp getIntf];
389 audio_volume_t i_volume;
391 aout_VolumeGet( p_intf, &i_volume );
393 [o_volumeslider setFloatValue: (float)(i_volume / AOUT_VOLUME_STEP)];
395 vout_OSDMessage( p_intf, "Vol %d%%", i_volume*100/AOUT_VOLUME_MAX );
398 - (IBAction)windowAction:(id)sender
400 id o_window = [NSApp keyWindow];
401 NSString *o_title = [sender title];
402 NSArray *o_windows = [NSApp orderedWindows];
403 NSEnumerator *o_enumerator = [o_windows objectEnumerator];
404 vout_thread_t *p_vout = vlc_object_find( [NSApp getIntf], VLC_OBJECT_VOUT,
409 while ((o_window = [o_enumerator nextObject]))
411 if( [[o_window className] isEqualToString: @"VLCWindow"] )
413 if( [o_title isEqualToString: _NS("Half Size") ] )
414 [o_window scaleWindowWithFactor: 0.5];
415 else if( [o_title isEqualToString: _NS("Normal Size") ] )
416 [o_window scaleWindowWithFactor: 1.0];
417 else if( [o_title isEqualToString: _NS("Double Size") ] )
418 [o_window scaleWindowWithFactor: 2.0];
419 else if( [o_title isEqualToString: _NS("Float on Top") ] )
420 [o_window toggleFloatOnTop];
421 else if( [o_title isEqualToString: _NS("Fit to Screen") ] )
423 if( ![o_window isZoomed] )
424 [o_window performZoom:self];
428 [o_window toggleFullscreen];
433 vlc_object_release( (vlc_object_t *)p_vout );
437 - (void)setupVarMenuItem:(NSMenuItem *)o_mi
438 target:(vlc_object_t *)p_object
439 var:(const char *)psz_variable
440 selector:(SEL)pf_callback
442 vlc_value_t val, text;
443 int i_type = var_Type( p_object, psz_variable );
445 switch( i_type & VLC_VAR_TYPE )
449 case VLC_VAR_VARIABLE:
451 case VLC_VAR_INTEGER:
454 /* Variable doesn't exist or isn't handled */
458 /* Make sure we want to display the variable */
459 if( i_type & VLC_VAR_HASCHOICE )
461 var_Change( p_object, psz_variable, VLC_VAR_CHOICESCOUNT, &val, NULL );
462 if( val.i_int == 0 ) return;
463 if( (i_type & VLC_VAR_TYPE) != VLC_VAR_VARIABLE && val.i_int == 1 )
467 /* Get the descriptive name of the variable */
468 var_Change( p_object, psz_variable, VLC_VAR_GETTEXT, &text, NULL );
469 [o_mi setTitle: [NSApp localizedString: text.psz_string ?
470 text.psz_string : strdup( psz_variable ) ]];
472 var_Get( p_object, psz_variable, &val );
473 if( i_type & VLC_VAR_HASCHOICE )
475 NSMenu *o_menu = [o_mi submenu];
477 [self setupVarMenu: o_menu forMenuItem: o_mi target:p_object
478 var:psz_variable selector:pf_callback];
480 if( text.psz_string ) free( text.psz_string );
485 switch( i_type & VLC_VAR_TYPE )
488 o_data = [[VLCMenuExt alloc] initWithVar: psz_variable Object: p_object->i_object_id
489 Value: val ofType: i_type];
490 [o_mi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
494 o_data = [[VLCMenuExt alloc] initWithVar: psz_variable Object: p_object->i_object_id
495 Value: val ofType: i_type];
496 [o_mi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
497 [o_mi setState: val.b_bool ? TRUE : FALSE ];
501 if( text.psz_string ) free( text.psz_string );
505 if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string );
506 if( text.psz_string ) free( text.psz_string );
510 - (void)setupVarMenu:(NSMenu *)o_menu
511 forMenuItem: (NSMenuItem *)o_parent
512 target:(vlc_object_t *)p_object
513 var:(const char *)psz_variable
514 selector:(SEL)pf_callback
516 vlc_value_t val, val_list, text_list;
517 int i_type, i, i_nb_items;
519 /* remove previous items */
520 i_nb_items = [o_menu numberOfItems];
521 for( i = 0; i < i_nb_items; i++ )
523 [o_menu removeItemAtIndex: 0];
526 /* Check the type of the object variable */
527 i_type = var_Type( p_object, psz_variable );
529 /* Make sure we want to display the variable */
530 if( i_type & VLC_VAR_HASCHOICE )
532 var_Change( p_object, psz_variable, VLC_VAR_CHOICESCOUNT, &val, NULL );
533 if( val.i_int == 0 ) return;
534 if( (i_type & VLC_VAR_TYPE) != VLC_VAR_VARIABLE && val.i_int == 1 )
542 switch( i_type & VLC_VAR_TYPE )
546 case VLC_VAR_VARIABLE:
548 case VLC_VAR_INTEGER:
551 /* Variable doesn't exist or isn't handled */
555 if( var_Get( p_object, psz_variable, &val ) < 0 )
560 if( var_Change( p_object, psz_variable, VLC_VAR_GETLIST,
561 &val_list, &text_list ) < 0 )
563 if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string );
567 /* make (un)sensitive */
568 [o_parent setEnabled: ( val_list.p_list->i_count > 1 )];
570 for( i = 0; i < val_list.p_list->i_count; i++ )
572 vlc_value_t another_val;
574 NSString *o_title = @"";
577 switch( i_type & VLC_VAR_TYPE )
579 case VLC_VAR_VARIABLE:
581 /* This is causing crashes for the moment.
582 o_title = [NSApp localizedString: text_list.p_list->p_values[i].psz_string ?
583 text_list.p_list->p_values[i].psz_string : val_list.p_list->p_values[i].psz_string ];
585 o_data = [[VLCMenuExt alloc] initWithVar: strdup(psz_variable) Object: p_object->i_object_id
586 Value: val ofType: i_type];
587 [o_lmi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
590 NSMenu *o_menu = [o_lmi submenu];
592 [self setupVarMenu: o_menu forMenuItem: o_lmi target:p_object
593 var:psz_variable selector:pf_callback];
598 another_val.psz_string =
599 strdup(val_list.p_list->p_values[i].psz_string);
601 o_title = [NSApp localizedString: text_list.p_list->p_values[i].psz_string ?
602 text_list.p_list->p_values[i].psz_string : val_list.p_list->p_values[i].psz_string ];
604 o_lmi = [o_menu addItemWithTitle: o_title action: pf_callback keyEquivalent: @""];
605 o_data = [[VLCMenuExt alloc] initWithVar: strdup(psz_variable) Object: p_object->i_object_id
606 Value: another_val ofType: i_type];
607 [o_lmi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
608 [o_lmi setTarget: self];
610 if( !strcmp( val.psz_string, val_list.p_list->p_values[i].psz_string ) )
611 [o_lmi setState: TRUE ];
615 case VLC_VAR_INTEGER:
617 o_title = text_list.p_list->p_values[i].psz_string ?
618 [NSApp localizedString: strdup( text_list.p_list->p_values[i].psz_string )] :
619 [NSString stringWithFormat: @"%d",
620 val_list.p_list->p_values[i].i_int];
622 o_lmi = [[o_menu addItemWithTitle: o_title action: pf_callback keyEquivalent: @""] retain ];
623 o_data = [[VLCMenuExt alloc] initWithVar: strdup(psz_variable) Object: p_object->i_object_id
624 Value: val_list.p_list->p_values[i] ofType: i_type];
625 [o_lmi setRepresentedObject: [NSValue valueWithPointer:[ o_data retain]]];
626 [o_lmi setTarget: self];
628 if( val_list.p_list->p_values[i].i_int == val.i_int )
629 [o_lmi setState: TRUE ];
637 /* clean up everything */
638 if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string );
639 var_Change( p_object, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list );
642 - (IBAction)toggleVar:(id)sender
644 NSMenuItem *o_mi = (NSMenuItem *)sender;
645 VLCMenuExt *o_data = [[o_mi representedObject] pointerValue];
646 [NSThread detachNewThreadSelector: @selector(toggleVarThread:)
647 toTarget: self withObject: o_data];
652 - (int)toggleVarThread: (id)_o_data
654 vlc_object_t *p_object;
655 NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
656 VLCMenuExt *o_data = (VLCMenuExt *)_o_data;
658 vlc_thread_set_priority( [NSApp getIntf] , VLC_THREAD_PRIORITY_LOW );
660 p_object = (vlc_object_t *)vlc_object_get( [NSApp getIntf],
663 if( p_object != NULL )
665 var_Set( p_object, strdup([o_data name]), [o_data value] );
666 vlc_object_release( p_object );
676 @implementation VLCControls (NSMenuValidation)
678 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
680 BOOL bEnabled = TRUE;
682 intf_thread_t * p_intf = [NSApp getIntf];
683 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
686 if( p_playlist != NULL )
688 vlc_mutex_lock( &p_playlist->object_lock );
691 #define p_input p_playlist->p_input
693 if( [[o_mi title] isEqualToString: _NS("Faster")] ||
694 [[o_mi title] isEqualToString: _NS("Slower")] )
696 if( p_playlist != NULL && p_input != NULL )
698 vlc_mutex_lock( &p_input->stream.stream_lock );
699 bEnabled = p_input->stream.b_pace_control;
700 vlc_mutex_unlock( &p_input->stream.stream_lock );
707 else if( [[o_mi title] isEqualToString: _NS("Stop")] )
709 if( p_playlist == NULL || p_input == NULL )
714 else if( [[o_mi title] isEqualToString: _NS("Previous")] ||
715 [[o_mi title] isEqualToString: _NS("Next")] )
717 if( p_playlist == NULL )
723 bEnabled = p_playlist->i_size > 1;
725 if( p_input != NULL )
727 vlc_mutex_lock( &p_input->stream.stream_lock );
728 bEnabled |= p_input->stream.i_area_nb > 1;
729 vlc_mutex_unlock( &p_input->stream.stream_lock );
733 else if( [[o_mi title] isEqualToString: _NS("Random")] )
736 var_Get( p_playlist, "random", &val );
737 i_state = val.b_bool ? NSOnState : NSOffState;
738 [o_mi setState: i_state];
740 else if( [[o_mi title] isEqualToString: _NS("Repeat One")] )
743 var_Get( p_playlist, "repeat", &val );
744 i_state = val.b_bool ? NSOnState : NSOffState;
745 [o_mi setState: i_state];
747 else if( [[o_mi title] isEqualToString: _NS("Repeat All")] )
750 var_Get( p_playlist, "loop", &val );
751 i_state = val.b_bool ? NSOnState : NSOffState;
752 [o_mi setState: i_state];
754 else if( [[o_mi title] isEqualToString: _NS("Step Forward")] ||
755 [[o_mi title] isEqualToString: _NS("Step Backward")] )
757 if( p_playlist != NULL && p_input != NULL )
759 vlc_mutex_lock( &p_input->stream.stream_lock );
760 bEnabled = p_input->stream.b_seekable;
761 vlc_mutex_unlock( &p_input->stream.stream_lock );
768 else if( [[o_mi title] isEqualToString: _NS("Mute")] )
770 [o_mi setState: p_intf->p_sys->b_mute ? NSOnState : NSOffState];
772 else if( [[o_mi title] isEqualToString: _NS("Fullscreen")] ||
773 [[o_mi title] isEqualToString: _NS("Half Size")] ||
774 [[o_mi title] isEqualToString: _NS("Normal Size")] ||
775 [[o_mi title] isEqualToString: _NS("Double Size")] ||
776 [[o_mi title] isEqualToString: _NS("Fit to Screen")] ||
777 [[o_mi title] isEqualToString: _NS("Float on Top")] )
780 NSArray *o_windows = [NSApp orderedWindows];
781 NSEnumerator *o_enumerator = [o_windows objectEnumerator];
784 if ( [[o_mi title] isEqualToString: _NS("Float on Top")] )
786 int i_state = config_GetInt( p_playlist, "video-on-top" ) ?
787 NSOnState : NSOffState;
788 [o_mi setState: i_state];
791 vout_thread_t *p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
795 while ((o_window = [o_enumerator nextObject]))
797 if( [[o_window className] isEqualToString: @"VLCWindow"] )
803 vlc_object_release( (vlc_object_t *)p_vout );
807 if( p_playlist != NULL )
809 vlc_mutex_unlock( &p_playlist->object_lock );
810 vlc_object_release( p_playlist );
818 /*****************************************************************************
819 * VLCMenuExt implementation
820 *****************************************************************************
821 * Object connected to a playlistitem which remembers the data belonging to
822 * the variable of the autogenerated menu
823 *****************************************************************************/
824 @implementation VLCMenuExt
826 - (id)initWithVar: (const char *)_psz_name Object: (int)i_id
827 Value: (vlc_value_t)val ofType: (int)_i_type
833 psz_name = strdup( _psz_name );