* the volume slider could only be dragged, not clicked. removed some
[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.13 2003/01/24 00:53:41 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     if ( p_aout != NULL )
357     {
358         i_volume = (int) [sender floatValue];
359         aout_VolumeSet( p_aout, i_volume * AOUT_VOLUME_STEP);
360         vlc_object_release( (vlc_object_t *)p_aout );
361     }
362 }
363
364 - (void)setVolumeSlider
365 {
366     intf_thread_t * p_intf = [NSApp getIntf];
367     aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
368                                                 FIND_ANYWHERE );
369     audio_volume_t i_volume;
370     
371     if ( p_aout != NULL )
372     {
373         aout_VolumeGet( p_aout, &i_volume );
374         vlc_object_release( (vlc_object_t *)p_aout );
375         [o_volumeslider setFloatValue: (float) (i_volume / AOUT_VOLUME_STEP)]; 
376     }
377     else
378     {
379         [o_volumeslider setFloatValue: config_GetInt( p_intf, "volume" )];
380     }
381 }
382
383 - (IBAction)fullscreen:(id)sender
384 {
385     id o_window = [NSApp keyWindow];
386     NSArray *o_windows = [NSApp windows];
387     NSEnumerator *o_enumerator = [o_windows objectEnumerator];
388     
389     while ((o_window = [o_enumerator nextObject]))
390     {
391         if( [[o_window className] isEqualToString: @"VLCWindow"] )
392         {
393             [o_window toggleFullscreen];
394         }
395     }
396 }
397
398 - (IBAction)deinterlace:(id)sender
399 {
400     intf_thread_t * p_intf = [NSApp getIntf];
401     BOOL bEnable = [sender state] == NSOffState;
402
403     if( bEnable )
404     {
405         config_PutPsz( p_intf, "filter", "deinterlace" );
406         config_PutPsz( p_intf, "deinterlace-mode", 
407                        [[sender title] lossyCString] );
408     }
409     else
410     {
411         config_PutPsz( p_intf, "filter", NULL );
412     }
413 }
414
415 - (IBAction)toggleProgram:(id)sender
416 {
417     NSMenuItem * o_mi = (NSMenuItem *)sender;
418     intf_thread_t * p_intf = [NSApp getIntf];
419
420     if( [o_mi state] == NSOffState )
421     {
422         u16 i_program_id = [o_mi tag];
423
424         input_ChangeProgram( p_intf->p_sys->p_input, i_program_id );
425         input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );
426     }
427 }
428
429 - (IBAction)toggleTitle:(id)sender
430 {
431     NSMenuItem * o_mi = (NSMenuItem *)sender;
432     intf_thread_t * p_intf = [NSApp getIntf];
433
434     if( [o_mi state] == NSOffState )
435     {
436         int i_title = [o_mi tag];
437
438 #define p_input p_intf->p_sys->p_input
439         input_ChangeArea( p_input, p_input->stream.pp_areas[i_title] );
440         input_SetStatus( p_input, INPUT_STATUS_PLAY );
441 #undef p_input
442     }
443 }
444
445 - (IBAction)toggleChapter:(id)sender
446 {
447     NSMenuItem * o_mi = (NSMenuItem *)sender;
448     intf_thread_t * p_intf = [NSApp getIntf];
449
450     if( [o_mi state] == NSOffState )
451     {
452         int i_chapter = [o_mi tag];
453
454 #define p_input p_intf->p_sys->p_input
455         p_input->stream.p_selected_area->i_part = i_chapter;
456         input_ChangeArea( p_input, p_input->stream.p_selected_area );
457         input_SetStatus( p_input, INPUT_STATUS_PLAY );
458 #undef p_input
459     }
460 }
461
462 - (IBAction)toggleLanguage:(id)sender
463 {
464     NSMenuItem * o_mi = (NSMenuItem *)sender;
465     intf_thread_t * p_intf = [NSApp getIntf];
466
467 #define p_input p_intf->p_sys->p_input
468
469     if( !p_intf->p_sys->b_audio_update )
470     {
471         NSValue * o_value = [o_mi representedObject];
472         es_descriptor_t * p_es = [o_value pointerValue];
473
474         if( [o_mi state] == NSOnState )
475         {
476             /* we just have one ES to disable */
477             input_ToggleES( p_input, p_es, 0 );
478         }
479         else
480         {
481             unsigned int i;
482             int i_cat = [o_mi tag];
483
484             vlc_mutex_lock( &p_input->stream.stream_lock );
485
486 #define ES p_input->stream.pp_selected_es[i]
487
488             /* unselect the selected ES in the same class */
489             for( i = 0; i < p_input->stream.i_selected_es_number; i++ )
490             {
491                 if( ES->i_cat == i_cat )
492                 {
493                     vlc_mutex_unlock( &p_input->stream.stream_lock );
494                     input_ToggleES( p_input, ES, 0 );
495                     vlc_mutex_lock( &p_input->stream.stream_lock );
496                     break;
497                 }
498             }
499
500 #undef ES
501
502             vlc_mutex_unlock( &p_input->stream.stream_lock );
503
504             input_ToggleES( p_input, p_es, 1 );
505         }
506     }
507
508 #undef p_input
509 }
510
511 - (IBAction)toggleVar:(id)sender
512 {
513     NSMenuItem * o_mi = (NSMenuItem *)sender;
514
515     if( [o_mi state] == NSOffState )
516     {
517         const char * psz_variable = (const char *)[o_mi tag];
518         const char * psz_value = [[o_mi title] cString];
519         vlc_object_t * p_object = (vlc_object_t *)
520             [[o_mi representedObject] pointerValue];
521         vlc_value_t val;
522         /* psz_string sucks */
523         val.psz_string = (char *)psz_value;
524
525         if ( var_Set( p_object, psz_variable, val ) < 0 )
526         {
527             msg_Warn( p_object, "cannot set variable (%s)", psz_value );
528         }
529     }
530 }
531
532 @end
533
534 @implementation VLCControls (NSMenuValidation)
535  
536 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
537 {
538     BOOL bEnabled = TRUE;
539     NSMenu * o_menu = [o_mi menu];
540     intf_thread_t * p_intf = [NSApp getIntf];
541
542     if( [[o_mi title] isEqualToString: _NS("Faster")] ||
543         [[o_mi title] isEqualToString: _NS("Slower")] )
544     {
545         if( p_intf->p_sys->p_input != NULL )
546         {
547 #define p_input p_intf->p_sys->p_input
548             vlc_mutex_lock( &p_input->stream.stream_lock );
549             bEnabled = p_input->stream.b_pace_control;
550             vlc_mutex_unlock( &p_input->stream.stream_lock );
551 #undef p_input
552         }
553         else
554         {
555             bEnabled = FALSE;
556         }
557     }
558     else if( [[o_mi title] isEqualToString: _NS("Stop")] )
559     {
560         bEnabled = p_intf->p_sys->p_input != NULL;
561     }
562     else if( [[o_mi title] isEqualToString: _NS("Previous")] ||
563              [[o_mi title] isEqualToString: _NS("Next")] )
564     {
565         playlist_t * p_playlist = vlc_object_find( p_intf, 
566                                                    VLC_OBJECT_PLAYLIST,
567                                                    FIND_ANYWHERE );
568         if( p_playlist == NULL )
569         {
570             bEnabled = FALSE;
571         }
572         else
573         {
574             vlc_mutex_lock( &p_playlist->object_lock );
575             bEnabled = p_playlist->i_size > 1;
576             vlc_mutex_unlock( &p_playlist->object_lock );
577             vlc_object_release( p_playlist );
578         }
579     }
580     else if( [[o_mi title] isEqualToString: _NS("Fullscreen")] )    
581     {
582         id o_window;
583         NSArray *o_windows = [NSApp windows];
584         NSEnumerator *o_enumerator = [o_windows objectEnumerator];
585         bEnabled = FALSE;
586         
587         while ((o_window = [o_enumerator nextObject]))
588         {
589             if( [[o_window className] isEqualToString: @"VLCWindow"] )
590             {
591                 bEnabled = TRUE;
592                 break;
593             }
594         }
595     }
596     else if( o_menu != nil && 
597              [[o_menu title] isEqualToString: _NS("Deinterlace")] )
598     { 
599         char * psz_filter = config_GetPsz( p_intf, "filter" );
600
601         if( psz_filter != NULL )
602         {
603             free( psz_filter );
604
605             psz_filter = config_GetPsz( p_intf, "deinterlace-mode" );
606         }
607
608         if( psz_filter != NULL )
609         {
610             if( strcmp( psz_filter, [[o_mi title] lossyCString] ) == 0 )
611             {
612                 [o_mi setState: NSOnState]; 
613             }
614             else
615             {
616                 [o_mi setState: NSOffState];
617             }
618
619             free( psz_filter );
620         } 
621         else
622         {
623             [o_mi setState: NSOffState];
624         }
625     } 
626
627     return( bEnabled );
628 }
629
630 @end