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