1 // 2 // CardLib - CardRegion class 3 // 4 // Freeware 5 // Copyright J Brown 2001 6 // 7 8 #include "cardlib.h" 9 10 HBITMAP CreateSinkBmp(HDC hdcCompat, HDC hdc, int width, int height); 11 12 void PaintRect(HDC hdc, RECT *rect, COLORREF colour); 13 14 CardRegion::CardRegion(CardWindow &parent, int Id, bool visible, int x, int y, int xOffset, int yOffset) 15 : id(Id), parentWnd(parent), xpos(x), ypos(y), xoffset(xOffset), yoffset(yOffset), fVisible(visible) 16 { 17 width = __cardwidth; 18 height = __cardheight; 19 20 crBackgnd = RGB(0, 64, 100); 21 22 uFaceDirType = CS_FACE_UP; 23 nFaceDirOption = 0; 24 uEmptyImage = CS_EI_SUNK; 25 26 fVisible = visible; 27 28 nThreedCount = 1; 29 30 Update(); //Update this stack's size+card count 31 32 hdcBackGnd = 0; 33 hbmBackGnd = 0; 34 hdcDragCard = 0; 35 hbmDragCard = 0; 36 37 nDragCardWidth = 0; 38 nDragCardHeight = 0; 39 40 CanDragCallback = 0; 41 CanDropCallback = 0; 42 AddCallback = 0; 43 RemoveCallback = 0; 44 ClickCallback = 0; 45 ClickReleaseCallback = 0; 46 DblClickCallback = 0; 47 48 uDragRule = CS_DRAG_ALL; 49 uDropRule = CS_DROP_ALL; 50 51 xjustify = yjustify = xadjust = yadjust = 0; 52 53 nFlashCount = 0; 54 fFlashVisible = false; 55 uFlashTimer = (UINT)-1; 56 57 fMouseDragging = false; 58 59 mxlock = CreateMutex(0, FALSE, 0); 60 } 61 62 CardRegion::~CardRegion() 63 { 64 CloseHandle(mxlock); 65 } 66 67 void CardRegion::SetBackColor(COLORREF cr) 68 { 69 crBackgnd = cr; 70 } 71 72 int CardRegion::CalcApparentCards(int realnum) 73 { 74 return ((realnum + nThreedCount - 1) - (realnum + nThreedCount - 1) % nThreedCount) / nThreedCount; 75 } 76 77 void CardRegion::CalcApparentCards() 78 { 79 nNumApparentCards = CalcApparentCards(cardstack.NumCards()); 80 } 81 82 83 void CardRegion::UpdateSize(void) 84 { 85 if(cardstack.NumCards() > 0) 86 { 87 if(xoffset > 0) 88 width = (nNumApparentCards - 1) * xoffset + __cardwidth; 89 else 90 width = (nNumApparentCards - 1) * -xoffset + __cardwidth; 91 92 if(yoffset > 0) 93 height = (nNumApparentCards - 1) * yoffset + __cardheight; 94 else 95 height = (nNumApparentCards - 1) * -yoffset + __cardheight; 96 } 97 else 98 { 99 width = __cardwidth; 100 height = __cardheight; 101 } 102 } 103 104 CardRegion *CardWindow::CreateRegion(int id, bool fVisible, int x, int y, int xoffset, int yoffset) 105 { 106 CardRegion *cr; 107 108 if(nNumCardRegions == MAXCARDSTACKS) 109 return FALSE; 110 111 cr = new CardRegion(*this, id, fVisible, x, y, xoffset, yoffset); 112 cr->SetBackColor(crBackgnd); 113 cr->SetBackCardIdx(nBackCardIdx); 114 115 Regions[nNumCardRegions++] = cr; 116 117 return cr; 118 } 119 120 int CardRegion::GetOverlapRatio(int x, int y, int w, int h) 121 { 122 RECT me, him; 123 RECT inter; 124 SetRect(&him, x, y, x+w, y+h); 125 SetRect(&me, xpos, ypos, xpos+width, ypos+height); 126 127 //see if the specified rectangle overlaps us 128 if(IntersectRect(&inter, &me, &him)) 129 { 130 int wi = inter.right - inter.left; 131 int hi = inter.bottom - inter.top; 132 133 int overlap = wi * hi; 134 int total = width * height; 135 136 int percent = (overlap << 16) / total; 137 return (percent * 100) >> 16; 138 } 139 //do not overlap 140 else 141 { 142 return 0; 143 } 144 } 145 146 bool CardRegion::SetDragRule(UINT uDragType, pCanDragProc proc) 147 { 148 switch(uDragType) 149 { 150 case CS_DRAG_NONE: case CS_DRAG_ALL: case CS_DRAG_TOP: 151 uDragRule = uDragType; 152 return true; 153 154 case CS_DRAG_CALLBACK: 155 uDragRule = uDragType; 156 CanDragCallback = proc; 157 return true; 158 159 default: 160 return false; 161 } 162 } 163 164 bool CardRegion::SetDropRule(UINT uDropType, pCanDropProc proc) 165 { 166 switch(uDropType) 167 { 168 case CS_DROP_NONE: case CS_DROP_ALL: 169 uDropRule = uDropType; 170 return true; 171 172 case CS_DROP_CALLBACK: 173 uDropRule = uDropType; 174 CanDropCallback = proc; 175 return true; 176 177 default: 178 return false; 179 } 180 } 181 182 void CardRegion::SetClickProc(pClickProc proc) 183 { 184 ClickCallback = proc; 185 } 186 187 void CardRegion::SetClickReleaseProc(pClickProc proc) 188 { 189 ClickReleaseCallback = proc; 190 } 191 192 void CardRegion::SetDblClickProc(pClickProc proc) 193 { 194 DblClickCallback = proc; 195 } 196 197 void CardRegion::SetAddCardProc(pAddProc proc) 198 { 199 AddCallback = proc; 200 } 201 202 void CardRegion::SetRemoveCardProc(pRemoveProc proc) 203 { 204 RemoveCallback = proc; 205 } 206 207 void CardRegion::Update() 208 { 209 CalcApparentCards(); 210 UpdateSize(); 211 UpdateFaceDir(cardstack); 212 } 213 214 215 bool CardRegion::SetThreedCount(int count) 216 { 217 if(count < 1) 218 { 219 return false; 220 } 221 else 222 { 223 nThreedCount = count; 224 return true; 225 } 226 } 227 228 void CardRegion::SetOffsets(int x, int y) 229 { 230 xoffset = x; 231 yoffset = y; 232 } 233 234 void CardRegion::SetPos(int x, int y) 235 { 236 xpos = x; 237 ypos = y; 238 } 239 240 void CardRegion::Show(bool fShow) 241 { 242 fVisible = fShow; 243 } 244 245 bool CardRegion::IsVisible() 246 { 247 return fVisible; 248 } 249 250 void CardRegion::SetPlacement(UINT xJustify, UINT yJustify, int xAdjust, int yAdjust) 251 { 252 xjustify = xJustify; 253 yjustify = yJustify; 254 xadjust = xAdjust; 255 yadjust = yAdjust; 256 } 257 258 void CardRegion::SetFaceDirection(UINT uDirType, int nOption) 259 { 260 switch(uDirType) 261 { 262 case CS_FACE_UP: case CS_FACE_DOWN: case CS_FACE_DOWNUP: 263 case CS_FACE_UPDOWN: case CS_FACE_ANY: 264 uFaceDirType = uDirType; 265 nFaceDirOption = nOption; 266 267 UpdateFaceDir(cardstack); 268 269 break; 270 } 271 } 272 273 UINT CardRegion::GetFaceDirection(int *pnOption) 274 { 275 if(pnOption) 276 *pnOption = nFaceDirOption; 277 278 return uFaceDirType; 279 } 280 281 void CardRegion::AdjustPosition(int winwidth, int winheight) 282 { 283 Update(); //Update this stack's card count + size 284 285 switch(xjustify) 286 { 287 default: case CS_XJUST_NONE: break; 288 289 case CS_XJUST_CENTER: //centered 290 xpos = (winwidth - (width & ~0x1)) / 2; 291 xpos += xadjust; 292 293 if(xoffset < 0) xpos += (width - __cardwidth); 294 295 break; 296 297 case CS_XJUST_RIGHT: //right-aligned 298 xpos = winwidth - __cardwidth;//width - 20; 299 xpos += xadjust; 300 break; 301 } 302 303 switch(yjustify) 304 { 305 default: case CS_YJUST_NONE: break; 306 307 case CS_YJUST_CENTER: //centered 308 ypos = (winheight - height) / 2; 309 ypos += yadjust; 310 if(yoffset < 0) ypos += (height - __cardheight); 311 break; 312 313 case CS_YJUST_BOTTOM: //bottom-aligned 314 ypos = winheight - __cardheight;//height - 20; 315 ypos += yadjust; 316 break; 317 } 318 319 } 320 321 322 void CardRegion::Flash(int count, int milliseconds) 323 { 324 if(count <= 0) return; 325 326 nFlashCount = count; 327 fFlashVisible = false; 328 uFlashTimer = SetTimer((HWND)parentWnd, (WPARAM)this, milliseconds, 0); 329 330 parentWnd.Redraw(); 331 } 332 333 void CardRegion::StopFlash() 334 { 335 if(uFlashTimer != (UINT)-1) 336 { 337 KillTimer((HWND)parentWnd, uFlashTimer); 338 nFlashCount = 0; 339 uFlashTimer = (UINT)-1; 340 fFlashVisible = true; 341 } 342 } 343 344 void CardRegion::DoFlash() 345 { 346 if(uFlashTimer != (UINT)-1) 347 { 348 fFlashVisible = !fFlashVisible; 349 350 if(--nFlashCount == 0) 351 { 352 KillTimer((HWND)parentWnd, uFlashTimer); 353 uFlashTimer = (UINT)-1; 354 fFlashVisible = true; 355 } 356 357 parentWnd.Redraw(); 358 } 359 } 360 361 int CardRegion::Id() 362 { 363 return id; 364 } 365 366 void CardRegion::SetEmptyImage(UINT uImage) 367 { 368 switch(uImage) 369 { 370 case CS_EI_NONE: 371 case CS_EI_SUNK: 372 case CS_EI_CIRC: 373 case CS_EI_X: 374 uEmptyImage = uImage; 375 break; 376 377 default: 378 uEmptyImage = CS_EI_NONE; 379 break; 380 } 381 382 } 383 384 void CardRegion::SetBackCardIdx(UINT uBackIdx) 385 { 386 if(uBackIdx >= 52 && uBackIdx <= 68) 387 nBackCardIdx = uBackIdx; 388 } 389 390 void CardRegion::SetCardStack(const CardStack &cs) 391 { 392 //make a complete copy of the specified stack.. 393 cardstack = cs; 394 395 // Update the face-direction and stack-size 396 Update(); 397 } 398 399 const CardStack & CardRegion::GetCardStack() 400 { 401 //return reference to our internal stack 402 return cardstack; 403 } 404 405 // 406 // Update specified card-stack using THIS stack's 407 // face direction rules! 408 // 409 void CardRegion::UpdateFaceDir(CardStack &cards) 410 { 411 int i, n, num; 412 413 num = cards.NumCards(); 414 415 //Now apply the face direction rules.. 416 switch(uFaceDirType) 417 { 418 case CS_FACE_UP: 419 420 for(i = 0; i < num; i++) 421 { 422 cards[i].SetFaceUp(true); 423 } 424 425 break; 426 427 case CS_FACE_DOWN: 428 429 for(i = 0; i < num; i++) 430 { 431 cards[i].SetFaceUp(false); 432 } 433 434 break; 435 436 case CS_FACE_DOWNUP: 437 438 num = cardstack.NumCards(); 439 n = min(nFaceDirOption, num); 440 441 //bottom n cards.. 442 for(i = 0; i < n; i++) 443 { 444 cards[num - i - 1].SetFaceUp(false); 445 } 446 447 for(i = n; i < num; i++) 448 { 449 cards[num - i - 1].SetFaceUp(true); 450 } 451 452 break; 453 454 case CS_FACE_UPDOWN: 455 456 num = cardstack.NumCards(); 457 n = min(nFaceDirOption, num); 458 459 for(i = 0; i < n; i++) 460 { 461 cards[num - i - 1].SetFaceUp(true); 462 } 463 464 for(i = n; i < num; i++) 465 { 466 cards[num - i - 1].SetFaceUp(false); 467 } 468 469 break; 470 471 case CS_FACE_ANY: //cards can be any orientation 472 default: 473 break; 474 } 475 } 476 477 bool CardRegion::MoveCard(CardRegion *pDestStack, int nNumCards, bool fAnimate) 478 { 479 HDC hdc; 480 481 int x, y; 482 483 if(pDestStack == 0) return false; //{ forcedfacedir = -1 ;return 0; } 484 485 if(nNumCards < 0 || nNumCards > cardstack.NumCards()) 486 return false; 487 488 x = xpos + xoffset * (nNumApparentCards - nNumCards); 489 y = ypos + yoffset * (nNumApparentCards - nNumCards); 490 491 oldx = x; 492 oldy = y; 493 494 dragstack = cardstack.Pop(nNumCards); 495 496 //Alter the drag-stack so that it's cards are the same way up 497 //as the destination. Use the destination's drag-rules 498 //instead of this ones!! 499 CardStack temp; 500 temp.Push(pDestStack->GetCardStack()); 501 temp.Push(dragstack); 502 503 pDestStack->UpdateFaceDir(temp); 504 505 dragstack = temp.Pop(nNumCards); 506 507 if(fAnimate) 508 { 509 iNumDragCards = nNumCards; 510 PrepareDragBitmaps(nNumCards); 511 } 512 513 Update(); //Update this stack's size+card count 514 515 if(fAnimate) 516 { 517 hdc = GetDC((HWND)parentWnd); 518 519 ZoomCard(hdc, x, y, pDestStack); 520 521 ReleaseDC((HWND)parentWnd, hdc); 522 ReleaseDragBitmaps(); 523 } 524 525 // Get a copy of the cardstack 526 CardStack cs = pDestStack->GetCardStack(); 527 cs.Push(dragstack); 528 529 pDestStack->SetCardStack(cs); 530 531 //cs = pDestStack->GetCardStack(); 532 //pDestStack->Update(); 533 //pDestStack->UpdateFaceDir(cs); 534 535 RedrawIfNotDim(pDestStack, false); 536 537 //forcedfacedir = -1; 538 return true; 539 } 540 541 // 542 // Simple wrappers 543 // 544 int CardRegion::NumCards() const 545 { 546 if(fMouseDragging) 547 return cardstack.NumCards() + dragstack.NumCards(); 548 else 549 return cardstack.NumCards(); 550 } 551 552 bool CardRegion::Lock() 553 { 554 DWORD dw = WaitForSingleObject(mxlock, 0); 555 556 if(dw == WAIT_OBJECT_0) 557 { 558 //TRACE("LockStack succeeded\n"); 559 return true; 560 } 561 else 562 { 563 //TRACE("LockStack failed\n"); 564 return false; 565 } 566 return false; 567 } 568 569 bool CardRegion::UnLock() 570 { 571 if(ReleaseMutex(mxlock)) 572 { 573 //TRACE("Unlocking stack\n"); 574 return true; 575 } 576 else 577 { 578 //TRACE("Unlocking stack failed\n"); 579 return false; 580 } 581 } 582 583 bool CardRegion::PlayCard(CardRegion *pDestStack, int value, int num) 584 { 585 //search the stack for the specified card value... 586 while(num--) 587 { 588 for(int i = 0; i < cardstack.NumCards(); i++) 589 { 590 if(cardstack[i].HiVal() == value) 591 { 592 //swap the card with one at top pos... 593 Card card = cardstack.RemoveCard(i); 594 cardstack.Push(card); 595 596 Redraw(); 597 598 MoveCard(pDestStack, 1, true); 599 break; 600 } 601 } 602 } 603 604 return true; 605 } 606 607 // 608 // Redraw the current stack if it has a different 609 // layout than the comparison stack. 610 // 611 void CardRegion::RedrawIfNotDim(CardRegion *pCompare, bool fFullRedraw) 612 { 613 // 614 // 615 // 616 if( pCompare->xoffset != xoffset || 617 pCompare->yoffset != yoffset || 618 pCompare->nThreedCount != nThreedCount || 619 pCompare->uFaceDirType != uFaceDirType || 620 pCompare->uFaceDirType != CS_FACE_ANY 621 ) 622 { 623 if(fFullRedraw) 624 parentWnd.Redraw(); 625 else 626 pCompare->Redraw(); 627 } 628 629 } 630 631 // 632 // SimulateDrag mimicks the complete drag+drop process. 633 // It basically just a MoveCard(..), but it calls the 634 // event callbacks as well. 635 // 636 bool CardRegion::SimulateDrag(CardRegion *pDestStack, int iNumDragCards, bool fAnimate) 637 { 638 if(pDestStack == 0) 639 return false; 640 641 if(CanDragCards(iNumDragCards) != false) 642 { 643 if(pDestStack->CanDropCards(cardstack)) 644 { 645 MoveCard(pDestStack, iNumDragCards, fAnimate); 646 647 if(RemoveCallback) 648 RemoveCallback(*this, iNumDragCards); 649 650 if(pDestStack->AddCallback) 651 pDestStack->AddCallback(*pDestStack, pDestStack->cardstack); 652 653 RedrawIfNotDim(pDestStack, true); 654 } 655 656 } 657 658 return true; 659 } 660