Source/Card.m
author Jens Alfke <jens@mooseyard.com>
Tue Jul 08 20:32:52 2008 -0700 (2008-07-08)
changeset 14 4585c74d809c
parent 8 45c82a071aca
permissions -rw-r--r--
Minor fixes.
     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 "GGBTextLayer.h"
    25 #import "QuartzUtils.h"
    26 
    27 
    28 @implementation Card
    29 
    30 
    31 static CGSize sCardSize = {100,150};
    32 
    33 static CATransform3D kFaceUpTransform, kFaceDownTransform;
    34 
    35 + (void) initialize
    36 {
    37     if( self==[Card class] ) {
    38         kFaceUpTransform = kFaceDownTransform = CATransform3DIdentity;
    39         // Construct a 180-degree rotation matrix:
    40         kFaceDownTransform.m11 = kFaceDownTransform.m33 = -1;
    41         // The more obvious way to create kFaceDownTransform would be to call
    42         // CATransform3DMakeRotation(pi,0,1,0), but due to round-off errors, that transform
    43         // will have non-zero values in some other places, making it appear to CA as a true
    44         // 3D transform; this will then cause unexpected clipping behaviors when used.
    45     }
    46 }
    47 
    48 
    49 + (NSRange) serialNumberRange;
    50 {
    51     NSAssert1(NO,@"%@ forgot to override +serialNumberRange",self);
    52     return NSMakeRange(0,0);
    53 }
    54 
    55 
    56 + (CGSize) cardSize                 {return sCardSize;}
    57 + (void) setCardSize: (CGSize)size  {sCardSize = size;}
    58 
    59 
    60 - (id) initWithSerialNumber: (int)serial position: (CGPoint)pos
    61 {
    62     self = [super init];
    63     if (self != nil) {
    64         _serialNumber = serial;
    65         self.bounds = CGRectMake(0,0,sCardSize.width,sCardSize.height);
    66         self.position = pos;
    67         self.edgeAntialiasingMask = 0;
    68         _back = [self createBack];
    69         [self addSublayer: _back];
    70         _front = [self createFront];
    71         _front.transform = kFaceDownTransform;
    72         [self addSublayer: _front];
    73     }
    74     return self;
    75 }
    76 
    77 
    78 - (id) copyWithZone: (NSZone*)zone
    79 {
    80     Card *clone = [super copyWithZone: zone];
    81     clone->_serialNumber = _serialNumber;
    82     return clone;
    83 }
    84 
    85 
    86 - (NSString*) description
    87 {
    88     return [NSString stringWithFormat: @"%@[#%i]",self.class,_serialNumber];
    89 }
    90 
    91 
    92 @synthesize serialNumber=_serialNumber;
    93 
    94 
    95 - (BOOL) faceUp
    96 {
    97     return _faceUp;
    98 }
    99 
   100 - (void) setFaceUp: (BOOL)up
   101 {
   102     if( up != _faceUp ) {
   103         // The Card has separate sub-layers for its front and back. At any time, one of them
   104         // is hidden, by having a 180 degree rotation about the Y axis.
   105         // To flip the card, both front and back layers are flipped over.
   106         CATransform3D xform;
   107         xform = up ?kFaceUpTransform :kFaceDownTransform;
   108         _front.transform = xform;
   109         
   110         xform = up ?kFaceDownTransform :kFaceUpTransform;
   111         _back.transform = xform;
   112         _faceUp = up;
   113     }
   114 }
   115 
   116 
   117 - (GGBLayer*) createFront
   118 {
   119     GGBLayer *front = [[GGBLayer alloc] init];
   120     front.bounds = CGRectMake(0,0,sCardSize.width,sCardSize.height);
   121     front.position = CGPointMake(sCardSize.width/2,sCardSize.height/2);
   122     front.edgeAntialiasingMask = 0;
   123     front.backgroundColor = kWhiteColor;
   124     front.cornerRadius = 8 * (sCardSize.height/150);
   125     front.borderWidth = 1;
   126     front.borderColor = CreateGray(0.7, 1.0);
   127     front.doubleSided = NO;         // this makes the layer invisible when it's flipped
   128     return [front autorelease];
   129 }
   130 
   131 
   132 - (GGBLayer*) createBack
   133 {
   134     CGSize size = self.bounds.size;
   135     GGBLayer *back = [[GGBLayer alloc] init];
   136     back.bounds = CGRectMake(0,0,size.width,size.height);
   137     back.position = CGPointMake(sCardSize.width/2,sCardSize.height/2);
   138 #if TARGET_OS_IPHONE
   139     back.backgroundColor = CreateRGB(0.0,0.5,0.5, 1.0);
   140 #else
   141     back.contents = (id) GetCGImageNamed(@"/Library/Desktop Pictures/Classic Aqua Blue.jpg");
   142 #endif
   143     back.contentsGravity = kCAGravityResize;
   144     back.masksToBounds = YES;
   145     back.borderWidth = 4 * (sCardSize.height/150);
   146     back.borderColor = kWhiteColor;
   147     back.cornerRadius = 8 * (sCardSize.height/150);
   148     back.edgeAntialiasingMask = 0;
   149     back.doubleSided = NO;          // this makes the layer invisible when it's flipped
   150     
   151 #if TARGET_OS_IPHONE
   152     // On iPhone, only Hiragana Kaku includes the coveted snowman glyph... who knows why?
   153     UIFont *font = [UIFont fontWithName: @"HiraKakuProN-W3" size: 1*size.width];
   154 #else
   155     NSFont *font = [NSFont systemFontOfSize: 1*size.width];
   156 #endif
   157     GGBTextLayer *label = [GGBTextLayer textLayerInSuperlayer: back
   158                                                      withText: @"\u2603"          // Unicode snowman character
   159                                                          font: font
   160                                                     alignment: kCALayerWidthSizable|kCALayerHeightSizable];
   161     label.foregroundColor = CreateGray(1.0,0.5);
   162     return [back autorelease];
   163 }    
   164 
   165 
   166 #pragma mark -
   167 #pragma mark DRAG-AND-DROP:
   168 
   169 
   170 #if ! TARGET_OS_IPHONE
   171 
   172 // An image from another app can be dragged onto a Card to change its background. */
   173 
   174 
   175 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
   176 {
   177     NSPasteboard *pb = [sender draggingPasteboard];
   178     if( [NSImage canInitWithPasteboard: pb] )
   179         return NSDragOperationCopy;
   180     else
   181         return NSDragOperationNone;
   182 }
   183 
   184 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
   185 {
   186     CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard],sender);
   187     if( image ) {
   188         GGBLayer *face = _faceUp ?_front :_back;
   189         face.contents = (id) image;
   190         face.contentsGravity = kCAGravityResizeAspectFill;
   191         face.masksToBounds = YES;
   192         return YES;
   193     } else
   194         return NO;
   195 }
   196 
   197 #endif
   198 
   199 @end