reactos/sdk/lib/3rdparty/cardlib/cardrgnmouse.cpp
2021-06-11 15:33:08 +03:00

648 lines
16 KiB
C++

//
// CardLib - CardRegion mouse-related stuff
//
// Freeware
// Copyright J Brown 2001
//
#include "cardlib.h"
#include <math.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;
}
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) - 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