macosx: invert scrolling direction if the devices inverts its event
[vlc.git] / modules / gui / macosx / controls.m
1 /*****************************************************************************
2  * controls.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2002-2011 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8  *          Christophe Massiot <massiot@via.ecp.fr>
9  *          Derk-Jan Hartman <hartman at videolan dot org>
10  *          Benjamin Pracht <bigben at videolan doit org>
11  *          Felix Paul K├╝hne <fkuehne at videolan dot org>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31 #include <stdlib.h>                                      /* malloc(), free() */
32 #include <sys/param.h>                                    /* for MAXPATHLEN */
33 #include <string.h>
34
35 #import "intf.h"
36 #import "vout.h"
37 #import "open.h"
38 #import "controls.h"
39 #import "playlist.h"
40 #import "CoreInteraction.h"
41 #import <vlc_keys.h>
42
43 #pragma mark -
44 /*****************************************************************************
45  * VLCControls implementation
46  *****************************************************************************/
47 @implementation VLCControls
48
49 - (id)init
50 {
51     [super init];
52     o_fs_panel = [[VLCFSPanel alloc] init];
53     return self;
54 }
55
56 - (void)awakeFromNib
57 {
58     [o_specificTime_mi setTitle: _NS("Jump To Time")];
59     [o_specificTime_cancel_btn setTitle: _NS("Cancel")];
60     [o_specificTime_ok_btn setTitle: _NS("OK")];
61     [o_specificTime_sec_lbl setStringValue: _NS("sec.")];
62     [o_specificTime_goTo_lbl setStringValue: _NS("Jump to time")];
63 }
64
65
66 - (void)dealloc
67 {
68     [[NSNotificationCenter defaultCenter] removeObserver: self];
69
70     [super dealloc];
71 }
72
73 - (IBAction)play:(id)sender
74 {
75     [[VLCCoreInteraction sharedInstance] play];
76 }
77
78 - (IBAction)stop:(id)sender
79 {
80     [[VLCCoreInteraction sharedInstance] stop];
81 }
82
83 - (IBAction)faster:(id)sender
84 {
85     [[VLCCoreInteraction sharedInstance] faster];
86 }
87
88 - (IBAction)slower:(id)sender
89 {
90     [[VLCCoreInteraction sharedInstance] slower];
91 }
92
93 - (IBAction)normalSpeed:(id)sender
94 {
95     [[VLCCoreInteraction sharedInstance] normalSpeed];
96 }
97
98 - (IBAction)prev:(id)sender
99 {
100     [[VLCCoreInteraction sharedInstance] previous];
101 }
102
103 - (IBAction)next:(id)sender
104 {
105     [[VLCCoreInteraction sharedInstance] next];
106 }
107
108 - (IBAction)random:(id)sender
109 {
110     [[VLCCoreInteraction sharedInstance] shuffle];
111
112     vlc_value_t val;
113     playlist_t *p_playlist = pl_Get( VLCIntf );
114     var_Get( p_playlist, "random", &val );
115         if(val.b_bool) {
116         [o_btn_shuffle setImage: [NSImage imageNamed:@"shuffle-on"]];
117         [o_btn_shuffle setAlternateImage: [NSImage imageNamed:@"shuffle-blue-pressed"]];
118     }
119     else
120     {
121         [o_btn_shuffle setImage: [NSImage imageNamed:@"shuffle"]];
122         [o_btn_shuffle setAlternateImage: [NSImage imageNamed:@"shuffle-pressed"]];
123     }
124 }
125
126 - (IBAction)repeatButtonAction:(id)sender
127 {
128     vlc_value_t looping,repeating;
129     intf_thread_t * p_intf = VLCIntf;
130     playlist_t * p_playlist = pl_Get( p_intf );
131
132     var_Get( p_playlist, "repeat", &repeating );
133     var_Get( p_playlist, "loop", &looping );
134
135     if( !repeating.b_bool && !looping.b_bool )
136     {
137         /* was: no repeating at all, switching to Repeat One */
138         [[VLCCoreInteraction sharedInstance] repeatOne];
139
140         [o_btn_repeat setImage: [NSImage imageNamed:@"repeat-one"]];
141         [o_btn_repeat setAlternateImage: [NSImage imageNamed:@"repeat-one-pressed"]];
142     }
143     else if( repeating.b_bool && !looping.b_bool )
144     {
145         /* was: Repeat One, switching to Repeat All */
146         [[VLCCoreInteraction sharedInstance] repeatAll];
147
148         [o_btn_repeat setImage: [NSImage imageNamed:@"repeat-all"]];
149         [o_btn_repeat setAlternateImage: [NSImage imageNamed:@"repeat-all-pressed"]];
150     }
151     else
152     {
153         /* was: Repeat All or bug in VLC, switching to Repeat Off */
154         [[VLCCoreInteraction sharedInstance] repeatOff];
155
156         [o_btn_repeat setImage: [NSImage imageNamed:@"repeat"]];
157         [o_btn_repeat setAlternateImage: [NSImage imageNamed:@"repeat-pressed"]];
158     }
159 }
160
161
162 - (IBAction)repeat:(id)sender
163 {
164     vlc_value_t val;
165     intf_thread_t * p_intf = VLCIntf;
166     playlist_t * p_playlist = pl_Get( p_intf );
167
168     var_Get( p_playlist, "repeat", &val );
169     if(! val.b_bool )
170         [[VLCCoreInteraction sharedInstance] repeatOne];
171     else
172         [[VLCCoreInteraction sharedInstance] repeatOff];
173 }
174
175 - (IBAction)loop:(id)sender
176 {
177     vlc_value_t val;
178     intf_thread_t * p_intf = VLCIntf;
179     playlist_t * p_playlist = pl_Get( p_intf );
180
181     var_Get( p_playlist, "loop", &val );
182     if(! val.b_bool )
183         [[VLCCoreInteraction sharedInstance] repeatAll];
184     else
185         [[VLCCoreInteraction sharedInstance] repeatOff];
186 }
187
188 - (IBAction)quitAfterPlayback:(id)sender
189 {
190     vlc_value_t val;
191     playlist_t * p_playlist = pl_Get( VLCIntf );
192     var_ToggleBool( p_playlist, "play-and-exit" );
193 }
194
195 - (IBAction)forward:(id)sender
196 {
197     [[VLCCoreInteraction sharedInstance] forward];
198 }
199
200 - (IBAction)backward:(id)sender
201 {
202     [[VLCCoreInteraction sharedInstance] backward];
203 }
204
205
206 - (IBAction)volumeUp:(id)sender
207 {
208     [[VLCCoreInteraction sharedInstance] volumeUp];
209 }
210
211 - (IBAction)volumeDown:(id)sender
212 {
213     [[VLCCoreInteraction sharedInstance] volumeDown];
214 }
215
216 - (IBAction)mute:(id)sender
217 {
218     [[VLCCoreInteraction sharedInstance] mute];
219 }
220
221 - (IBAction)volumeSliderUpdated:(id)sender
222 {
223     [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]];
224 }
225
226 - (IBAction)showPosition: (id)sender
227 {
228     input_thread_t * p_input = pl_CurrentInput( VLCIntf );
229     if( p_input != NULL )
230     {
231         vout_thread_t *p_vout = input_GetVout( p_input );
232         if( p_vout != NULL )
233         {
234             var_SetInteger( VLCIntf->p_libvlc, "key-action", ACTIONID_POSITION );
235             vlc_object_release( (vlc_object_t *)p_vout );
236         }
237         vlc_object_release( p_input );
238     }
239 }
240
241 - (IBAction)toogleFullscreen:(id)sender {
242     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
243 }
244
245 - (BOOL) isFullscreen {
246     id o_vout_view = [[VLCCoreInteraction sharedInstance] voutView];
247     if( o_vout_view )
248     {
249         return [o_vout_view isFullscreen];
250     }
251     return NO;
252 }
253
254 - (IBAction)windowAction:(id)sender
255 {
256     NSString *o_title = [sender title];
257     input_thread_t * p_input = pl_CurrentInput( VLCIntf );
258
259     if( p_input != NULL )
260     {
261         vout_thread_t *p_vout = input_GetVout( p_input );
262         if( p_vout != NULL )
263         {
264             id o_vout_view = [[VLCCoreInteraction sharedInstance] voutView];
265             if( o_vout_view )
266             {
267                 if( [o_title isEqualToString: _NS("Half Size") ] )
268                     [o_vout_view scaleWindowWithFactor: 0.5 animate: YES];
269                 else if( [o_title isEqualToString: _NS("Normal Size") ] )
270                     [o_vout_view scaleWindowWithFactor: 1.0 animate: YES];
271                 else if( [o_title isEqualToString: _NS("Double Size") ] )
272                     [o_vout_view scaleWindowWithFactor: 2.0 animate: YES];
273                 else if( [o_title isEqualToString: _NS("Float on Top") ] )
274                     [o_vout_view toggleFloatOnTop];
275                 else if( [o_title isEqualToString: _NS("Fit to Screen") ] )
276                 {
277                     id o_window = [o_vout_view voutWindow];
278                     if( ![o_window isZoomed] )
279                         [o_window performZoom:self];
280                 }
281                 else if( [o_title isEqualToString: _NS("Snapshot") ] )
282                 {
283                     [o_vout_view snapshot];
284                 }
285                 else
286                 {
287                     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
288                 }
289             }
290             vlc_object_release( (vlc_object_t *)p_vout );
291         }
292         else
293         {
294             [[VLCCoreInteraction sharedInstance] toggleFullscreen];
295         }
296         vlc_object_release( p_input );
297     }
298 }
299
300 - (IBAction)telxTransparent:(id)sender
301 {
302     vlc_object_t *p_vbi;
303     p_vbi = (vlc_object_t *) vlc_object_find_name( pl_Get( VLCIntf ), "zvbi" );
304     if( p_vbi )
305     {
306         var_SetBool( p_vbi, "vbi-opaque", [sender state] );
307         [sender setState: ![sender state]];
308         vlc_object_release( p_vbi );
309     }
310 }
311
312 - (IBAction)telxNavLink:(id)sender
313 {
314     intf_thread_t * p_intf = VLCIntf;
315     vlc_object_t *p_vbi;
316     int i_page = 0;
317
318     if( [[sender title] isEqualToString: _NS("Index")] )
319         i_page = 'i' << 16;
320     else if( [[sender title] isEqualToString: _NS("Red")] )
321         i_page = 'r' << 16;
322     else if( [[sender title] isEqualToString: _NS("Green")] )
323         i_page = 'g' << 16;
324     else if( [[sender title] isEqualToString: _NS("Yellow")] )
325         i_page = 'y' << 16;
326     else if( [[sender title] isEqualToString: _NS("Blue")] )
327         i_page = 'b' << 16;
328     if( i_page == 0 ) return;
329
330     p_vbi = (vlc_object_t *) vlc_object_find_name( pl_Get( VLCIntf ), "zvbi" );
331     if( p_vbi )
332     {
333         var_SetInteger( p_vbi, "vbi-page", i_page );
334         vlc_object_release( p_vbi );
335     }
336 }
337
338 - (IBAction)lockVideosAspectRatio:(id)sender
339 {
340     [[VLCCoreInteraction sharedInstance] setAspectRatioLocked: [sender state]];
341     [sender setState: [[VLCCoreInteraction sharedInstance] aspectRatioIsLocked]];
342 }
343
344 - (IBAction)addSubtitleFile:(id)sender
345 {
346     NSInteger i_returnValue = 0;
347     input_thread_t * p_input = pl_CurrentInput( VLCIntf );
348     if( !p_input ) return;
349
350     input_item_t *p_item = input_GetItem( p_input );
351     if( !p_item ) return;
352
353     char *path = input_item_GetURI( p_item );
354     if( !path ) path = strdup( "" );
355
356     NSOpenPanel * openPanel = [NSOpenPanel openPanel];
357     [openPanel setCanChooseFiles: YES];
358     [openPanel setCanChooseDirectories: NO];
359     [openPanel setAllowsMultipleSelection: YES];
360     [openPanel setAllowedFileTypes: [NSArray arrayWithObjects: @"cdg",@"@idx",@"srt",@"sub",@"utf",@"ass",@"ssa",@"aqt",@"jss",@"psb",@"rt",@"smi",@"txt",@"smil", nil]];
361     [openPanel setDirectoryURL:[NSURL fileURLWithPath:[[NSString stringWithUTF8String:path] stringByExpandingTildeInPath]]];
362     i_returnValue = [openPanel runModal];
363     free( path );
364
365     if( i_returnValue == NSOKButton )
366     {
367         NSUInteger c = 0;
368         if( !p_input ) return;
369
370         c = [[openPanel URLs] count];
371
372         for (int i = 0; i < c ; i++)
373         {
374             msg_Dbg( VLCIntf, "loading subs from %s", [[[[openPanel URLs] objectAtIndex: i] path] UTF8String] );
375             if( input_AddSubtitle( p_input, [[[[openPanel URLs] objectAtIndex: i] path] UTF8String], TRUE ) )
376                 msg_Warn( VLCIntf, "unable to load subtitles from '%s'",
377                          [[[[openPanel URLs] objectAtIndex: i] path] UTF8String] );
378         }
379     }
380 }
381
382 - (id)fspanel
383 {
384     if( o_fs_panel )
385         return o_fs_panel;
386     else
387     {
388         msg_Err( VLCIntf, "FSPanel is nil" );
389         return NULL;
390     }
391 }
392
393 - (void)scrollWheel:(NSEvent *)theEvent
394 {
395     intf_thread_t * p_intf = VLCIntf;
396     BOOL b_invertedEventFromDevice = NO;
397     if ([theEvent respondsToSelector:@selector(isDirectionInvertedFromDevice)])
398     {
399         if ([theEvent isDirectionInvertedFromDevice])
400             b_invertedEventFromDevice = YES;
401     }
402
403     float f_yabsvalue = [theEvent deltaY] > 0.0f ? [theEvent deltaY] : -[theEvent deltaY];
404     float f_xabsvalue = [theEvent deltaX] > 0.0f ? [theEvent deltaX] : -[theEvent deltaX];
405     int i, i_yvlckey, i_xvlckey;
406
407     if (b_invertedEventFromDevice)
408     {
409         if ([theEvent deltaY] > 0.0f)
410             i_yvlckey = KEY_MOUSEWHEELDOWN;
411         else
412             i_yvlckey = KEY_MOUSEWHEELUP;
413
414         if ([theEvent deltaX] > 0.0f)
415             i_xvlckey = KEY_MOUSEWHEELRIGHT;
416         else
417             i_xvlckey = KEY_MOUSEWHEELLEFT;
418     }
419     else
420     {
421         if ([theEvent deltaY] < 0.0f)
422             i_yvlckey = KEY_MOUSEWHEELDOWN;
423         else
424             i_yvlckey = KEY_MOUSEWHEELUP;
425
426         if ([theEvent deltaX] < 0.0f)
427             i_xvlckey = KEY_MOUSEWHEELRIGHT;
428         else
429             i_xvlckey = KEY_MOUSEWHEELLEFT;
430     }
431
432     /* Send multiple key event, depending on the intensity of the event */
433     for (i = 0; i < (int)(f_yabsvalue/4.+1.) && f_yabsvalue > 0.05 ; i++)
434         var_SetInteger( p_intf->p_libvlc, "key-pressed", i_yvlckey );
435
436     /* Prioritize Y event (sound volume) over X event */
437     if (f_yabsvalue < 0.05)
438     {
439         for (i = 0; i < (int)(f_xabsvalue/6.+1.) && f_xabsvalue > 0.05; i++)
440          var_SetInteger( p_intf->p_libvlc, "key-pressed", i_xvlckey );
441     }
442 }
443
444 - (BOOL)keyEvent:(NSEvent *)o_event
445 {
446     BOOL eventHandled = NO;
447     unichar key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
448
449     if( key )
450     {
451         input_thread_t * p_input = pl_CurrentInput( VLCIntf );
452         if( p_input != NULL )
453         {
454             vout_thread_t *p_vout = input_GetVout( p_input );
455
456             if( p_vout != NULL )
457             {
458                 /* Escape */
459                 if( key == (unichar) 0x1b )
460                 {
461                     id o_vout_view = [[VLCCoreInteraction sharedInstance] voutView];
462                     if( o_vout_view && [o_vout_view isFullscreen] )
463                     {
464                         [o_vout_view toggleFullscreen];
465                         eventHandled = YES;
466                     }
467                 }
468                 else if( key == ' ' )
469                 {
470                     [self play:self];
471                     eventHandled = YES;
472                 }
473                 vlc_object_release( (vlc_object_t *)p_vout );
474             }
475             vlc_object_release( p_input );
476         }
477     }
478     return eventHandled;
479 }
480
481 - (IBAction)goToSpecificTime:(id)sender
482 {
483     if( sender == o_specificTime_cancel_btn )
484     {
485         [NSApp endSheet: o_specificTime_win];
486         [o_specificTime_win close];
487     }
488     else if( sender == o_specificTime_ok_btn )
489     {
490         input_thread_t * p_input = pl_CurrentInput( VLCIntf );
491         if( p_input )
492         {
493             int64_t timeInSec = 0;
494             NSString * fieldContent = [o_specificTime_enter_fld stringValue];
495             if( [[fieldContent componentsSeparatedByString: @":"] count] > 1 &&
496                 [[fieldContent componentsSeparatedByString: @":"] count] <= 3 )
497             {
498                 NSArray * ourTempArray = \
499                     [fieldContent componentsSeparatedByString: @":"];
500
501                 if( [[fieldContent componentsSeparatedByString: @":"] count] == 3 )
502                 {
503                     timeInSec += ([[ourTempArray objectAtIndex: 0] intValue] * 3600); //h
504                     timeInSec += ([[ourTempArray objectAtIndex: 1] intValue] * 60); //m
505                     timeInSec += [[ourTempArray objectAtIndex: 2] intValue];        //s
506                 }
507                 else
508                 {
509                     timeInSec += ([[ourTempArray objectAtIndex: 0] intValue] * 60); //m
510                     timeInSec += [[ourTempArray objectAtIndex: 1] intValue]; //s
511                 }
512             }
513             else
514                 timeInSec = [fieldContent intValue];
515
516             input_Control( p_input, INPUT_SET_TIME, (int64_t)(timeInSec * 1000000));
517             vlc_object_release( p_input );
518         }
519
520         [NSApp endSheet: o_specificTime_win];
521         [o_specificTime_win close];
522     }
523     else
524     {
525         input_thread_t * p_input = pl_CurrentInput( VLCIntf );
526         if( p_input )
527         {
528             /* we can obviously only do that if an input is available */
529             vlc_value_t pos, length;
530             var_Get( p_input, "time", &pos );
531             [o_specificTime_enter_fld setIntValue: (pos.i_time / 1000000)];
532             var_Get( p_input, "length", &length );
533             [o_specificTime_stepper setMaxValue: (length.i_time / 1000000)];
534
535             [NSApp beginSheet: o_specificTime_win modalForWindow: \
536                 [NSApp mainWindow] modalDelegate: self didEndSelector: nil \
537                 contextInfo: nil];
538             [o_specificTime_win makeKeyWindow];
539             vlc_object_release( p_input );
540         }
541     }
542 }
543
544 @end