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