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 
CardRegion(CardWindow & parent,int Id,bool visible,int x,int y,int xOffset,int yOffset)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 
~CardRegion()62 CardRegion::~CardRegion()
63 {
64     CloseHandle(mxlock);
65 }
66 
SetBackColor(COLORREF cr)67 void CardRegion::SetBackColor(COLORREF cr)
68 {
69     crBackgnd = cr;
70 }
71 
CalcApparentCards(int realnum)72 int CardRegion::CalcApparentCards(int realnum)
73 {
74     return ((realnum + nThreedCount - 1) - (realnum + nThreedCount - 1) % nThreedCount) / nThreedCount;
75 }
76 
CalcApparentCards()77 void CardRegion::CalcApparentCards()
78 {
79     nNumApparentCards = CalcApparentCards(cardstack.NumCards());
80 }
81 
82 
UpdateSize(void)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 
CreateRegion(int id,bool fVisible,int x,int y,int xoffset,int yoffset)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 
GetOverlapRatio(int x,int y,int w,int h)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 
SetDragRule(UINT uDragType,pCanDragProc proc)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 
SetDropRule(UINT uDropType,pCanDropProc proc)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 
SetClickProc(pClickProc proc)182 void CardRegion::SetClickProc(pClickProc proc)
183 {
184     ClickCallback = proc;
185 }
186 
SetClickReleaseProc(pClickProc proc)187 void CardRegion::SetClickReleaseProc(pClickProc proc)
188 {
189     ClickReleaseCallback = proc;
190 }
191 
SetDblClickProc(pClickProc proc)192 void CardRegion::SetDblClickProc(pClickProc proc)
193 {
194     DblClickCallback = proc;
195 }
196 
SetAddCardProc(pAddProc proc)197 void CardRegion::SetAddCardProc(pAddProc proc)
198 {
199     AddCallback = proc;
200 }
201 
SetRemoveCardProc(pRemoveProc proc)202 void CardRegion::SetRemoveCardProc(pRemoveProc proc)
203 {
204     RemoveCallback = proc;
205 }
206 
Update()207 void CardRegion::Update()
208 {
209     CalcApparentCards();
210     UpdateSize();
211     UpdateFaceDir(cardstack);
212 }
213 
214 
SetThreedCount(int count)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 
SetOffsets(int x,int y)228 void CardRegion::SetOffsets(int x, int y)
229 {
230     xoffset = x;
231     yoffset = y;
232 }
233 
SetPos(int x,int y)234 void CardRegion::SetPos(int x, int y)
235 {
236     xpos = x;
237     ypos = y;
238 }
239 
Show(bool fShow)240 void CardRegion::Show(bool fShow)
241 {
242     fVisible = fShow;
243 }
244 
IsVisible()245 bool CardRegion::IsVisible()
246 {
247     return fVisible;
248 }
249 
SetPlacement(UINT xJustify,UINT yJustify,int xAdjust,int yAdjust)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 
SetFaceDirection(UINT uDirType,int nOption)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 
GetFaceDirection(int * pnOption)273 UINT CardRegion::GetFaceDirection(int *pnOption)
274 {
275     if(pnOption)
276         *pnOption = nFaceDirOption;
277 
278     return uFaceDirType;
279 }
280 
AdjustPosition(int winwidth,int winheight)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 
Flash(int count,int milliseconds)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 
StopFlash()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 
DoFlash()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 
Id()361 int CardRegion::Id()
362 {
363     return id;
364 }
365 
SetEmptyImage(UINT uImage)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 
SetBackCardIdx(UINT uBackIdx)384 void CardRegion::SetBackCardIdx(UINT uBackIdx)
385 {
386     if(uBackIdx >= 52 && uBackIdx <= 68)
387         nBackCardIdx = uBackIdx;
388 }
389 
SetCardStack(const CardStack & cs)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 
GetCardStack()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 //
UpdateFaceDir(CardStack & cards)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 
MoveCard(CardRegion * pDestStack,int nNumCards,bool fAnimate)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 //
NumCards() const544 int CardRegion::NumCards() const
545 {
546     if(fMouseDragging)
547         return cardstack.NumCards() + dragstack.NumCards();
548     else
549         return cardstack.NumCards();
550 }
551 
Lock()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 
UnLock()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 
PlayCard(CardRegion * pDestStack,int value,int num)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 //
RedrawIfNotDim(CardRegion * pCompare,bool fFullRedraw)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 //
SimulateDrag(CardRegion * pDestStack,int iNumDragCards,bool fAnimate)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