1 // 2 // CardLib - CardRegion mouse-related stuff 3 // 4 // Freeware 5 // Copyright J Brown 2001 6 // 7 8 #include "cardlib.h" 9 10 #include <math.h> 11 12 #if 1 13 #define TRACE(s) 14 #else 15 #define TRACE(s) printf("%s(%i): %s",__FILE__,__LINE__,s) 16 #endif 17 18 double __CARDZOOMSPEED = 32; 19 20 int ClipCard(HDC hdc, int x, int y, int width, int height); 21 void DrawCard(HDC hdc, int x, int y, HDC hdcSource, int width, int height); 22 23 #ifdef _DEBUG 24 25 static pDebugClickProc DebugStackClickProc = 0; 26 27 void CardLib_SetStackClickProc(pDebugClickProc proc) 28 { 29 DebugStackClickProc = proc; 30 } 31 32 #endif 33 34 CardRegion *CardWindow::GetBestStack(int x, int y, int w, int h) 35 { 36 int maxoverlap = 0; 37 int maxoverlapidx = -1; 38 39 //find the stack which is most covered by the dropped 40 //cards. Only include those which allow drops. 41 // 42 for(int i = 0; i < nNumCardRegions; i++) 43 { 44 int percent = Regions[i]->GetOverlapRatio(x, y, w, h); 45 46 //if this stack has the biggest coverage yet 47 if(percent > maxoverlap && Regions[i]->IsVisible()) 48 { 49 maxoverlap = percent; 50 maxoverlapidx = i; 51 } 52 } 53 54 //if we found a stack to drop onto 55 if(maxoverlapidx != -1) 56 { 57 return Regions[maxoverlapidx]; 58 } 59 else 60 { 61 return 0; 62 } 63 } 64 65 bool CardRegion::IsPointInStack(int x, int y) 66 { 67 int axpos = xoffset < 0 ? xpos + (nNumApparentCards-1)*xoffset : xpos; 68 int aypos = yoffset < 0 ? ypos + (nNumApparentCards-1)*yoffset : ypos; 69 70 if(x >= axpos && x < axpos + width && y >= aypos && y < aypos + height && fVisible) 71 return true; 72 else 73 return false; 74 } 75 76 int CardRegion::GetNumDragCards(int x, int y) 77 { 78 int cardindex = 0; //index from stack start 79 int maxidx; 80 81 //make x,y relative to the stack's upper left corner 82 x -= xpos + (xoffset < 0 ? (nNumApparentCards/*cardstack.NumCards()*/ - 1) * xoffset : 0); 83 y -= ypos + (yoffset < 0 ? (nNumApparentCards/*cardstack.NumCards()*/ - 1) * yoffset : 0); 84 85 //if stack is empty, cannot drag any cards from it 86 if(cardstack.NumCards() <= 0) 87 return 0; 88 89 //see which card in the stack has been clicked on 90 //top-bottom ordering 91 if(yoffset > 0) 92 { 93 if(y < height - __cardheight) 94 cardindex = y / yoffset; 95 else 96 cardindex = cardstack.NumCards() - 1; 97 } 98 else if(yoffset < 0) 99 { 100 if(y < __cardheight) 101 cardindex = cardstack.NumCards() - 1; 102 else 103 cardindex = cardstack.NumCards() - ((y - __cardheight) / -yoffset) - 2; 104 } 105 else //yoffset == 0 106 { 107 cardindex = cardstack.NumCards() - 1; 108 } 109 110 maxidx = cardindex; 111 112 //if left-right 113 if(xoffset > 0) 114 { 115 if(x < width - __cardwidth) 116 cardindex = x / xoffset; 117 else 118 cardindex = cardstack.NumCards() - 1; 119 } 120 else if(xoffset < 0) 121 { 122 if(x < __cardwidth) 123 cardindex = cardstack.NumCards() - 1; 124 else 125 cardindex = cardstack.NumCards() - ((x - __cardwidth) / -xoffset) - 2; 126 } 127 else 128 { 129 cardindex = cardstack.NumCards() - 1; 130 } 131 132 if(cardindex > maxidx) cardindex = maxidx; 133 134 if(cardindex > cardstack.NumCards()) 135 cardindex = 1; 136 137 //if are trying to drag too many cards at once 138 return cardstack.NumCards() - cardindex; 139 } 140 141 bool CardRegion::CanDragCards(int iNumCards) 142 { 143 if(iNumCards <= 0) return false; 144 if(nThreedCount > 1 && iNumCards > 1) return false; 145 146 if(WaitForSingleObject(mxlock, 0) != WAIT_OBJECT_0) 147 { 148 // TRACE("Failed to gain access to card stack\n"); 149 return false; 150 } 151 152 ReleaseMutex(mxlock); 153 154 switch(uDragRule) 155 { 156 case CS_DRAG_ALL: 157 return true; 158 159 case CS_DRAG_TOP: 160 161 if(iNumCards == 1) 162 return true; 163 else 164 return false; 165 166 case CS_DRAG_NONE: 167 return false; 168 169 case CS_DRAG_CALLBACK: 170 171 if(CanDragCallback) 172 { 173 return CanDragCallback(*this, iNumCards); 174 } 175 else 176 { 177 return false; 178 } 179 180 default: 181 return false; 182 } 183 } 184 185 bool CardRegion::CanDropCards(CardStack &cards) 186 { 187 if(WaitForSingleObject(mxlock, 0) != WAIT_OBJECT_0) 188 { 189 return false; 190 } 191 192 ReleaseMutex(mxlock); 193 194 switch(uDropRule) 195 { 196 case CS_DROP_ALL: 197 return true; 198 199 case CS_DROP_NONE: 200 return false; 201 202 case CS_DROP_CALLBACK: 203 204 if(CanDropCallback) 205 { 206 return CanDropCallback(*this, cards); 207 } 208 else 209 { 210 return false; 211 } 212 213 default: 214 return false; 215 } 216 } 217 218 bool CardRegion::OnLButtonDblClk(int x, int y) 219 { 220 iNumDragCards = GetNumDragCards(x, y); 221 222 if(DblClickCallback) 223 DblClickCallback(*this, iNumDragCards); 224 225 return true; 226 } 227 228 bool CardRegion::OnLButtonDown(int x, int y) 229 { 230 iNumDragCards = GetNumDragCards(x, y); 231 232 #ifdef _DEBUG 233 if(DebugStackClickProc) 234 { 235 if(!DebugStackClickProc(*this)) 236 return false; 237 } 238 #endif 239 240 if(ClickCallback) 241 ClickCallback(*this, iNumDragCards); 242 243 if(CanDragCards(iNumDragCards) != false) 244 { 245 246 //offset of the mouse cursor relative to the top-left corner 247 //of the cards that are being dragged 248 mousexoffset = x - xpos - xoffset * (nNumApparentCards - iNumDragCards); 249 mouseyoffset = y - ypos - yoffset * (nNumApparentCards - iNumDragCards); 250 251 if(xoffset < 0) 252 mousexoffset += -xoffset * (iNumDragCards - 1); 253 254 if(yoffset < 0) 255 mouseyoffset += -yoffset * (iNumDragCards - 1); 256 257 //remove the cards from the source stack 258 dragstack = cardstack.Pop(iNumDragCards); 259 260 //prepare the back buffer, and the drag image 261 PrepareDragBitmaps(iNumDragCards); 262 263 oldx = x - mousexoffset; 264 oldy = y - mouseyoffset; 265 266 Update(); //Update this stack's card count + size 267 268 SetCapture((HWND)parentWnd); 269 270 //set AFTER settings the dragstack... 271 fMouseDragging = true; 272 273 return true; 274 } 275 276 return false; 277 } 278 279 void CardRegion::ClickRelease(int x, int y) 280 { 281 iNumDragCards = GetNumDragCards(x, y); 282 283 if (ClickReleaseCallback) 284 ClickReleaseCallback(*this, iNumDragCards); 285 } 286 287 bool CardRegion::OnLButtonUp(int x, int y) 288 { 289 CardRegion *pDestStack = 0; 290 HDC hdc; 291 int dropstackid = CS_DROPZONE_NODROP; 292 293 RECT dragrect; 294 DropZone *dropzone; 295 296 fMouseDragging = false; 297 298 //first of all, see if any drop zones have been registered 299 SetRect(&dragrect, x-mousexoffset, y-mouseyoffset, x-mousexoffset+nDragCardWidth, y-mouseyoffset+nDragCardHeight); 300 301 dropzone = parentWnd.GetDropZoneFromRect(&dragrect); 302 303 if(dropzone) 304 { 305 dropstackid = dropzone->DropCards(dragstack); 306 307 if(dropstackid != CS_DROPZONE_NODROP) 308 pDestStack = parentWnd.CardRegionFromId(dropstackid); 309 else 310 pDestStack = 0; 311 } 312 else 313 { 314 pDestStack = parentWnd.GetBestStack(x - mousexoffset, y - mouseyoffset, nDragCardWidth, nDragCardHeight); 315 } 316 317 // If have found a stack to drop onto 318 // 319 TRACE ( "can I drop card?\n" ); 320 if(pDestStack && pDestStack->CanDropCards(dragstack)) 321 { 322 TRACE ( "yes, dropping card\n" ); 323 hdc = GetDC((HWND)parentWnd); 324 // UseNicePalette(hdc); 325 ZoomCard(hdc, x - mousexoffset, y - mouseyoffset, pDestStack); 326 ReleaseDC((HWND)parentWnd, hdc); 327 328 // 329 //add the cards to the destination stack 330 // 331 CardStack temp = pDestStack->GetCardStack(); 332 temp.Push(dragstack); 333 334 pDestStack->SetCardStack(temp); 335 // pDestStack->Update(); //Update this stack's card count + size 336 // pDestStack->UpdateFaceDir(temp); 337 338 // Call the remove callback on THIS stack, if one is specified 339 // 340 if(RemoveCallback) 341 RemoveCallback(*this, iNumDragCards); 342 343 // Call the add callback, if one is specified 344 // 345 if(pDestStack->AddCallback) 346 pDestStack->AddCallback(*pDestStack, pDestStack->cardstack);//index, deststack->numcards); 347 348 RedrawIfNotDim(pDestStack, true); 349 TRACE ( "done dropping card\n" ); 350 } 351 352 // 353 // Otherwise, let the cards snap back onto this stack 354 // 355 else 356 { 357 TRACE ( "no, putting card back\n" ); 358 hdc = GetDC((HWND)parentWnd); 359 TRACE ( "calling ZoomCard()\n" ); 360 ZoomCard(hdc, x - mousexoffset, y - mouseyoffset, this); 361 TRACE ( "cardstack += dragstack\n" ); 362 cardstack += dragstack; 363 TRACE ( "calling ReleaseDC()\n" ); 364 ReleaseDC((HWND)parentWnd, hdc); 365 366 TRACE ( "calling Update()\n" ); 367 Update(); //Update this stack's card count + size 368 TRACE ( "done putting card back\n" ); 369 } 370 371 ReleaseDragBitmaps(); 372 ReleaseCapture(); 373 374 TRACE ( "OnLButtonUp() done\n" ); 375 return true; 376 } 377 378 bool CardRegion::OnMouseMove(int x, int y) 379 { 380 HDC hdc; 381 382 hdc = GetDC((HWND)parentWnd); 383 384 x -= mousexoffset; 385 y -= mouseyoffset; 386 387 MoveDragCardTo(hdc, x, y); 388 389 //BitBlt(hdc, nDragCardWidth+10, 0, nDragCardWidth, nDragCardHeight, hdcBackGnd, 0, 0, SRCCOPY); 390 //BitBlt(hdc, 0, 0, nDragCardWidth, nDragCardHeight, hdcDragCard, 0, 0, SRCCOPY); 391 392 ReleaseDC((HWND)parentWnd, hdc); 393 394 oldx = x; 395 oldy = y; 396 397 return true; 398 } 399 400 // 401 // There is a bug in BitBlt when the source x,y 402 // become < 0. So this wrapper function simply adjusts 403 // the coords so that we never try to blt in from this range 404 // 405 BOOL ClippedBitBlt(HDC hdcDest, int x, int y, int width, int height, HDC hdcSrc, int srcx, int srcy, DWORD dwROP) 406 { 407 if(srcx < 0) 408 { 409 x = 0 - srcx; 410 width = width + srcx; 411 srcx = 0; 412 } 413 414 if(srcy < 0) 415 { 416 y = 0 - srcy; 417 height = height + srcy; 418 srcy = 0; 419 } 420 421 return BitBlt(hdcDest, x, y, width, height, hdcSrc, srcx, srcy, dwROP); 422 } 423 424 void CardRegion::MoveDragCardTo(HDC hdc, int x, int y) 425 { 426 RECT inter, rect1, rect2; 427 428 //mask off the new position of the drag-card, so 429 //that it will not be painted over 430 ClipCard(hdc, x, y, nDragCardWidth, nDragCardHeight); 431 432 //restore the area covered by the card at its previous position 433 BitBlt(hdc, oldx, oldy, nDragCardWidth, nDragCardHeight, hdcBackGnd, 0, 0, SRCCOPY); 434 435 //remove clipping so we can draw the card at its new place 436 SelectClipRgn(hdc, NULL); 437 438 //if the card's old and new positions overlap, then we 439 //need some funky code to update the "saved background" image, 440 SetRect(&rect1, oldx, oldy, oldx+nDragCardWidth, oldy+nDragCardHeight); 441 SetRect(&rect2, x, y, x+nDragCardWidth, y+nDragCardHeight); 442 443 if(IntersectRect(&inter, &rect1, &rect2)) 444 { 445 int interwidth = inter.right-inter.left; 446 int interheight = inter.bottom-inter.top; 447 int destx, desty, srcx, srcy; 448 449 if(rect2.left > rect1.left) 450 { 451 destx = 0; srcx = nDragCardWidth - interwidth; 452 } 453 else 454 { 455 destx = nDragCardWidth - interwidth; srcx = 0; 456 } 457 458 if(rect2.top > rect1.top) 459 { 460 desty = 0; srcy = nDragCardHeight - interheight; 461 } 462 else 463 { 464 desty = nDragCardHeight - interheight; srcy = 0; 465 } 466 467 //shift the bit we didn't use for the restore (due to the clipping) 468 //into the opposite corner 469 BitBlt(hdcBackGnd, destx,desty, interwidth, interheight, hdcBackGnd, srcx, srcy, SRCCOPY); 470 471 ExcludeClipRect(hdcBackGnd, destx, desty, destx+interwidth, desty+interheight); 472 473 //this bit requires us to clip the BitBlt (from screen to background) 474 //as BitBlt is a bit buggy it seems 475 ClippedBitBlt(hdcBackGnd, 0,0, nDragCardWidth, nDragCardHeight, hdc, x, y, SRCCOPY); 476 SelectClipRgn(hdcBackGnd, NULL); 477 } 478 else 479 { 480 BitBlt(hdcBackGnd, 0,0, nDragCardWidth, nDragCardHeight, hdc, x, y, SRCCOPY); 481 } 482 483 //finally draw the card to the screen 484 DrawCard(hdc, x, y, hdcDragCard, nDragCardWidth, nDragCardHeight); 485 } 486 487 488 //extern "C" int _fltused(void) { return 0; } 489 //extern "C" int _ftol(void) { return 0; } 490 491 // 492 // Better do this in fixed-point, to stop 493 // VC from linking in floatingpoint-long conversions 494 // 495 //#define FIXED_PREC_MOVE 496 #ifdef FIXED_PREC_MOVE 497 #define PRECISION 12 498 void ZoomCard(HDC hdc, int xpos, int ypos, CARDSTACK *dest) 499 { 500 long dx, dy, x , y; 501 502 503 int apparentcards; 504 x = xpos << PRECISION; y = ypos << PRECISION; 505 506 oldx = (int)xpos; 507 oldy = (int)ypos; 508 509 apparentcards=dest->numcards/dest->threedcount; 510 511 int idestx = dest->xpos + dest->xoffset * (apparentcards);// - iNumDragCards); 512 int idesty = dest->ypos + dest->yoffset * (apparentcards);// - iNumDragCards); 513 514 //normalise the motion vector 515 dx = (idestx<<PRECISION) - x; 516 dy = (idesty<<PRECISION) - y; 517 long recip = (1 << PRECISION) / 1;//sqrt(dx*dx + dy*dy); 518 519 dx *= recip * 16;//CARDZOOMSPEED; 520 dy *= recip * 16;//CARDZOOMSPEED; 521 522 //if(dx < 0) dxinc = 1.001; else 523 524 for(;;) 525 { 526 int ix, iy; 527 x += dx; 528 y += dy; 529 530 ix = (int)x>>PRECISION; 531 iy = (int)y>>PRECISION; 532 if(dx < 0 && ix < idestx) ix = idestx; 533 else if(dx > 0 && ix > idestx) ix = idestx; 534 535 if(dy < 0 && iy < idesty) iy = idesty; 536 else if(dy > 0 && iy > idesty) iy = idesty; 537 538 MoveDragCardTo(hdc, ix, iy); 539 540 if(ix == idestx && iy == idesty) 541 break; 542 543 oldx = (int)x >> PRECISION; 544 oldy = (int)y >> PRECISION; 545 546 //dx *= 1.2; 547 //dy *= 1.2; 548 549 Sleep(10); 550 } 551 } 552 #else 553 void CardRegion::ZoomCard(HDC hdc, int xpos, int ypos, CardRegion *pDestStack) 554 { 555 TRACE ( "ENTER ZoomCard()\n" ); 556 double dx, dy, x ,y; 557 int apparentcards; 558 x = (double)xpos; y = (double)ypos; 559 560 oldx = (int)x; 561 oldy = (int)y; 562 563 apparentcards = pDestStack->cardstack.NumCards() / pDestStack->nThreedCount; 564 565 int idestx = pDestStack->xpos + pDestStack->xoffset * (apparentcards); 566 int idesty = pDestStack->ypos + pDestStack->yoffset * (apparentcards); 567 568 if(pDestStack->yoffset < 0) 569 idesty += pDestStack->yoffset * (iNumDragCards-1); 570 571 if(pDestStack->xoffset < 0) 572 idestx += pDestStack->xoffset * (iNumDragCards-1); 573 574 //normalise the motion vector 575 dx = idestx - x; 576 dy = idesty - y; 577 if ( fabs(dx) + fabs(dy) < 0.001f ) 578 { 579 MoveDragCardTo(hdc, idestx, idesty); 580 return; 581 } 582 double recip = 1.0 / sqrt(dx*dx + dy*dy); 583 dx *= recip * __CARDZOOMSPEED; dy *= recip * __CARDZOOMSPEED; 584 585 //if(dx < 0) dxinc = 1.001; else 586 587 for(;;) 588 { 589 bool attarget = true; 590 int ix, iy; 591 x += dx; 592 y += dy; 593 594 ix = (int)x; 595 iy = (int)y; 596 597 if(dx < 0.0 && ix < idestx) ix = idestx; 598 else if(dx > 0.0 && ix > idestx) ix = idestx; 599 else attarget = false; 600 601 if(dy < 0.0 && iy < idesty) iy = idesty; 602 else if(dy > 0.0 && iy > idesty) iy = idesty; 603 else attarget = false; 604 605 //if the target stack wants the drag cards drawn differently 606 //to how they are, then redraw the drag card image just before 607 //the cards land 608 /*if(attarget == true) 609 { 610 for(int i = 0; i < iNumDragCards; i++) 611 { 612 int xdraw = pDestStack->xoffset*i; 613 int ydraw = pDestStack->yoffset*i; 614 615 if(pDestStack->yoffset < 0) 616 ydraw = -pDestStack->yoffset * (iNumDragCards-i-1); 617 if(pDestStack->xoffset < 0) 618 xdraw = -pDestStack->xoffset * (iNumDragCards-i-1); 619 620 if(pDestStack->facedirection == CS_FACEUP && 621 pDestStack->numcards+i >= dest->numfacedown) 622 { 623 //cdtDraw(hdcDragCard, xdraw, ydraw, iDragCards[i], ectFACES, 0); 624 } 625 else 626 { 627 //cdtDraw(hdcDragCard, xdraw, ydraw, CARDSTACK::backcard, ectBACKS, 0); 628 } 629 } 630 }*/ 631 632 MoveDragCardTo(hdc, ix, iy); 633 634 if(attarget || (ix == idestx && iy == idesty)) 635 break; 636 637 oldx = (int)x; 638 oldy = (int)y; 639 640 //dx *= 1.2; 641 //dy *= 1.2; 642 643 Sleep(10); 644 } 645 TRACE ( "EXIT ZoomCard()\n" ); 646 } 647 #endif 648