77fca87ffc5adcccf6365295edee1981beac5528
[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.10 2003/01/22 01:48:06 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)fastForward:(id)sender;
60
61 - (IBAction)prev:(id)sender;
62 - (IBAction)next:(id)sender;
63 - (IBAction)loop:(id)sender;
64
65 - (IBAction)volumeUp:(id)sender;
66 - (IBAction)volumeDown:(id)sender;
67 - (IBAction)mute:(id)sender;
68 - (IBAction)volumeSliderUpdate:(id)sender;
69 - (IBAction)fullscreen:(id)sender;
70 - (IBAction)deinterlace:(id)sender;
71
72 - (IBAction)toggleProgram:(id)sender;
73 - (IBAction)toggleTitle:(id)sender;
74 - (IBAction)toggleChapter:(id)sender;
75 - (IBAction)toggleLanguage:(id)sender;
76 - (IBAction)toggleVar:(id)sender;
77
78 - (void)setVolumeSlider;
79
80 @end
81
82 /*****************************************************************************
83  * VLCControls implementation 
84  *****************************************************************************/
85 @implementation VLCControls
86
87 - (IBAction)play:(id)sender
88 {
89     intf_thread_t * p_intf = [NSApp getIntf];
90     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
91                                                        FIND_ANYWHERE );
92     if( p_playlist == NULL )
93     {
94         return;
95     }
96
97     if ( p_intf->p_sys->p_input != NULL && p_intf->p_sys->p_input->stream.control.i_status != PAUSE_S)
98     {
99         input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PAUSE );
100         vlc_object_release( p_playlist );
101     }
102     else
103     {
104         /* If the playlist is empty, open a file requester instead */
105         vlc_mutex_lock( &p_playlist->object_lock );
106         if( p_playlist->i_size )
107         {
108             vlc_mutex_unlock( &p_playlist->object_lock );
109             playlist_Play( p_playlist );
110             vlc_object_release( p_playlist );
111         }
112         else
113         {
114             vlc_mutex_unlock( &p_playlist->object_lock );
115             vlc_object_release( p_playlist );
116
117             [o_open openFile: nil];
118         }
119     }
120 }
121
122 - (IBAction)stop:(id)sender
123 {
124     intf_thread_t * p_intf = [NSApp getIntf];
125     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
126                                                        FIND_ANYWHERE );
127     if( p_playlist == NULL )
128     {
129         return;
130     }
131
132     playlist_Stop( p_playlist );
133     vlc_object_release( p_playlist );
134     p_intf->p_sys->b_stopping = 1;
135 }
136
137 - (IBAction)faster:(id)sender
138 {
139     intf_thread_t * p_intf = [NSApp getIntf];
140
141     if( p_intf->p_sys->p_input == NULL )
142     {
143         return;
144     }
145
146     input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_FASTER );
147 }
148
149 - (IBAction)slower:(id)sender
150 {
151     intf_thread_t * p_intf = [NSApp getIntf];
152
153     if( p_intf->p_sys->p_input == NULL )
154     {
155         return;
156     }
157
158     input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_SLOWER );
159 }
160
161 - (IBAction)fastForward:(id)sender
162 {
163     playlist_t * p_playlist = vlc_object_find( [NSApp getIntf], VLC_OBJECT_PLAYLIST,
164                                                        FIND_ANYWHERE );
165                                                        
166     i_ff++;
167     switch( [[NSApp currentEvent] type] )
168     {
169         /* A button does not send a NSLeftMouseDown unfortunately.
170          * Therefore we need to count. I know, it is ugly. We could have used
171          * a bool as well, but now we can also accellerate after a certain period.
172          * Currently this method is called every second if the button is pressed.
173          * You can set this value in intf.m (hartman)
174          */
175         case NSPeriodic:
176             if (i_ff == 1)
177             {
178                 [self faster:self];
179             }
180             else if ( i_ff == 5 )
181             {
182                 [self faster:self];
183             }
184             else if ( i_ff == 15 )
185             {
186                 [self faster:self];
187             }
188             break;
189
190         case NSLeftMouseUp:
191             i_ff = 0;
192             vlc_mutex_lock( &p_playlist->object_lock );
193             if( p_playlist->i_size )
194             {
195                 vlc_mutex_unlock( &p_playlist->object_lock );
196                 playlist_Play( p_playlist );
197             }
198             break;
199
200         default:
201             break;
202     }
203     vlc_object_release( p_playlist );
204 }
205
206 - (IBAction)prev:(id)sender
207 {
208     intf_thread_t * p_intf = [NSApp getIntf];
209     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
210                                                        FIND_ANYWHERE );
211     if( p_playlist == NULL )
212     {
213         return;
214     }
215
216     playlist_Prev( p_playlist );
217     vlc_object_release( p_playlist );
218 }
219
220 - (IBAction)next:(id)sender
221 {
222     intf_thread_t * p_intf = [NSApp getIntf];
223     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
224                                                        FIND_ANYWHERE );
225     if( p_playlist == NULL )
226     {
227         return;
228     }
229
230     playlist_Next( p_playlist );
231     vlc_object_release( p_playlist );
232 }
233
234 - (IBAction)loop:(id)sender
235 {
236     NSMenuItem * o_mi = (NSMenuItem *)sender;
237     intf_thread_t * p_intf = [NSApp getIntf];
238     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
239                                                        FIND_ANYWHERE );
240     if( p_playlist == NULL )
241     {
242         return;
243     }
244
245     if( p_intf->p_sys->b_loop )
246     {
247         [o_mi setState: NSOffState];
248         config_PutInt( p_playlist, "loop", 0 );
249     }
250     else
251     {
252         [o_mi setState: NSOnState];
253         config_PutInt( p_playlist, "loop", 1 );
254     }
255
256     p_intf->p_sys->b_loop = !p_intf->p_sys->b_loop;
257
258     vlc_object_release( p_playlist );
259 }
260
261 - (IBAction)volumeUp:(id)sender
262 {
263     intf_thread_t * p_intf = [NSApp getIntf];
264     aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
265                                                 FIND_ANYWHERE );
266     if ( p_aout != NULL )
267     {
268         if (p_intf->p_sys->b_mute)
269         {
270             [self mute:o_mi_mute];
271         }
272         aout_VolumeUp( p_aout, 1, NULL );
273         vlc_object_release( (vlc_object_t *)p_aout );
274     }
275     [self setVolumeSlider];
276 }
277
278 - (IBAction)volumeDown:(id)sender
279 {
280     intf_thread_t * p_intf = [NSApp getIntf];
281     aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
282                                                 FIND_ANYWHERE );
283     if ( p_aout != NULL )
284     {
285         if (p_intf->p_sys->b_mute)
286         {
287             [self mute:o_mi_mute];
288         }
289         aout_VolumeDown( p_aout, 1, NULL );
290         vlc_object_release( (vlc_object_t *)p_aout );
291     }
292     [self setVolumeSlider];
293 }
294
295 - (IBAction)mute:(id)sender
296 {
297     intf_thread_t * p_intf = [NSApp getIntf];
298     aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
299                                                 FIND_ANYWHERE );
300     audio_volume_t i_volume;
301
302     if ( p_aout != NULL )
303     {
304         aout_VolumeMute( p_aout, &i_volume );
305         vlc_object_release( (vlc_object_t *)p_aout );
306     }
307
308     p_intf->p_sys->b_mute = (i_volume == 0);
309     [o_mi_mute setState: p_intf->p_sys->b_mute ? NSOnState : NSOffState];
310     [o_volumeslider setEnabled: p_intf->p_sys->b_mute ? FALSE : TRUE];
311     [self setVolumeSlider];
312 }
313
314 - (IBAction)volumeSliderUpdate:(id)sender
315 {
316     intf_thread_t * p_intf = [NSApp getIntf];
317     aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
318                                                 FIND_ANYWHERE );
319     audio_volume_t i_volume;
320
321     switch( [[NSApp currentEvent] type] )
322     {
323         case NSLeftMouseDragged:
324             if ( p_aout != NULL )
325             {
326                 i_volume = (int) [sender floatValue];
327                 aout_VolumeSet( p_aout, i_volume * AOUT_VOLUME_STEP);
328                 vlc_object_release( (vlc_object_t *)p_aout );
329             }
330             break;
331
332         default:
333             if ( p_aout != NULL ) vlc_object_release( (vlc_object_t *)p_aout );
334             break;
335     }
336 }
337
338 - (void)setVolumeSlider
339 {
340     intf_thread_t * p_intf = [NSApp getIntf];
341     aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
342                                                 FIND_ANYWHERE );
343     audio_volume_t i_volume;
344     
345     if ( p_aout != NULL )
346     {
347         aout_VolumeGet( p_aout, &i_volume );
348         vlc_object_release( (vlc_object_t *)p_aout );
349         [o_volumeslider setFloatValue: (float) (i_volume / AOUT_VOLUME_STEP)]; 
350     }
351     else
352     {
353         [o_volumeslider setFloatValue: config_GetInt( p_intf, "volume" )];
354     }
355 }
356
357 - (IBAction)fullscreen:(id)sender
358 {
359     id o_window = [NSApp keyWindow];
360     NSArray *o_windows = [NSApp windows];
361     NSEnumerator *o_enumerator = [o_windows objectEnumerator];
362     
363     while ((o_window = [o_enumerator nextObject]))
364     {
365         if( [[o_window className] isEqualToString: @"VLCWindow"] )
366         {
367             [o_window toggleFullscreen];
368         }
369     }
370 }
371
372 - (IBAction)deinterlace:(id)sender
373 {
374     intf_thread_t * p_intf = [NSApp getIntf];
375     BOOL bEnable = [sender state] == NSOffState;
376
377     if( bEnable )
378     {
379         config_PutPsz( p_intf, "filter", "deinterlace" );
380         config_PutPsz( p_intf, "deinterlace-mode", 
381                        [[sender title] lossyCString] );
382     }
383     else
384     {
385         config_PutPsz( p_intf, "filter", NULL );
386     }
387 }
388
389 - (IBAction)toggleProgram:(id)sender
390 {
391     NSMenuItem * o_mi = (NSMenuItem *)sender;
392     intf_thread_t * p_intf = [NSApp getIntf];
393
394     if( [o_mi state] == NSOffState )
395     {
396         u16 i_program_id = [o_mi tag];
397
398         input_ChangeProgram( p_intf->p_sys->p_input, i_program_id );
399         input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );
400     }
401 }
402
403 - (IBAction)toggleTitle:(id)sender
404 {
405     NSMenuItem * o_mi = (NSMenuItem *)sender;
406     intf_thread_t * p_intf = [NSApp getIntf];
407
408     if( [o_mi state] == NSOffState )
409     {
410         int i_title = [o_mi tag];
411
412 #define p_input p_intf->p_sys->p_input
413         input_ChangeArea( p_input, p_input->stream.pp_areas[i_title] );
414         input_SetStatus( p_input, INPUT_STATUS_PLAY );
415 #undef p_input
416     }
417 }
418
419 - (IBAction)toggleChapter:(id)sender
420 {
421     NSMenuItem * o_mi = (NSMenuItem *)sender;
422     intf_thread_t * p_intf = [NSApp getIntf];
423
424     if( [o_mi state] == NSOffState )
425     {
426         int i_chapter = [o_mi tag];
427
428 #define p_input p_intf->p_sys->p_input
429         p_input->stream.p_selected_area->i_part = i_chapter;
430         input_ChangeArea( p_input, p_input->stream.p_selected_area );
431         input_SetStatus( p_input, INPUT_STATUS_PLAY );
432 #undef p_input
433     }
434 }
435
436 - (IBAction)toggleLanguage:(id)sender
437 {
438     NSMenuItem * o_mi = (NSMenuItem *)sender;
439     intf_thread_t * p_intf = [NSApp getIntf];
440
441 #define p_input p_intf->p_sys->p_input
442
443     if( !p_intf->p_sys->b_audio_update )
444     {
445         NSValue * o_value = [o_mi representedObject];
446         es_descriptor_t * p_es = [o_value pointerValue];
447
448         if( [o_mi state] == NSOnState )
449         {
450             /* we just have one ES to disable */
451             input_ToggleES( p_input, p_es, 0 );
452         }
453         else
454         {
455             unsigned int i;
456             int i_cat = [o_mi tag];
457
458             vlc_mutex_lock( &p_input->stream.stream_lock );
459
460 #define ES p_input->stream.pp_selected_es[i]
461
462             /* unselect the selected ES in the same class */
463             for( i = 0; i < p_input->stream.i_selected_es_number; i++ )
464             {
465                 if( ES->i_cat == i_cat )
466                 {
467                     vlc_mutex_unlock( &p_input->stream.stream_lock );
468                     input_ToggleES( p_input, ES, 0 );
469                     vlc_mutex_lock( &p_input->stream.stream_lock );
470                     break;
471                 }
472             }
473
474 #undef ES
475
476             vlc_mutex_unlock( &p_input->stream.stream_lock );
477
478             input_ToggleES( p_input, p_es, 1 );
479         }
480     }
481
482 #undef p_input
483 }
484
485 - (IBAction)toggleVar:(id)sender
486 {
487     NSMenuItem * o_mi = (NSMenuItem *)sender;
488
489     if( [o_mi state] == NSOffState )
490     {
491         const char * psz_variable = (const char *)[o_mi tag];
492         const char * psz_value = [[o_mi title] cString];
493         vlc_object_t * p_object = (vlc_object_t *)
494             [[o_mi representedObject] pointerValue];
495         vlc_value_t val;
496         /* psz_string sucks */
497         val.psz_string = (char *)psz_value;
498
499         if ( var_Set( p_object, psz_variable, val ) < 0 )
500         {
501             msg_Warn( p_object, "cannot set variable (%s)", psz_value );
502         }
503     }
504 }
505
506 @end
507
508 @implementation VLCControls (NSMenuValidation)
509  
510 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
511 {
512     BOOL bEnabled = TRUE;
513     NSMenu * o_menu = [o_mi menu];
514     intf_thread_t * p_intf = [NSApp getIntf];
515
516     if( [[o_mi title] isEqualToString: _NS("Faster")] ||
517         [[o_mi title] isEqualToString: _NS("Slower")] )
518     {
519         if( p_intf->p_sys->p_input != NULL )
520         {
521 #define p_input p_intf->p_sys->p_input
522             vlc_mutex_lock( &p_input->stream.stream_lock );
523             bEnabled = p_input->stream.b_pace_control;
524             vlc_mutex_unlock( &p_input->stream.stream_lock );
525 #undef p_input
526         }
527         else
528         {
529             bEnabled = FALSE;
530         }
531     }
532     else if( [[o_mi title] isEqualToString: _NS("Stop")] )
533     {
534         bEnabled = p_intf->p_sys->p_input != NULL;
535     }
536     else if( [[o_mi title] isEqualToString: _NS("Previous")] ||
537              [[o_mi title] isEqualToString: _NS("Next")] )
538     {
539         playlist_t * p_playlist = vlc_object_find( p_intf, 
540                                                    VLC_OBJECT_PLAYLIST,
541                                                    FIND_ANYWHERE );
542         if( p_playlist == NULL )
543         {
544             bEnabled = FALSE;
545         }
546         else
547         {
548             vlc_mutex_lock( &p_playlist->object_lock );
549             bEnabled = p_playlist->i_size > 1;
550             vlc_mutex_unlock( &p_playlist->object_lock );
551             vlc_object_release( p_playlist );
552         }
553     }
554     else if( [[o_mi title] isEqualToString: _NS("Fullscreen")] )    
555     {
556         id o_window;
557         NSArray *o_windows = [NSApp windows];
558         NSEnumerator *o_enumerator = [o_windows objectEnumerator];
559         bEnabled = FALSE;
560         
561         while ((o_window = [o_enumerator nextObject]))
562         {
563             if( [[o_window className] isEqualToString: @"VLCWindow"] )
564             {
565                 bEnabled = TRUE;
566                 break;
567             }
568         }
569     }
570     else if( o_menu != nil && 
571              [[o_menu title] isEqualToString: _NS("Deinterlace")] )
572     { 
573         char * psz_filter = config_GetPsz( p_intf, "filter" );
574
575         if( psz_filter != NULL )
576         {
577             free( psz_filter );
578
579             psz_filter = config_GetPsz( p_intf, "deinterlace-mode" );
580         }
581
582         if( psz_filter != NULL )
583         {
584             if( strcmp( psz_filter, [[o_mi title] lossyCString] ) == 0 )
585             {
586                 [o_mi setState: NSOnState]; 
587             }
588             else
589             {
590                 [o_mi setState: NSOffState];
591             }
592
593             free( psz_filter );
594         } 
595         else
596         {
597             [o_mi setState: NSOffState];
598         }
599     } 
600
601     return( bEnabled );
602 }
603
604 @end