]> git.sesse.net Git - vlc/blob - modules/gui/macosx/fspanel.m
* forward-port [17501] and [17500]
[vlc] / modules / gui / macosx / fspanel.m
1 /*****************************************************************************\r
2  * fspanel.m: MacOS X full screen panel\r
3  *****************************************************************************\r
4  * Copyright (C) 2006 the VideoLAN team\r
5  * $Id$\r
6  *\r
7  * Authors: J\8er\99me Decoodt <djc at videolan dot org>\r
8  *          Felix K\9fhne <fkuehne at videolan dot org>\r
9  *\r
10  * This program is free software; you can redistribute it and/or modify\r
11  * it under the terms of the GNU General Public License as published by\r
12  * the Free Software Foundation; either version 2 of the License, or\r
13  * (at your option) any later version.\r
14  * \r
15  * This program is distributed in the hope that it will be useful,\r
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
18  * GNU General Public License for more details.\r
19  *\r
20  * You should have received a copy of the GNU General Public License\r
21  * along with this program; if not, write to the Free Software\r
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.\r
23  *****************************************************************************/\r
24 \r
25 /*****************************************************************************\r
26  * Preamble\r
27  *****************************************************************************/\r
28 #import "intf.h"\r
29 #import "controls.h"\r
30 #import "vout.h"\r
31 #import "fspanel.h"\r
32 \r
33 #define KEEP_VISIBLE_AFTER_ACTION 4 /* time in half-sec until this panel will hide again after an user's action */\r
34 \r
35 /*****************************************************************************\r
36  * VLCFSPanel\r
37  *****************************************************************************/\r
38 @implementation VLCFSPanel\r
39 /* We override this initializer so we can set the NSBorderlessWindowMask styleMask, and set a few other important settings */\r
40 - (id)initWithContentRect:(NSRect)contentRect \r
41                 styleMask:(unsigned int)aStyle \r
42                   backing:(NSBackingStoreType)bufferingType \r
43                     defer:(BOOL)flag\r
44 {\r
45     id win=[super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:bufferingType defer:flag];\r
46     [win setOpaque:NO];\r
47     [win setHasShadow: NO];\r
48     [win setBackgroundColor:[NSColor clearColor]];\r
49     /* let the window sit on top of everything else and start out completely transparent */\r
50     [win setLevel:NSFloatingWindowLevel];\r
51     [win setAlphaValue:0.0];\r
52     return win;\r
53 }\r
54 \r
55 - (void)awakeFromNib\r
56 {\r
57     [self setContentView:[[VLCFSPanelView alloc] initWithFrame: [self frame]]];\r
58     BOOL isInside=(NSPointInRect([NSEvent mouseLocation],[self frame]));\r
59     [[self contentView] addTrackingRect:[[self contentView] bounds] owner:self userData:nil assumeInside:isInside];\r
60     if (isInside)\r
61     [self mouseEntered:NULL];\r
62     if (!isInside)\r
63     [self mouseExited:NULL];\r
64 }\r
65 \r
66 /* Windows created with NSBorderlessWindowMask normally can't be key, but we want ours to be */\r
67 - (BOOL)canBecomeKeyWindow\r
68 {\r
69     return YES;\r
70 }\r
71 \r
72 - (BOOL)mouseDownCanMoveWindow\r
73 {\r
74     return YES;\r
75 }\r
76 \r
77 -(void)dealloc\r
78 {\r
79     if( hideAgainTimer )\r
80         [hideAgainTimer release];\r
81     [self setFadeTimer:nil];\r
82     [super dealloc];\r
83 }\r
84 \r
85 - (void)setPlay\r
86 {\r
87     [[self contentView] setPlay];\r
88 }\r
89 \r
90 - (void)setPause\r
91 {\r
92     [[self contentView] setPause];\r
93 }\r
94 \r
95 - (void)setStreamTitle:(NSString *)o_title\r
96 {\r
97     [[self contentView] setStreamTitle: o_title];\r
98 }\r
99 \r
100 - (void)setStreamPos:(float) f_pos andTime:(NSString *)o_time\r
101 {\r
102     [[self contentView] setStreamPos:f_pos andTime: o_time];\r
103 }\r
104 \r
105 - (void)setSeekable:(BOOL) b_seekable\r
106 {\r
107     [[self contentView] setSeekable: b_seekable];\r
108 }\r
109 \r
110 - (void)setVolumeLevel: (float)f_volumeLevel\r
111 {\r
112     [[self contentView] setVolumeLevel: f_volumeLevel];\r
113 }\r
114 \r
115 /* This routine is called repeatedly to fade in the window */\r
116 - (void)focus:(NSTimer *)timer\r
117 {\r
118     if( [self alphaValue] < 1.0 )\r
119         [self setAlphaValue:[self alphaValue]+0.1];\r
120     if( [self alphaValue] >= 1.0 )\r
121     {\r
122         [self setAlphaValue: 1.0];\r
123         [self setFadeTimer:nil];\r
124         if( b_fadeQueued )\r
125         {\r
126             b_fadeQueued=NO;\r
127             [self setFadeTimer:[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(unfocus:) userInfo:NULL repeats:YES]];\r
128         }\r
129     }\r
130 }\r
131 \r
132 /* This routine is called repeatedly to hide the window */\r
133 - (void)unfocus:(NSTimer *)timer\r
134 {\r
135     if( b_keptVisible )\r
136     {\r
137         b_keptVisible = NO;\r
138         b_fadeQueued = NO;\r
139         [[self fadeTimer] release];\r
140         [self setFadeTimer: NULL];\r
141         [self fadeIn];\r
142         return;\r
143     }\r
144     if( [self alphaValue] > 0.0 )\r
145         [self setAlphaValue:[self alphaValue]-0.1];\r
146     if( [self alphaValue] <= 0.1 )\r
147     {\r
148         [self setAlphaValue:0.0];\r
149         [self setFadeTimer:nil];\r
150         if( b_fadeQueued )\r
151         {\r
152             b_fadeQueued=NO;\r
153             [self setFadeTimer:\r
154                 [NSTimer scheduledTimerWithTimeInterval:0.1 \r
155                                                  target:self \r
156                                                selector:@selector(focus:) \r
157                                                userInfo:NULL \r
158                                                 repeats:YES]];\r
159         }\r
160     }\r
161 }\r
162 \r
163 - (void)mouseExited:(NSEvent *)theEvent\r
164 {\r
165     /* give up our focus, so the vout may show us again without letting the user clicking it */\r
166     if( [[[[VLCMain sharedInstance] getControls] getVoutView] isFullscreen] )\r
167         [[[[[VLCMain sharedInstance] getControls] getVoutView] window] makeKeyWindow];\r
168 }\r
169 \r
170 - (void)fadeIn\r
171 {\r
172     if( [self alphaValue] < 1.0 )\r
173     {\r
174         if (![self fadeTimer])\r
175             [self setFadeTimer:[NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(focus:) userInfo:[NSNumber numberWithShort:1] repeats:YES]];\r
176         else if ([[[self fadeTimer] userInfo] shortValue]==0)\r
177             b_fadeQueued=YES;\r
178     }\r
179     [self autoHide];\r
180 }\r
181 \r
182 - (void)fadeOut\r
183 {\r
184     if( ( [self alphaValue] > 0.0 ) )\r
185     {\r
186         if (![self fadeTimer])\r
187             [self setFadeTimer:[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(unfocus:) userInfo:[NSNumber numberWithShort:0] repeats:YES]];\r
188         else if ([[[self fadeTimer] userInfo] shortValue]==1)\r
189             b_fadeQueued=YES;\r
190     }\r
191 }\r
192 \r
193 /* triggers a timer to autoHide us again after some seconds of no activity */\r
194 - (void)autoHide\r
195 {\r
196     /* this will tell the timer to start over again or to start at all */\r
197     b_keptVisible = YES;\r
198     \r
199     /* get us a valid timer */\r
200     if(! b_alreadyCounting )\r
201     {\r
202         hideAgainTimer = [NSTimer scheduledTimerWithTimeInterval: 0.5\r
203                                                           target: self \r
204                                                         selector: @selector(keepVisible:)\r
205                                                         userInfo: nil \r
206                                                          repeats: YES];\r
207         [hideAgainTimer fire];\r
208         [hideAgainTimer retain];\r
209         b_alreadyCounting = YES;\r
210     }\r
211 }\r
212 \r
213 - (void)keepVisible:(NSTimer *)timer\r
214 {\r
215     /* if the user triggered an action, start over again */\r
216     if( b_keptVisible )\r
217     {\r
218         i_timeToKeepVisibleInSec = KEEP_VISIBLE_AFTER_ACTION;\r
219         b_keptVisible = NO;\r
220     }\r
221     \r
222     /* count down until we hide ourselfes again and do so if necessary */\r
223     i_timeToKeepVisibleInSec -= 1;\r
224     if( i_timeToKeepVisibleInSec < 1 )\r
225     {\r
226         [self fadeOut];\r
227         [timer invalidate];\r
228         [timer release];\r
229         b_alreadyCounting = NO;\r
230         timer = NULL;\r
231     }\r
232 }\r
233 \r
234 /* A getter and setter for our main timer that handles window fading */\r
235 - (NSTimer *)fadeTimer\r
236 {\r
237     return fadeTimer;\r
238 }\r
239 \r
240 - (void)setFadeTimer:(NSTimer *)timer\r
241 {\r
242     [timer retain];\r
243     [fadeTimer invalidate];\r
244     [fadeTimer release];\r
245     fadeTimer=timer;\r
246 }\r
247 \r
248 - (void)mouseDown:(NSEvent *)theEvent\r
249 {\r
250     mouseClic = [theEvent locationInWindow];\r
251 }\r
252 \r
253 - (void)mouseDragged:(NSEvent *)theEvent\r
254 {\r
255     NSPoint point = [NSEvent mouseLocation];\r
256     point.x -= mouseClic.x;\r
257     point.y -= mouseClic.y;\r
258     [self setFrameOrigin:point];\r
259 }\r
260 @end\r
261 \r
262 /*****************************************************************************\r
263  * FSPanelView\r
264  *****************************************************************************/\r
265 @implementation VLCFSPanelView\r
266 \r
267 #define addButton( o_button, imageOff, imageOn, _x, _y, action )                                \\r
268     s_rc.origin.x = _x;                                                                         \\r
269     s_rc.origin.y = _y;                                                                         \\r
270     o_button = [[NSButton alloc] initWithFrame: s_rc];                                 \\r
271     [o_button setButtonType: NSMomentaryChangeButton];                                          \\r
272     [o_button setBezelStyle: NSRegularSquareBezelStyle];                                        \\r
273     [o_button setBordered: NO];                                                                 \\r
274     [o_button setFont:[NSFont systemFontOfSize:0]];                                             \\r
275     [o_button setImage:[NSImage imageNamed:imageOff]];                                 \\r
276     [o_button setAlternateImage:[NSImage imageNamed:imageOn]];                         \\r
277     [o_button sizeToFit];                                                                       \\r
278     [o_button setTarget: self];                                                                 \\r
279     [o_button setAction: @selector(action:)];                                                   \\r
280     [self addSubview:o_button];\r
281 \r
282 #define addTextfield( o_text, align, font, color, size )                                    \\r
283     o_text = [[NSTextField alloc] initWithFrame: s_rc];                            \\r
284     [o_text setDrawsBackground: NO];                                                        \\r
285     [o_text setBordered: NO];                                                               \\r
286     [o_text setEditable: NO];                                                               \\r
287     [o_text setSelectable: NO];                                                             \\r
288     [o_text setStringValue: _NS("(no item is being played)")];                                                    \\r
289     [o_text setAlignment: align];                                                           \\r
290     [o_text setTextColor: [NSColor color]];                                                 \\r
291     [o_text setFont:[NSFont font:[NSFont smallSystemFontSize] - size]];                     \\r
292     [self addSubview:o_text];\r
293 \r
294 - (id)initWithFrame:(NSRect)frameRect\r
295 {\r
296     id view = [super initWithFrame:frameRect];\r
297     fillColor = [[NSColor clearColor] retain];\r
298     NSRect s_rc = [self frame];\r
299     NSImage * image;\r
300     addButton( o_prev, @"fs_skip_previous" , @"fs_skip_previous_highlight", 174, 15, prev );\r
301     addButton( o_slow, @"fs_rewind"        , @"fs_rewind_highlight"       , 211, 14, slower );\r
302     addButton( o_play, @"fs_play"          , @"fs_play_highlight"         , 267, 10, play );\r
303     addButton( o_fast, @"fs_forward"       , @"fs_forward_highlight"      , 313, 14, faster );\r
304     addButton( o_next, @"fs_skip_next"     , @"fs_skip_next_highlight"    , 365, 15, next );\r
305     addButton( o_fullscreen, @"fs_exit_fullscreen", @"fs_exit_fullscreen_hightlight", 507, 13, windowAction );\r
306 /*\r
307     addButton( o_button, @"image (off state)", @"image (on state)", 38, 51, something );\r
308  */\r
309 \r
310     /* time slider */\r
311     s_rc = [self frame];\r
312     s_rc.origin.x = 15;\r
313     s_rc.origin.y = 53;\r
314     s_rc.size.width = 518;\r
315     s_rc.size.height = 9;\r
316     o_fs_timeSlider = [[VLCFSTimeSlider alloc] initWithFrame: s_rc];\r
317     [o_fs_timeSlider setMinValue:0];\r
318     [o_fs_timeSlider setMaxValue:10000];\r
319     [o_fs_timeSlider setFloatValue: 0];\r
320     [o_fs_timeSlider setContinuous: YES];\r
321     [o_fs_timeSlider setTarget: self];\r
322     [o_fs_timeSlider setAction: @selector(fsTimeSliderUpdate:)];\r
323     [self addSubview: o_fs_timeSlider];\r
324 \r
325     /* volume slider */\r
326     s_rc = [self frame];\r
327     s_rc.origin.x = 26;\r
328     s_rc.origin.y = 17.5;\r
329     s_rc.size.width = 95;\r
330     s_rc.size.height = 10;\r
331     o_fs_volumeSlider = [[VLCFSVolumeSlider alloc] initWithFrame: s_rc];\r
332     [o_fs_volumeSlider setMinValue:0];\r
333     [o_fs_volumeSlider setMaxValue:32];\r
334     [o_fs_volumeSlider setFloatValue: 0];\r
335     [o_fs_volumeSlider setContinuous: YES];\r
336     [o_fs_volumeSlider setTarget: self];\r
337     [o_fs_volumeSlider setAction: @selector(fsVolumeSliderUpdate:)];\r
338     [self addSubview: o_fs_volumeSlider];\r
339     \r
340     /* time counter and stream title output fields */\r
341     s_rc = [self frame];\r
342     s_rc.origin.x = 98;\r
343     s_rc.origin.y = 64;\r
344     s_rc.size.width = 352;\r
345     s_rc.size.height = 14;\r
346     addTextfield( o_streamTitle_txt, NSCenterTextAlignment, systemFontOfSize, whiteColor, 0 );\r
347     s_rc.origin.x = 486;\r
348     s_rc.origin.y = 64;\r
349     s_rc.size.width = 50;\r
350     addTextfield( o_streamPosition_txt, NSRightTextAlignment, systemFontOfSize, whiteColor, 0 );\r
351 \r
352     return view;\r
353 }\r
354 \r
355 - (void)dealloc\r
356 {\r
357     [o_fs_timeSlider release];\r
358     [o_fs_volumeSlider release];\r
359     [o_prev release];\r
360     [o_next release];\r
361     [o_slow release];\r
362     [o_play release];\r
363     [o_fast release];\r
364     [o_fullscreen release];\r
365     [o_streamTitle_txt release];\r
366     [o_streamPosition_txt release];\r
367     [super dealloc];\r
368 }\r
369 \r
370 - (void)setPlay\r
371 {\r
372     NSImage *image;\r
373     image = [NSImage imageNamed:@"fs_play"];\r
374     [o_play setImage:image];\r
375     image = [NSImage imageNamed:@"fs_play_highlight"];\r
376     [o_play setAlternateImage:image];\r
377 }\r
378 \r
379 - (void)setPause\r
380 {\r
381     NSImage *image;\r
382     image = [NSImage imageNamed:@"fs_pause"];\r
383     [o_play setImage:image];\r
384     image = [NSImage imageNamed:@"fs_pause_highlight"];\r
385     [o_play setAlternateImage:image];\r
386 }\r
387 \r
388 - (void)setStreamTitle:(NSString *)o_title\r
389 {\r
390     [o_streamTitle_txt setStringValue: o_title];\r
391 }\r
392 \r
393 - (void)setStreamPos:(float) f_pos andTime:(NSString *)o_time\r
394 {\r
395     [o_streamPosition_txt setStringValue: o_time];\r
396     [o_fs_timeSlider setFloatValue: f_pos];\r
397 }\r
398 \r
399 - (void)setSeekable:(BOOL)b_seekable\r
400 {\r
401     [o_slow setEnabled: b_seekable];\r
402     [o_fast setEnabled: b_seekable];\r
403     [o_fs_timeSlider setEnabled: b_seekable];\r
404 }\r
405 \r
406 - (void)setVolumeLevel: (float)f_volumeLevel\r
407 {\r
408     [o_fs_volumeSlider setFloatValue: f_volumeLevel];\r
409 }\r
410 \r
411 - (IBAction)play:(id)sender\r
412 {\r
413     [[[VLCMain sharedInstance] getControls] play: sender];\r
414 }\r
415 \r
416 - (IBAction)faster:(id)sender\r
417 {\r
418     [[[VLCMain sharedInstance] getControls] faster: sender];\r
419 }\r
420 \r
421 - (IBAction)slower:(id)sender\r
422 {\r
423     [[[VLCMain sharedInstance] getControls] slower: sender];\r
424 }\r
425 \r
426 - (IBAction)prev:(id)sender\r
427 {\r
428     [[[VLCMain sharedInstance] getControls] prev: sender];\r
429 }\r
430 \r
431 - (IBAction)next:(id)sender\r
432 {\r
433     [[[VLCMain sharedInstance] getControls] next: sender];\r
434 }\r
435 \r
436 - (IBAction)windowAction:(id)sender\r
437 {\r
438     [[[VLCMain sharedInstance] getControls] windowAction: sender];\r
439 }\r
440 \r
441 - (IBAction)fsTimeSliderUpdate:(id)sender\r
442 {\r
443     [[VLCMain sharedInstance] timesliderUpdate: sender];\r
444 }\r
445 \r
446 - (IBAction)fsVolumeSliderUpdate:(id)sender\r
447 {\r
448     [[[VLCMain sharedInstance] getControls] volumeSliderUpdated: sender];\r
449 }\r
450 \r
451 #define addImage(image, _x, _y, mode, _width)                                               \\r
452     img = [NSImage imageNamed:image];                                              \\r
453     image_rect.size = [img size];                                                           \\r
454     image_rect.origin.x = 0;                                                                \\r
455     image_rect.origin.y = 0;                                                                \\r
456     frame.origin.x = _x;                                                                    \\r
457     frame.origin.y = _y;                                                                    \\r
458     frame.size = [img size];                                                                \\r
459     if( _width ) frame.size.width = _width;                                                 \\r
460     [img drawInRect:frame fromRect:image_rect operation:mode fraction:1];\r
461 \r
462 - (void)drawRect:(NSRect)rect\r
463 {\r
464         NSRect frame = [self frame];\r
465     NSRect image_rect;\r
466     NSImage *img;\r
467     addImage( @"fs_background", 0, 0, NSCompositeCopy, 0 );\r
468     addImage( @"fs_volume_slider_bar", 26, 22, NSCompositeSourceOver, 0 );\r
469     addImage( @"fs_volume_mute", 16, 18, NSCompositeSourceOver, 0 );\r
470     addImage( @"fs_volume_max", 124, 17, NSCompositeSourceOver, 0 );\r
471     addImage( @"fs_time_slider", 15, 53, NSCompositeSourceOver, 0);\r
472 }\r
473 \r
474 @end\r
475 \r
476 /*****************************************************************************\r
477  * VLCFSTimeSlider\r
478  *****************************************************************************/\r
479 @implementation VLCFSTimeSlider\r
480 - (void)drawKnobInRect:(NSRect)knobRect\r
481 {\r
482     NSRect image_rect;\r
483     NSImage *img = [NSImage imageNamed:@"fs_time_slider_knob_highlight"];\r
484     image_rect.size = [img size];\r
485     image_rect.origin.x = 0;\r
486     image_rect.origin.y = 0;\r
487     knobRect.origin.x += (knobRect.size.width - image_rect.size.width) / 2;\r
488     knobRect.size.width = image_rect.size.width;\r
489     knobRect.size.height = image_rect.size.height;\r
490     [img drawInRect:knobRect fromRect:image_rect operation:NSCompositeSourceOver fraction:1];\r
491 }\r
492 \r
493 - (void)drawRect:(NSRect)rect\r
494 {\r
495     /* Draw default to make sure the slider behaves correctly */\r
496     [[NSGraphicsContext currentContext] saveGraphicsState];\r
497     NSRectClip(NSZeroRect);\r
498     [super drawRect:rect];\r
499     [[NSGraphicsContext currentContext] restoreGraphicsState];\r
500     \r
501     NSRect knobRect = [[self cell] knobRectFlipped:NO];\r
502     knobRect.origin.y+=7.5;\r
503     [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];\r
504     [self drawKnobInRect: knobRect];\r
505 }\r
506 \r
507 @end\r
508 \r
509 /*****************************************************************************\r
510 * VLCFSVolumeSlider\r
511 *****************************************************************************/\r
512 @implementation VLCFSVolumeSlider\r
513 - (void)drawKnobInRect:(NSRect) knobRect\r
514 {\r
515     NSRect image_rect;\r
516     NSImage *img = [NSImage imageNamed:@"fs_volume_slider_knob"];\r
517     image_rect.size = [img size];\r
518     image_rect.origin.x = 0;\r
519     image_rect.origin.y = 0;\r
520     knobRect.origin.x += (knobRect.size.width - image_rect.size.width) / 2;\r
521     knobRect.size.width = image_rect.size.width;\r
522     knobRect.size.height = image_rect.size.height;\r
523     [img drawInRect:knobRect fromRect:image_rect operation:NSCompositeSourceOver fraction:1];\r
524 }\r
525 \r
526 - (void)drawRect:(NSRect)rect\r
527 {\r
528     /* Draw default to make sure the slider behaves correctly */\r
529     [[NSGraphicsContext currentContext] saveGraphicsState];\r
530     NSRectClip(NSZeroRect);\r
531     [super drawRect:rect];\r
532     [[NSGraphicsContext currentContext] restoreGraphicsState];\r
533     \r
534     NSRect knobRect = [[self cell] knobRectFlipped:NO];\r
535     knobRect.origin.y+=6;\r
536     [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];\r
537     [self drawKnobInRect: knobRect];\r
538 }\r
539 \r
540 @end