// // CardLib - CardRegion mouse-related stuff // // Freeware // Copyright J Brown 2001 // #include "cardlib.h" #include #if 1 #define TRACE(s) #else #define TRACE(s) printf("%s(%i): %s",__FILE__,__LINE__,s) #endif double __CARDZOOMSPEED = 32; int ClipCard(HDC hdc, int x, int y, int width, int height); void DrawCard(HDC hdc, int x, int y, HDC hdcSource, int width, int height); #ifdef _DEBUG static pDebugClickProc DebugStackClickProc = 0; void CardLib_SetStackClickProc(pDebugClickProc proc) { DebugStackClickProc = proc; } #endif CardRegion *CardWindow::GetBestStack(int x, int y, int w, int h) { int maxoverlap = 0; int maxoverlapidx = -1; //find the stack which is most covered by the dropped //cards. Only include those which allow drops. // for(int i = 0; i < nNumCardRegions; i++) { int percent = Regions[i]->GetOverlapRatio(x, y, w, h); //if this stack has the biggest coverage yet if(percent > maxoverlap && Regions[i]->IsVisible()) { maxoverlap = percent; maxoverlapidx = i; } } //if we found a stack to drop onto if(maxoverlapidx != -1) { return Regions[maxoverlapidx]; } else { return 0; } } bool CardRegion::IsPointInStack(int x, int y) { int axpos = xoffset < 0 ? xpos + (nNumApparentCards-1)*xoffset : xpos; int aypos = yoffset < 0 ? ypos + (nNumApparentCards-1)*yoffset : ypos; if(x >= axpos && x < axpos + width && y >= aypos && y < aypos + height && fVisible) return true; else return false; } int CardRegion::GetNumDragCards(int x, int y) { int cardindex = 0; //index from stack start int maxidx; //make x,y relative to the stack's upper left corner x -= xpos + (xoffset < 0 ? (nNumApparentCards/*cardstack.NumCards()*/ - 1) * xoffset : 0); y -= ypos + (yoffset < 0 ? (nNumApparentCards/*cardstack.NumCards()*/ - 1) * yoffset : 0); //if stack is empty, cannot drag any cards from it if(cardstack.NumCards() <= 0) return 0; //see which card in the stack has been clicked on //top-bottom ordering if(yoffset > 0) { if(y < height - __cardheight) cardindex = y / yoffset; else cardindex = cardstack.NumCards() - 1; } else if(yoffset < 0) { if(y < __cardheight) cardindex = cardstack.NumCards() - 1; else cardindex = cardstack.NumCards() - ((y - __cardheight) / -yoffset) - 2; } else //yoffset == 0 { cardindex = cardstack.NumCards() - 1; } maxidx = cardindex; //if left-right if(xoffset > 0) { if(x < width - __cardwidth) cardindex = x / xoffset; else cardindex = cardstack.NumCards() - 1; } else if(xoffset < 0) { if(x < __cardwidth) cardindex = cardstack.NumCards() - 1; else cardindex = cardstack.NumCards() - ((x - __cardwidth) / -xoffset) - 2; } else { cardindex = cardstack.NumCards() - 1; } if(cardindex > maxidx) cardindex = maxidx; if(cardindex > cardstack.NumCards()) cardindex = 1; //if are trying to drag too many cards at once return cardstack.NumCards() - cardindex; } bool CardRegion::CanDragCards(int iNumCards) { if(iNumCards <= 0) return false; if(nThreedCount > 1 && iNumCards > 1) return false; if(WaitForSingleObject(mxlock, 0) != WAIT_OBJECT_0) { // TRACE("Failed to gain access to card stack\n"); return false; } ReleaseMutex(mxlock); switch(uDragRule) { case CS_DRAG_ALL: return true; case CS_DRAG_TOP: if(iNumCards == 1) return true; else return false; case CS_DRAG_NONE: return false; case CS_DRAG_CALLBACK: if(CanDragCallback) { return CanDragCallback(*this, iNumCards); } else { return false; } default: return false; } } bool CardRegion::CanDropCards(CardStack &cards) { if(WaitForSingleObject(mxlock, 0) != WAIT_OBJECT_0) { return false; } ReleaseMutex(mxlock); switch(uDropRule) { case CS_DROP_ALL: return true; case CS_DROP_NONE: return false; case CS_DROP_CALLBACK: if(CanDropCallback) { return CanDropCallback(*this, cards); } else { return false; } default: return false; } } bool CardRegion::OnLButtonDblClk(int x, int y) { iNumDragCards = GetNumDragCards(x, y); if(DblClickCallback) DblClickCallback(*this, iNumDragCards); return true; } bool CardRegion::OnLButtonDown(int x, int y) { iNumDragCards = GetNumDragCards(x, y); #ifdef _DEBUG if(DebugStackClickProc) { if(!DebugStackClickProc(*this)) return false; } #endif if(ClickCallback) ClickCallback(*this, iNumDragCards); if(CanDragCards(iNumDragCards) != false) { //offset of the mouse cursor relative to the top-left corner //of the cards that are being dragged mousexoffset = x - xpos - xoffset * (nNumApparentCards - iNumDragCards); mouseyoffset = y - ypos - yoffset * (nNumApparentCards - iNumDragCards); if(xoffset < 0) mousexoffset += -xoffset * (iNumDragCards - 1); if(yoffset < 0) mouseyoffset += -yoffset * (iNumDragCards - 1); //remove the cards from the source stack dragstack = cardstack.Pop(iNumDragCards); //prepare the back buffer, and the drag image PrepareDragBitmaps(iNumDragCards); oldx = x - mousexoffset; oldy = y - mouseyoffset; Update(); //Update this stack's card count + size SetCapture((HWND)parentWnd); //set AFTER settings the dragstack... fMouseDragging = true; return true; } return false; } void CardRegion::ClickRelease(int x, int y) { iNumDragCards = GetNumDragCards(x, y); if (ClickReleaseCallback) ClickReleaseCallback(*this, iNumDragCards); } bool CardRegion::OnLButtonUp(int x, int y) { CardRegion *pDestStack = 0; HDC hdc; int dropstackid = CS_DROPZONE_NODROP; RECT dragrect; DropZone *dropzone; fMouseDragging = false; //first of all, see if any drop zones have been registered SetRect(&dragrect, x-mousexoffset, y-mouseyoffset, x-mousexoffset+nDragCardWidth, y-mouseyoffset+nDragCardHeight); dropzone = parentWnd.GetDropZoneFromRect(&dragrect); if(dropzone) { dropstackid = dropzone->DropCards(dragstack); if(dropstackid != CS_DROPZONE_NODROP) pDestStack = parentWnd.CardRegionFromId(dropstackid); else pDestStack = 0; } else { pDestStack = parentWnd.GetBestStack(x - mousexoffset, y - mouseyoffset, nDragCardWidth, nDragCardHeight); } // If have found a stack to drop onto // TRACE ( "can I drop card?\n" ); if(pDestStack && pDestStack->CanDropCards(dragstack)) { TRACE ( "yes, dropping card\n" ); hdc = GetDC((HWND)parentWnd); // UseNicePalette(hdc); ZoomCard(hdc, x - mousexoffset, y - mouseyoffset, pDestStack); ReleaseDC((HWND)parentWnd, hdc); // //add the cards to the destination stack // CardStack temp = pDestStack->GetCardStack(); temp.Push(dragstack); pDestStack->SetCardStack(temp); // pDestStack->Update(); //Update this stack's card count + size // pDestStack->UpdateFaceDir(temp); // Call the remove callback on THIS stack, if one is specified // if(RemoveCallback) RemoveCallback(*this, iNumDragCards); // Call the add callback, if one is specified // if(pDestStack->AddCallback) pDestStack->AddCallback(*pDestStack, pDestStack->cardstack);//index, deststack->numcards); RedrawIfNotDim(pDestStack, true); TRACE ( "done dropping card\n" ); } // // Otherwise, let the cards snap back onto this stack // else { TRACE ( "no, putting card back\n" ); hdc = GetDC((HWND)parentWnd); TRACE ( "calling ZoomCard()\n" ); ZoomCard(hdc, x - mousexoffset, y - mouseyoffset, this); TRACE ( "cardstack += dragstack\n" ); cardstack += dragstack; TRACE ( "calling ReleaseDC()\n" ); ReleaseDC((HWND)parentWnd, hdc); TRACE ( "calling Update()\n" ); Update(); //Update this stack's card count + size TRACE ( "done putting card back\n" ); } ReleaseDragBitmaps(); ReleaseCapture(); TRACE ( "OnLButtonUp() done\n" ); return true; } bool CardRegion::OnMouseMove(int x, int y) { HDC hdc; hdc = GetDC((HWND)parentWnd); x -= mousexoffset; y -= mouseyoffset; MoveDragCardTo(hdc, x, y); //BitBlt(hdc, nDragCardWidth+10, 0, nDragCardWidth, nDragCardHeight, hdcBackGnd, 0, 0, SRCCOPY); //BitBlt(hdc, 0, 0, nDragCardWidth, nDragCardHeight, hdcDragCard, 0, 0, SRCCOPY); ReleaseDC((HWND)parentWnd, hdc); oldx = x; oldy = y; return true; } // // There is a bug in BitBlt when the source x,y // become < 0. So this wrapper function simply adjusts // the coords so that we never try to blt in from this range // BOOL ClippedBitBlt(HDC hdcDest, int x, int y, int width, int height, HDC hdcSrc, int srcx, int srcy, DWORD dwROP) { if(srcx < 0) { x = 0 - srcx; width = width + srcx; srcx = 0; } if(srcy < 0) { y = 0 - srcy; height = height + srcy; srcy = 0; } return BitBlt(hdcDest, x, y, width, height, hdcSrc, srcx, srcy, dwROP); } void CardRegion::MoveDragCardTo(HDC hdc, int x, int y) { RECT inter, rect1, rect2; //mask off the new position of the drag-card, so //that it will not be painted over ClipCard(hdc, x, y, nDragCardWidth, nDragCardHeight); //restore the area covered by the card at its previous position BitBlt(hdc, oldx, oldy, nDragCardWidth, nDragCardHeight, hdcBackGnd, 0, 0, SRCCOPY); //remove clipping so we can draw the card at its new place SelectClipRgn(hdc, NULL); //if the card's old and new positions overlap, then we //need some funky code to update the "saved background" image, SetRect(&rect1, oldx, oldy, oldx+nDragCardWidth, oldy+nDragCardHeight); SetRect(&rect2, x, y, x+nDragCardWidth, y+nDragCardHeight); if(IntersectRect(&inter, &rect1, &rect2)) { int interwidth = inter.right-inter.left; int interheight = inter.bottom-inter.top; int destx, desty, srcx, srcy; if(rect2.left > rect1.left) { destx = 0; srcx = nDragCardWidth - interwidth; } else { destx = nDragCardWidth - interwidth; srcx = 0; } if(rect2.top > rect1.top) { desty = 0; srcy = nDragCardHeight - interheight; } else { desty = nDragCardHeight - interheight; srcy = 0; } //shift the bit we didn't use for the restore (due to the clipping) //into the opposite corner BitBlt(hdcBackGnd, destx,desty, interwidth, interheight, hdcBackGnd, srcx, srcy, SRCCOPY); ExcludeClipRect(hdcBackGnd, destx, desty, destx+interwidth, desty+interheight); //this bit requires us to clip the BitBlt (from screen to background) //as BitBlt is a bit buggy it seems ClippedBitBlt(hdcBackGnd, 0,0, nDragCardWidth, nDragCardHeight, hdc, x, y, SRCCOPY); SelectClipRgn(hdcBackGnd, NULL); } else { BitBlt(hdcBackGnd, 0,0, nDragCardWidth, nDragCardHeight, hdc, x, y, SRCCOPY); } //finally draw the card to the screen DrawCard(hdc, x, y, hdcDragCard, nDragCardWidth, nDragCardHeight); } //extern "C" int _fltused(void) { return 0; } //extern "C" int _ftol(void) { return 0; } // // Better do this in fixed-point, to stop // VC from linking in floatingpoint-long conversions // //#define FIXED_PREC_MOVE #ifdef FIXED_PREC_MOVE #define PRECISION 12 void ZoomCard(HDC hdc, int xpos, int ypos, CARDSTACK *dest) { long dx, dy, x , y; int apparentcards; x = xpos << PRECISION; y = ypos << PRECISION; oldx = (int)xpos; oldy = (int)ypos; apparentcards=dest->numcards/dest->threedcount; int idestx = dest->xpos + dest->xoffset * (apparentcards);// - iNumDragCards); int idesty = dest->ypos + dest->yoffset * (apparentcards);// - iNumDragCards); //normalise the motion vector dx = (idestx<>PRECISION; iy = (int)y>>PRECISION; if(dx < 0 && ix < idestx) ix = idestx; else if(dx > 0 && ix > idestx) ix = idestx; if(dy < 0 && iy < idesty) iy = idesty; else if(dy > 0 && iy > idesty) iy = idesty; MoveDragCardTo(hdc, ix, iy); if(ix == idestx && iy == idesty) break; oldx = (int)x >> PRECISION; oldy = (int)y >> PRECISION; //dx *= 1.2; //dy *= 1.2; Sleep(10); } } #else void CardRegion::ZoomCard(HDC hdc, int xpos, int ypos, CardRegion *pDestStack) { TRACE ( "ENTER ZoomCard()\n" ); double dx, dy, x ,y; int apparentcards; x = (double)xpos; y = (double)ypos; oldx = (int)x; oldy = (int)y; apparentcards = pDestStack->cardstack.NumCards() / pDestStack->nThreedCount; int idestx = pDestStack->xpos + pDestStack->xoffset * (apparentcards); int idesty = pDestStack->ypos + pDestStack->yoffset * (apparentcards); if(pDestStack->yoffset < 0) idesty += pDestStack->yoffset * (iNumDragCards-1); if(pDestStack->xoffset < 0) idestx += pDestStack->xoffset * (iNumDragCards-1); //normalise the motion vector dx = idestx - x; dy = idesty - y; if ( fabs(dx) + fabs(dy) < 0.001f ) { MoveDragCardTo(hdc, idestx, idesty); return; } double recip = 1.0 / sqrt(dx*dx + dy*dy); dx *= recip * __CARDZOOMSPEED; dy *= recip * __CARDZOOMSPEED; //if(dx < 0) dxinc = 1.001; else for(;;) { bool attarget = true; int ix, iy; x += dx; y += dy; ix = (int)x; iy = (int)y; if(dx < 0.0 && ix < idestx) ix = idestx; else if(dx > 0.0 && ix > idestx) ix = idestx; else attarget = false; if(dy < 0.0 && iy < idesty) iy = idesty; else if(dy > 0.0 && iy > idesty) iy = idesty; else attarget = false; //if the target stack wants the drag cards drawn differently //to how they are, then redraw the drag card image just before //the cards land /*if(attarget == true) { for(int i = 0; i < iNumDragCards; i++) { int xdraw = pDestStack->xoffset*i; int ydraw = pDestStack->yoffset*i; if(pDestStack->yoffset < 0) ydraw = -pDestStack->yoffset * (iNumDragCards-i-1); if(pDestStack->xoffset < 0) xdraw = -pDestStack->xoffset * (iNumDragCards-i-1); if(pDestStack->facedirection == CS_FACEUP && pDestStack->numcards+i >= dest->numfacedown) { //cdtDraw(hdcDragCard, xdraw, ydraw, iDragCards[i], ectFACES, 0); } else { //cdtDraw(hdcDragCard, xdraw, ydraw, CARDSTACK::backcard, ectBACKS, 0); } } }*/ MoveDragCardTo(hdc, ix, iy); if(attarget || (ix == idestx && iy == idesty)) break; oldx = (int)x; oldy = (int)y; //dx *= 1.2; //dy *= 1.2; Sleep(10); } TRACE ( "EXIT ZoomCard()\n" ); } #endif