[CRT] spawn: define a unicode environment when needed
[reactos.git] / sdk / lib / 3rdparty / cardlib / cardregion.cpp
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 }