* ./extras/MacOSX/Resources/English.lproj/MainMenu.nib/objects.nib
[vlc.git] / modules / gui / macosx / controls.m
1 /*****************************************************************************
2  * controls.m: MacOS X interface plugin
3  *****************************************************************************
4  * Copyright (C) 2002 VideoLAN
5  * $Id: controls.m,v 1.12 2003/01/23 22:57:28 hartman Exp $
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8  *          Christophe Massiot <massiot@via.ecp.fr>
9  *          Derk-Jan Hartman <thedj@users.sourceforge.net>
10  *
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.
15  * 
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.
20  *
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  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <stdlib.h>                                      /* malloc(), free() */
30 #include <sys/param.h>                                    /* for MAXPATHLEN */
31 #include <string.h>
32
33 #include <vlc/vlc.h>
34 #include <vlc/intf.h>
35 #include <vlc/aout.h>
36
37 #include <Cocoa/Cocoa.h> 
38 #include <CoreAudio/AudioHardware.h>
39
40 #include "intf.h"
41 #include "vout.h"
42
43 /*****************************************************************************
44  * VLCControls interface 
45  *****************************************************************************/
46 @interface VLCControls : NSObject
47 {
48     IBOutlet id o_open;
49     IBOutlet id o_main;
50     IBOutlet id o_mi_mute;
51     IBOutlet id o_volumeslider;
52     int i_ff;
53 }
54
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;
61
62 - (IBAction)prev:(id)sender;
63 - (IBAction)next:(id)sender;
64 - (IBAction)loop:(id)sender;
65
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;
72
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;
78
79 - (void)setVolumeSlider;
80
81 @end
82
83 /*****************************************************************************
84  * VLCControls implementation 
85  *****************************************************************************/
86 @implementation VLCControls
87
88 - (IBAction)play:(id)sender
89 {
90     intf_thread_t * p_intf = [NSApp getIntf];
91     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
92                                                        FIND_ANYWHERE );
93     if( p_playlist == NULL )
94     {
95         return;
96     }
97
98     if ( p_intf->p_sys->p_input != NULL && p_intf->p_sys->p_input->stream.control.i_status != PAUSE_S)
99     {
100         input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PAUSE );
101         vlc_object_release( p_playlist );
102     }
103     else
104     {
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 )
108         {
109             vlc_mutex_unlock( &p_playlist->object_lock );
110             playlist_Play( p_playlist );
111             vlc_object_release( p_playlist );
112         }
113         else
114         {
115             vlc_mutex_unlock( &p_playlist->object_lock );
116             vlc_object_release( p_playlist );
117
118             [o_open openFile: nil];
119         }
120     }
121 }
122
123 - (IBAction)stop:(id)sender
124 {
125     intf_thread_t * p_intf = [NSApp getIntf];
126     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
127                                                        FIND_ANYWHERE );
128     if( p_playlist == NULL )
129     {
130         return;
131     }
132
133     playlist_Stop( p_playlist );
134     vlc_object_release( p_playlist );
135     p_intf->p_sys->b_stopping = 1;
136 }
137
138 - (IBAction)faster:(id)sender
139 {
140     intf_thread_t * p_intf = [NSApp getIntf];
141
142     if( p_intf->p_sys->p_input == NULL )
143     {
144         return;
145     }
146
147     input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_FASTER );
148 }
149
150 - (IBAction)slower:(id)sender
151 {
152     intf_thread_t * p_intf = [NSApp getIntf];
153
154     if( p_intf->p_sys->p_input == NULL )
155     {
156         return;
157     }
158
159     input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_SLOWER );
160 }
161
162 - (IBAction)slowMotion:(id)sender
163 {
164     i_ff++;
165     switch( [[NSApp currentEvent] type] )
166     {
167         case NSPeriodic:
168             if ( i_ff == 1 )
169             {
170                 [self slower:sender];
171             }
172             break;
173     
174         case NSLeftMouseUp:
175             if ( i_ff > 1 )
176             {
177                 intf_thread_t * p_intf = [NSApp getIntf];
178                 
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)
182                 {
183                     input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PAUSE );
184                 }
185             }
186             i_ff = 0;
187             break;
188
189         default:
190             break;
191     }
192 }
193
194 - (IBAction)fastForward:(id)sender
195 {
196     playlist_t * p_playlist = vlc_object_find( [NSApp getIntf], VLC_OBJECT_PLAYLIST,
197                                                        FIND_ANYWHERE );
198                                                        
199     i_ff++;
200     switch( [[NSApp currentEvent] type] )
201     {
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)
207          */
208         case NSPeriodic:
209             if ( i_ff == 1 )
210             {
211                 [self faster:self];
212             }
213             else if ( i_ff == 5 )
214             {
215                 [self faster:self];
216             }
217             else if ( i_ff == 15 )
218             {
219                 [self faster:self];
220             }
221             break;
222
223         case NSLeftMouseUp:
224             i_ff = 0;
225             
226             vlc_mutex_lock( &p_playlist->object_lock );
227             int i_playlist_size =  p_playlist->i_size ;
228             vlc_mutex_unlock( &p_playlist->object_lock );
229             if( i_playlist_size )
230             {
231                 playlist_Play( p_playlist );
232             }
233             break;
234
235         default:
236             break;
237     }
238     vlc_object_release( p_playlist );
239 }
240
241 - (IBAction)prev:(id)sender
242 {
243     intf_thread_t * p_intf = [NSApp getIntf];
244     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
245                                                        FIND_ANYWHERE );
246     if( p_playlist == NULL )
247     {
248         return;
249     }
250
251     playlist_Prev( p_playlist );
252     vlc_object_release( p_playlist );
253 }
254
255 - (IBAction)next:(id)sender
256 {
257     intf_thread_t * p_intf = [NSApp getIntf];
258     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
259                                                        FIND_ANYWHERE );
260     if( p_playlist == NULL )
261     {
262         return;
263     }
264
265     playlist_Next( p_playlist );
266     vlc_object_release( p_playlist );
267 }
268
269 - (IBAction)loop:(id)sender
270 {
271     NSMenuItem * o_mi = (NSMenuItem *)sender;
272     intf_thread_t * p_intf = [NSApp getIntf];
273     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
274                                                        FIND_ANYWHERE );
275     if( p_playlist == NULL )
276     {
277         return;
278     }
279
280     if( p_intf->p_sys->b_loop )
281     {
282         [o_mi setState: NSOffState];
283         config_PutInt( p_playlist, "loop", 0 );
284     }
285     else
286     {
287         [o_mi setState: NSOnState];
288         config_PutInt( p_playlist, "loop", 1 );
289     }
290
291     p_intf->p_sys->b_loop = !p_intf->p_sys->b_loop;
292
293     vlc_object_release( p_playlist );
294 }
295
296 - (IBAction)volumeUp:(id)sender
297 {
298     intf_thread_t * p_intf = [NSApp getIntf];
299     aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
300                                                 FIND_ANYWHERE );
301     if ( p_aout != NULL )
302     {
303         if (p_intf->p_sys->b_mute)
304         {
305             [self mute:o_mi_mute];
306         }
307         aout_VolumeUp( p_aout, 1, NULL );
308         vlc_object_release( (vlc_object_t *)p_aout );
309     }
310     [self setVolumeSlider];
311 }
312
313 - (IBAction)volumeDown:(id)sender
314 {
315     intf_thread_t * p_intf = [NSApp getIntf];
316     aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
317                                                 FIND_ANYWHERE );
318     if ( p_aout != NULL )
319     {
320         if (p_intf->p_sys->b_mute)
321         {
322             [self mute:o_mi_mute];
323         }
324         aout_VolumeDown( p_aout, 1, NULL );
325         vlc_object_release( (vlc_object_t *)p_aout );
326     }
327     [self setVolumeSlider];
328 }
329
330 - (IBAction)mute:(id)sender
331 {
332     intf_thread_t * p_intf = [NSApp getIntf];
333     aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
334                                                 FIND_ANYWHERE );
335     audio_volume_t i_volume;
336
337     if ( p_aout != NULL )
338     {
339         aout_VolumeMute( p_aout, &i_volume );
340         vlc_object_release( (vlc_object_t *)p_aout );
341     }
342
343     p_intf->p_sys->b_mute = (i_volume == 0);
344     [o_mi_mute setState: p_intf->p_sys->b_mute ? NSOnState : NSOffState];
345     [o_volumeslider setEnabled: p_intf->p_sys->b_mute ? FALSE : TRUE];
346     [self setVolumeSlider];
347 }
348
349 - (IBAction)volumeSliderUpdate:(id)sender
350 {
351     intf_thread_t * p_intf = [NSApp getIntf];
352     aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
353                                                 FIND_ANYWHERE );
354     audio_volume_t i_volume;
355
356     switch( [[NSApp currentEvent] type] )
357     {
358         case NSLeftMouseDragged:
359             if ( p_aout != NULL )
360             {
361                 i_volume = (int) [sender floatValue];
362                 aout_VolumeSet( p_aout, i_volume * AOUT_VOLUME_STEP);
363                 vlc_object_release( (vlc_object_t *)p_aout );
364             }
365             break;
366
367         default:
368             if ( p_aout != NULL ) vlc_object_release( (vlc_object_t *)p_aout );
369             break;
370     }
371 }
372
373 - (void)setVolumeSlider
374 {
375     intf_thread_t * p_intf = [NSApp getIntf];
376     aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
377                                                 FIND_ANYWHERE );
378     audio_volume_t i_volume;
379     
380     if ( p_aout != NULL )
381     {
382         aout_VolumeGet( p_aout, &i_volume );
383         vlc_object_release( (vlc_object_t *)p_aout );
384         [o_volumeslider setFloatValue: (float) (i_volume / AOUT_VOLUME_STEP)]; 
385     }
386     else
387     {
388         [o_volumeslider setFloatValue: config_GetInt( p_intf, "volume" )];
389     }
390 }
391
392 - (IBAction)fullscreen:(id)sender
393 {
394     id o_window = [NSApp keyWindow];
395     NSArray *o_windows = [NSApp windows];
396     NSEnumerator *o_enumerator = [o_windows objectEnumerator];
397     
398     while ((o_window = [o_enumerator nextObject]))
399     {
400         if( [[o_window className] isEqualToString: @"VLCWindow"] )
401         {
402             [o_window toggleFullscreen];
403         }
404     }
405 }
406
407 - (IBAction)deinterlace:(id)sender
408 {
409     intf_thread_t * p_intf = [NSApp getIntf];
410     BOOL bEnable = [sender state] == NSOffState;
411
412     if( bEnable )
413     {
414         config_PutPsz( p_intf, "filter", "deinterlace" );
415         config_PutPsz( p_intf, "deinterlace-mode", 
416                        [[sender title] lossyCString] );
417     }
418     else
419     {
420         config_PutPsz( p_intf, "filter", NULL );
421     }
422 }
423
424 - (IBAction)toggleProgram:(id)sender
425 {
426     NSMenuItem * o_mi = (NSMenuItem *)sender;
427     intf_thread_t * p_intf = [NSApp getIntf];
428
429     if( [o_mi state] == NSOffState )
430     {
431         u16 i_program_id = [o_mi tag];
432
433         input_ChangeProgram( p_intf->p_sys->p_input, i_program_id );
434         input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );
435     }
436 }
437
438 - (IBAction)toggleTitle:(id)sender
439 {
440     NSMenuItem * o_mi = (NSMenuItem *)sender;
441     intf_thread_t * p_intf = [NSApp getIntf];
442
443     if( [o_mi state] == NSOffState )
444     {
445         int i_title = [o_mi tag];
446
447 #define p_input p_intf->p_sys->p_input
448         input_ChangeArea( p_input, p_input->stream.pp_areas[i_title] );
449         input_SetStatus( p_input, INPUT_STATUS_PLAY );
450 #undef p_input
451     }
452 }
453
454 - (IBAction)toggleChapter:(id)sender
455 {
456     NSMenuItem * o_mi = (NSMenuItem *)sender;
457     intf_thread_t * p_intf = [NSApp getIntf];
458
459     if( [o_mi state] == NSOffState )
460     {
461         int i_chapter = [o_mi tag];
462
463 #define p_input p_intf->p_sys->p_input
464         p_input->stream.p_selected_area->i_part = i_chapter;
465         input_ChangeArea( p_input, p_input->stream.p_selected_area );
466         input_SetStatus( p_input, INPUT_STATUS_PLAY );
467 #undef p_input
468     }
469 }
470
471 - (IBAction)toggleLanguage:(id)sender
472 {
473     NSMenuItem * o_mi = (NSMenuItem *)sender;
474     intf_thread_t * p_intf = [NSApp getIntf];
475
476 #define p_input p_intf->p_sys->p_input
477
478     if( !p_intf->p_sys->b_audio_update )
479     {
480         NSValue * o_value = [o_mi representedObject];
481         es_descriptor_t * p_es = [o_value pointerValue];
482
483         if( [o_mi state] == NSOnState )
484         {
485             /* we just have one ES to disable */
486             input_ToggleES( p_input, p_es, 0 );
487         }
488         else
489         {
490             unsigned int i;
491             int i_cat = [o_mi tag];
492
493             vlc_mutex_lock( &p_input->stream.stream_lock );
494
495 #define ES p_input->stream.pp_selected_es[i]
496
497             /* unselect the selected ES in the same class */
498             for( i = 0; i < p_input->stream.i_selected_es_number; i++ )
499             {
500                 if( ES->i_cat == i_cat )
501                 {
502                     vlc_mutex_unlock( &p_input->stream.stream_lock );
503                     input_ToggleES( p_input, ES, 0 );
504                     vlc_mutex_lock( &p_input->stream.stream_lock );
505                     break;
506                 }
507             }
508
509 #undef ES
510
511             vlc_mutex_unlock( &p_input->stream.stream_lock );
512
513             input_ToggleES( p_input, p_es, 1 );
514         }
515     }
516
517 #undef p_input
518 }
519
520 - (IBAction)toggleVar:(id)sender
521 {
522     NSMenuItem * o_mi = (NSMenuItem *)sender;
523
524     if( [o_mi state] == NSOffState )
525     {
526         const char * psz_variable = (const char *)[o_mi tag];
527         const char * psz_value = [[o_mi title] cString];
528         vlc_object_t * p_object = (vlc_object_t *)
529             [[o_mi representedObject] pointerValue];
530         vlc_value_t val;
531         /* psz_string sucks */
532         val.psz_string = (char *)psz_value;
533
534         if ( var_Set( p_object, psz_variable, val ) < 0 )
535         {
536             msg_Warn( p_object, "cannot set variable (%s)", psz_value );
537         }
538     }
539 }
540
541 @end
542
543 @implementation VLCControls (NSMenuValidation)
544  
545 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
546 {
547     BOOL bEnabled = TRUE;
548     NSMenu * o_menu = [o_mi menu];
549     intf_thread_t * p_intf = [NSApp getIntf];
550
551     if( [[o_mi title] isEqualToString: _NS("Faster")] ||
552         [[o_mi title] isEqualToString: _NS("Slower")] )
553     {
554         if( p_intf->p_sys->p_input != NULL )
555         {
556 #define p_input p_intf->p_sys->p_input
557             vlc_mutex_lock( &p_input->stream.stream_lock );
558             bEnabled = p_input->stream.b_pace_control;
559             vlc_mutex_unlock( &p_input->stream.stream_lock );
560 #undef p_input
561         }
562         else
563         {
564             bEnabled = FALSE;
565         }
566     }
567     else if( [[o_mi title] isEqualToString: _NS("Stop")] )
568     {
569         bEnabled = p_intf->p_sys->p_input != NULL;
570     }
571     else if( [[o_mi title] isEqualToString: _NS("Previous")] ||
572              [[o_mi title] isEqualToString: _NS("Next")] )
573     {
574         playlist_t * p_playlist = vlc_object_find( p_intf, 
575                                                    VLC_OBJECT_PLAYLIST,
576                                                    FIND_ANYWHERE );
577         if( p_playlist == NULL )
578         {
579             bEnabled = FALSE;
580         }
581         else
582         {
583             vlc_mutex_lock( &p_playlist->object_lock );
584             bEnabled = p_playlist->i_size > 1;
585             vlc_mutex_unlock( &p_playlist->object_lock );
586             vlc_object_release( p_playlist );
587         }
588     }
589     else if( [[o_mi title] isEqualToString: _NS("Fullscreen")] )    
590     {
591         id o_window;
592         NSArray *o_windows = [NSApp windows];
593         NSEnumerator *o_enumerator = [o_windows objectEnumerator];
594         bEnabled = FALSE;
595         
596         while ((o_window = [o_enumerator nextObject]))
597         {
598             if( [[o_window className] isEqualToString: @"VLCWindow"] )
599             {
600                 bEnabled = TRUE;
601                 break;
602             }
603         }
604     }
605     else if( o_menu != nil && 
606              [[o_menu title] isEqualToString: _NS("Deinterlace")] )
607     { 
608         char * psz_filter = config_GetPsz( p_intf, "filter" );
609
610         if( psz_filter != NULL )
611         {
612             free( psz_filter );
613
614             psz_filter = config_GetPsz( p_intf, "deinterlace-mode" );
615         }
616
617         if( psz_filter != NULL )
618         {
619             if( strcmp( psz_filter, [[o_mi title] lossyCString] ) == 0 )
620             {
621                 [o_mi setState: NSOnState]; 
622             }
623             else
624             {
625                 [o_mi setState: NSOffState];
626             }
627
628             free( psz_filter );
629         } 
630         else
631         {
632             [o_mi setState: NSOffState];
633         }
634     } 
635
636     return( bEnabled );
637 }
638
639 @end