ebce2af8d811a244e987156b324d0d14b1224e32
[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.11 2003/01/23 22:25:32 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             vlc_mutex_lock( &p_playlist->object_lock );
226             if( p_playlist->i_size )
227             {
228                 playlist_Play( p_playlist );
229             }
230             vlc_mutex_unlock( &p_playlist->object_lock );
231             break;
232
233         default:
234             break;
235     }
236     vlc_object_release( p_playlist );
237 }
238
239 - (IBAction)prev:(id)sender
240 {
241     intf_thread_t * p_intf = [NSApp getIntf];
242     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
243                                                        FIND_ANYWHERE );
244     if( p_playlist == NULL )
245     {
246         return;
247     }
248
249     playlist_Prev( p_playlist );
250     vlc_object_release( p_playlist );
251 }
252
253 - (IBAction)next:(id)sender
254 {
255     intf_thread_t * p_intf = [NSApp getIntf];
256     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
257                                                        FIND_ANYWHERE );
258     if( p_playlist == NULL )
259     {
260         return;
261     }
262
263     playlist_Next( p_playlist );
264     vlc_object_release( p_playlist );
265 }
266
267 - (IBAction)loop:(id)sender
268 {
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,
272                                                        FIND_ANYWHERE );
273     if( p_playlist == NULL )
274     {
275         return;
276     }
277
278     if( p_intf->p_sys->b_loop )
279     {
280         [o_mi setState: NSOffState];
281         config_PutInt( p_playlist, "loop", 0 );
282     }
283     else
284     {
285         [o_mi setState: NSOnState];
286         config_PutInt( p_playlist, "loop", 1 );
287     }
288
289     p_intf->p_sys->b_loop = !p_intf->p_sys->b_loop;
290
291     vlc_object_release( p_playlist );
292 }
293
294 - (IBAction)volumeUp:(id)sender
295 {
296     intf_thread_t * p_intf = [NSApp getIntf];
297     aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
298                                                 FIND_ANYWHERE );
299     if ( p_aout != NULL )
300     {
301         if (p_intf->p_sys->b_mute)
302         {
303             [self mute:o_mi_mute];
304         }
305         aout_VolumeUp( p_aout, 1, NULL );
306         vlc_object_release( (vlc_object_t *)p_aout );
307     }
308     [self setVolumeSlider];
309 }
310
311 - (IBAction)volumeDown:(id)sender
312 {
313     intf_thread_t * p_intf = [NSApp getIntf];
314     aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
315                                                 FIND_ANYWHERE );
316     if ( p_aout != NULL )
317     {
318         if (p_intf->p_sys->b_mute)
319         {
320             [self mute:o_mi_mute];
321         }
322         aout_VolumeDown( p_aout, 1, NULL );
323         vlc_object_release( (vlc_object_t *)p_aout );
324     }
325     [self setVolumeSlider];
326 }
327
328 - (IBAction)mute:(id)sender
329 {
330     intf_thread_t * p_intf = [NSApp getIntf];
331     aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
332                                                 FIND_ANYWHERE );
333     audio_volume_t i_volume;
334
335     if ( p_aout != NULL )
336     {
337         aout_VolumeMute( p_aout, &i_volume );
338         vlc_object_release( (vlc_object_t *)p_aout );
339     }
340
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];
345 }
346
347 - (IBAction)volumeSliderUpdate:(id)sender
348 {
349     intf_thread_t * p_intf = [NSApp getIntf];
350     aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
351                                                 FIND_ANYWHERE );
352     audio_volume_t i_volume;
353
354     switch( [[NSApp currentEvent] type] )
355     {
356         case NSLeftMouseDragged:
357             if ( p_aout != NULL )
358             {
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 );
362             }
363             break;
364
365         default:
366             if ( p_aout != NULL ) vlc_object_release( (vlc_object_t *)p_aout );
367             break;
368     }
369 }
370
371 - (void)setVolumeSlider
372 {
373     intf_thread_t * p_intf = [NSApp getIntf];
374     aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
375                                                 FIND_ANYWHERE );
376     audio_volume_t i_volume;
377     
378     if ( p_aout != NULL )
379     {
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)]; 
383     }
384     else
385     {
386         [o_volumeslider setFloatValue: config_GetInt( p_intf, "volume" )];
387     }
388 }
389
390 - (IBAction)fullscreen:(id)sender
391 {
392     id o_window = [NSApp keyWindow];
393     NSArray *o_windows = [NSApp windows];
394     NSEnumerator *o_enumerator = [o_windows objectEnumerator];
395     
396     while ((o_window = [o_enumerator nextObject]))
397     {
398         if( [[o_window className] isEqualToString: @"VLCWindow"] )
399         {
400             [o_window toggleFullscreen];
401         }
402     }
403 }
404
405 - (IBAction)deinterlace:(id)sender
406 {
407     intf_thread_t * p_intf = [NSApp getIntf];
408     BOOL bEnable = [sender state] == NSOffState;
409
410     if( bEnable )
411     {
412         config_PutPsz( p_intf, "filter", "deinterlace" );
413         config_PutPsz( p_intf, "deinterlace-mode", 
414                        [[sender title] lossyCString] );
415     }
416     else
417     {
418         config_PutPsz( p_intf, "filter", NULL );
419     }
420 }
421
422 - (IBAction)toggleProgram:(id)sender
423 {
424     NSMenuItem * o_mi = (NSMenuItem *)sender;
425     intf_thread_t * p_intf = [NSApp getIntf];
426
427     if( [o_mi state] == NSOffState )
428     {
429         u16 i_program_id = [o_mi tag];
430
431         input_ChangeProgram( p_intf->p_sys->p_input, i_program_id );
432         input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );
433     }
434 }
435
436 - (IBAction)toggleTitle:(id)sender
437 {
438     NSMenuItem * o_mi = (NSMenuItem *)sender;
439     intf_thread_t * p_intf = [NSApp getIntf];
440
441     if( [o_mi state] == NSOffState )
442     {
443         int i_title = [o_mi tag];
444
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 );
448 #undef p_input
449     }
450 }
451
452 - (IBAction)toggleChapter:(id)sender
453 {
454     NSMenuItem * o_mi = (NSMenuItem *)sender;
455     intf_thread_t * p_intf = [NSApp getIntf];
456
457     if( [o_mi state] == NSOffState )
458     {
459         int i_chapter = [o_mi tag];
460
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 );
465 #undef p_input
466     }
467 }
468
469 - (IBAction)toggleLanguage:(id)sender
470 {
471     NSMenuItem * o_mi = (NSMenuItem *)sender;
472     intf_thread_t * p_intf = [NSApp getIntf];
473
474 #define p_input p_intf->p_sys->p_input
475
476     if( !p_intf->p_sys->b_audio_update )
477     {
478         NSValue * o_value = [o_mi representedObject];
479         es_descriptor_t * p_es = [o_value pointerValue];
480
481         if( [o_mi state] == NSOnState )
482         {
483             /* we just have one ES to disable */
484             input_ToggleES( p_input, p_es, 0 );
485         }
486         else
487         {
488             unsigned int i;
489             int i_cat = [o_mi tag];
490
491             vlc_mutex_lock( &p_input->stream.stream_lock );
492
493 #define ES p_input->stream.pp_selected_es[i]
494
495             /* unselect the selected ES in the same class */
496             for( i = 0; i < p_input->stream.i_selected_es_number; i++ )
497             {
498                 if( ES->i_cat == i_cat )
499                 {
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 );
503                     break;
504                 }
505             }
506
507 #undef ES
508
509             vlc_mutex_unlock( &p_input->stream.stream_lock );
510
511             input_ToggleES( p_input, p_es, 1 );
512         }
513     }
514
515 #undef p_input
516 }
517
518 - (IBAction)toggleVar:(id)sender
519 {
520     NSMenuItem * o_mi = (NSMenuItem *)sender;
521
522     if( [o_mi state] == NSOffState )
523     {
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];
528         vlc_value_t val;
529         /* psz_string sucks */
530         val.psz_string = (char *)psz_value;
531
532         if ( var_Set( p_object, psz_variable, val ) < 0 )
533         {
534             msg_Warn( p_object, "cannot set variable (%s)", psz_value );
535         }
536     }
537 }
538
539 @end
540
541 @implementation VLCControls (NSMenuValidation)
542  
543 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
544 {
545     BOOL bEnabled = TRUE;
546     NSMenu * o_menu = [o_mi menu];
547     intf_thread_t * p_intf = [NSApp getIntf];
548
549     if( [[o_mi title] isEqualToString: _NS("Faster")] ||
550         [[o_mi title] isEqualToString: _NS("Slower")] )
551     {
552         if( p_intf->p_sys->p_input != NULL )
553         {
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 );
558 #undef p_input
559         }
560         else
561         {
562             bEnabled = FALSE;
563         }
564     }
565     else if( [[o_mi title] isEqualToString: _NS("Stop")] )
566     {
567         bEnabled = p_intf->p_sys->p_input != NULL;
568     }
569     else if( [[o_mi title] isEqualToString: _NS("Previous")] ||
570              [[o_mi title] isEqualToString: _NS("Next")] )
571     {
572         playlist_t * p_playlist = vlc_object_find( p_intf, 
573                                                    VLC_OBJECT_PLAYLIST,
574                                                    FIND_ANYWHERE );
575         if( p_playlist == NULL )
576         {
577             bEnabled = FALSE;
578         }
579         else
580         {
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 );
585         }
586     }
587     else if( [[o_mi title] isEqualToString: _NS("Fullscreen")] )    
588     {
589         id o_window;
590         NSArray *o_windows = [NSApp windows];
591         NSEnumerator *o_enumerator = [o_windows objectEnumerator];
592         bEnabled = FALSE;
593         
594         while ((o_window = [o_enumerator nextObject]))
595         {
596             if( [[o_window className] isEqualToString: @"VLCWindow"] )
597             {
598                 bEnabled = TRUE;
599                 break;
600             }
601         }
602     }
603     else if( o_menu != nil && 
604              [[o_menu title] isEqualToString: _NS("Deinterlace")] )
605     { 
606         char * psz_filter = config_GetPsz( p_intf, "filter" );
607
608         if( psz_filter != NULL )
609         {
610             free( psz_filter );
611
612             psz_filter = config_GetPsz( p_intf, "deinterlace-mode" );
613         }
614
615         if( psz_filter != NULL )
616         {
617             if( strcmp( psz_filter, [[o_mi title] lossyCString] ) == 0 )
618             {
619                 [o_mi setState: NSOnState]; 
620             }
621             else
622             {
623                 [o_mi setState: NSOffState];
624             }
625
626             free( psz_filter );
627         } 
628         else
629         {
630             [o_mi setState: NSOffState];
631         }
632     } 
633
634     return( bEnabled );
635 }
636
637 @end