Lars Martin Hambro <lars_martin4 AT hotmail DOT com>
[reactos.git] / base / applications / 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 #include <stdlib.h>
10
11 #include "globals.h"
12 #include "cardlib.h"
13 #include "cardbutton.h"
14 #include "cardregion.h"
15 #include "cardwindow.h"
16 #include "cardcolor.h"
17
18 extern HPALETTE __holdplacepal;
19
20 HPALETTE UseNicePalette(HDC hdc, HPALETTE hPalette)
21 {
22 HPALETTE hOld;
23
24 hOld = SelectPalette(hdc, hPalette, FALSE);
25 RealizePalette(hdc);
26
27 return hOld;
28 }
29
30 void RestorePalette(HDC hdc, HPALETTE hOldPal)
31 {
32 SelectPalette(hdc, hOldPal, TRUE);
33 }
34
35 HPALETTE MakePaletteFromCols(COLORREF cols[], int nNumColours);
36 void PaintRect(HDC hdc, RECT *rect, COLORREF colour);
37 HBITMAP CreateSinkBmp(HDC hdcCompat, HDC hdc, COLORREF col, int width, int height);
38 void GetSinkCols(COLORREF crBase, COLORREF *fg, COLORREF *bg, COLORREF *sh1, COLORREF *sh2);
39
40 void LoadCardBitmaps();
41 void FreeCardBitmaps();
42
43 static TCHAR szCardName[] = _T("CardWnd32");
44 static bool fRegistered = false;
45 static LONG uCardBitmapRef = 0;
46
47
48 void RegisterCardWindow()
49 {
50 WNDCLASSEX wc;
51
52 //Window class for the main application parent window
53 wc.cbSize = sizeof(wc);
54 wc.style = CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW;
55 wc.lpfnWndProc = CardWindow::CardWndProc;
56 wc.cbClsExtra = 0;
57 wc.cbWndExtra = sizeof(CardWindow *);
58 wc.hInstance = GetModuleHandle(0);
59 wc.hIcon = 0;
60 wc.hCursor = LoadCursor (NULL, IDC_ARROW);
61 wc.hbrBackground = 0;
62 wc.lpszMenuName = 0;
63 wc.lpszClassName = szCardName;
64 wc.hIconSm = 0;
65
66 RegisterClassEx(&wc);
67 }
68
69 CardWindow::CardWindow() : m_hWnd(0)
70 {
71 HDC hdc = GetDC(0);
72
73 nNumButtons = 0;
74 nNumCardRegions = 0;
75 nNumDropZones = 0;
76 nBackCardIdx = 53;
77
78 ResizeWndCallback = 0;
79 hbmBackImage = 0;
80 hdcBackImage = 0;
81
82 srand((unsigned)GetTickCount());
83
84 //All colours (buttons, highlights, decks)
85 //are calculated off this single base colour
86 crBackgnd = PALETTERGB(0,80,0);//PALETTERGB(0,64,100);
87
88 // If uCardBitmapRef was previously zero, then
89 // load the card bitmaps
90 if(1 == InterlockedIncrement(&uCardBitmapRef))
91 {
92 LoadCardBitmaps();
93
94 __hPalette = CreateCardPalette();
95
96 __hdcPlaceHolder = CreateCompatibleDC(hdc);
97
98 __holdplacepal = UseNicePalette(__hdcPlaceHolder, __hPalette);
99
100 __hbmPlaceHolder = CreateSinkBmp(hdc, __hdcPlaceHolder, crBackgnd, __cardwidth, __cardheight);
101
102 }
103
104 ReleaseDC(0, hdc);
105
106 //register the window class if necessary
107 if(!fRegistered)
108 {
109 fRegistered = true;
110 RegisterCardWindow();
111 }
112
113 }
114
115 BOOL CardWindow::Create(HWND hwndParent, DWORD dwExStyle, DWORD dwStyle, int x, int y, int width, int height)
116 {
117 if(m_hWnd)
118 return FALSE;
119
120 //Create the window associated with this object
121 m_hWnd = CreateWindowEx(WS_EX_CLIENTEDGE, szCardName, 0,
122 WS_CHILD | WS_VISIBLE,
123 0,0,100,100,
124 hwndParent, 0, GetModuleHandle(0), this);
125
126 return TRUE;
127 }
128
129 BOOL CardWindow::Destroy()
130 {
131 DestroyWindow(m_hWnd);
132 m_hWnd = 0;
133
134 return TRUE;
135 }
136
137 CardWindow::~CardWindow()
138 {
139 if(m_hWnd)
140 DestroyWindow(m_hWnd);
141
142 DeleteAll();
143
144 if(0 == InterlockedDecrement(&uCardBitmapRef))
145 {
146 FreeCardBitmaps();
147
148 DeleteObject(__hbmPlaceHolder);
149 DeleteDC (__hdcPlaceHolder);
150
151 RestorePalette(__hdcPlaceHolder, __holdplacepal);
152
153 if(__hPalette)
154 DeleteObject(__hPalette);
155 }
156 }
157
158 bool CardWindow::DeleteAll()
159 {
160 int i;
161
162 for(i = 0; i < nNumCardRegions; i++)
163 {
164 delete Regions[i];
165 }
166
167 for(i = 0; i < nNumButtons; i++)
168 {
169 delete Buttons[i];
170 }
171
172 for(i = 0; i < nNumDropZones; i++)
173 {
174 delete dropzone[i];
175 }
176
177 nNumCardRegions = nNumButtons = nNumDropZones = 0;
178
179 return true;
180 }
181
182 void CardWindow::SetBackColor(COLORREF cr)
183 {
184 crBackgnd = cr;
185 int i;
186
187 //
188 // Create the exact palette we need to render the buttons/stacks
189 //
190 RestorePalette(__hdcPlaceHolder, __holdplacepal);
191
192 if(__hPalette)
193 DeleteObject(__hPalette);
194
195 __hPalette = CreateCardPalette();
196
197 //
198 // re-create the place-holder!
199 HDC hdc = GetDC(m_hWnd);
200
201 DeleteObject(__hbmPlaceHolder);
202
203 __holdplacepal = UseNicePalette(__hdcPlaceHolder, __hPalette);
204
205 __hbmPlaceHolder = CreateSinkBmp(hdc, __hdcPlaceHolder, crBackgnd, __cardwidth, __cardheight);
206 //SelectObject(__hdcPlaceHolder, __hbmPlaceHolder);
207
208 //reset all buttons to same colour
209 for(i = 0; i < nNumButtons; i++)
210 {
211 if(Buttons[i]->GetStyle() & CB_PUSHBUTTON)
212 {
213 Buttons[i]->SetBackColor(ColorScaleRGB(crBackgnd, RGB(255,255,255), 0.1));
214 }
215 else
216 {
217 Buttons[i]->SetBackColor(crBackgnd);
218 }
219 }
220
221 for(i = 0; i < nNumCardRegions; i++)
222 {
223 Regions[i]->SetBackColor(crBackgnd);
224 }
225
226
227 ReleaseDC(m_hWnd, hdc);
228 }
229
230 COLORREF CardWindow::GetBackColor()
231 {
232 return crBackgnd;
233 }
234
235 CardButton* CardWindow::CardButtonFromPoint(int x, int y)
236 {
237 CardButton *bptr = 0;
238
239 POINT pt;
240 pt.x = x;
241 pt.y = y;
242
243 //Search BACKWARDS...to reflect the implicit Z-order that
244 //the button creation provided
245 for(int i = nNumButtons - 1; i >= 0; i--)
246 {
247 bptr = Buttons[i];
248 if(PtInRect(&bptr->rect, pt) && bptr->fVisible)
249 return bptr;
250 }
251
252 return 0;
253 }
254
255 CardRegion* CardWindow::CardRegionFromPoint(int x, int y)
256 {
257 POINT pt;
258 pt.x = x;
259 pt.y = y;
260
261 //Search BACKWARDS...to reflect the implicit Z-order that
262 //the stack creation provided
263 for(int i = nNumCardRegions - 1; i >= 0; i--)
264 {
265 if(Regions[i]->IsPointInStack(x, y))
266 return Regions[i];
267 }
268
269 return 0;
270 }
271
272 //
273 // Forward all window messages onto the appropriate
274 // class instance
275 //
276 LRESULT CALLBACK CardWindow::CardWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
277 {
278 CardWindow *cw = (CardWindow *)GetWindowLong(hwnd, 0);
279 return cw->WndProc(hwnd, iMsg, wParam, lParam);
280 }
281
282 void CardWindow::Paint(HDC hdc)
283 {
284 int i;
285 RECT rect;
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 UINT CardWindow::GetBackCardIdx()
739 {
740 return nBackCardIdx;
741 }
742
743 void CardWindow::PaintCardRgn(HDC hdc, int dx, int dy, int width, int height, int sx, int sy)
744 {
745 RECT rect;
746
747 //if just a solid background colour
748 if(hbmBackImage == 0)
749 {
750 SetRect(&rect, dx, dy, dx+width, dy+height);
751
752 /*if(GetVersion() < 0x80000000)
753 {
754 PaintRect(hdc, &rect, MAKE_PALETTERGB(crBackgnd));
755 }
756 else*/
757 {
758 HBRUSH hbr = CreateSolidBrush(MAKE_PALETTERGB(crBackgnd));
759 FillRect(hdc, &rect, hbr);
760 DeleteObject(hbr);
761 }
762 }
763 //otherwise, paint using the bitmap
764 else
765 {
766 // Draw whatever part of background we can
767 BitBlt(hdc, dx, dy, width, height, hdcBackImage, sx, sy, SRCCOPY);
768
769 // Now we need to paint any area outside the bitmap,
770 // just in case the bitmap is too small to fill whole window
771 if(0)//sx + width > bm.bmWidth || sy + height > bm.bmHeight)
772 {
773 // Find out size of bitmap
774 BITMAP bm;
775 GetObject(hbmBackImage, sizeof(bm), &bm);
776
777 HRGN hr1 = CreateRectRgn(sx, sy, sx+width, sy+height);
778 HRGN hr2 = CreateRectRgn(0, 0, bm.bmWidth, bm.bmHeight);
779 HRGN hr3 = CreateRectRgn(0,0, 1, 1);
780 HRGN hr4 = CreateRectRgn(0,0, 1, 1);
781
782 CombineRgn(hr3, hr1, hr2, RGN_DIFF);
783
784 GetClipRgn(hdc, hr4);
785
786 CombineRgn(hr3, hr4, hr3, RGN_AND);
787 SelectClipRgn(hdc, hr3);
788
789 // Fill remaining space not filled with bitmap
790 HBRUSH hbr = CreateSolidBrush(crBackgnd);
791 FillRgn(hdc, hr3, hbr);
792 DeleteObject(hbr);
793
794 // Clean up
795 SelectClipRgn(hdc, hr4);
796
797 DeleteObject(hr1);
798 DeleteObject(hr2);
799 DeleteObject(hr3);
800 DeleteObject(hr4);
801 }
802 }
803 }
804
805 void CardWindow::SetBackImage(HBITMAP hBitmap)
806 {
807 //delete current image?? NO!
808 if(hdcBackImage == 0)
809 {
810 hdcBackImage = CreateCompatibleDC(0);
811 }
812
813 hbmBackImage = hBitmap;
814
815 if(hBitmap)
816 SelectObject(hdcBackImage, hBitmap);
817 }