Fixed some problems reported by the CLANG static analyzer.
5 // Created by Jens Alfke on 3/7/08.
6 // Copyright 2008 __MyCompanyName__. All rights reserved.
10 #import "QuartzUtils.h"
14 NSString* const GGBLayerStyleChangedNotification = @"GGBLayerStyleChanged";
17 @implementation GGBLayer
20 - (NSString*) description
22 return [NSString stringWithFormat: @"%@[(%g,%g)]", self.class,self.position.x,self.position.y];
28 [self setNeedsDisplay];
29 for( CALayer *layer in self.sublayers )
30 if( [layer isKindOfClass: [GGBLayer class]] )
31 ((GGBLayer*)layer).redisplayAll;
33 [layer setNeedsDisplay];
38 - (void)addAnimation:(CAAnimation *)anim forKey:(NSString *)key
40 NSLog(@"%@[%p] addAnimation: %p forKey: %@",[self class],self,anim,key);
41 [super addAnimation: anim forKey: key];
46 - (void) animateAndBlock: (NSString*)keyPath from: (id)from to: (id)to duration: (NSTimeInterval)duration
48 //WARNING: This code works, but is a mess. I hope to find a better way to do this. --Jens 3/16/08
49 CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath: keyPath];
50 anim.duration= duration;
51 anim.fromValue = from;
53 anim.removedOnCompletion = YES;
55 [self addAnimation:anim forKey: @"animateAndBlock:"];
56 _curAnimation = (id)[self animationForKey: @"animateAndBlock:"];
57 [self setValue: to forKeyPath: keyPath]; // animation doesn't update the property value
59 if( self.presentationLayer ) {
60 // Now wait for it to finish:
61 while( _curAnimation ) {
62 [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode//NSEventTrackingRunLoopMode
63 beforeDate: [NSDate dateWithTimeIntervalSinceNow: 0.1]];
70 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
72 if( anim==_curAnimation ) {
78 - (void) setStyle: (NSDictionary*)style
80 if( style != _styleDict ) {
82 [[NSNotificationCenter defaultCenter] removeObserver: self
83 name: GGBLayerStyleChangedNotification
86 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_styleChanged)
87 name: GGBLayerStyleChangedNotification
89 setObj(&_styleDict,style);
91 [super setStyle: style];
94 - (void) _styleChanged
96 // Reapply the style, so any changes in the dict will take effect.
97 [super setStyle: _styleDict];
103 [[NSNotificationCenter defaultCenter] removeObserver: self
104 name: GGBLayerStyleChangedNotification
110 - (void) setValue: (id)value ofStyleProperty: (NSString*)prop
113 id oldValue = [_styleDict objectForKey: prop];
114 if( oldValue != value ) {
116 [_styleDict setObject: value forKey: prop];
118 [_styleDict removeObjectForKey: prop];
119 [[NSNotificationCenter defaultCenter] postNotificationName: GGBLayerStyleChangedNotification
123 [self setValue: value forKey: prop];
127 - (void) makeSublayersPerformSelector: (SEL)selector withObject: (id)object
129 for( GGBLayer *layer in self.sublayers ) {
130 [layer performSelector: selector withObject: object withObject: nil];
131 [layer makeSublayersPerformSelector: selector withObject: object];
135 - (void) changedTransform
137 [self makeSublayersPerformSelector: @selector(aggregateTransformChanged) withObject: nil];
140 - (void) aggregateTransformChanged
145 - (CATransform3D) aggregateTransform
147 CATransform3D xform = CATransform3DIdentity;
148 for( CALayer *layer=self; layer; layer=layer.superlayer ) {
149 xform = CATransform3DConcat(layer.transform,xform);
150 xform = CATransform3DConcat(layer.sublayerTransform,xform);
156 NSString* StringFromTransform3D( CATransform3D xform )
158 NSMutableString *str = [NSMutableString string];
159 const CGFloat *np = (const CGFloat*)&xform;
160 for( int i=0; i<16; i++ ) {
161 if( i>0 && (i%4)==0 )
162 [str appendString: @"\n"];
163 [str appendFormat: @"%7.2f ", *np++];
172 #pragma mark IPHONE VERSION:
175 - (id) copyWithZone: (NSZone*)zone
177 GGBLayer *clone = [[[self class] alloc] init];
178 clone.bounds = self.bounds;
179 clone.position = self.position;
180 clone.zPosition = self.zPosition;
181 clone.anchorPoint = self.anchorPoint;
182 clone.transform = self.transform;
183 clone.hidden = self.hidden;
184 clone.doubleSided = self.doubleSided;
185 clone.sublayerTransform = self.sublayerTransform;
186 clone.masksToBounds = self.masksToBounds;
187 clone.contents = self.contents; // doesn't copy contents (shallow-copy)
188 clone.contentsRect = self.contentsRect;
189 clone.contentsGravity = self.contentsGravity;
190 clone.minificationFilter = self.minificationFilter;
191 clone.magnificationFilter = self.magnificationFilter;
192 clone.opaque = self.opaque;
193 clone.needsDisplayOnBoundsChange = self.needsDisplayOnBoundsChange;
194 clone.edgeAntialiasingMask = self.edgeAntialiasingMask;
195 clone.backgroundColor = self.backgroundColor;
196 clone.opacity = self.opacity;
197 clone.compositingFilter = self.compositingFilter;
198 clone.filters = self.filters;
199 clone.backgroundFilters = self.backgroundFilters;
200 clone.actions = self.actions;
201 clone.name = self.name;
202 clone.style = self.style;
204 clone.cornerRadius = self.cornerRadius;
205 clone.borderWidth = self.borderWidth;
206 clone.borderColor = self.borderColor;
208 for( GGBLayer *sublayer in self.sublayers ) {
209 sublayer = [sublayer copyWithZone: zone];
210 [clone addSublayer: sublayer];
216 - (CGFloat) cornerRadius {return _cornerRadius;}
217 - (CGFloat) borderWidth {return _borderWidth;}
218 - (CGColorRef) backgroundColor {return _realBGColor;}
219 - (CGColorRef) borderColor {return _borderColor;}
221 - (void) setCornerRadius: (CGFloat)r
223 if( r != _cornerRadius ) {
225 [self setNeedsDisplay];
230 - (void) setBorderWidth: (CGFloat)w
232 if( w != _borderWidth ) {
234 self.needsDisplayOnBoundsChange = (_borderWidth>0.0 && _borderColor!=NULL);
235 [self setNeedsDisplay];
240 - (void) setBackgroundColor: (CGColorRef)color
242 if( color != _realBGColor ) {
243 CGColorRelease(_realBGColor);
244 _realBGColor = CGColorRetain(color);
245 [self setNeedsDisplay];
250 - (void) setBorderColor: (CGColorRef)color
252 if( color != _borderColor ) {
253 CGColorRelease(_borderColor);
254 _borderColor = CGColorRetain(color);
255 self.needsDisplayOnBoundsChange = (_borderWidth>0.0 && _borderColor!=NULL);
256 [self setNeedsDisplay];
261 - (void)drawInContext:(CGContextRef)ctx
263 [super drawInContext: ctx];
265 CGContextSaveGState(ctx);
268 CGRect interior = CGRectInset(self.bounds, _borderWidth,_borderWidth);
269 CGContextSetFillColorWithColor(ctx, _realBGColor);
270 if( _cornerRadius <= 0.0 ) {
271 CGContextFillRect(ctx,interior);
273 CGContextBeginPath(ctx);
274 AddRoundRect(ctx,interior,_cornerRadius);
275 CGContextFillPath(ctx);
279 if( _borderWidth > 0.0 && _borderColor!=NULL ) {
280 CGRect border = CGRectInset(self.bounds, _borderWidth/2.0, _borderWidth/2.0);
281 CGContextSetStrokeColorWithColor(ctx, _borderColor);
282 CGContextSetLineWidth(ctx, _borderWidth);
284 if( _cornerRadius <= 0.0 ) {
285 CGContextStrokeRect(ctx,border);
287 CGContextBeginPath(ctx);
288 AddRoundRect(ctx,border,_cornerRadius);
289 CGContextStrokePath(ctx);
293 CGContextRestoreGState(ctx);
300 #pragma mark MAC OS VERSION:
303 - (id) copyWithZone: (NSZone*)zone
305 // NSLayer isn't copyable, but it is archivable. So create a copy by archiving to
306 // a temporary data block, then unarchiving a new layer from that block.
308 // One complication is that, due to a bug in Core Animation, CALayer can't archive
309 // a pattern-based CGColor. So as a workaround, clear the background before archiving,
310 // then restore it afterwards.
312 // Also, archiving a CALayer with an image in it leaks memory. (Filed as rdar://5786865 )
313 // As a workaround, clear the contents before archiving, then restore.
315 CGColorRef bg = CGColorRetain(self.backgroundColor);
316 self.backgroundColor = NULL;
317 id contents = [self.contents retain];
320 NSData *data = [NSKeyedArchiver archivedDataWithRootObject: self];
322 self.backgroundColor = bg;
323 self.contents = contents;
325 GGBLayer *clone = [NSKeyedUnarchiver unarchiveObjectWithData: data];
326 clone.backgroundColor = bg;
327 clone.contents = contents;
331 return [clone retain];
343 #pragma mark UTILITIES:
346 void BeginDisableAnimations(void)
348 [CATransaction begin];
349 [CATransaction setValue:(id)kCFBooleanTrue
350 forKey:kCATransactionDisableActions];
353 void EndDisableAnimations(void)
355 [CATransaction commit];
359 void ChangeSuperlayer( CALayer *layer, CALayer *newSuperlayer, int index )
361 // Disable actions, else the layer will move to the wrong place and then back!
362 [CATransaction flush];
363 BeginDisableAnimations();
365 CGPoint pos = layer.position;
366 if( layer.superlayer )
367 pos = [newSuperlayer convertPoint: pos fromLayer: layer.superlayer];
369 [layer removeFromSuperlayer];
370 layer.position = pos;
372 [newSuperlayer insertSublayer: layer atIndex: index];
374 [newSuperlayer addSublayer: layer];
377 EndDisableAnimations();
381 void RemoveImmediately( CALayer *layer )
383 [CATransaction flush];
384 BeginDisableAnimations();
385 [layer removeFromSuperlayer];
386 EndDisableAnimations();
390 CGColorRef GetEffectiveBackground( CALayer *layer )
392 for( ; layer; layer=layer.superlayer ) {
393 CGColorRef bg = layer.backgroundColor;