mirror of
https://github.com/reactos/reactos.git
synced 2024-08-19 18:02:55 +00:00
660 lines
13 KiB
C++
660 lines
13 KiB
C++
//
|
|
// CardLib - CardRegion class
|
|
//
|
|
// Freeware
|
|
// Copyright J Brown 2001
|
|
//
|
|
|
|
#include "cardlib.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;
|
|
|
|
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;
|
|
ClickReleaseCallback = 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::SetClickReleaseProc(pClickProc proc)
|
|
{
|
|
ClickReleaseCallback = 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:
|
|
case CS_EI_CIRC:
|
|
case CS_EI_X:
|
|
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)
|
|
{
|
|
if(pDestStack->CanDropCards(cardstack))
|
|
{
|
|
MoveCard(pDestStack, iNumDragCards, fAnimate);
|
|
|
|
if(RemoveCallback)
|
|
RemoveCallback(*this, iNumDragCards);
|
|
|
|
if(pDestStack->AddCallback)
|
|
pDestStack->AddCallback(*pDestStack, pDestStack->cardstack);
|
|
|
|
RedrawIfNotDim(pDestStack, true);
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
}
|