ftp.nice.ch/pub/next/games/card/Solitaire.2.1.s.tar.gz#/Solitaire.2.1.s/Solitaire/CardSet.subproj/CardPileView.m

This is CardPileView.m in view mode; [Download] [Up]

/* indent:4  tabsize:8  font:fixed-width */

#import "CardPileView.h"
#import "CardPile.h"
#import "Card.h"
#import "Pasteboard-CardSet.h"

/*--------------------------------------------------------------------------
|
|    CardPileView Globals
|
|    CardPilePBoardType is a private pasteboard type used by the 
|    CardPileView class.
|
\---------------------------------------------------------------------------*/

static NXAtom CardPilePBoardType;
/* static id cardImage; */


@implementation CardPileView

/*---------------------------------------------------------------------------
|
|    + initialize
|
|    returns: (id) [super initialize]
|
|----------------------------------------------------------------------------
|
|    Initialize the private pasteboard type used by the CardPileView and load 
|    all images used by the CardPileView class.
|            
\----------------------------------------------------------------------------*/

+ initialize
{
    if (self == [CardPileView class])
    {
        CardPilePBoardType = NXUniqueString("CardPilePBoardType");
/*        cardImage = [[NXImage allocFromZone:[self zone]] init]; */
    }
    [super initialize];
    return self;
}


/*---------------------------------------------------------------------------
|
|    - initFrame:(NXRect *)theFrame
|
|    returns: (id) [super initFrame:]
|
|----------------------------------------------------------------------------
|
|    Create an empty CardPile object to be managed by the CardPileView and
|    register the view with the dragging mechanism to accept our private 
|    pasteboard type.
|            
\----------------------------------------------------------------------------*/

- initFrame:(NXRect *) theFrame
{
    /* Default background color is felt green or light gray */
    NXColor aColor = NXConvertRGBToColor(0.0, 51.0/255.0, 34.0/255.0);

    [super initFrame:theFrame];
    cardPile = [[CardPile allocFromZone:[self zone]] init];
    [self setBackgroundColor:aColor];
    backgroundGray = NX_LTGRAY;
    [self setDrawOutline:YES];
    [self registerForDraggedTypes:&CardPilePBoardType count:1];

    /*----------------------------------------------------------------------
    |
    |    If the view is large enough to allow cards to be
    |    stacked vertically or horizontally, initialize the
    |    offsets appropriately to give an apparent "height"
    |    to the pile
    |
    \----------------------------------------------------------------------*/

    if ([cardPile returnSize] == CS_SMALL)
    {
        if (theFrame->size.width > CS_SMALLCARDWIDTH)
        {
            xOffset = 0.2;
        }

        if (theFrame->size.height > CS_SMALLCARDHEIGHT)
        {
            yOffset = 0.2;
        }
    }
    else
    {
        if (theFrame->size.width > CS_CARDWIDTH)
        {
            xOffset = 0.5;
        }

        if (theFrame->size.height > CS_CARDHEIGHT)
        {
            yOffset = 0.5;
        }
    }

    return self;
}


/*---------------------------------------------------------------------------
|
|    - (NXColor)backgroundColor
|
|    returns: (NXColor) backgroundColor
|
|----------------------------------------------------------------------------
|
|    Returns the current background color.
|
\----------------------------------------------------------------------------*/

- (NXColor)backgroundColor
{
    return backgroundColor;
}


/*---------------------------------------------------------------------------
|
|    - setBackgroundColor:(NXColor)aColor
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Sets the current background color.
|
\----------------------------------------------------------------------------*/

- setBackgroundColor:(NXColor)aColor
{    
    backgroundColor = aColor;
    return self;
}


/*---------------------------------------------------------------------------
|
|    - setCardSize:(CardSize)aSize
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Sets the current card size.
|
\----------------------------------------------------------------------------*/

- setCardSize:(CardSize)aSize
{
    [cardPile setCardSize:aSize];
    if ([cardPile returnSize] == CS_SMALL)
    {
        if (bounds.size.width > CS_SMALLCARDWIDTH)
        {
            xOffset = 0.2;
        }

        if (bounds.size.height > CS_SMALLCARDHEIGHT)
        {
            yOffset = 0.2;
        }
    }
    else
    {
        if (bounds.size.width > CS_CARDWIDTH)
        {
            xOffset = 0.5;
        }

        if (bounds.size.height > CS_CARDHEIGHT)
        {
            yOffset = 0.5;
        }
    }

    return self;
}

/*---------------------------------------------------------------------------
|
|    - (BOOL)willDrawOutline
|
|    returns: (BOOL) drawOutline
|
|----------------------------------------------------------------------------
|
|    Returns the state of the drawOutline flag.
|
\----------------------------------------------------------------------------*/

- (BOOL)willDrawOutline
{
    return drawOutline;
}


/*---------------------------------------------------------------------------
|
|    - setDrawOutline:(BOOL)aFlag
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Sets the state of the drawOutline flag.
|
\----------------------------------------------------------------------------*/

- setDrawOutline:(BOOL)aFlag
{
    drawOutline = aFlag;
    return self;
}


/*---------------------------------------------------------------------------
|
|    - free
|
|    returns: (id) [super free]
|
|----------------------------------------------------------------------------
|
|    Discard the cardPile before freeing the CardPileView.
|            
\----------------------------------------------------------------------------*/

- free
{
    [cardPile free];
    if (beneath) [beneath free];
    return [super free];
}


/*---------------------------------------------------------------------------
|
|    - setDelegate:anObject
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Set the delegate and create a set of delegateFlags that provide a quick 
|    way to check what methods the current delegate responds to.
|            
\----------------------------------------------------------------------------*/

- setDelegate:anObject
{
    delegate = anObject;
    delegateFlags = 0;
    
    if (delegate)
    {
        if ([delegate respondsTo:@selector(clickedCard:in:)])
        {
            delegateFlags |= CS_CLICKED;
        }
        
        if ([delegate respondsTo:@selector(draggedPile:from:)])
        {
            delegateFlags |= CS_DRAGGED;
        }

        if ([delegate respondsTo:@selector(canAcceptPile:from:in:)])
        {
            delegateFlags |= CS_CANACCEPT;
        }

        if ([delegate respondsTo:@selector(acceptPile:in:)])
        {
            delegateFlags |= CS_ACCEPT;
        }

        if ([delegate respondsTo:@selector(removedPile:from:)])
        {
            delegateFlags |= CS_REMOVED;
        }
        
        if ([delegate respondsTo:@selector(doubleClickedCard:in:)])
        {
            delegateFlags |= CS_DOUBLECLICKED;
        } 

        /*-------------------------------------------------------------------
        |
        |    If the delegate responds to getOffset::call it
        |    immediately to set new offset values for the view
        |
        \------------------------------------------------------------------*/

        if ([delegate respondsTo:@selector ( getOffset::forSize:)])
        {
            delegateFlags |= CS_GETOFFSET;
            [delegate getOffset:&xOffset :&yOffset 
	                          forSize:[cardPile returnSize]];
        }
    }
    return self;
}


/*---------------------------------------------------------------------------
|
|    - delegate
|
|    returns: (id) delegate
|
|----------------------------------------------------------------------------
|
|    Allow clients to find out the id of our delegate
|
\----------------------------------------------------------------------------*/

- delegate
{
    return delegate;
}


/*---------------------------------------------------------------------------
|
|    - cardPile
|
|    returns: (id) cardPile
|
|----------------------------------------------------------------------------
|
|    Allow clients to find out the id of our private cardPile
|
\----------------------------------------------------------------------------*/

- cardPile
{
    return cardPile;
}


/*---------------------------------------------------------------------------
|
|    - drawSelf:(NXRect *)theRect :(int)rectCount
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Draw a visual representation of our CardPile
|
\----------------------------------------------------------------------------*/

- drawSelf:(NXRect *)theRect :(int)rectCount
{
    NXRect	cardRect = {0, bounds.size.height - CS_CARDHEIGHT,
			     CS_CARDWIDTH, CS_CARDHEIGHT};
    NXRect	bezelRect;
    int 	cardCount = [cardPile cardCount];
    int		cardIndex;
    NXPoint	lastOrigin = {-1000, -1000};
    id		drawLastCard = nil;
    int		depth;

    if (delegateFlags & CS_GETOFFSET)
    {
	[delegate getOffset:&xOffset:&yOffset forSize:[cardPile returnSize]];
    }

    if ([cardPile returnSize] == CS_SMALL)
    {
        cardRect.origin.x = 0.0;
        cardRect.origin.y = bounds.size.height - CS_SMALLCARDHEIGHT;
        cardRect.size.width = CS_SMALLCARDWIDTH;
        cardRect.size.height = CS_SMALLCARDHEIGHT;
    }
  
    /*-----------------------------------------------------------------------
    |
    |	Preserve area under the card (first time only)
    |
    \-----------------------------------------------------------------------*/

    if (coversOthers && !beneath)
    {
	beneath = [[NXBitmapImageRep allocFromZone:[self zone]] 
	                 initData:NULL fromRect:theRect];
    }
  
    /*----------------------------------------------------------------------
    |
    |    Draw a neutral background with a bezel
    |
    \---------------------------------------------------------------------*/

    /* Support added for a color background if using a color machine */
    if ((depth = [window depthLimit]) == NX_DefaultDepth) {
        depth = [Window defaultDepthLimit];
    }
    if(depth == NX_TwoBitGrayDepth) {
        if (!coversOthers)
	{
	    PSsetgray(backgroundGray);
	    PSrectfill(theRect->origin.x, theRect->origin.y, 
			theRect->size.width, theRect->size.height);
	}
	else if (beneath)
	{
	    [beneath draw];
	}
        if (drawOutline)
        {
            NXDrawGroove(&cardRect, theRect);
        }
    }
    else {
        if (!coversOthers)
	{
	    NXSetColor(backgroundColor);
	    PSrectfill(theRect->origin.x, theRect->origin.y, 
			theRect->size.width, theRect->size.height);
	}
	else if (beneath)
	{
	    [beneath draw];
	}
	
        if (drawOutline)
        {
            PSsetgray(NX_BLACK);
            bezelRect = cardRect;
            bezelRect.size.width -= 1.0;
            bezelRect.size.height -= 1.0;
            NXFrameRect(&bezelRect);
            bezelRect.origin.x += 1.0;
            bezelRect.origin.y += 1.0;
            PSsetgray(NX_WHITE);
            NXFrameRect(&bezelRect);
        }
    }

    /*----------------------------------------------------------------------
    |
    |    Draw each card in turn
    |
    \---------------------------------------------------------------------*/

    for (cardIndex = 0; cardIndex < cardCount; cardIndex++)
    {
        id        theCard = [cardPile cardAt:cardIndex];

        /*-------------------------------------------------------------------
        |
        |    Draw the card unless cards are being dragged
        |    and this is one of them
        |
        \-------------------------------------------------------------------*/

        if ((!useDragCardPile) || (!dragCardPile) ||
                ([dragCardPile cardIndex:theCard] == NX_NOT_IN_LIST))
        {

            /*--------------------------------------------------------------
            |
            |    If the cards are widely spread draw the whole card
            |
            \-------------------------------------------------------------*/
    
            if ((yOffset > 2) || (xOffset > 2))
            {
                [theCard drawCardAt:&cardRect.origin];
            }
            else
    
            /*-------------------------------------------------------------
            |
            |    Since the cards are closely spaced, don't draw
            |    an outline unless it is at least 2 pixels from the
            |    last card drawn.  Regardless, keep track of the
            |    most recent card so we can draw the image after
            |    the stack
            |
            \--------------------------------------------------------------*/
    
            {
                if ((cardRect.origin.x - lastOrigin.x >= 2.0) ||
                        (lastOrigin.y - cardRect.origin.y >= 2.0))
                {
                    [theCard drawOutlineAt:&cardRect.origin];
                    lastOrigin = cardRect.origin;
                }
                drawLastCard = theCard;
            }
    
            /*--------------------------------------------------------------
            |
            |    Reset the origin for the next card
            |
            \--------------------------------------------------------------*/
        
            cardRect.origin.x += xOffset;
            cardRect.origin.y -= yOffset;
        }
    }

    /*--------------------------------------------------------------------
    |
    |    If a final card needs to be drawn, draw it over the
    |    last outline drawn
    |
    \-------------------------------------------------------------------*/

    if (drawLastCard)
    {
        [drawLastCard drawCardAt:&lastOrigin];
    }
    return self;
}


/*---------------------------------------------------------------------------
|
|    - (BOOL) pileCovered:sender
|
|    returns: (BOOL) YES if one or more of the covering piles 
|                    contain cards
|
|             (BOOL) NO if there are no covering piles or they don't 
|                    contain cards
|
|----------------------------------------------------------------------------
|
|    Determine if this card pile is covered by other non-empty card piles.
|
\---------------------------------------------------------------------------*/

- (BOOL)pileCovered:sender
{
    int coveringCards = 0;
    int count;
    id covers[4] = {coverPile1, coverPile2, 
                    coverPile3, coverPile4};

    for (count = 0; count < 4; count++)
    {
	if (covers[count])
	{
	    coveringCards += [[covers[count] cardPile] cardCount];
	}
    }
    return (coveringCards > 0);
}


/*---------------------------------------------------------------------------
|
|    - (BOOL)pileCoveredBy:aCardPileView
|
|    returns: (BOOL) YES if the receiving pile is covered by the 
|                    specified pile
|
|             (BOOL) NO if the specified pile does not cover the 
|                    receiver, or is empty.
|
|----------------------------------------------------------------------------
|
|	Determine if this card pile is covered by other non-empty card piles.
|
\---------------------------------------------------------------------------*/

- (BOOL)pileCoveredBy:aCardPileView
{
    int coveringCards = 0;
    int count;
    id covers[4] = {coverPile1, coverPile2, 
                    coverPile3, coverPile4};

    for (count = 0; count < 4; count++)
    {
	if (covers[count] == aCardPileView)
	{
	    coveringCards += [[covers[count] cardPile] cardCount];
	    break;
	}
    }
    return (coveringCards > 0);
}


/*---------------------------------------------------------------------------
|
|    - setCoverPile:(int)offset to:aPile
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Sets the indicated cover pile id.  Offset must be in the range
|    1 - 4.
|
\---------------------------------------------------------------------------*/

- setCoverPile:(int)offset to:aPile
{
    switch (offset)
    {
    case 1:
        coverPile1 = aPile;
	break;
    case 2:
        coverPile2 = aPile;
	break;
    case 3:
        coverPile3 = aPile;
	break;
    case 4:
        coverPile4 = aPile;
	break;
    default:
        break;
    }
    return self;
}


/*---------------------------------------------------------------------------
|
|    - setCoversOthers:(BOOL)doesCover
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    If the pile overlaps other piles, set this to TRUE so it doesn't
|    draw the background.
|
\---------------------------------------------------------------------------*/

- setCoversOthers:(BOOL)doesCover
{
    coversOthers = doesCover;
    return self;
}


/*---------------------------------------------------------------------------
|
|	- resetBacking:sender
|
|	returns:	(id)	self
|
|----------------------------------------------------------------------------
|
|	Deletes the previous backing image so that a new one will be cached.
|
\---------------------------------------------------------------------------*/

- resetBacking:sender
{
    if (beneath)
    {
	[beneath free];
	beneath = nil;
    }
    return self;
}


/*---------------------------------------------------------------------------
|
|    - mouseDown:(NXEvent *) thisEvent
|
|    returns:       (id)        self
|
|----------------------------------------------------------------------------
|
|    Deal with clicking and dragging of cards
|
\----------------------------------------------------------------------------*/

- mouseDown:(NXEvent *) thisEvent
{
    NXPoint    thePoint = thisEvent->location;
    NXEvent    nextEvent;
    id         theCard;
    int        oldMask;
    
    oldMask = [window addToEventMask:NX_LMOUSEDRAGGEDMASK];
    
    /*------------------------------------------------------------------------
    |
    |    Determine which card was clicked on
    |
    \-----------------------------------------------------------------------*/

    [self convertPoint:&thePoint fromView:nil];
    theCard = [self findCardAt:&thePoint];

    /*------------------------------------------------------------------------
    |
    |    Let our delegate know about the double click or single click if 
    |    it cares
    |
    \-----------------------------------------------------------------------*/

        if ((thisEvent->data.mouse.click == 2) && 
	               (delegateFlags & CS_DOUBLECLICKED))
        {
            [delegate doubleClickedCard:theCard in:self];
            return self;
        }
        else if (delegateFlags & CS_CLICKED)
        {
            [delegate clickedCard:theCard in:self];
        }

    /*------------------------------------------------------------------------
    |
    |    Find out what card is under the cursor NOW for
    |    dragging purposes and see if the delegate will
    |    allow us to drag it
    |
    \-----------------------------------------------------------------------*/

    if ([NXApp peekNextEvent:NX_LMOUSEDRAGGEDMASK into:&nextEvent 
            waitFor:0.5 threshold:NX_MODALRESPTHRESHOLD])
    {                                    
        theCard = [self findCardAt:&thePoint];

        if ((theCard) && (delegateFlags & CS_DRAGGED))
        {
            dragCardPile = [[CardPile allocFromZone:[self zone]] 
	                           initForCardSize:[cardPile returnSize]];
            [dragCardPile addCard:theCard];
            if ([delegate draggedPile:dragCardPile from:self])
            {
                NXPoint    theOffset = {0, 0};
                id         thePasteboard;
                NXRect     cardRect;
                id         cardImage;

                /*----------------------------------------------------------
                |
                |    If it can be dragged, calculate the size of the image
                |    to be dragged
                |
                \---------------------------------------------------------*/

                [self getRect:&cardRect forCard:theCard];
                cardRect.size.height += ([dragCardPile cardCount] - 1) 
		                              * yOffset;
                cardRect.origin.y -= ([dragCardPile cardCount] - 1) 
		                              * yOffset;
                cardRect.size.width += ([dragCardPile cardCount] - 1) 
		                              * xOffset;
    
                /*----------------------------------------------------------
                |
                |    Create the image and pasteboard
                |
                \---------------------------------------------------------*/

                cardImage = [[NXImage allocFromZone:[self zone]] 
		                           initSize:&cardRect.size];
                [cardImage useDrawMethod:@selector(drawDragCard:)
		                                inObject:self];
                thePasteboard = [Pasteboard newName:"NXDragPBoard"];
                [thePasteboard declareTypes:&CardPilePBoardType num:1
		               owner:self];
                [thePasteboard writeType:CardPilePBoardType
		               asObject:dragCardPile];

                /*----------------------------------------------------------
                |
                |    Drag it
                |
                \----------------------------------------------------------*/

                [self dragImage:cardImage
                        at:&cardRect.origin
                        offset:&theOffset
                        event:thisEvent
                        pasteboard:thePasteboard
                        source:self
                        slideBack:YES];

                /*---------------------------------------------------------
                |
                |    Destroy the image and pasteboard
                |
                \---------------------------------------------------------*/

                [cardImage free];
                [thePasteboard free];
            }

            /*-------------------------------------------------------------
            |
            |    Get rid of the temporary drag pile
            |
            \-------------------------------------------------------------*/

            [[dragCardPile empty] free];
            dragCardPile = nil;
        }
    }
    
    [window setEventMask:oldMask];
    return self;
}


/*--------------------------------------------------------------------------
|
|    - (NXDragOperation)draggingSourceOperationMaskForLocal:(BOOL)flag
|
|    returns: (NXDragOperation) NX_DragOperationGeneric if dragging within the
|                               same application (turn this off some time and
|                               run two copies of Solitaire!)
|
|             (NXDragOperation) NX_DragOperationNone otherwise
|
|---------------------------------------------------------------------------
|
|    Let the dragging mechanism know that only generic dragging is available,
|    and then only within the same application
|
\--------------------------------------------------------------------------*/

- (NXDragOperation)draggingSourceOperationMaskForLocal:(BOOL)flag
{
    if (flag)
    {
        return NX_DragOperationGeneric;
    }
    return NX_DragOperationNone;
}


/*----------------------------------------------------------------------------
|
|    - draggedImage:(NXImage *)image beganAt:(NXPoint *)screenPoint
|
|    returns:       (id)        self
|
|-----------------------------------------------------------------------------
|
|    After the dragged image has been displayed, redraw ourselves without the 
|    cards being dragged.
|
\---------------------------------------------------------------------------*/

- draggedImage:(NXImage *)image beganAt:(NXPoint *)screenPoint
{
    NXPing();
    useDragCardPile = YES;
    [self display];
    useDragCardPile = NO;
    return self;
}


/*----------------------------------------------------------------------------
|
|    - draggedImage:(NXImage *) image endedAt:(NXPoint *) screenPoint
|            deposited:(BOOL) flag
|
|    returns:       (id)        self
|
|-----------------------------------------------------------------------------
|
|    After the cards have been dragged successfully, remove them from the
|    pile and notify our delegate, if appropriate.  Redraw ourselves whether
|    cards were removed or not.
|
\---------------------------------------------------------------------------*/

- draggedImage:(NXImage *)image endedAt:(NXPoint *)screenPoint 
                              deposited:(BOOL)flag
{
    int cardIndex;

    if (flag)
    {
        for (cardIndex = 0; cardIndex < [dragCardPile cardCount]; cardIndex++)
        {
            [cardPile removeCard:[dragCardPile cardAt:cardIndex]];
        }

        if (delegateFlags & CS_REMOVED)
        {
            [delegate removedPile:dragCardPile from:self];
        }
    }
    [self display];
    NXPing();
    return self;
}


/*---------------------------------------------------------------------------
|
|    - (NXDragOperation) draggingEntered:sender
|
|    returns: (NXDragOperation) NX_DragOperationGeneric if the pile being
|                               dragged could be dropped here
|
|             (NXDragOperation) NX_DragOperationNone otherwise
|
|----------------------------------------------------------------------------
|
|    Ask our delegate if the cards dragged in can be dropped.
|
\----------------------------------------------------------------------------*/

- (NXDragOperation) draggingEntered:sender
{
    id dropPile;
    NXDragOperation theOperation = NX_DragOperationNone;

    [[sender draggingPasteboard] readType:CardPilePBoardType 
                                 asObject:&dropPile];

    if  ((dragCardPile) || ((delegateFlags & CS_CANACCEPT) 
             && ([delegate canAcceptPile:dropPile from:sender in:self])))
    {
        theOperation = NX_DragOperationGeneric;
    }
    [dropPile free];
    return theOperation;
}


/*---------------------------------------------------------------------------
|
|    - (BOOL) prepareForDragOperation:sender
|
|    returns: (BOOL) YES
|
|----------------------------------------------------------------------------
|
|    Add cards dropped on our pile to the top of the pile and notify our 
|    delegate, if possible.  Redisplay the pile afterwards in any case.
|
\----------------------------------------------------------------------------*/

- (BOOL)prepareForDragOperation:sender
{
    id        dropPile;
    int       cardIndex;

    if ([sender draggingSource] == self)
    {
        [self display];
        NXPing();
        return NO;
    }

    [[sender draggingPasteboard] readType:CardPilePBoardType 
                                 asObject:&dropPile];

    for (cardIndex = 0; cardIndex < [dropPile cardCount]; cardIndex++)
    {
        [cardPile insertCard:[dropPile cardAt:cardIndex] at:CS_TOP];
    }

    if (delegateFlags & CS_ACCEPT)
    {
        [delegate acceptPile:dropPile in:self];
    }

    [[dropPile empty] free];

    [self display];
    NXPing();

    return YES;
}


/*---------------------------------------------------------------------------
|
|    - (BOOL)performDragOperation:sender
|
|    returns: (BOOL) YES
|
|----------------------------------------------------------------------------
|
|    The drag operation is always successful.
|
\----------------------------------------------------------------------------*/

- (BOOL)performDragOperation:sender
{
    return YES;
}


/*---------------------------------------------------------------------------
|
|    - findCardAt:(NXPoint *) thePoint
|
|    returns: (id) The id of a card in our cardPile that was clicked on
|
|             (id) nil if no card in our cardPile is under thePoint
|
|----------------------------------------------------------------------------
|
|    Find a card in our cardPile based on location
|
\----------------------------------------------------------------------------*/

- findCardAt:(NXPoint *)thePoint
{
    id		theCard = nil;
    NXRect	cardRect = {0, 0, CS_CARDWIDTH, CS_CARDHEIGHT};
    int		counter;

    if ([cardPile returnSize] == CS_SMALL) {
        cardRect.origin.x = cardRect.origin.y = 0.0;
        cardRect.size.width = CS_SMALLCARDWIDTH;
        cardRect.size.height = CS_SMALLCARDHEIGHT;
    }

    /*----------------------------------------------------------------------
    |
    |    Search for a card hit from the top of the stack down
    |
    \---------------------------------------------------------------------*/

    for (counter = [cardPile cardCount] - 1; counter >= 0; counter--)
    {
        cardRect.origin.x = counter * xOffset;
        if ([cardPile returnSize] == CS_SMALL)
        {
            cardRect.origin.y = (bounds.size.height - CS_SMALLCARDHEIGHT) 
	                            - (counter * yOffset);
        }
        else
        {
            cardRect.origin.y = (bounds.size.height - CS_CARDHEIGHT) 
	                            - (counter * yOffset);
        }
        if (NXMouseInRect(thePoint, &cardRect, NO))
        {
            theCard = [cardPile cardAt:counter];
            break;
        }
    }
    return theCard;
}


/*---------------------------------------------------------------------------
|
|    - getRect:(NXRect *) cardRect forCard:aCard
|
|    returns: (id) self if cardRect has been set
|
|             (id) nil if aCard isn't in our cardPile
|
|----------------------------------------------------------------------------
|
|    Supply the bounding rectangle for a card in our pile
|
\----------------------------------------------------------------------------*/

- getRect:(NXRect *)cardRect forCard:aCard
{
    int cardIndex = [cardPile cardIndex:aCard];
    
    /*----------------------------------------------------------------------
    |
    |    Return immediately if the card isn't in the cardPile
    |
    \---------------------------------------------------------------------*/

    if (cardIndex == NX_NOT_IN_LIST)
    {
        return nil;
    }

    /*----------------------------------------------------------------------
    |
    |    Calculate and supply the bounding rectangle
    |
    \---------------------------------------------------------------------*/
    if ([cardPile returnSize] == CS_SMALL) 
    {
        cardRect->origin.x = xOffset * cardIndex;
        cardRect->origin.y = ( bounds.size.height - CS_SMALLCARDHEIGHT) 
	      - (yOffset * cardIndex);
        cardRect->size.width = CS_SMALLCARDWIDTH;
        cardRect->size.height = CS_SMALLCARDHEIGHT;
    }
    else
    {
        cardRect->origin.x = xOffset * cardIndex;
        cardRect->origin.y = (bounds.size.height - CS_CARDHEIGHT) 
	      - (yOffset * cardIndex);
        cardRect->size.width = CS_CARDWIDTH;
        cardRect->size.height = CS_CARDHEIGHT;
    }

    return self;
}


/*---------------------------------------------------------------------------
|
|    - drawDragCard:sender
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Draw the pile of cards being dragged
|
\----------------------------------------------------------------------------*/

- drawDragCard:sender
{
    NXPoint        tempPoint = { 0, 0 };
    int            counter;

    tempPoint.y += ([dragCardPile cardCount] - 1) * yOffset;
    for (counter = 0; counter < [dragCardPile cardCount]; counter++)
    {
        [[dragCardPile cardAt:counter] drawCardAt:&tempPoint];
        tempPoint.x += xOffset;
        tempPoint.y -= yOffset;
    }
    return self;
}


/*---------------------------------------------------------------------------
|
|    - setTag:(int)theTag
|
|    returns: (id) self
|
|----------------------------------------------------------------------------
|
|    Set the pile's tag.
|
\----------------------------------------------------------------------------*/

- setTag:(int)theTag
{
    tag = theTag;
    return self;
}


/*---------------------------------------------------------------------------
|
|    - (int)tag
|
|    returns: (int) the pile's tag
|
|----------------------------------------------------------------------------
|
|    Return the pile's tag.
|
\----------------------------------------------------------------------------*/

- (int)tag
{
    return tag;
}

@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.