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.