MacOSX/Framework/VLCVideoView.m: Code clean up & implement Objective-C 2.0 @property.
[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 /******************************************************************************
36  * Soon deprecated stuff 
37  */
38
39 /* This is a forward reference to VLCOpenGLVoutView specified in minimal_macosx
40    library.  We could get rid of this, but it prevents warnings from the 
41    compiler. (Scheduled to deletion) */
42 @interface VLCOpenGLVoutView : NSView
43 - (void)detachFromVout;
44 @end
45
46 /* Depreacted methods */
47 @interface VLCVideoView (Deprecated)
48 - (void)setStretchesVideo:(BOOL)value;
49 - (BOOL)stretchesVideo;
50
51 - (void)addVoutSubview:(NSView *)aView;  /* (Scheduled to deletion) */
52 - (void)removeVoutSubview:(NSView *)aView;  /* (Scheduled to deletion) */
53 @end
54
55 /******************************************************************************
56  * VLCVideoView (Private) 
57  */
58
59 @interface VLCVideoView (Private)
60 /* Method */
61 - (void)addVoutLayer:(CALayer *)aLayer;
62 @end
63
64 /******************************************************************************
65  * Interface & Implementation VLCConstraintLayoutManager
66  *
67  * Manage the size of the video layer 
68  */
69 @interface VLCConstraintLayoutManager : CAConstraintLayoutManager
70 {
71     CGSize originalVideoSize;
72     BOOL  fillScreenEntirely;
73 }
74 @property BOOL  fillScreenEntirely;
75 @property CGSize originalVideoSize;
76 @end
77
78 @implementation VLCConstraintLayoutManager 
79 @synthesize  fillScreenEntirely;
80 @synthesize  originalVideoSize;
81 - (id)init
82 {
83     if( self = [super init] )
84     {
85         self.originalVideoSize = CGSizeMake(0., 0.);
86         self.fillScreenEntirely = NO;
87     }
88     return self;
89
90 }
91 + (id)layoutManager
92 {
93     return [[[VLCConstraintLayoutManager alloc] init] autorelease];
94 }
95 - (void)layoutSublayersOfLayer:(CALayer *)layer
96 {
97     /* Called by CA, when our rootLayer needs layout */
98     [super layoutSublayersOfLayer:layer];
99
100     /* After having done everything normally resize the vlcopengllayer */
101     if( [[layer sublayers] count] > 0 && [[[[layer sublayers] objectAtIndex:0] name] isEqualToString:@"vlcopengllayer"])
102     {
103         CALayer * videolayer = [[layer sublayers] objectAtIndex:0];
104         CGRect bounds = layer.bounds;
105         float new_height = (bounds.size.width * originalVideoSize.height) / originalVideoSize.width;
106
107         if( fillScreenEntirely )
108         {
109             if( bounds.size.height > new_height )
110                 bounds.size.height = new_height;
111             else
112                 bounds.size.width = (bounds.size.height * originalVideoSize.width) / originalVideoSize.height;
113         }
114         else
115         {
116             if( bounds.size.height > new_height )
117                 bounds.size.width = (bounds.size.height * originalVideoSize.width) / originalVideoSize.height;
118             else
119                 bounds.size.height = new_height;
120         }
121
122         bounds.origin = CGPointMake( 0.0, 0.0 );
123         videolayer.bounds = bounds;
124         videolayer.position = CGPointMake((layer.bounds.size.width - layer.bounds.origin.x)/2, (layer.bounds.size.height - layer.bounds.origin.y)/2);
125     }
126 }
127 @end
128
129 /******************************************************************************
130  * Implementation VLCVideoView 
131  */
132
133 @implementation VLCVideoView
134
135 - (id)initWithFrame:(NSRect)rect
136 {
137     if (self = [super initWithFrame:rect]) 
138     {
139         delegate = nil;
140         [self setBackColor:[NSColor blackColor]];
141         [self setStretchesVideo:NO];
142         [self setAutoresizesSubviews:YES];
143         [self setFillScreen: NO];
144         layoutManager = [[VLCConstraintLayoutManager layoutManager] retain];
145     }
146     return self;
147 }
148
149 - (void)dealloc
150 {
151     [layoutManager release];
152     delegate = nil;
153     [backColor release];
154     [super dealloc];
155 }
156
157 - (void)drawRect:(NSRect)aRect
158 {
159     [self lockFocus];
160     [backColor set];
161     NSRectFill(aRect);
162     [self unlockFocus];
163 }
164
165 - (BOOL)isOpaque
166 {
167     return YES;
168 }
169
170 @synthesize delegate;
171 @synthesize backColor;
172
173 - (BOOL)fillScreen
174 {
175     return [layoutManager fillScreenEntirely];
176 }
177
178 - (void)setFillScreen:(BOOL)fillScreen
179 {
180     [layoutManager setFillScreenEntirely:fillScreen];
181     [[self layer] setNeedsLayout];
182 }
183 @end
184
185 /******************************************************************************
186  * Implementation VLCVideoView  (Private)
187  */
188
189 @implementation VLCVideoView (Private)
190
191 /* This is called by the libvlc module 'opengllayer' as soon as there is one 
192  * vout available
193  */
194 - (void)addVoutLayer:(CALayer *)aLayer
195 {
196     [CATransaction begin];
197     [self setWantsLayer: YES];
198         CALayer * rootLayer = [self layer];
199
200     aLayer.name = @"vlcopengllayer";
201
202     [layoutManager setOriginalVideoSize:aLayer.bounds.size];
203     [rootLayer setLayoutManager:layoutManager];
204     [rootLayer insertSublayer:aLayer atIndex:0];
205
206     [aLayer setNeedsLayout];
207     [aLayer setNeedsDisplay];
208     [rootLayer setNeedsDisplay];
209     [rootLayer layoutIfNeeded];
210     [CATransaction commit];
211 }
212
213 @end
214
215 /******************************************************************************
216  * Implementation VLCVideoView  (Deprecated)
217  */
218
219 @implementation VLCVideoView (Deprecated)
220
221 - (void)setStretchesVideo:(BOOL)value
222 {
223     stretchesVideo = value;
224 }
225
226 - (BOOL)stretchesVideo
227 {
228     return stretchesVideo;
229 }
230
231 /* This is called by the libvlc module 'minimal_macosx' as soon as there is one 
232  * vout available
233  */
234 - (void)addVoutSubview:(NSView *)aView /* (Scheduled to deletion) */
235 {
236     /* This is where the real video comes from */
237     if( [[self subviews] count] )
238     {
239         /* XXX: This is a hack until core gets fixed */
240         int i;
241         for( i = 0; i < [[self subviews] count]; i++ )
242         {
243             [[[self subviews] objectAtIndex:i] detachFromVout];
244             [[[self subviews] objectAtIndex:i] retain];
245             [[[self subviews] objectAtIndex:i] removeFromSuperview];
246         }
247     }
248
249     [aView setFrame:[self bounds]];
250     
251     [self addSubview:aView];
252
253     // TODO: Should we let the media player specify these values?
254     [aView setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
255 }
256
257 - (void)removeVoutSubview:(NSView *)view /* (Scheduled to deletion) */
258 {
259     // Should we do something?  I don't know, however the protocol requires
260     // this to be implemented
261 }
262 @end