imported catch-22 sol clone with authors permission
[reactos.git] / rosapps / games / solitaire / cardlib / cardwindow.cpp
1 //
2 // CardLib - CardWindow class
3 //
4 // Freeware
5 // Copyright J Brown 2001
6 //
7 #include <windows.h>
8 #include <tchar.h>
9
10 #include "globals.h"
11 #include "cardlib.h"
12 #include "cardbutton.h"
13 #include "cardregion.h"
14 #include "cardwindow.h"
15 #include "cardcolor.h"
16
17 extern HPALETTE __holdplacepal;
18
19 HPALETTE UseNicePalette(HDC hdc, HPALETTE hPalette)
20 {
21 HPALETTE hOld;
22
23 hOld = SelectPalette(hdc, hPalette, FALSE);
24 RealizePalette(hdc);
25
26 return hOld;
27 }
28
29 void RestorePalette(HDC hdc, HPALETTE hOldPal)
30 {
31 SelectPalette(hdc, hOldPal, TRUE);
32 }
33
34 HPALETTE MakePaletteFromCols(COLORREF cols[], int nNumColours);
35 void PaintRect(HDC hdc, RECT *rect, COLORREF colour);
36 HBITMAP CreateSinkBmp(HDC hdcCompat, HDC hdc, COLORREF col, int width, int height);
37 void GetSinkCols(COLORREF crBase, COLORREF *fg, COLORREF *bg, COLORREF *sh1, COLORREF *sh2);
38
39 void LoadCardBitmaps();
40 void FreeCardBitmaps();
41
42 static TCHAR szCardName[] = _T("CardWnd32");
43 static bool fRegistered = false;
44 static LONG uCardBitmapRef = 0;
45
46
47 void RegisterCardWindow()
48 {
49 WNDCLASSEX wc;
50
51 //Window class for the main application parent window
52 wc.cbSize = sizeof(wc);
53 wc.style = CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW;
54 wc.lpfnWndProc = CardWindow::CardWndProc;
55 wc.cbClsExtra = 0;
56 wc.cbWndExtra = sizeof(CardWindow *);
57 wc.hInstance = GetModuleHandle(0);
58 wc.hIcon = 0;
59 wc.hCursor = LoadCursor (NULL, IDC_ARROW);
60 wc.hbrBackground = 0;
61 wc.lpszMenuName = 0;
62 wc.lpszClassName = szCardName;
63 wc.hIconSm = 0;
64
65 RegisterClassEx(&wc);
66 }
67
68 CardWindow::CardWindow() : m_hWnd(0)
69 {
70 HDC hdc = GetDC(0);
71
72 nNumButtons = 0;
73 nNumCardRegions = 0;
74 nNumDropZones = 0;
75 nBackCardIdx = 53;
76
77 ResizeWndCallback = 0;
78 hbmBackImage = 0;
79 hdcBackImage = 0;
80
81 srand((unsigned)GetTickCount());
82
83 //All colours (buttons, highlights, decks)
84 //are calculated off this single base colour
85 crBackgnd = PALETTERGB(0,80,0);//PALETTERGB(0,64,100);
86
87 // If uCardBitmapRef was previously zero, then
88 // load the card bitmaps
89 if(1 == InterlockedIncrement(&uCardBitmapRef))
90 {
91 LoadCardBitmaps();
92
93 __hPalette = CreateCardPalette();
94
95 __hdcPlaceHolder = CreateCompatibleDC(hdc);
96
97 __holdplacepal = UseNicePalette(__hdcPlaceHolder, __hPalette);
98
99 __hbmPlaceHolder = CreateSinkBmp(hdc, __hdcPlaceHolder, crBackgnd, __cardwidth, __cardheight);
100
101 }
102
103 ReleaseDC(0, hdc);
104
105 //register the window class if necessary
106 if(!fRegistered)
107 {
108 fRegistered = true;
109 RegisterCardWindow();
110 }
111
112 }
113
114 BOOL CardWindow::Create(HWND hwndParent, DWORD dwExStyle, DWORD dwStyle, int x, int y, int width, int height)
115 {
116 if(m_hWnd)
117 return FALSE;
118
119 //Create the window associated with this object
120 m_hWnd = CreateWindowEx(WS_EX_CLIENTEDGE, szCardName, 0,
121 WS_CHILD | WS_VISIBLE,
122 0,0,100,100,
123 hwndParent, 0, GetModuleHandle(0), this);
124
125 return TRUE;
126 }
127
128 BOOL CardWindow::Destroy()
129 {
130 DestroyWindow(m_hWnd);
131 m_hWnd = 0;
132
133 return TRUE;
134 }
135
136 CardWindow::~CardWindow()
137 {
138 if(m_hWnd)
139 DestroyWindow(m_hWnd);
140
141 DeleteAll();
142
143 if(0 == InterlockedDecrement(&uCardBitmapRef))
144 {
145 FreeCardBitmaps();
146
147 DeleteObject(__hbmPlaceHolder);
148 DeleteDC (__hdcPlaceHolder);
149
150 RestorePalette(__hdcPlaceHolder, __holdplacepal);
151
152 if(__hPalette)
153 DeleteObject(__hPalette);
154 }
155 }
156
157 bool CardWindow::DeleteAll()
158 {
159 int i;
160
161 for(i = 0; i < nNumCardRegions; i++)
162 {
163 delete Regions[i];
164 }
165
166 for(i = 0; i < nNumButtons; i++)
167 {
168 delete Buttons[i];
169 }
170
171 for(i = 0; i < nNumDropZones; i++)
172 {
173 delete dropzone[i];
174 }
175
176 nNumCardRegions = nNumButtons = nNumDropZones = 0;
177
178 return true;
179 }
180
181 void CardWindow::SetBackColor(COLORREF cr)
182 {
183 crBackgnd = cr;
184 int i;
185
186 //
187 // Create the exact palette we need to render the buttons/stacks
188 //
189 RestorePalette(__hdcPlaceHolder, __holdplacepal);
190
191 if(__hPalette)
192 DeleteObject(__hPalette);
193
194 __hPalette = CreateCardPalette();
195
196 //
197 // re-create the place-holder!
198 HDC hdc = GetDC(m_hWnd);
199
200 DeleteObject(__hbmPlaceHolder);
201
202 __holdplacepal = UseNicePalette(__hdcPlaceHolder, __hPalette);
203
204 __hbmPlaceHolder = CreateSinkBmp(hdc, __hdcPlaceHolder, crBackgnd, __cardwidth, __cardheight);
205 //SelectObject(__hdcPlaceHolder, __hbmPlaceHolder);
206
207 //reset all buttons to same colour
208 for(i = 0; i < nNumButtons; i++)
209 {
210 if(Buttons[i]->GetStyle() & CB_PUSHBUTTON)
211 {
212 Buttons[i]->SetBackColor(ColorScaleRGB(crBackgnd, RGB(255,255,255), 0.1));
213 }
214 else
215 {
216 Buttons[i]->SetBackColor(crBackgnd);
217 }
218 }
219
220 for(i = 0; i < nNumCardRegions; i++)
221 {
222 Regions[i]->SetBackColor(crBackgnd);
223 }
224
225
226 ReleaseDC(m_hWnd, hdc);
227 }
228
229 COLORREF CardWindow::GetBackColor()
230 {
231 return crBackgnd;
232 }
233
234 CardButton* CardWindow::CardButtonFromPoint(int x, int y)
235 {
236 CardButton *bptr = 0;
237
238 POINT pt;
239 pt.x = x;
240 pt.y = y;
241
242 //Search BACKWARDS...to reflect the implicit Z-order that
243 //the button creation provided
244 for(int i = nNumButtons - 1; i >= 0; i--)
245 {
246 bptr = Buttons[i];
247 if(PtInRect(&bptr->rect, pt) && bptr->fVisible)
248 return bptr;
249 }
250
251 return 0;
252 }
253
254 CardRegion* CardWindow::CardRegionFromPoint(int x, int y)
255 {
256 POINT pt;
257 pt.x = x;
258 pt.y = y;
259
260 //Search BACKWARDS...to reflect the implicit Z-order that
261 //the stack creation provided
262 for(int i = nNumCardRegions - 1; i >= 0; i--)
263 {
264 if(Regions[i]->IsPointInStack(x, y))
265 return Regions[i];
266 }
267
268 return 0;
269 }
270
271 //
272 // Forward all window messages onto the appropriate
273 // class instance
274 //
275 LRESULT CALLBACK CardWindow::CardWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
276 {
277 CardWindow *cw = (CardWindow *)GetWindowLong(hwnd, 0);
278 return cw->WndProc(hwnd, iMsg, wParam, lParam);
279 }
280
281 void CardWindow::Paint(HDC hdc)
282 {
283 int i;
284 RECT rect;
285 int xpos = 10;
286 HPALETTE hOldPal;
287
288 hOldPal = UseNicePalette(hdc, __hPalette);
289
290 //
291 // Clip the card stacks so that they won't
292 // get painted over
293 //
294 for(i = 0; i < nNumCardRegions; i++)
295 {
296 Regions[i]->Clip(hdc);
297 }
298
299 //
300 // Clip the buttons
301 //
302 for(i = 0; i < nNumButtons; i++)
303 {
304 Buttons[i]->Clip(hdc);
305 }
306
307
308 // Now paint the whole screen with background colour,
309 //
310 GetClientRect(m_hWnd, &rect);
311
312 //PaintRect(hdc, &rect, MAKE_PALETTERGB(crBackgnd));
313 PaintCardRgn(hdc, 0, 0, rect.right, rect.bottom, 0, 0);
314 SelectClipRgn(hdc, NULL);
315
316 // Don't let cards draw over buttons, so clip buttons again
317 //
318 for(i = 0; i < nNumButtons; i++)
319 {
320 Buttons[i]->Clip(hdc);
321 }
322
323 // Paint each card stack in turn
324 //
325 for(i = 0; i < nNumCardRegions; i++)
326 {
327 Regions[i]->Render(hdc);
328 }
329
330 // Paint each button now
331 //
332 SelectClipRgn(hdc, NULL);
333
334 for(i = 0; i < nNumButtons; i++)
335 {
336 Buttons[i]->Redraw();
337 }
338
339 RestorePalette(hdc, hOldPal);
340 }
341
342
343
344
345 LRESULT CALLBACK CardWindow::WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
346 {
347 HDC hdc;
348 PAINTSTRUCT ps;
349
350 CREATESTRUCT *cs;
351
352 static CardButton *buttonptr = 0;
353 static CardRegion *stackptr = 0;
354
355 int x, y, i;
356
357 switch(iMsg)
358 {
359 case WM_NCCREATE:
360
361 // When we created this window, we passed in the
362 // pointer to the class object (CardWindow *) in the
363 // call to CreateWindow.
364 cs = (CREATESTRUCT *)lParam;
365
366 //
367 // associate this class with the window
368 //
369 SetWindowLong(hwnd, 0, (LONG)cs->lpCreateParams);
370
371 return 1;
372
373 case WM_NCDESTROY:
374 // Don't delete anything here..
375 break;
376
377 case WM_SIZE:
378 nWidth = LOWORD(lParam);
379 nHeight = HIWORD(lParam);
380
381 //
382 // reposition all the stacks and buttons
383 // in case any of them are centered, right-justified etc
384 //
385 for(i = 0; i < nNumCardRegions; i++)
386 {
387 Regions[i]->AdjustPosition(nWidth, nHeight);
388 }
389
390 for(i = 0; i < nNumButtons; i++)
391 {
392 Buttons[i]->AdjustPosition(nWidth, nHeight);
393 }
394
395 //
396 // Call the user-defined resize proc AFTER all the stacks
397 // have been positioned
398 //
399 if(ResizeWndCallback)
400 ResizeWndCallback(nWidth, nHeight);
401
402 return 0;
403
404 case WM_PAINT:
405
406 hdc = BeginPaint(hwnd, &ps);
407
408 Paint(hdc);
409
410 EndPaint(hwnd, &ps);
411 return 0;
412
413 case WM_TIMER:
414
415 //find the timer object in the registered funcs
416 /*if(wParam >= 0x10000)
417 {
418 for(i = 0; i < nRegFuncs; i++)
419 {
420 if(RegFuncs[i].id == wParam)
421 {
422 KillTimer(hwnd, wParam);
423
424 //call the registered function!!
425 RegFuncs[i].func(RegFuncs[i].dwParam);
426
427 RegFuncs[i] = RegFuncs[nRegFuncs-1];
428 nRegFuncs--;
429 }
430 }
431 }
432 else*/
433 {
434 //find the cardstack
435 CardRegion *stackobj = (CardRegion *)wParam;//CardStackFromId(wParam);
436 stackobj->DoFlash();
437 }
438
439 return 0;
440
441 case WM_LBUTTONDBLCLK:
442
443 x = (short)LOWORD(lParam);
444 y = (short)HIWORD(lParam);
445
446 if((buttonptr = CardButtonFromPoint(x, y)) != 0)
447 {
448 buttonptr->OnLButtonDown(hwnd, x, y);
449 return 0;
450 }
451
452 if((stackptr = CardRegionFromPoint(x, y)) != 0)
453 {
454 stackptr->OnLButtonDblClk(x, y);
455 stackptr = 0;
456 }
457
458 return 0;
459
460 case WM_LBUTTONDOWN:
461
462 x = (short)LOWORD(lParam);
463 y = (short)HIWORD(lParam);
464
465 //if clicked on a button
466 if((buttonptr = CardButtonFromPoint(x, y)) != 0)
467 {
468 if(buttonptr->OnLButtonDown(hwnd, x, y) == 0)
469 buttonptr = 0;
470
471 return 0;
472 }
473
474 if((stackptr = CardRegionFromPoint(x, y)) != 0)
475 {
476 if(!stackptr->OnLButtonDown(x, y))
477 stackptr = 0;
478 }
479
480 return 0;
481
482 case WM_LBUTTONUP:
483
484 x = (short)LOWORD(lParam);
485 y = (short)HIWORD(lParam);
486
487 //
488 // if we were clicking a button
489 //
490 if(buttonptr != 0)
491 {
492 buttonptr->OnLButtonUp(hwnd, x, y);
493 buttonptr = 0;
494 return 0;
495 }
496
497 if(stackptr != 0)
498 {
499 stackptr->OnLButtonUp(x, y);
500 stackptr = 0;
501 return 0;
502 }
503
504 return 0;
505
506 case WM_MOUSEMOVE:
507
508 x = (short)LOWORD(lParam);
509 y = (short)HIWORD(lParam);
510
511 // if we were clicking a button
512 if(buttonptr != 0)
513 {
514 buttonptr->OnMouseMove(hwnd, x, y);
515 return 0;
516 }
517
518 if(stackptr != 0)
519 {
520 return stackptr->OnMouseMove(x, y);
521 }
522
523 return 0;
524
525 }
526
527 return DefWindowProc (hwnd, iMsg, wParam, lParam);
528 }
529
530
531 CardRegion* CardWindow::CardRegionFromId(int id)
532 {
533 for(int i = 0; i < nNumCardRegions; i++)
534 {
535 if(Regions[i]->id == id)
536 return Regions[i];
537 }
538
539 return 0;
540 }
541
542 CardButton* CardWindow::CardButtonFromId(int id)
543 {
544 for(int i = 0; i < nNumButtons; i++)
545 {
546 if(Buttons[i]->id == id)
547 return Buttons[i];
548 }
549
550 return 0;
551 }
552
553 void CardWindow::Redraw()
554 {
555 InvalidateRect(m_hWnd, 0, 0);
556 UpdateWindow(m_hWnd);
557 }
558
559 bool CardWindow::DeleteButton(CardButton *pButton)
560 {
561 for(int i = 0; i < nNumButtons; i++)
562 {
563 if(Buttons[i] == pButton)
564 {
565 CardButton *cb = Buttons[i];
566
567 //shift any after this one backwards
568 for(int j = i; j < nNumButtons - 1; j++)
569 {
570 Buttons[j] = Buttons[j + 1];
571 }
572
573 delete cb;
574 nNumButtons--;
575
576 return true;
577 }
578 }
579
580 return false;
581 }
582
583 bool CardWindow::DeleteRegion(CardRegion *pRegion)
584 {
585 for(int i = 0; i < nNumCardRegions; i++)
586 {
587 if(Regions[i] == pRegion)
588 {
589 CardRegion *cr = Regions[i];
590
591 //shift any after this one backwards
592 for(int j = i; j < nNumCardRegions - 1; j++)
593 {
594 Regions[j] = Regions[j + 1];
595 }
596
597 delete cr;
598 nNumCardRegions--;
599
600 return true;
601 }
602 }
603
604 return false;
605 }
606
607 void CardWindow::EmptyStacks(void)
608 {
609 for(int i = 0; i < nNumCardRegions; i++)
610 {
611 Regions[i]->Clear();
612 Regions[i]->Update();
613 }
614
615 Redraw();
616 }
617
618 bool CardWindow::DistributeStacks(int nIdFrom, int nNumStacks, UINT xJustify, int xSpacing, int nStartX)
619 {
620 int numvisiblestacks = 0;
621 int curx = nStartX;
622 int startindex = -1;
623 int i;
624
625 //find the stack which starts with our ID
626 for(i = 0; i < nNumCardRegions; i++)
627 {
628 if(Regions[i]->Id() == nIdFrom)
629 {
630 startindex = i;
631 break;
632 }
633 }
634
635 //if didn't find, return
636 if(i == nNumCardRegions) return false;
637
638 //count the stacks that are visible
639 for(i = startindex; i < startindex + nNumStacks; i++)
640 {
641 if(Regions[i]->IsVisible())
642 numvisiblestacks++;
643 }
644
645 if(xJustify == CS_XJUST_CENTER)
646 {
647 //startx -= ((numvisiblestacks + spacing) * cardwidth - spacing) / 2;
648 int viswidth;
649 viswidth = numvisiblestacks * __cardwidth;
650 viswidth += xSpacing * (numvisiblestacks - 1);
651 curx = -(viswidth - __cardwidth) / 2;
652
653 for(i = startindex; i < startindex + nNumStacks; i++)
654 {
655 if(Regions[i]->IsVisible())
656 {
657 Regions[i]->xadjust = curx;
658 Regions[i]->xjustify = CS_XJUST_CENTER;
659 curx += Regions[i]->width + xSpacing;
660 }
661
662 }
663 }
664
665 if(xJustify == CS_XJUST_RIGHT)
666 {
667 nStartX -= ((numvisiblestacks + xSpacing) * __cardwidth - xSpacing);
668 }
669
670 if(xJustify == CS_XJUST_NONE)
671 {
672 for(i = startindex; i < startindex + nNumStacks; i++)
673 {
674 if(Regions[i]->IsVisible())
675 {
676 Regions[i]->xpos = curx;
677 curx += Regions[i]->width + xSpacing;
678 Regions[i]->UpdateSize();
679 }
680
681 }
682 }
683
684 return 0;
685 }
686
687 void CardWindow::Update()
688 {
689 for(int i = 0; i < nNumCardRegions; i++)
690 {
691 Regions[i]->AdjustPosition(nWidth, nHeight);
692 }
693 }
694
695
696 void CardWindow::SetResizeProc(pResizeWndProc proc)
697 {
698 ResizeWndCallback = proc;
699 }
700
701
702 HPALETTE CardWindow::CreateCardPalette()
703 {
704 COLORREF cols[10];
705 int nNumCols;
706
707
708 //include button text colours
709 cols[0] = RGB(0, 0, 0);
710 cols[1] = RGB(255, 255, 255);
711
712 //include the base background colour
713 cols[1] = crBackgnd;
714
715 //include the standard button colours...
716 cols[3] = CardButton::GetHighlight(crBackgnd);
717 cols[4] = CardButton::GetShadow(crBackgnd);
718 cols[5] = CardButton::GetFace(crBackgnd);
719
720 //include the sunken image bitmap colours...
721 GetSinkCols(crBackgnd, &cols[6], &cols[7], &cols[8], &cols[9]);
722
723 nNumCols = 10;
724
725 return MakePaletteFromCols(cols, nNumCols);
726 }
727
728 void CardWindow::SetBackCardIdx(UINT uBackIdx)
729 {
730 if(uBackIdx >= 52 && uBackIdx <= 68)
731 nBackCardIdx = uBackIdx;
732
733 for(int i = 0; i < nNumCardRegions; i++)
734 Regions[i]->SetBackCardIdx(uBackIdx);
735
736 }
737
738 void CardWindow::PaintCardRgn(HDC hdc, int dx, int dy, int width, int height, int sx, int sy)
739 {
740 RECT rect;
741
742 //if just a solid background colour
743 if(hbmBackImage == 0)
744 {
745 SetRect(&rect, dx, dy, dx+width, dy+height);
746
747 /*if(GetVersion() < 0x80000000)
748 {
749 PaintRect(hdc, &rect, MAKE_PALETTERGB(crBackgnd));
750 }
751 else*/
752 {
753 HBRUSH hbr = CreateSolidBrush(MAKE_PALETTERGB(crBackgnd));
754 FillRect(hdc, &rect, hbr);
755 DeleteObject(hbr);
756 }
757 }
758 //otherwise, paint using the bitmap
759 else
760 {
761 // Draw whatever part of background we can
762 BitBlt(hdc, dx, dy, width, height, hdcBackImage, sx, sy, SRCCOPY);
763
764 // Now we need to paint any area outside the bitmap,
765 // just in case the bitmap is too small to fill whole window
766 if(0)//sx + width > bm.bmWidth || sy + height > bm.bmHeight)
767 {
768 // Find out size of bitmap
769 BITMAP bm;
770 GetObject(hbmBackImage, sizeof(bm), &bm);
771
772 HRGN hr1 = CreateRectRgn(sx, sy, sx+width, sy+height);
773 HRGN hr2 = CreateRectRgn(0, 0, bm.bmWidth, bm.bmHeight);
774 HRGN hr3 = CreateRectRgn(0,0, 1, 1);
775 HRGN hr4 = CreateRectRgn(0,0, 1, 1);
776
777 CombineRgn(hr3, hr1, hr2, RGN_DIFF);
778
779 GetClipRgn(hdc, hr4);
780
781 CombineRgn(hr3, hr4, hr3, RGN_AND);
782 SelectClipRgn(hdc, hr3);
783
784 // Fill remaining space not filled with bitmap
785 HBRUSH hbr = CreateSolidBrush(crBackgnd);
786 FillRgn(hdc, hr3, hbr);
787 DeleteObject(hbr);
788
789 // Clean up
790 SelectClipRgn(hdc, hr4);
791
792 DeleteObject(hr1);
793 DeleteObject(hr2);
794 DeleteObject(hr3);
795 DeleteObject(hr4);
796 }
797 }
798 }
799
800 void CardWindow::SetBackImage(HBITMAP hBitmap)
801 {
802 //delete current image?? NO!
803 if(hdcBackImage == 0)
804 {
805 hdcBackImage = CreateCompatibleDC(0);
806 }
807
808 hbmBackImage = hBitmap;
809
810 if(hBitmap)
811 SelectObject(hdcBackImage, hBitmap);
812 }