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