Source/Card.m
author Jens Alfke <jens@mooseyard.com>
Fri Mar 07 11:43:02 2008 -0800 (2008-03-07)
changeset 0 e9f7ba4718e1
child 1 3eb7be1dd7b6
permissions -rw-r--r--
Initial check-in into Mercurial. Branched from 1.0 release of Apple's sample code. No longer requires garbage collection. Fixed some memory leaks of CG objects. Fixed a bug when advancing to the 8th row in the Checkers game.
     1 /*  This code is based on Apple's "GeekGameBoard" sample code, version 1.0.
     2     http://developer.apple.com/samplecode/GeekGameBoard/
     3     Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved.
     4 
     5     Redistribution and use in source and binary forms, with or without modification, are permitted
     6     provided that the following conditions are met:
     7 
     8     * Redistributions of source code must retain the above copyright notice, this list of conditions
     9       and the following disclaimer.
    10     * Redistributions in binary form must reproduce the above copyright notice, this list of
    11       conditions and the following disclaimer in the documentation and/or other materials provided
    12       with the distribution.
    13 
    14     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
    15     IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
    16     FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
    17     BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    18     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
    19     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
    20     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
    21     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    22 */
    23 #import "Card.h"
    24 #import "QuartzUtils.h"
    25 
    26 
    27 @implementation Card
    28 
    29 
    30 static CATransform3D kFaceUpTransform, kFaceDownTransform;
    31 
    32 + (void) initialize
    33 {
    34     if( self==[Card class] ) {
    35         kFaceUpTransform = kFaceDownTransform = CATransform3DIdentity;
    36         // Construct a 180-degree rotation matrix:
    37         kFaceDownTransform.m11 = kFaceDownTransform.m33 = -1;
    38         // The more obvious way to create kFaceDownTransform would be to call
    39         // CATransform3DMakeRotation(pi,0,1,0), but due to round-off errors, that transform
    40         // will have non-zero values in some other places, making it appear to CA as a true
    41         // 3D transform; this will then cause unexpected clipping behaviors when used.
    42     }
    43 }
    44 
    45 
    46 + (NSRange) serialNumberRange;
    47 {
    48     NSAssert1(NO,@"%@ forgot to override +serialNumberRange",self);
    49     return NSMakeRange(0,0);
    50 }
    51 
    52 
    53 - (id) initWithSerialNumber: (int)serial position: (CGPoint)pos
    54 {
    55     self = [super init];
    56     if (self != nil) {
    57         _serialNumber = serial;
    58         self.bounds = CGRectMake(0,0,kCardWidth,kCardHeight);
    59         self.position = pos;
    60         self.edgeAntialiasingMask = 0;
    61         _back = [self createBack];
    62         [self addSublayer: _back];
    63         _front = [self createFront];
    64         _front.transform = kFaceDownTransform;
    65         [self addSublayer: _front];
    66     }
    67     return self;
    68 }
    69 
    70 
    71 - (void)encodeWithCoder:(NSCoder *)aCoder
    72 {
    73     [super encodeWithCoder: aCoder];
    74     [aCoder encodeInt: _serialNumber forKey: @"serialNumber"];
    75 }
    76 
    77 - (id)initWithCoder:(NSCoder *)aDecoder
    78 {
    79     self = [super initWithCoder: aDecoder];
    80     if( self ) {
    81         _serialNumber = [aDecoder decodeIntForKey: @"serialNumber"];
    82     }
    83     return self;
    84 }
    85 
    86 
    87 - (NSString*) description
    88 {
    89     return [NSString stringWithFormat: @"%@[#%i]",self.class,_serialNumber];
    90 }
    91 
    92 
    93 @synthesize serialNumber=_serialNumber;
    94 
    95 
    96 - (BOOL) faceUp
    97 {
    98     return _faceUp;
    99 }
   100 
   101 - (void) setFaceUp: (BOOL)up
   102 {
   103     if( up != _faceUp ) {
   104         // The Card has separate sub-layers for its front and back. At any time, one of them
   105         // is hidden, by having a 180 degree rotation about the Y axis.
   106         // To flip the card, both front and back layers are flipped over.
   107         CATransform3D xform;
   108         xform = up ?kFaceUpTransform :kFaceDownTransform;
   109         _front.transform = xform;
   110         
   111         xform = up ?kFaceDownTransform :kFaceUpTransform;
   112         _back.transform = xform;
   113         _faceUp = up;
   114     }
   115 }
   116 
   117 
   118 - (CALayer*) createFront
   119 {
   120     CALayer *front = [[CALayer alloc] init];
   121     front.bounds = CGRectMake(0,0,kCardWidth,kCardHeight);
   122     front.position = CGPointMake(kCardWidth/2,kCardHeight/2);
   123     front.edgeAntialiasingMask = 0;
   124     front.backgroundColor = kWhiteColor;
   125     front.cornerRadius = 8;
   126     front.borderWidth = 1;
   127     front.borderColor = CGColorCreateGenericGray(0.7, 1.0);
   128     front.doubleSided = NO;         // this makes the layer invisible when it's flipped
   129     return [front autorelease];
   130 }
   131 
   132 
   133 - (CALayer*) createBack
   134 {
   135     CGSize size = self.bounds.size;
   136     CALayer *back = [[CALayer alloc] init];
   137     back.bounds = CGRectMake(0,0,size.width,size.height);
   138     back.position = CGPointMake(kCardWidth/2,kCardHeight/2);
   139     back.contents = (id) GetCGImageNamed(@"/Library/Desktop Pictures/Classic Aqua Blue.jpg");
   140     back.contentsGravity = kCAGravityResize;
   141     back.masksToBounds = YES;
   142     back.borderWidth = 4;
   143     back.borderColor = kWhiteColor;
   144     back.cornerRadius = 8;
   145     back.edgeAntialiasingMask = 0;
   146     back.doubleSided = NO;          // this makes the layer invisible when it's flipped
   147     
   148     CATextLayer *label = AddTextLayer(back, @"\u2603",          // Unicode snowman character
   149                                       [NSFont systemFontOfSize: 0.9*size.width],
   150                                       kCALayerWidthSizable|kCALayerHeightSizable);
   151     label.foregroundColor = CGColorCreateGenericGray(1.0,0.5);
   152     return [back autorelease];
   153 }    
   154 
   155 
   156 #pragma mark -
   157 #pragma mark DRAG-AND-DROP:
   158 
   159 
   160 // An image from another app can be dragged onto a Card to change its background. */
   161 
   162 
   163 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
   164 {
   165     NSPasteboard *pb = [sender draggingPasteboard];
   166     if( [NSImage canInitWithPasteboard: pb] )
   167         return NSDragOperationCopy;
   168     else
   169         return NSDragOperationNone;
   170 }
   171 
   172 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
   173 {
   174     CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard]);
   175     if( image ) {
   176         CALayer *face = _faceUp ?_front :_back;
   177         face.contents = (id) image;
   178         face.contentsGravity = kCAGravityResizeAspectFill;
   179         face.masksToBounds = YES;
   180         return YES;
   181     } else
   182         return NO;
   183 }
   184 
   185 
   186 @end