MacOSX/Framework/VLCLibrary.m: Clear potential memory leak & formatting clean up.
[vlc.git] / extras / MacOSX / Framework / Sources / VLCVideoView.m
1 /*****************************************************************************
2  * VLCVideoView.h: VLC.framework VLCVideoView implementation
3  *****************************************************************************
4  * Copyright (C) 2007 Pierre d'Herbemont
5  * Copyright (C) 2007 the VideoLAN team
6  * $Id$
7  *
8  * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #import "VLCVideoView.h"
26 #import "VLCLibrary.h"
27 #import "VLCEventManager.h"
28
29 /* Libvlc */
30 #include <vlc/vlc.h>
31 #include <vlc/libvlc.h>
32
33 #import <QuartzCore/QuartzCore.h>
34
35 /* Notifications */
36 NSString * VLCVideoViewEnteredFullScreen    = @"VLCVideoViewEnteredFullScreen";
37 NSString * VLCVideoViewLeftFullScreen       = @"VLCVideoViewLeftFullScreen";
38
39 /******************************************************************************
40  * Soon deprecated stuff 
41  */
42
43 /* This is a forward reference to VLCOpenGLVoutView specified in minimal_macosx
44    library.  We could get rid of this, but it prevents warnings from the 
45    compiler. (Scheduled to deletion) */
46 @interface VLCOpenGLVoutView : NSView
47 - (void)detachFromVout;
48 @end
49
50 /* Depreacted methods */
51 @interface VLCVideoView (Deprecated)
52 - (void)setStretchesVideo:(BOOL)value;
53 - (BOOL)stretchesVideo;
54
55 - (void)addVoutSubview:(NSView *)aView;  /* (Scheduled to deletion) */
56 - (void)removeVoutSubview:(NSView *)aView;  /* (Scheduled to deletion) */
57 @end
58
59 /******************************************************************************
60  * VLCVideoView (Private) 
61  */
62
63 @interface VLCVideoView (Private)
64 /* Method */
65 - (void)addVoutLayer:(CALayer *)aLayer;
66 @end
67
68 /******************************************************************************
69  * Interface & Implementation VLCConstraintLayoutManager
70  *
71  * Manage the size of the video layer 
72  */
73 @interface VLCConstraintLayoutManager : CAConstraintLayoutManager
74 {
75     CGSize originalVideoSize;
76     BOOL  fillScreenEntirely;
77 }
78 @property BOOL  fillScreenEntirely;
79 @property CGSize originalVideoSize;
80 @end
81
82 @implementation VLCConstraintLayoutManager 
83 @synthesize  fillScreenEntirely;
84 @synthesize  originalVideoSize;
85 - (id)init
86 {
87     if( self = [super init] )
88     {
89         self.originalVideoSize = CGSizeMake(0., 0.);
90         self.fillScreenEntirely = NO;
91     }
92     return self;
93
94 }
95 + (id)layoutManager
96 {
97     return [[[VLCConstraintLayoutManager alloc] init] autorelease];
98 }
99 - (void)layoutSublayersOfLayer:(CALayer *)layer
100 {
101     /* Called by CA, when our rootLayer needs layout */
102     [super layoutSublayersOfLayer:layer];
103
104     /* After having done everything normally resize the vlcopengllayer */
105     if( [[layer sublayers] count] > 0 && [[[[layer sublayers] objectAtIndex:0] name] isEqualToString:@"vlcopengllayer"])
106     {
107         CALayer * videolayer = [[layer sublayers] objectAtIndex:0];
108         CGRect bounds = layer.bounds;
109         float new_height = (bounds.size.width * originalVideoSize.height) / originalVideoSize.width;
110
111         if( fillScreenEntirely )
112         {
113             if( bounds.size.height > new_height )
114                 bounds.size.height = new_height;
115             else
116                 bounds.size.width = (bounds.size.height * originalVideoSize.width) / originalVideoSize.height;
117         }
118         else
119         {
120             if( bounds.size.height > new_height )
121                 bounds.size.width = (bounds.size.height * originalVideoSize.width) / originalVideoSize.height;
122             else
123                 bounds.size.height = new_height;
124         }
125
126         bounds.origin = CGPointMake( 0.0, 0.0 );
127         videolayer.bounds = bounds;
128         videolayer.position = CGPointMake((layer.bounds.size.width - layer.bounds.origin.x)/2, (layer.bounds.size.height - layer.bounds.origin.y)/2);
129     }
130 }
131 @end
132
133 /******************************************************************************
134  * Implementation VLCVideoView 
135  */
136
137 @implementation VLCVideoView
138
139 - (BOOL)fillScreen
140 {
141     return [layoutManager fillScreenEntirely];
142 }
143 - (void)setFillScreen:(BOOL)fillScreen
144 {
145     [layoutManager setFillScreenEntirely:fillScreen];
146     [[self layer] setNeedsLayout];
147 }
148
149 - (BOOL)fullScreen
150 {
151     return fullScreen;
152 }
153
154 - (void)setFullScreen:(BOOL)newFullScreen
155 {
156     if( newFullScreen )
157     {
158         fullScreen = YES;
159         [self enterFullscreen];
160     }
161     else
162     {
163         fullScreen = NO;
164         [self leaveFullscreen];
165     }
166 }
167
168
169 - (id)initWithFrame:(NSRect)rect
170 {
171     if (self = [super initWithFrame:rect]) 
172     {
173         delegate = nil;
174         [self setBackColor:[NSColor blackColor]];
175         [self setStretchesVideo:NO];
176         [self setAutoresizesSubviews:YES];
177         [self setFillScreen: NO];
178         layoutManager = [[VLCConstraintLayoutManager layoutManager] retain];
179     }
180     return self;
181 }
182
183 - (void)dealloc
184 {
185     [layoutManager release];
186     delegate = nil;
187     [backColor release];
188     [super dealloc];
189 }
190
191 - (void)setDelegate:(id)value
192 {
193     delegate = value;
194 }
195
196 - (id)delegate
197 {
198     return delegate;
199 }
200
201 - (void)setBackColor:(NSColor *)value
202 {
203     if (backColor != value)
204     {
205         [backColor release];
206         backColor = [value retain];
207     }
208 }
209
210 - (NSColor *)backColor
211 {
212     return backColor;
213 }
214
215 /* This is a LibVLC notification that we're about to enter into full screen,
216    there is no other place where I can see where we can trap this event */
217 - (void)enterFullscreen
218 {
219     // Go ahead and send a notification to the world we're going into full screen
220     [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self 
221                                                    withDelegateMethod:nil 
222                                                  withNotificationName:VLCVideoViewEnteredFullScreen];
223     
224     [super enterFullScreenMode:[[self window] screen] withOptions:nil];
225     if( !self.fullScreen ) self.fullScreen = YES;
226 }
227
228 /* This is a LibVLC notification that we're about to enter leaving full screen,
229    there is no other place where I can see where we can trap this event */
230 - (void)leaveFullscreen
231 {
232     // Go ahead and send a notification to the world we're leaving full screen
233     [[VLCEventManager sharedManager] callOnMainThreadDelegateOfObject:self 
234                                                    withDelegateMethod:nil 
235                                                  withNotificationName:VLCVideoViewLeftFullScreen];
236     
237     // There is nothing else to do, as this object strictly displays the video feed
238     [super exitFullScreenModeWithOptions:nil];
239     if( self.fullScreen ) self.fullScreen = NO;
240 }
241
242 - (void)drawRect:(NSRect)aRect
243 {
244     [self lockFocus];
245     [backColor set];
246     NSRectFill(aRect);
247     [self unlockFocus];
248 }
249
250 - (BOOL)isOpaque
251 {
252     return YES;
253 }
254
255 - (void)mouseDown:(NSEvent *)theEvent
256 {
257     if([theEvent clickCount] != 2)
258         return;
259     if(self.fullScreen)
260         [self leaveFullscreen];
261     else
262         [self enterFullscreen];
263 }
264 @end
265
266 /******************************************************************************
267  * Implementation VLCVideoView  (Private)
268  */
269
270 @implementation VLCVideoView (Private)
271
272 /* This is called by the libvlc module 'opengllayer' as soon as there is one 
273  * vout available
274  */
275 - (void)addVoutLayer:(CALayer *)aLayer
276 {
277     [CATransaction begin];
278     [self setWantsLayer: YES];
279         CALayer * rootLayer = [self layer];
280
281     aLayer.name = @"vlcopengllayer";
282
283     [layoutManager setOriginalVideoSize:aLayer.bounds.size];
284     [rootLayer setLayoutManager:layoutManager];
285     [rootLayer insertSublayer:aLayer atIndex:0];
286
287     [aLayer setNeedsLayout];
288     [aLayer setNeedsDisplay];
289     [rootLayer setNeedsDisplay];
290     [rootLayer layoutIfNeeded];
291     [CATransaction commit];
292 }
293
294 @end
295
296 /******************************************************************************
297  * Implementation VLCVideoView  (Deprecated)
298  */
299
300 @implementation VLCVideoView (Deprecated)
301
302 - (void)setStretchesVideo:(BOOL)value
303 {
304     stretchesVideo = value;
305 }
306
307 - (BOOL)stretchesVideo
308 {
309     return stretchesVideo;
310 }
311
312 /* This is called by the libvlc module 'minimal_macosx' as soon as there is one 
313  * vout available
314  */
315 - (void)addVoutSubview:(NSView *)aView /* (Scheduled to deletion) */
316 {
317     /* This is where the real video comes from */
318     if( [[self subviews] count] )
319     {
320         /* XXX: This is a hack until core gets fixed */
321         int i;
322         for( i = 0; i < [[self subviews] count]; i++ )
323         {
324             [[[self subviews] objectAtIndex:i] detachFromVout];
325             [[[self subviews] objectAtIndex:i] retain];
326             [[[self subviews] objectAtIndex:i] removeFromSuperview];
327         }
328     }
329
330     [aView setFrame:[self bounds]];
331     
332     [self addSubview:aView];
333
334     // TODO: Should we let the media player specify these values?
335     [aView setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
336 }
337
338 - (void)removeVoutSubview:(NSView *)view /* (Scheduled to deletion) */
339 {
340     // Should we do something?  I don't know, however the protocol requires
341     // this to be implemented
342 }
343 @end