update SVN properties
[reactos.git] / rosapps / games / solitaire / cardlib / cardregion.cpp
index 5eb1c3f..b37d086 100644 (file)
-//\r
-//    CardLib - CardRegion class\r
-//\r
-//    Freeware\r
-//    Copyright J Brown 2001\r
-//\r
-#include <windows.h>\r
-\r
-#include "cardlib.h"\r
-#include "cardregion.h"\r
-#include "cardwindow.h"\r
-#include "cardcolor.h"\r
-\r
-HBITMAP CreateSinkBmp(HDC hdcCompat, HDC hdc, int width, int height);\r
-\r
-void PaintRect(HDC hdc, RECT *rect, COLORREF colour);\r
-\r
-CardRegion::CardRegion(CardWindow &parent, int Id, bool visible, int x, int y, int xOffset, int yOffset) \r
-: id(Id), parentWnd(parent), xpos(x), ypos(y), xoffset(xOffset), yoffset(yOffset), fVisible(visible)\r
-{\r
-    width  = __cardwidth;\r
-    height = __cardheight;\r
-\r
-    crBackgnd  = RGB(0, 64, 100);\r
-\r
-    uFaceDirType   = CS_FACE_UP;\r
-    nFaceDirOption = 0;\r
-    uEmptyImage  = CS_EI_SUNK;\r
-\r
-    fVisible     = visible;\r
-\r
-    nThreedCount = 1;\r
-    nBackCardIdx = 53;\r
-\r
-    Update();                //Update this stack's size+card count\r
-\r
-    hdcBackGnd = 0;\r
-    hbmBackGnd = 0;\r
-    hdcDragCard = 0;\r
-    hbmDragCard = 0;\r
-\r
-    nDragCardWidth = 0;\r
-    nDragCardHeight = 0;\r
-    \r
-    CanDragCallback  = 0;\r
-    CanDropCallback  = 0;\r
-    AddCallback      = 0;\r
-    RemoveCallback   = 0;\r
-    ClickCallback    = 0;\r
-    DblClickCallback = 0;\r
-\r
-    uDragRule = CS_DRAG_ALL;\r
-    uDropRule = CS_DROP_ALL;\r
-\r
-    xjustify = yjustify = xadjust = yadjust = 0;\r
-\r
-    nFlashCount        = 0;\r
-    fFlashVisible    = false;\r
-    uFlashTimer        = (UINT)-1;\r
-\r
-    fMouseDragging = false;\r
-\r
-    mxlock = CreateMutex(0, FALSE, 0);\r
-}\r
-\r
-CardRegion::~CardRegion()\r
-{\r
-    CloseHandle(mxlock);\r
-}\r
-\r
-void CardRegion::SetBackColor(COLORREF cr)\r
-{\r
-    crBackgnd = cr;\r
-}\r
-\r
-int CardRegion::CalcApparentCards(int realnum)\r
-{\r
-    return ((realnum + nThreedCount - 1) - (realnum + nThreedCount - 1) % nThreedCount) / nThreedCount;\r
-}\r
-\r
-void CardRegion::CalcApparentCards()\r
-{\r
-    nNumApparentCards = CalcApparentCards(cardstack.NumCards());\r
-}\r
-\r
-\r
-void CardRegion::UpdateSize(void)\r
-{\r
-    if(cardstack.NumCards() > 0)\r
-    {\r
-        if(xoffset > 0)\r
-            width  = (nNumApparentCards - 1) * xoffset + __cardwidth;\r
-        else\r
-            width  = (nNumApparentCards - 1) * -xoffset + __cardwidth;\r
-\r
-        if(yoffset > 0)\r
-            height = (nNumApparentCards - 1) * yoffset + __cardheight;\r
-        else\r
-            height = (nNumApparentCards - 1) * -yoffset + __cardheight;\r
-    }\r
-    else\r
-    {\r
-        width = __cardwidth;\r
-        height = __cardheight;\r
-    }\r
-}\r
-\r
-CardRegion *CardWindow::CreateRegion(int id, bool fVisible, int x, int y, int xoffset, int yoffset)\r
-{\r
-    CardRegion *cr;\r
-\r
-    if(nNumCardRegions == MAXCARDSTACKS)\r
-        return FALSE;\r
-\r
-    cr = new CardRegion(*this, id, fVisible, x, y, xoffset, yoffset);\r
-    cr->SetBackColor(crBackgnd);\r
-    cr->SetBackCardIdx(nBackCardIdx);\r
-\r
-    Regions[nNumCardRegions++] = cr;\r
-    \r
-    return cr;\r
-}\r
-\r
-int CardRegion::GetOverlapRatio(int x, int y, int w, int h)\r
-{\r
-    RECT me, him;\r
-    RECT inter;\r
-    SetRect(&him, x, y, x+w, y+h);\r
-    SetRect(&me,  xpos, ypos, xpos+width, ypos+height);\r
-\r
-    //see if the specified rectangle overlaps us\r
-    if(IntersectRect(&inter, &me, &him))\r
-    {\r
-        int wi = inter.right  - inter.left;\r
-        int hi = inter.bottom - inter.top;\r
-\r
-        int overlap = wi * hi;\r
-        int total   = width * height;\r
-\r
-        int percent = (overlap << 16) / total;\r
-        return (percent * 100) >> 16;\r
-    }\r
-    //do not overlap\r
-    else\r
-    {\r
-        return 0;\r
-    }\r
-}\r
-\r
-bool CardRegion::SetDragRule(UINT uDragType, pCanDragProc proc)\r
-{ \r
-    switch(uDragType)\r
-    {\r
-    case CS_DRAG_NONE: case CS_DRAG_ALL: case CS_DRAG_TOP:\r
-        uDragRule = uDragType;\r
-        return true;\r
-\r
-    case CS_DRAG_CALLBACK:\r
-        uDragRule = uDragType;\r
-        CanDragCallback = proc;\r
-        return true;\r
-\r
-    default:\r
-        return false;\r
-    }\r
-}\r
-\r
-bool CardRegion::SetDropRule(UINT uDropType, pCanDropProc proc)\r
-{ \r
-    switch(uDropType)\r
-    {\r
-    case CS_DROP_NONE: case CS_DROP_ALL: \r
-        uDropRule = uDropType;\r
-        return true;\r
-\r
-    case CS_DROP_CALLBACK:\r
-        uDropRule = uDropType;\r
-        CanDropCallback = proc;\r
-        return true;\r
-\r
-    default:\r
-        return false;\r
-    }\r
-}\r
-\r
-void CardRegion::SetClickProc(pClickProc proc)\r
-{\r
-    ClickCallback = proc;\r
-}\r
-\r
-void CardRegion::SetDblClickProc(pClickProc proc)\r
-{\r
-    DblClickCallback = proc;\r
-}\r
-\r
-void CardRegion::SetAddCardProc(pAddProc proc)\r
-{\r
-    AddCallback = proc;\r
-}\r
-\r
-void CardRegion::SetRemoveCardProc(pRemoveProc proc)\r
-{\r
-    RemoveCallback = proc;\r
-}\r
-\r
-void CardRegion::Update()\r
-{\r
-    CalcApparentCards();\r
-    UpdateSize(); \r
-    UpdateFaceDir(cardstack);\r
-}\r
-\r
-\r
-bool CardRegion::SetThreedCount(int count)\r
-{\r
-    if(count < 1) \r
-    {\r
-        return false;\r
-    }\r
-    else\r
-    {\r
-        nThreedCount = count;\r
-        return true;\r
-    }\r
-}\r
-\r
-void CardRegion::SetOffsets(int x, int y)\r
-{\r
-    xoffset = x;\r
-    yoffset = y;\r
-}\r
-\r
-void CardRegion::SetPos(int x, int y)\r
-{\r
-    xpos = x;\r
-    ypos = y;\r
-}\r
-\r
-void CardRegion::Show(bool fShow)\r
-{\r
-    fVisible = fShow;\r
-}\r
-\r
-bool CardRegion::IsVisible()\r
-{ \r
-    return fVisible;\r
-}\r
-\r
-void CardRegion::SetPlacement(UINT xJustify, UINT yJustify, int xAdjust, int yAdjust)\r
-{\r
-    xjustify = xJustify;\r
-    yjustify = yJustify;\r
-    xadjust  = xAdjust;\r
-    yadjust  = yAdjust;\r
-}\r
-\r
-void CardRegion::SetFaceDirection(UINT uDirType, int nOption)\r
-{\r
-    switch(uDirType)\r
-    {\r
-    case CS_FACE_UP:     case CS_FACE_DOWN: case CS_FACE_DOWNUP:\r
-    case CS_FACE_UPDOWN: case CS_FACE_ANY:\r
-        uFaceDirType    = uDirType;\r
-        nFaceDirOption  = nOption;\r
-\r
-        UpdateFaceDir(cardstack);\r
-\r
-        break;\r
-    }\r
-}\r
-\r
-UINT CardRegion::GetFaceDirection(int *pnOption)\r
-{\r
-    if(pnOption)\r
-        *pnOption = nFaceDirOption;\r
-\r
-    return uFaceDirType;\r
-}\r
-\r
-void CardRegion::AdjustPosition(int winwidth, int winheight)\r
-{\r
-    Update();            //Update this stack's card count + size\r
-\r
-    switch(xjustify)\r
-    {\r
-    default: case CS_XJUST_NONE: break;\r
-    \r
-    case CS_XJUST_CENTER:        //centered\r
-        xpos = (winwidth - (width & ~0x1)) / 2;\r
-        xpos += xadjust;\r
-\r
-        if(xoffset < 0)    xpos += (width - __cardwidth);\r
-    \r
-        break;\r
-\r
-    case CS_XJUST_RIGHT:        //right-aligned\r
-        xpos = winwidth - __cardwidth;//width - 20;\r
-        xpos += xadjust;\r
-        break;\r
-    }\r
-\r
-    switch(yjustify)\r
-    {\r
-    default: case CS_YJUST_NONE: break;\r
-    \r
-    case CS_YJUST_CENTER:        //centered\r
-        ypos = (winheight - height) / 2;\r
-        ypos += yadjust;\r
-        if(yoffset < 0)    ypos += (height - __cardheight);\r
-        break;\r
-\r
-    case CS_YJUST_BOTTOM:        //bottom-aligned\r
-        ypos = winheight - __cardheight;//height - 20;\r
-        ypos += yadjust;\r
-        break;\r
-    }\r
-\r
-}\r
-\r
-\r
-void CardRegion::Flash(int count, int milliseconds)\r
-{\r
-    if(count <= 0) return;\r
-\r
-    nFlashCount        = count;\r
-    fFlashVisible   = false;\r
-    uFlashTimer        = SetTimer((HWND)parentWnd, (WPARAM)this, milliseconds, 0);\r
-    \r
-    parentWnd.Redraw();\r
-}\r
-\r
-void CardRegion::StopFlash()\r
-{\r
-    if(uFlashTimer != (UINT)-1)\r
-    {\r
-        KillTimer((HWND)parentWnd, uFlashTimer);\r
-        nFlashCount        = 0;\r
-        uFlashTimer        = (UINT)-1;\r
-        fFlashVisible    = true;\r
-    }\r
-}\r
-\r
-void CardRegion::DoFlash()\r
-{\r
-    if(uFlashTimer != (UINT)-1)\r
-    {\r
-        fFlashVisible = !fFlashVisible;\r
-\r
-        if(--nFlashCount == 0)\r
-        {\r
-            KillTimer((HWND)parentWnd, uFlashTimer);\r
-            uFlashTimer = (UINT)-1;\r
-            fFlashVisible = true;\r
-        }\r
-    \r
-        parentWnd.Redraw();\r
-    }\r
-}\r
-\r
-int CardRegion::Id()\r
-{\r
-    return id;\r
-}\r
-\r
-void CardRegion::SetEmptyImage(UINT uImage)\r
-{\r
-    switch(uImage)\r
-    {\r
-    case CS_EI_NONE: case CS_EI_SUNK:\r
-        uEmptyImage = uImage;\r
-        break;\r
-\r
-    default:\r
-        uEmptyImage = CS_EI_NONE;\r
-        break;\r
-    }\r
-    \r
-}\r
-\r
-void CardRegion::SetBackCardIdx(UINT uBackIdx)\r
-{\r
-    if(uBackIdx >= 52 && uBackIdx <= 68)\r
-        nBackCardIdx = uBackIdx;\r
-}\r
-\r
-void CardRegion::SetCardStack(const CardStack &cs)\r
-{ \r
-    //make a complete copy of the specified stack..\r
-    cardstack = cs; \r
-\r
-    // Update the face-direction and stack-size\r
-    Update();\r
-}\r
-\r
-const CardStack & CardRegion::GetCardStack()\r
-{ \r
-    //return reference to our internal stack\r
-    return cardstack; \r
-}\r
-\r
-//\r
-//    Update specified card-stack using THIS stack's\r
-//  face direction rules!\r
-//\r
-void CardRegion::UpdateFaceDir(CardStack &cards)\r
-{\r
-    int i, n, num;\r
-\r
-    num = cards.NumCards();\r
-\r
-    //Now apply the face direction rules..\r
-    switch(uFaceDirType)\r
-    {\r
-    case CS_FACE_UP:\r
-\r
-        for(i = 0; i < num; i++)\r
-        {\r
-            cards[i].SetFaceUp(true);\r
-        }\r
-\r
-        break;\r
-\r
-    case CS_FACE_DOWN:\r
-\r
-        for(i = 0; i < num; i++)\r
-        {\r
-            cards[i].SetFaceUp(false);\r
-        }\r
-\r
-        break;\r
-\r
-    case CS_FACE_DOWNUP:\r
-\r
-        num = cardstack.NumCards();\r
-        n = min(nFaceDirOption, num);\r
-\r
-        //bottom n cards..\r
-        for(i = 0; i < n; i++)\r
-        {\r
-            cards[num - i - 1].SetFaceUp(false);\r
-        }\r
-\r
-        for(i = n; i < num; i++)\r
-        {\r
-            cards[num - i - 1].SetFaceUp(true);\r
-        }\r
-\r
-        break;\r
-\r
-    case CS_FACE_UPDOWN:\r
-\r
-        num = cardstack.NumCards();\r
-        n = min(nFaceDirOption, num);\r
-\r
-        for(i = 0; i < n; i++)\r
-        {\r
-            cards[num - i - 1].SetFaceUp(true);\r
-        }\r
-\r
-        for(i = n; i < num; i++)\r
-        {\r
-            cards[num - i - 1].SetFaceUp(false);\r
-        }\r
-\r
-        break;\r
-\r
-    case CS_FACE_ANY:    //cards can be any orientation\r
-    default:\r
-        break;\r
-    }\r
-}\r
-\r
-bool CardRegion::MoveCard(CardRegion *pDestStack, int nNumCards, bool fAnimate)\r
-{\r
-    HDC hdc;\r
-\r
-    int x, y;\r
-\r
-    if(pDestStack == 0) return false; //{ forcedfacedir = -1 ;return 0; }\r
-\r
-    if(nNumCards < 0 || nNumCards > cardstack.NumCards())\r
-        return false;\r
-\r
-    x = xpos + xoffset * (nNumApparentCards - nNumCards);\r
-    y = ypos + yoffset * (nNumApparentCards - nNumCards);\r
-\r
-    oldx = x;\r
-    oldy = y;\r
-    \r
-    dragstack = cardstack.Pop(nNumCards);\r
-\r
-    //Alter the drag-stack so that it's cards are the same way up\r
-    //as the destination. Use the destination's drag-rules\r
-    //instead of this ones!!\r
-    CardStack temp;\r
-    temp.Push(pDestStack->GetCardStack());\r
-    temp.Push(dragstack);\r
-\r
-    pDestStack->UpdateFaceDir(temp);\r
-\r
-    dragstack = temp.Pop(nNumCards);\r
-\r
-    if(fAnimate)\r
-    {\r
-        iNumDragCards = nNumCards;\r
-        PrepareDragBitmaps(nNumCards);\r
-    }\r
-\r
-    Update();        //Update this stack's size+card count\r
-\r
-    if(fAnimate)\r
-    {\r
-        hdc = GetDC((HWND)parentWnd);\r
-\r
-        ZoomCard(hdc, x, y, pDestStack);\r
-        \r
-        ReleaseDC((HWND)parentWnd, hdc);\r
-        ReleaseDragBitmaps();\r
-    }\r
-\r
-    // Get a copy of the cardstack\r
-    CardStack cs = pDestStack->GetCardStack();\r
-    cs.Push(dragstack);\r
-    \r
-    pDestStack->SetCardStack(cs);\r
-    \r
-    //cs = pDestStack->GetCardStack();\r
-    //pDestStack->Update();\r
-    //pDestStack->UpdateFaceDir(cs);\r
-\r
-    RedrawIfNotDim(pDestStack, false);\r
-\r
-    //forcedfacedir = -1;\r
-    return true;\r
-}\r
-\r
-//\r
-//    Simple wrappers\r
-//\r
-int CardRegion::NumCards() const\r
-{\r
-    if(fMouseDragging)\r
-        return cardstack.NumCards() + dragstack.NumCards();\r
-    else\r
-        return cardstack.NumCards(); \r
-}\r
-\r
-bool CardRegion::Lock()\r
-{\r
-    DWORD dw = WaitForSingleObject(mxlock, 0);\r
-\r
-    if(dw == WAIT_OBJECT_0)\r
-    {\r
-        //TRACE("LockStack succeeded\n");\r
-        return true; \r
-    }\r
-    else\r
-    {\r
-        //TRACE("LockStack failed\n");\r
-        return false;\r
-    }\r
-    return false;\r
-}\r
-\r
-bool CardRegion::UnLock()\r
-{\r
-    if(ReleaseMutex(mxlock))\r
-    {\r
-        //TRACE("Unlocking stack\n");\r
-        return true;\r
-    }\r
-    else\r
-    {\r
-        //TRACE("Unlocking stack failed\n");    \r
-        return false;\r
-    }\r
-}\r
-\r
-bool CardRegion::PlayCard(CardRegion *pDestStack, int value, int num)\r
-{\r
-    //search the stack for the specified card value...\r
-    while(num--)\r
-    {\r
-        for(int i = 0; i < cardstack.NumCards(); i++)\r
-        {\r
-            if(cardstack[i].HiVal() == value)\r
-            {\r
-                //swap the card with one at top pos...\r
-                Card card = cardstack.RemoveCard(i);\r
-                cardstack.Push(card);\r
-\r
-                Redraw();\r
-\r
-                MoveCard(pDestStack, 1, true);\r
-                break;\r
-            }\r
-        }\r
-    }\r
-\r
-    return true;\r
-}\r
-\r
-//\r
-//    Redraw the current stack if it has a different\r
-//    layout than the comparison stack.\r
-//\r
-void CardRegion::RedrawIfNotDim(CardRegion *pCompare, bool fFullRedraw)\r
-{\r
-    //\r
-    //\r
-    //\r
-    if( pCompare->xoffset != xoffset || \r
-        pCompare->yoffset != yoffset || \r
-        pCompare->nThreedCount != nThreedCount ||\r
-        pCompare->uFaceDirType != uFaceDirType ||\r
-        pCompare->uFaceDirType != CS_FACE_ANY\r
-        )\r
-    {\r
-        if(fFullRedraw)\r
-            parentWnd.Redraw();\r
-        else\r
-            pCompare->Redraw();\r
-    }\r
-    \r
-}\r
-\r
-//\r
-//    SimulateDrag mimicks the complete drag+drop process.\r
-//  It basically just a MoveCard(..), but it calls the\r
-//  event callbacks as well.\r
-//\r
-bool CardRegion::SimulateDrag(CardRegion *pDestStack, int iNumDragCards, bool fAnimate)\r
-{\r
-    if(pDestStack == 0)\r
-        return false;\r
-\r
-    if(CanDragCards(iNumDragCards) != false)\r
-    {\r
-        //make a list of the cards that would be in the drag list\r
-        CardStack tempstack = cardstack.Top(iNumDragCards);\r
-\r
-        if(pDestStack->CanDropCards(tempstack)) \r
-        {\r
-            MoveCard(pDestStack, iNumDragCards, fAnimate);        \r
-                \r
-            if(RemoveCallback)\r
-                RemoveCallback(*this, iNumDragCards);\r
-\r
-            if(pDestStack->AddCallback)\r
-                pDestStack->AddCallback(*pDestStack, pDestStack->cardstack);\r
-        \r
-            RedrawIfNotDim(pDestStack, true);\r
-        }    \r
-\r
-    }\r
-\r
-    return true;\r
-}\r
+//
+//    CardLib - CardRegion class
+//
+//    Freeware
+//    Copyright J Brown 2001
+//
+#include <windows.h>
+
+#include "cardlib.h"
+#include "cardregion.h"
+#include "cardwindow.h"
+#include "cardcolor.h"
+
+HBITMAP CreateSinkBmp(HDC hdcCompat, HDC hdc, int width, int height);
+
+void PaintRect(HDC hdc, RECT *rect, COLORREF colour);
+
+CardRegion::CardRegion(CardWindow &parent, int Id, bool visible, int x, int y, int xOffset, int yOffset) 
+: id(Id), parentWnd(parent), xpos(x), ypos(y), xoffset(xOffset), yoffset(yOffset), fVisible(visible)
+{
+    width  = __cardwidth;
+    height = __cardheight;
+
+    crBackgnd  = RGB(0, 64, 100);
+
+    uFaceDirType   = CS_FACE_UP;
+    nFaceDirOption = 0;
+    uEmptyImage  = CS_EI_SUNK;
+
+    fVisible     = visible;
+
+    nThreedCount = 1;
+    nBackCardIdx = 53;
+
+    Update();                //Update this stack's size+card count
+
+    hdcBackGnd = 0;
+    hbmBackGnd = 0;
+    hdcDragCard = 0;
+    hbmDragCard = 0;
+
+    nDragCardWidth = 0;
+    nDragCardHeight = 0;
+    
+    CanDragCallback  = 0;
+    CanDropCallback  = 0;
+    AddCallback      = 0;
+    RemoveCallback   = 0;
+    ClickCallback    = 0;
+    DblClickCallback = 0;
+
+    uDragRule = CS_DRAG_ALL;
+    uDropRule = CS_DROP_ALL;
+
+    xjustify = yjustify = xadjust = yadjust = 0;
+
+    nFlashCount        = 0;
+    fFlashVisible    = false;
+    uFlashTimer        = (UINT)-1;
+
+    fMouseDragging = false;
+
+    mxlock = CreateMutex(0, FALSE, 0);
+}
+
+CardRegion::~CardRegion()
+{
+    CloseHandle(mxlock);
+}
+
+void CardRegion::SetBackColor(COLORREF cr)
+{
+    crBackgnd = cr;
+}
+
+int CardRegion::CalcApparentCards(int realnum)
+{
+    return ((realnum + nThreedCount - 1) - (realnum + nThreedCount - 1) % nThreedCount) / nThreedCount;
+}
+
+void CardRegion::CalcApparentCards()
+{
+    nNumApparentCards = CalcApparentCards(cardstack.NumCards());
+}
+
+
+void CardRegion::UpdateSize(void)
+{
+    if(cardstack.NumCards() > 0)
+    {
+        if(xoffset > 0)
+            width  = (nNumApparentCards - 1) * xoffset + __cardwidth;
+        else
+            width  = (nNumApparentCards - 1) * -xoffset + __cardwidth;
+
+        if(yoffset > 0)
+            height = (nNumApparentCards - 1) * yoffset + __cardheight;
+        else
+            height = (nNumApparentCards - 1) * -yoffset + __cardheight;
+    }
+    else
+    {
+        width = __cardwidth;
+        height = __cardheight;
+    }
+}
+
+CardRegion *CardWindow::CreateRegion(int id, bool fVisible, int x, int y, int xoffset, int yoffset)
+{
+    CardRegion *cr;
+
+    if(nNumCardRegions == MAXCARDSTACKS)
+        return FALSE;
+
+    cr = new CardRegion(*this, id, fVisible, x, y, xoffset, yoffset);
+    cr->SetBackColor(crBackgnd);
+    cr->SetBackCardIdx(nBackCardIdx);
+
+    Regions[nNumCardRegions++] = cr;
+    
+    return cr;
+}
+
+int CardRegion::GetOverlapRatio(int x, int y, int w, int h)
+{
+    RECT me, him;
+    RECT inter;
+    SetRect(&him, x, y, x+w, y+h);
+    SetRect(&me,  xpos, ypos, xpos+width, ypos+height);
+
+    //see if the specified rectangle overlaps us
+    if(IntersectRect(&inter, &me, &him))
+    {
+        int wi = inter.right  - inter.left;
+        int hi = inter.bottom - inter.top;
+
+        int overlap = wi * hi;
+        int total   = width * height;
+
+        int percent = (overlap << 16) / total;
+        return (percent * 100) >> 16;
+    }
+    //do not overlap
+    else
+    {
+        return 0;
+    }
+}
+
+bool CardRegion::SetDragRule(UINT uDragType, pCanDragProc proc)
+{ 
+    switch(uDragType)
+    {
+    case CS_DRAG_NONE: case CS_DRAG_ALL: case CS_DRAG_TOP:
+        uDragRule = uDragType;
+        return true;
+
+    case CS_DRAG_CALLBACK:
+        uDragRule = uDragType;
+        CanDragCallback = proc;
+        return true;
+
+    default:
+        return false;
+    }
+}
+
+bool CardRegion::SetDropRule(UINT uDropType, pCanDropProc proc)
+{ 
+    switch(uDropType)
+    {
+    case CS_DROP_NONE: case CS_DROP_ALL: 
+        uDropRule = uDropType;
+        return true;
+
+    case CS_DROP_CALLBACK:
+        uDropRule = uDropType;
+        CanDropCallback = proc;
+        return true;
+
+    default:
+        return false;
+    }
+}
+
+void CardRegion::SetClickProc(pClickProc proc)
+{
+    ClickCallback = proc;
+}
+
+void CardRegion::SetDblClickProc(pClickProc proc)
+{
+    DblClickCallback = proc;
+}
+
+void CardRegion::SetAddCardProc(pAddProc proc)
+{
+    AddCallback = proc;
+}
+
+void CardRegion::SetRemoveCardProc(pRemoveProc proc)
+{
+    RemoveCallback = proc;
+}
+
+void CardRegion::Update()
+{
+    CalcApparentCards();
+    UpdateSize(); 
+    UpdateFaceDir(cardstack);
+}
+
+
+bool CardRegion::SetThreedCount(int count)
+{
+    if(count < 1) 
+    {
+        return false;
+    }
+    else
+    {
+        nThreedCount = count;
+        return true;
+    }
+}
+
+void CardRegion::SetOffsets(int x, int y)
+{
+    xoffset = x;
+    yoffset = y;
+}
+
+void CardRegion::SetPos(int x, int y)
+{
+    xpos = x;
+    ypos = y;
+}
+
+void CardRegion::Show(bool fShow)
+{
+    fVisible = fShow;
+}
+
+bool CardRegion::IsVisible()
+{ 
+    return fVisible;
+}
+
+void CardRegion::SetPlacement(UINT xJustify, UINT yJustify, int xAdjust, int yAdjust)
+{
+    xjustify = xJustify;
+    yjustify = yJustify;
+    xadjust  = xAdjust;
+    yadjust  = yAdjust;
+}
+
+void CardRegion::SetFaceDirection(UINT uDirType, int nOption)
+{
+    switch(uDirType)
+    {
+    case CS_FACE_UP:     case CS_FACE_DOWN: case CS_FACE_DOWNUP:
+    case CS_FACE_UPDOWN: case CS_FACE_ANY:
+        uFaceDirType    = uDirType;
+        nFaceDirOption  = nOption;
+
+        UpdateFaceDir(cardstack);
+
+        break;
+    }
+}
+
+UINT CardRegion::GetFaceDirection(int *pnOption)
+{
+    if(pnOption)
+        *pnOption = nFaceDirOption;
+
+    return uFaceDirType;
+}
+
+void CardRegion::AdjustPosition(int winwidth, int winheight)
+{
+    Update();            //Update this stack's card count + size
+
+    switch(xjustify)
+    {
+    default: case CS_XJUST_NONE: break;
+    
+    case CS_XJUST_CENTER:        //centered
+        xpos = (winwidth - (width & ~0x1)) / 2;
+        xpos += xadjust;
+
+        if(xoffset < 0)    xpos += (width - __cardwidth);
+    
+        break;
+
+    case CS_XJUST_RIGHT:        //right-aligned
+        xpos = winwidth - __cardwidth;//width - 20;
+        xpos += xadjust;
+        break;
+    }
+
+    switch(yjustify)
+    {
+    default: case CS_YJUST_NONE: break;
+    
+    case CS_YJUST_CENTER:        //centered
+        ypos = (winheight - height) / 2;
+        ypos += yadjust;
+        if(yoffset < 0)    ypos += (height - __cardheight);
+        break;
+
+    case CS_YJUST_BOTTOM:        //bottom-aligned
+        ypos = winheight - __cardheight;//height - 20;
+        ypos += yadjust;
+        break;
+    }
+
+}
+
+
+void CardRegion::Flash(int count, int milliseconds)
+{
+    if(count <= 0) return;
+
+    nFlashCount        = count;
+    fFlashVisible   = false;
+    uFlashTimer        = SetTimer((HWND)parentWnd, (WPARAM)this, milliseconds, 0);
+    
+    parentWnd.Redraw();
+}
+
+void CardRegion::StopFlash()
+{
+    if(uFlashTimer != (UINT)-1)
+    {
+        KillTimer((HWND)parentWnd, uFlashTimer);
+        nFlashCount        = 0;
+        uFlashTimer        = (UINT)-1;
+        fFlashVisible    = true;
+    }
+}
+
+void CardRegion::DoFlash()
+{
+    if(uFlashTimer != (UINT)-1)
+    {
+        fFlashVisible = !fFlashVisible;
+
+        if(--nFlashCount == 0)
+        {
+            KillTimer((HWND)parentWnd, uFlashTimer);
+            uFlashTimer = (UINT)-1;
+            fFlashVisible = true;
+        }
+    
+        parentWnd.Redraw();
+    }
+}
+
+int CardRegion::Id()
+{
+    return id;
+}
+
+void CardRegion::SetEmptyImage(UINT uImage)
+{
+    switch(uImage)
+    {
+    case CS_EI_NONE: case CS_EI_SUNK:
+        uEmptyImage = uImage;
+        break;
+
+    default:
+        uEmptyImage = CS_EI_NONE;
+        break;
+    }
+    
+}
+
+void CardRegion::SetBackCardIdx(UINT uBackIdx)
+{
+    if(uBackIdx >= 52 && uBackIdx <= 68)
+        nBackCardIdx = uBackIdx;
+}
+
+void CardRegion::SetCardStack(const CardStack &cs)
+{ 
+    //make a complete copy of the specified stack..
+    cardstack = cs; 
+
+    // Update the face-direction and stack-size
+    Update();
+}
+
+const CardStack & CardRegion::GetCardStack()
+{ 
+    //return reference to our internal stack
+    return cardstack; 
+}
+
+//
+//    Update specified card-stack using THIS stack's
+//  face direction rules!
+//
+void CardRegion::UpdateFaceDir(CardStack &cards)
+{
+    int i, n, num;
+
+    num = cards.NumCards();
+
+    //Now apply the face direction rules..
+    switch(uFaceDirType)
+    {
+    case CS_FACE_UP:
+
+        for(i = 0; i < num; i++)
+        {
+            cards[i].SetFaceUp(true);
+        }
+
+        break;
+
+    case CS_FACE_DOWN:
+
+        for(i = 0; i < num; i++)
+        {
+            cards[i].SetFaceUp(false);
+        }
+
+        break;
+
+    case CS_FACE_DOWNUP:
+
+        num = cardstack.NumCards();
+        n = min(nFaceDirOption, num);
+
+        //bottom n cards..
+        for(i = 0; i < n; i++)
+        {
+            cards[num - i - 1].SetFaceUp(false);
+        }
+
+        for(i = n; i < num; i++)
+        {
+            cards[num - i - 1].SetFaceUp(true);
+        }
+
+        break;
+
+    case CS_FACE_UPDOWN:
+
+        num = cardstack.NumCards();
+        n = min(nFaceDirOption, num);
+
+        for(i = 0; i < n; i++)
+        {
+            cards[num - i - 1].SetFaceUp(true);
+        }
+
+        for(i = n; i < num; i++)
+        {
+            cards[num - i - 1].SetFaceUp(false);
+        }
+
+        break;
+
+    case CS_FACE_ANY:    //cards can be any orientation
+    default:
+        break;
+    }
+}
+
+bool CardRegion::MoveCard(CardRegion *pDestStack, int nNumCards, bool fAnimate)
+{
+    HDC hdc;
+
+    int x, y;
+
+    if(pDestStack == 0) return false; //{ forcedfacedir = -1 ;return 0; }
+
+    if(nNumCards < 0 || nNumCards > cardstack.NumCards())
+        return false;
+
+    x = xpos + xoffset * (nNumApparentCards - nNumCards);
+    y = ypos + yoffset * (nNumApparentCards - nNumCards);
+
+    oldx = x;
+    oldy = y;
+    
+    dragstack = cardstack.Pop(nNumCards);
+
+    //Alter the drag-stack so that it's cards are the same way up
+    //as the destination. Use the destination's drag-rules
+    //instead of this ones!!
+    CardStack temp;
+    temp.Push(pDestStack->GetCardStack());
+    temp.Push(dragstack);
+
+    pDestStack->UpdateFaceDir(temp);
+
+    dragstack = temp.Pop(nNumCards);
+
+    if(fAnimate)
+    {
+        iNumDragCards = nNumCards;
+        PrepareDragBitmaps(nNumCards);
+    }
+
+    Update();        //Update this stack's size+card count
+
+    if(fAnimate)
+    {
+        hdc = GetDC((HWND)parentWnd);
+
+        ZoomCard(hdc, x, y, pDestStack);
+        
+        ReleaseDC((HWND)parentWnd, hdc);
+        ReleaseDragBitmaps();
+    }
+
+    // Get a copy of the cardstack
+    CardStack cs = pDestStack->GetCardStack();
+    cs.Push(dragstack);
+    
+    pDestStack->SetCardStack(cs);
+    
+    //cs = pDestStack->GetCardStack();
+    //pDestStack->Update();
+    //pDestStack->UpdateFaceDir(cs);
+
+    RedrawIfNotDim(pDestStack, false);
+
+    //forcedfacedir = -1;
+    return true;
+}
+
+//
+//    Simple wrappers
+//
+int CardRegion::NumCards() const
+{
+    if(fMouseDragging)
+        return cardstack.NumCards() + dragstack.NumCards();
+    else
+        return cardstack.NumCards(); 
+}
+
+bool CardRegion::Lock()
+{
+    DWORD dw = WaitForSingleObject(mxlock, 0);
+
+    if(dw == WAIT_OBJECT_0)
+    {
+        //TRACE("LockStack succeeded\n");
+        return true; 
+    }
+    else
+    {
+        //TRACE("LockStack failed\n");
+        return false;
+    }
+    return false;
+}
+
+bool CardRegion::UnLock()
+{
+    if(ReleaseMutex(mxlock))
+    {
+        //TRACE("Unlocking stack\n");
+        return true;
+    }
+    else
+    {
+        //TRACE("Unlocking stack failed\n");    
+        return false;
+    }
+}
+
+bool CardRegion::PlayCard(CardRegion *pDestStack, int value, int num)
+{
+    //search the stack for the specified card value...
+    while(num--)
+    {
+        for(int i = 0; i < cardstack.NumCards(); i++)
+        {
+            if(cardstack[i].HiVal() == value)
+            {
+                //swap the card with one at top pos...
+                Card card = cardstack.RemoveCard(i);
+                cardstack.Push(card);
+
+                Redraw();
+
+                MoveCard(pDestStack, 1, true);
+                break;
+            }
+        }
+    }
+
+    return true;
+}
+
+//
+//    Redraw the current stack if it has a different
+//    layout than the comparison stack.
+//
+void CardRegion::RedrawIfNotDim(CardRegion *pCompare, bool fFullRedraw)
+{
+    //
+    //
+    //
+    if( pCompare->xoffset != xoffset || 
+        pCompare->yoffset != yoffset || 
+        pCompare->nThreedCount != nThreedCount ||
+        pCompare->uFaceDirType != uFaceDirType ||
+        pCompare->uFaceDirType != CS_FACE_ANY
+        )
+    {
+        if(fFullRedraw)
+            parentWnd.Redraw();
+        else
+            pCompare->Redraw();
+    }
+    
+}
+
+//
+//    SimulateDrag mimicks the complete drag+drop process.
+//  It basically just a MoveCard(..), but it calls the
+//  event callbacks as well.
+//
+bool CardRegion::SimulateDrag(CardRegion *pDestStack, int iNumDragCards, bool fAnimate)
+{
+    if(pDestStack == 0)
+        return false;
+
+    if(CanDragCards(iNumDragCards) != false)
+    {
+        //make a list of the cards that would be in the drag list
+        CardStack tempstack = cardstack.Top(iNumDragCards);
+
+        if(pDestStack->CanDropCards(tempstack)) 
+        {
+            MoveCard(pDestStack, iNumDragCards, fAnimate);        
+                
+            if(RemoveCallback)
+                RemoveCallback(*this, iNumDragCards);
+
+            if(pDestStack->AddCallback)
+                pDestStack->AddCallback(*pDestStack, pDestStack->cardstack);
+        
+            RedrawIfNotDim(pDestStack, true);
+        }    
+
+    }
+
+    return true;
+}