MacOSX/VLC_app: port the fullscreen zoom and the fspanel from gui/macosx to VLC_app.
[vlc.git] / extras / MacOSX / VLC_app / Sources / VLCAppAdditions.m
1 /*****************************************************************************
2  * VLCAppAdditions.m: Helpful additions to NS* classes
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  *          Felix Kühne <fkuehne at videolan dot org>
10  *          Jérôme Decoodt <djc at videolan dot org>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 #import "VLCAppAdditions.h"
28 #import <QuartzCore/QuartzCore.h>
29
30 @implementation NSIndexPath (VLCAppAddition)
31 - (NSIndexPath *)indexPathByRemovingFirstIndex
32 {
33     if( [self length] <= 1 )
34         return [[[NSIndexPath alloc] init] autorelease];
35
36     NSIndexPath * ret;
37     NSUInteger * ints = malloc(sizeof(NSUInteger)*[self length]);
38     if( !ints ) return nil;
39     [self getIndexes:ints];
40
41     ret = [NSIndexPath indexPathWithIndexes:ints+1 length:[self length]-1];
42
43     free(ints);
44     return ret;
45 }
46 - (NSUInteger)lastIndex
47 {
48     if(![self length])
49         return 0;
50     return [self indexAtPosition:[self length]-1];
51 }
52 @end
53
54 @implementation NSArray (VLCAppAddition)
55 - (id)objectAtIndexPath:(NSIndexPath *)path withNodeKeyPath:(NSString *)nodeKeyPath
56 {
57     if( ![path length] || !nodeKeyPath )
58         return self;
59
60     id object = [self objectAtIndex:[path indexAtPosition:0]];
61     id subarray = [object valueForKeyPath:nodeKeyPath];
62     if([path length] == 1)
63         return subarray ? subarray : object;
64
65     if(!subarray)
66         return object;
67     return [subarray objectAtIndexPath:[path indexPathByRemovingFirstIndex] withNodeKeyPath:nodeKeyPath];
68 }
69 @end
70
71 @implementation NSView (VLCAppAdditions)
72 - (void)moveSubviewsToVisible
73 {
74     for(NSView * view in [self subviews])
75     {
76         if( ([view autoresizingMask] & NSViewHeightSizable) &&
77             !NSContainsRect([view frame], [self bounds]) )
78         {
79             NSRect newFrame = NSIntersectionRect( [self bounds], [view frame] );
80             if( !NSIsEmptyRect(newFrame) )
81                 [view setFrame:NSIntersectionRect( [self bounds], [view frame] )];
82         }
83     }
84 }
85 @end
86
87 /* Split view that supports slider animation */
88 @implementation VLCOneSplitView
89 @synthesize fixedCursorDuringResize;
90 - (float)sliderPosition
91 {
92     return [[[self subviews] objectAtIndex:0] frame].size.height;
93 }
94 - (void)setSliderPosition:(float)newPosition
95 {
96     [self setPosition:newPosition ofDividerAtIndex:0];
97 }
98 + (id)defaultAnimationForKey:(NSString *)key
99 {
100     if([key isEqualToString:@"sliderPosition"])
101     {
102         return [CABasicAnimation animation];
103     }
104     return [super defaultAnimationForKey: key];
105 }
106 - (void)adjustSubviews
107 {
108     if( !fixedCursorDuringResize )
109     {
110         [super adjustSubviews];
111         return;
112     }
113     NSRect frame0 = [[[self subviews] objectAtIndex:0] frame];
114     NSRect frame1 = [[[self subviews] objectAtIndex:1] frame];
115     frame1.size.height = [self bounds].size.height - frame0.size.height - [self dividerThickness];
116     if( frame1.size.height < 0. )
117     {
118         float delta = -frame1.size.height;
119         frame1.size.height = 0.;
120         frame0.size.height -= delta;
121         frame1.origin.y = frame0.size.height + [self dividerThickness];
122         [[[self subviews] objectAtIndex:1] setFrame: frame0];
123     }
124     [[[self subviews] objectAtIndex:1] setFrame: frame1];
125 }
126 @end
127
128 /*****************************************************************************
129  * NSScreen (VLCAdditions)
130  *
131  *  Missing extension to NSScreen
132  *****************************************************************************/
133
134 @implementation NSScreen (VLCAdditions)
135
136 static NSMutableArray *blackoutWindows = NULL;
137
138 + (void)load
139 {
140     /* init our fake object attribute */
141     blackoutWindows = [[NSMutableArray alloc] initWithCapacity:1];
142 }
143
144 + (NSScreen *)screenWithDisplayID: (CGDirectDisplayID)displayID
145 {
146     int i;
147  
148     for( i = 0; i < [[NSScreen screens] count]; i++ )
149     {
150         NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
151         if([screen displayID] == displayID)
152             return screen;
153     }
154     return nil;
155 }
156
157 - (BOOL)isMainScreen
158 {
159     return ([self displayID] == [[[NSScreen screens] objectAtIndex:0] displayID]);
160 }
161
162 - (BOOL)isScreen: (NSScreen*)screen
163 {
164     return ([self displayID] == [screen displayID]);
165 }
166
167 - (CGDirectDisplayID)displayID
168 {
169     return (CGDirectDisplayID)_screenNumber;
170 }
171
172 - (void)blackoutOtherScreens
173 {
174     unsigned int i;
175
176     /* Free our previous blackout window (follow blackoutWindow alloc strategy) */
177     [blackoutWindows makeObjectsPerformSelector:@selector(close)];
178     [blackoutWindows removeAllObjects];
179
180  
181     for(i = 0; i < [[NSScreen screens] count]; i++)
182     {
183         NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
184         VLCWindow *blackoutWindow;
185         NSRect screen_rect;
186  
187         if([self isScreen: screen])
188             continue;
189
190         screen_rect = [screen frame];
191         screen_rect.origin.x = screen_rect.origin.y = 0.0f;
192
193         /* blackoutWindow alloc strategy
194             - The NSMutableArray blackoutWindows has the blackoutWindow references
195             - blackoutOtherDisplays is responsible for alloc/releasing its Windows
196         */
197         blackoutWindow = [[VLCWindow alloc] initWithContentRect: screen_rect styleMask: NSBorderlessWindowMask
198                 backing: NSBackingStoreBuffered defer: NO screen: screen];
199         [blackoutWindow setBackgroundColor:[NSColor blackColor]];
200         [blackoutWindow setLevel: NSFloatingWindowLevel]; /* Disappear when Expose is triggered */
201  
202         [blackoutWindow orderFront: self];
203
204         [blackoutWindows addObject: blackoutWindow];
205         [blackoutWindow release];
206     }
207 }
208
209 + (void)unblackoutScreens
210 {
211     unsigned int i;
212
213     for(i = 0; i < [blackoutWindows count]; i++)
214     {
215         VLCWindow *blackoutWindow = [blackoutWindows objectAtIndex: i];
216         [blackoutWindow close];
217     }
218 }
219
220 @end
221
222 /*****************************************************************************
223  * VLCWindow
224  *
225  *  Missing extension to NSWindow
226  *****************************************************************************/
227
228 @implementation VLCWindow
229 - (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask
230     backing:(NSBackingStoreType)backingType defer:(BOOL)flag
231 {
232     self = [super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag];
233     if( self )
234         isset_canBecomeKeyWindow = NO;
235     return self;
236 }
237 - (void)setCanBecomeKeyWindow: (BOOL)canBecomeKey
238 {
239     isset_canBecomeKeyWindow = YES;
240     canBecomeKeyWindow = canBecomeKey;
241 }
242
243 - (BOOL)canBecomeKeyWindow
244 {
245     if(isset_canBecomeKeyWindow)
246         return canBecomeKeyWindow;
247
248     return [super canBecomeKeyWindow];
249 }
250 @end
251
252 /*****************************************************************************
253  * VLCImageCustomizedSlider
254  *
255  *  Slider personalized by backgroundImage and knobImage
256  *****************************************************************************/
257 @implementation VLCImageCustomizedSlider
258 @synthesize backgroundImage;
259 @synthesize knobImage;
260
261 - (id)initWithFrame:(NSRect)frame
262 {
263     if(self = [super initWithFrame:frame])
264     {
265         knobImage = nil;
266         backgroundImage = nil;
267     }
268     return self;
269 }
270
271 - (void)dealloc
272 {
273     [knobImage release];
274     [knobImage release];
275     [super dealloc];
276 }
277
278 - (void)drawKnobInRect:(NSRect) knobRect
279 {
280     NSRect imageRect;
281     imageRect.size = [self.knobImage size];
282     imageRect.origin.x = 0;
283     imageRect.origin.y = 0;
284     knobRect.origin.x += (knobRect.size.width - imageRect.size.width) / 2;
285     knobRect.origin.y += (knobRect.size.width - imageRect.size.width) / 2;
286     knobRect.size.width = imageRect.size.width;
287     knobRect.size.height = imageRect.size.height;
288     [self.knobImage drawInRect:knobRect fromRect:imageRect operation:NSCompositeSourceOver fraction:1];
289 }
290
291 - (void)drawBackgroundInRect:(NSRect) drawRect
292 {
293     NSRect imageRect = drawRect;
294     imageRect.origin.y += ([self.backgroundImage size].height - [self bounds].size.height ) / 2;
295     [self.backgroundImage drawInRect:drawRect fromRect:imageRect operation:NSCompositeSourceOver fraction:1];
296 }
297
298 - (void)drawRect:(NSRect)rect
299 {
300     /* Draw default to make sure the slider behaves correctly */
301     [[NSGraphicsContext currentContext] saveGraphicsState];
302     NSRectClip(NSZeroRect);
303     [super drawRect:rect];
304     [[NSGraphicsContext currentContext] restoreGraphicsState];
305     if( self.backgroundImage ) 
306         [self drawBackgroundInRect: rect];
307     if( self.knobImage ) 
308     {
309         NSRect knobRect = [[self cell] knobRectFlipped:NO];
310         [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
311         [self drawKnobInRect: knobRect];
312     }
313 }
314
315 @end
316
317 /*****************************************************************************
318  * NSImageView (VLCAppAdditions)
319  *
320  *  Make the image view  move the window by mouse down by default
321  *****************************************************************************/
322
323 @implementation NSImageView (VLCAppAdditions)
324 - (void)mouseDownCanMoveWindow
325 {
326     return YES;
327 }
328 @end
329