1 /*****************************************************************************
2 * controls.m: MacOS X interface plugin
3 *****************************************************************************
4 * Copyright (C) 2002-2003 VideoLAN
5 * $Id: controls.m,v 1.42 2003/06/30 01:51:10 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];
78 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
80 if( p_playlist == NULL )
85 playlist_Stop( p_playlist );
86 vlc_object_release( p_playlist );
89 - (IBAction)faster:(id)sender
91 intf_thread_t * p_intf = [NSApp getIntf];
93 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
95 if( p_playlist == NULL )
100 vlc_mutex_lock( &p_playlist->object_lock );
101 if( p_playlist->p_input != NULL )
103 input_SetStatus( p_playlist->p_input, INPUT_STATUS_FASTER );
105 vlc_mutex_unlock( &p_playlist->object_lock );
107 vlc_object_release( p_playlist );
110 - (IBAction)slower:(id)sender
112 intf_thread_t * p_intf = [NSApp getIntf];
114 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
116 if( p_playlist == NULL )
121 vlc_mutex_lock( &p_playlist->object_lock );
122 if( p_playlist->p_input != NULL )
124 input_SetStatus( p_playlist->p_input, INPUT_STATUS_SLOWER );
126 vlc_mutex_unlock( &p_playlist->object_lock );
128 vlc_object_release( p_playlist );
131 - (IBAction)prev:(id)sender
134 intf_thread_t * p_intf = [NSApp getIntf];
136 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
138 if( p_playlist == NULL )
143 vlc_mutex_lock( &p_playlist->object_lock );
145 if( p_playlist->p_input == NULL )
147 vlc_mutex_unlock( &p_playlist->object_lock );
148 vlc_object_release( p_playlist );
152 vlc_mutex_lock( &p_playlist->p_input->stream.stream_lock );
154 #define p_area p_playlist->p_input->stream.p_selected_area
155 NSLog( @"current title: %d, all titles: %d\ncurrent chapter: %d, all chapters: %d", p_area->i_id, p_playlist->p_input->stream.i_area_nb, p_area->i_part, p_area->i_part_nb);
156 if( p_area->i_part > 0 && p_area->i_part_nb > 1)
159 vlc_mutex_unlock( &p_playlist->p_input->stream.stream_lock );
160 vlc_mutex_unlock( &p_playlist->object_lock );
161 var_Get( p_playlist->p_input, "prev-chapter", &val );
162 var_Set( p_playlist->p_input, "prev-chapter", val );
164 p_intf->p_sys->b_input_update = VLC_TRUE;
166 else if( p_area->i_id > 1 )
168 NSLog(@"Prev Title");
169 vlc_mutex_unlock( &p_playlist->p_input->stream.stream_lock );
170 vlc_mutex_unlock( &p_playlist->object_lock );
171 var_Get( p_playlist->p_input, "prev-title", &val );
172 var_Set( p_playlist->p_input, "prev-title", val );
174 p_intf->p_sys->b_input_update = VLC_TRUE;
178 NSLog(@"Prev PlaylistItem");
179 vlc_mutex_unlock( &p_playlist->p_input->stream.stream_lock );
180 vlc_mutex_unlock( &p_playlist->object_lock );
181 playlist_Prev( p_playlist );
183 NSLog( @"current title: %d, all titles: %d\ncurrent chapter: %d, all chapters: %d", p_area->i_id, p_playlist->p_input->stream.i_area_nb, p_area->i_part, p_area->i_part_nb);
186 vlc_object_release( p_playlist );
189 - (IBAction)next:(id)sender
192 intf_thread_t * p_intf = [NSApp getIntf];
194 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
196 if( p_playlist == NULL )
201 vlc_mutex_lock( &p_playlist->object_lock );
203 if( p_playlist->p_input == NULL )
205 vlc_mutex_unlock( &p_playlist->object_lock );
206 vlc_object_release( p_playlist );
210 vlc_mutex_lock( &p_playlist->p_input->stream.stream_lock );
212 #define p_area p_playlist->p_input->stream.p_selected_area
213 NSLog( @"current title: %d, all titles: %d\ncurrent chapter: %d, all chapters: %d", p_area->i_id, p_playlist->p_input->stream.i_area_nb, p_area->i_part, p_area->i_part_nb);
214 if( p_area->i_part < p_area->i_part_nb && p_area->i_part_nb > 1 )
217 vlc_mutex_unlock( &p_playlist->p_input->stream.stream_lock );
218 vlc_mutex_unlock( &p_playlist->object_lock );
219 var_Get( p_playlist->p_input, "next-chapter", &val );
220 var_Set( p_playlist->p_input, "next-chapter", val );
222 p_intf->p_sys->b_input_update = VLC_TRUE;
224 else if( p_area->i_id < p_playlist->p_input->stream.i_area_nb )
226 NSLog(@"Next Title");
227 vlc_mutex_unlock( &p_playlist->p_input->stream.stream_lock );
228 vlc_mutex_unlock( &p_playlist->object_lock );
229 var_Get( p_playlist->p_input, "next-title", &val );
230 var_Set( p_playlist->p_input, "next-title", val );
232 p_intf->p_sys->b_input_update = VLC_TRUE;
236 NSLog(@"Next PlaylistItem");
237 vlc_mutex_unlock( &p_playlist->p_input->stream.stream_lock );
238 vlc_mutex_unlock( &p_playlist->object_lock );
239 playlist_Next( p_playlist );
241 NSLog( @"current title: %d, all titles: %d\ncurrent chapter: %d, all chapters: %d", p_area->i_id, p_playlist->p_input->stream.i_area_nb, p_area->i_part, p_area->i_part_nb);
244 vlc_object_release( p_playlist );
247 - (IBAction)loop:(id)sender
249 intf_thread_t * p_intf = [NSApp getIntf];
251 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
253 if( p_playlist == NULL )
258 config_PutInt( p_playlist, "loop",
259 !config_GetInt( p_playlist, "loop" ) );
261 vlc_object_release( p_playlist );
264 - (IBAction)forward:(id)sender
266 intf_thread_t * p_intf = [NSApp getIntf];
267 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
269 if( p_playlist == NULL || p_playlist->p_input == NULL )
271 if ( p_playlist != NULL ) vlc_object_release( p_playlist );
275 input_Seek( p_playlist->p_input, 5, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
276 vlc_object_release( p_playlist );
279 - (IBAction)backward:(id)sender
281 intf_thread_t * p_intf = [NSApp getIntf];
282 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
284 if( p_playlist == NULL || p_playlist->p_input == NULL )
286 if ( p_playlist != NULL ) vlc_object_release( p_playlist );
290 input_Seek( p_playlist->p_input, -5, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
291 vlc_object_release( p_playlist );
294 - (IBAction)volumeUp:(id)sender
296 intf_thread_t * p_intf = [NSApp getIntf];
298 if( p_intf->p_sys->b_mute )
303 aout_VolumeUp( p_intf, 1, NULL );
305 [self updateVolumeSlider];
308 - (IBAction)volumeDown:(id)sender
310 intf_thread_t * p_intf = [NSApp getIntf];
312 if( p_intf->p_sys->b_mute )
317 aout_VolumeDown( p_intf, 1, NULL );
319 [self updateVolumeSlider];
322 - (IBAction)mute:(id)sender
324 intf_thread_t * p_intf = [NSApp getIntf];
325 audio_volume_t i_volume;
327 aout_VolumeMute( p_intf, &i_volume );
328 p_intf->p_sys->b_mute = ( i_volume == 0 );
330 [self updateVolumeSlider];
333 - (IBAction)volumeSliderUpdated:(id)sender
335 intf_thread_t * p_intf = [NSApp getIntf];
336 audio_volume_t i_volume = (audio_volume_t)[sender intValue];
338 aout_VolumeSet( p_intf, i_volume * AOUT_VOLUME_STEP );
341 - (void)updateVolumeSlider
343 intf_thread_t * p_intf = [NSApp getIntf];
344 audio_volume_t i_volume;
346 aout_VolumeGet( p_intf, &i_volume );
348 [o_volumeslider setFloatValue: (float)(i_volume / AOUT_VOLUME_STEP)];
351 - (IBAction)windowAction:(id)sender
353 id o_window = [NSApp keyWindow];
354 NSString *o_title = [sender title];
355 NSArray *o_windows = [NSApp windows];
356 NSEnumerator *o_enumerator = [o_windows objectEnumerator];
357 vout_thread_t *p_vout = vlc_object_find( [NSApp getIntf], VLC_OBJECT_VOUT,
362 while ((o_window = [o_enumerator nextObject]))
364 if( [[o_window className] isEqualToString: @"VLCWindow"] )
366 if( [o_title isEqualToString: _NS("Fullscreen") ] )
367 [o_window toggleFullscreen];
368 else if( [o_title isEqualToString: _NS("Half Size") ] )
369 [o_window scaleWindowWithFactor: 0.5];
370 else if( [o_title isEqualToString: _NS("Normal Size") ] )
371 [o_window scaleWindowWithFactor: 1.0];
372 else if( [o_title isEqualToString: _NS("Double Size") ] )
373 [o_window scaleWindowWithFactor: 2.0];
374 else if( [o_title isEqualToString: _NS("Float On Top") ] )
375 [o_window toggleFloatOnTop];
376 else if( [o_title isEqualToString: _NS("Fit To Screen") ] )
378 if( ![o_window isZoomed] )
379 [o_window performZoom:self];
383 vlc_object_release( (vlc_object_t *)p_vout );
387 - (void)setupVarMenuItem:(NSMenuItem *)o_mi
388 target:(vlc_object_t *)p_object
389 var:(const char *)psz_variable
390 selector:(SEL)pf_callback
392 vlc_value_t val, text;
393 int i_type = var_Type( p_object, psz_variable );
395 switch( i_type & VLC_VAR_TYPE )
399 case VLC_VAR_VARIABLE:
401 case VLC_VAR_INTEGER:
404 /* Variable doesn't exist or isn't handled */
408 /* Make sure we want to display the variable */
409 if( i_type & VLC_VAR_HASCHOICE )
411 var_Change( p_object, psz_variable, VLC_VAR_CHOICESCOUNT, &val, NULL );
412 if( val.i_int == 0 ) return;
413 if( (i_type & VLC_VAR_TYPE) != VLC_VAR_VARIABLE && val.i_int == 1 )
417 /* Get the descriptive name of the variable */
418 var_Change( p_object, psz_variable, VLC_VAR_GETTEXT, &text, NULL );
419 [o_mi setTitle: [NSApp localizedString: text.psz_string ?
420 text.psz_string : strdup( psz_variable ) ]];
422 var_Get( p_object, psz_variable, &val );
423 if( i_type & VLC_VAR_HASCHOICE )
425 NSMenu *o_menu = [o_mi submenu];
427 [self setupVarMenu: o_menu forMenuItem: o_mi target:p_object
428 var:psz_variable selector:pf_callback];
430 if( text.psz_string ) free( text.psz_string );
435 switch( i_type & VLC_VAR_TYPE )
438 o_data = [[VLCMenuExt alloc] initWithVar: psz_variable Object: p_object->i_object_id
439 Value: val ofType: i_type];
440 [o_mi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
444 o_data = [[VLCMenuExt alloc] initWithVar: psz_variable Object: p_object->i_object_id
445 Value: val ofType: i_type];
446 [o_mi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
447 [o_mi setState: val.b_bool ? TRUE : FALSE ];
451 if( text.psz_string ) free( text.psz_string );
455 if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string );
456 if( text.psz_string ) free( text.psz_string );
460 - (void)setupVarMenu:(NSMenu *)o_menu
461 forMenuItem: (NSMenuItem *)o_parent
462 target:(vlc_object_t *)p_object
463 var:(const char *)psz_variable
464 selector:(SEL)pf_callback
466 vlc_value_t val, val_list, text_list;
467 int i_type, i, i_nb_items;
469 /* remove previous items */
470 i_nb_items = [o_menu numberOfItems];
471 for( i = 0; i < i_nb_items; i++ )
473 [o_menu removeItemAtIndex: 0];
476 /* Check the type of the object variable */
477 i_type = var_Type( p_object, psz_variable );
479 /* Make sure we want to display the variable */
480 if( i_type & VLC_VAR_HASCHOICE )
482 var_Change( p_object, psz_variable, VLC_VAR_CHOICESCOUNT, &val, NULL );
483 if( val.i_int == 0 ) return;
484 if( (i_type & VLC_VAR_TYPE) != VLC_VAR_VARIABLE && val.i_int == 1 )
492 switch( i_type & VLC_VAR_TYPE )
496 case VLC_VAR_VARIABLE:
498 case VLC_VAR_INTEGER:
501 /* Variable doesn't exist or isn't handled */
505 if( var_Get( p_object, psz_variable, &val ) < 0 )
510 if( var_Change( p_object, psz_variable, VLC_VAR_GETLIST,
511 &val_list, &text_list ) < 0 )
513 if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string );
517 /* make (un)sensitive */
518 [o_parent setEnabled: ( val_list.p_list->i_count > 1 )];
520 for( i = 0; i < val_list.p_list->i_count; i++ )
522 vlc_value_t another_val;
524 NSString *o_title = @"";
527 switch( i_type & VLC_VAR_TYPE )
529 case VLC_VAR_VARIABLE:
531 /* This is causing crashes for the moment.
532 o_title = [NSApp localizedString: text_list.p_list->p_values[i].psz_string ?
533 text_list.p_list->p_values[i].psz_string : val_list.p_list->p_values[i].psz_string ];
535 o_data = [[VLCMenuExt alloc] initWithVar: strdup(psz_variable) Object: p_object->i_object_id
536 Value: val ofType: i_type];
537 [o_lmi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
540 NSMenu *o_menu = [o_lmi submenu];
542 [self setupVarMenu: o_menu forMenuItem: o_lmi target:p_object
543 var:psz_variable selector:pf_callback];
548 another_val.psz_string =
549 strdup(val_list.p_list->p_values[i].psz_string);
551 o_title = [NSApp localizedString: text_list.p_list->p_values[i].psz_string ?
552 text_list.p_list->p_values[i].psz_string : val_list.p_list->p_values[i].psz_string ];
554 o_lmi = [o_menu addItemWithTitle: o_title action: pf_callback keyEquivalent: @""];
555 o_data = [[VLCMenuExt alloc] initWithVar: strdup(psz_variable) Object: p_object->i_object_id
556 Value: another_val ofType: i_type];
557 [o_lmi setRepresentedObject: [NSValue valueWithPointer:[o_data retain]]];
558 [o_lmi setTarget: self];
560 if( !strcmp( val.psz_string, val_list.p_list->p_values[i].psz_string ) )
561 [o_lmi setState: TRUE ];
565 case VLC_VAR_INTEGER:
567 o_title = text_list.p_list->p_values[i].psz_string ?
568 [NSApp localizedString: strdup( text_list.p_list->p_values[i].psz_string )] :
569 [NSString stringWithFormat: @"%d",
570 val_list.p_list->p_values[i].i_int];
572 o_lmi = [[o_menu addItemWithTitle: o_title action: pf_callback keyEquivalent: @""] retain ];
573 o_data = [[VLCMenuExt alloc] initWithVar: strdup(psz_variable) Object: p_object->i_object_id
574 Value: val_list.p_list->p_values[i] ofType: i_type];
575 [o_lmi setRepresentedObject: [NSValue valueWithPointer:[ o_data retain]]];
576 [o_lmi setTarget: self];
578 if( val_list.p_list->p_values[i].i_int == val.i_int )
579 [o_lmi setState: TRUE ];
587 /* clean up everything */
588 if( (i_type & VLC_VAR_TYPE) == VLC_VAR_STRING ) free( val.psz_string );
589 var_Change( p_object, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list );
592 - (IBAction)toggleVar:(id)sender
594 NSMenuItem *o_mi = (NSMenuItem *)sender;
595 VLCMenuExt *o_data = [[o_mi representedObject] pointerValue];
596 [NSThread detachNewThreadSelector: @selector(toggleVarThread:)
597 toTarget: self withObject: o_data];
602 - (int)toggleVarThread: (id)_o_data
604 vlc_object_t *p_object;
605 NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
606 VLCMenuExt *o_data = (VLCMenuExt *)_o_data;
608 vlc_thread_set_priority( [NSApp getIntf] , VLC_THREAD_PRIORITY_LOW );
610 p_object = (vlc_object_t *)vlc_object_get( [NSApp getIntf],
613 if( p_object != NULL )
615 var_Set( p_object, strdup([o_data name]), [o_data value] );
616 vlc_object_release( p_object );
626 @implementation VLCControls (NSMenuValidation)
628 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
630 BOOL bEnabled = TRUE;
631 intf_thread_t * p_intf = [NSApp getIntf];
633 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
636 if( p_playlist != NULL )
638 vlc_mutex_lock( &p_playlist->object_lock );
641 #define p_input p_playlist->p_input
643 if( [[o_mi title] isEqualToString: _NS("Faster")] ||
644 [[o_mi title] isEqualToString: _NS("Slower")] )
646 if( p_playlist != NULL && p_input != NULL )
648 vlc_mutex_lock( &p_input->stream.stream_lock );
649 bEnabled = p_input->stream.b_pace_control;
650 vlc_mutex_unlock( &p_input->stream.stream_lock );
657 else if( [[o_mi title] isEqualToString: _NS("Stop")] )
659 if( p_playlist == NULL || p_input == NULL )
664 else if( [[o_mi title] isEqualToString: _NS("Previous")] ||
665 [[o_mi title] isEqualToString: _NS("Next")] )
667 if( p_playlist == NULL )
673 bEnabled = p_playlist->i_size > 1;
675 if( p_input != NULL )
677 vlc_mutex_lock( &p_input->stream.stream_lock );
678 bEnabled |= p_input->stream.i_area_nb > 1;
679 vlc_mutex_unlock( &p_input->stream.stream_lock );
683 else if( [[o_mi title] isEqualToString: _NS("Loop")] )
685 int i_state = config_GetInt( p_playlist, "loop" ) ?
686 NSOnState : NSOffState;
688 [o_mi setState: i_state];
690 else if( [[o_mi title] isEqualToString: _NS("Step Forward")] ||
691 [[o_mi title] isEqualToString: _NS("Step Backward")] )
693 if( p_playlist != NULL && p_input != NULL )
695 vlc_mutex_lock( &p_input->stream.stream_lock );
696 bEnabled = p_input->stream.b_seekable;
697 vlc_mutex_unlock( &p_input->stream.stream_lock );
704 else if( [[o_mi title] isEqualToString: _NS("Mute")] )
706 [o_mi setState: p_intf->p_sys->b_mute ? NSOnState : NSOffState];
708 else if( [[o_mi title] isEqualToString: _NS("Fullscreen")] ||
709 [[o_mi title] isEqualToString: _NS("Half Size")] ||
710 [[o_mi title] isEqualToString: _NS("Normal Size")] ||
711 [[o_mi title] isEqualToString: _NS("Double Size")] ||
712 [[o_mi title] isEqualToString: _NS("Fit To Screen")] ||
713 [[o_mi title] isEqualToString: _NS("Float On Top")] )
716 NSArray *o_windows = [NSApp windows];
717 NSEnumerator *o_enumerator = [o_windows objectEnumerator];
720 if ( [[o_mi title] isEqualToString: _NS("Float On Top")] )
722 int i_state = config_GetInt( p_playlist, "macosx-float" ) ?
723 NSOnState : NSOffState;
724 [o_mi setState: i_state];
727 vout_thread_t *p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
731 while ((o_window = [o_enumerator nextObject]))
733 if( [[o_window className] isEqualToString: @"VLCWindow"] )
739 vlc_object_release( (vlc_object_t *)p_vout );
743 if( p_playlist != NULL )
745 vlc_mutex_unlock( &p_playlist->object_lock );
746 vlc_object_release( p_playlist );
754 /*****************************************************************************
755 * VLCMenuExt implementation
756 *****************************************************************************
757 * Object connected to a playlistitem which remembers the data belonging to
758 * the variable of the autogenerated menu
759 *****************************************************************************/
760 @implementation VLCMenuExt
762 - (id)initWithVar: (const char *)_psz_name Object: (int)i_id
763 Value: (vlc_value_t)val ofType: (int)_i_type
769 psz_name = strdup( _psz_name );