1 /*
2  * PROJECT:      Spider Solitaire
3  * LICENSE:      See COPYING in top level directory
4  * FILE:         base/applications/games/spider/spider.cpp
5  * PURPOSE:      Window and message queue for Spider Solitaire
6  * PROGRAMMER:   Gregor Schneider
7  */
8 
9 #include "spider.h"
10 
11 #include <commctrl.h>
12 #include <tchar.h>
13 
14 TCHAR szHelpPath[MAX_PATH];
15 
16 DWORD        dwAppStartTime;
17 HWND         hwndMain;
18 HINSTANCE    hInstance;
19 
20 TCHAR szAppName[128];
21 TCHAR MsgQuit[128];
22 TCHAR MsgAbout[128];
23 TCHAR MsgWin[128];
24 TCHAR MsgDeal[128];
25 DWORD dwDifficulty;
26 
27 CardWindow SpiderWnd;
28 
29 typedef struct _CardBack
30 {
31     HWND hSelf;
32     WNDPROC hOldProc;
33     INT hdcNum;
34     INT imgNum;
35     BOOL bSelected;
36 } CARDBACK, *PCARDBACK;
37 
38 LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
39 
40 void MakePath(TCHAR *szDest, UINT nDestLen, const TCHAR *szExt)
41 {
42     TCHAR *ptr;
43 
44     ptr = szDest + GetModuleFileName(GetModuleHandle(0), szDest, nDestLen) - 1;
45     while(*ptr-- != '.');
46     lstrcpy(ptr + 1, szExt);
47 }
48 
49 INT_PTR CALLBACK DifficultyDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
50 {
51     switch (uMsg)
52     {
53     case WM_INITDIALOG:
54         CheckRadioButton(hDlg, IDC_DIF_ONECOLOR, IDC_DIF_FOURCOLORS, IDC_DIF_ONECOLOR);
55         return TRUE;
56 
57     case WM_COMMAND:
58         switch(LOWORD(wParam))
59         {
60             case IDOK:
61                 if (IsDlgButtonChecked(hDlg, IDC_DIF_ONECOLOR) == BST_CHECKED)
62                     dwDifficulty = IDC_DIF_ONECOLOR;
63                 else if (IsDlgButtonChecked(hDlg, IDC_DIF_TWOCOLORS) == BST_CHECKED)
64                     dwDifficulty = IDC_DIF_TWOCOLORS;
65                 else if (IsDlgButtonChecked(hDlg, IDC_DIF_FOURCOLORS) == BST_CHECKED)
66                     dwDifficulty = IDC_DIF_FOURCOLORS;
67 
68                 NewGame();
69                 EndDialog(hDlg, TRUE);
70                 return TRUE;
71 
72             case IDCANCEL:
73                 EndDialog(hDlg, FALSE);
74                 return TRUE;
75         }
76         break;
77     }
78     return FALSE;
79 }
80 
81 int WINAPI _tWinMain(HINSTANCE hInst, HINSTANCE hPrev, LPTSTR szCmdLine, int iCmdShow)
82 {
83     HWND        hwnd;
84     MSG            msg;
85     WNDCLASS    wndclass;
86     INITCOMMONCONTROLSEX ice;
87     HACCEL        hAccelTable;
88 
89     hInstance = hInst;
90 
91     /* Load application title */
92     LoadString(hInst, IDS_SPI_NAME, szAppName, sizeof(szAppName) / sizeof(szAppName[0]));
93     /* Load MsgBox() texts here to avoid loading them many times later */
94     LoadString(hInst, IDS_SPI_ABOUT, MsgAbout, sizeof(MsgAbout) / sizeof(MsgAbout[0]));
95     LoadString(hInst, IDS_SPI_QUIT, MsgQuit, sizeof(MsgQuit) / sizeof(MsgQuit[0]));
96     LoadString(hInst, IDS_SPI_WIN, MsgWin, sizeof(MsgWin) / sizeof(MsgWin[0]));
97     LoadString(hInst, IDS_SPI_DEAL, MsgDeal, sizeof(MsgDeal) / sizeof(MsgDeal[0]));
98 
99     /* Window class for the main application parent window */
100     wndclass.style             = 0;
101     wndclass.lpfnWndProc       = WndProc;
102     wndclass.cbClsExtra        = 0;
103     wndclass.cbWndExtra        = 0;
104     wndclass.hInstance         = hInst;
105     wndclass.hIcon             = LoadIcon (hInst, MAKEINTRESOURCE(IDI_SPIDER));
106     wndclass.hCursor           = LoadCursor (NULL, IDC_ARROW);
107     wndclass.hbrBackground     = (HBRUSH)NULL;
108     wndclass.lpszMenuName      = MAKEINTRESOURCE(IDR_MENU1);
109     wndclass.lpszClassName     = szAppName;
110 
111     RegisterClass(&wndclass);
112 
113     ice.dwSize = sizeof(ice);
114     ice.dwICC = ICC_BAR_CLASSES;
115     InitCommonControlsEx(&ice);
116 
117     srand((unsigned)GetTickCount());
118 
119     /* InitCardLib(); */
120 
121     /* Construct the path to our help file */
122     MakePath(szHelpPath, MAX_PATH, _T(".hlp"));
123 
124     hwnd = CreateWindow(szAppName,
125                         szAppName,
126                         WS_OVERLAPPEDWINDOW,
127                         CW_USEDEFAULT,
128                         CW_USEDEFAULT,
129                         0,  /*The real size will be computed in WndProc through WM_GETMINMAXINFO */
130                         0,  /* The real size will be computed in WndProc through WM_GETMINMAXINFO */
131                         NULL,
132                         NULL,
133                         hInst,
134                         NULL);
135 
136     hwndMain = hwnd;
137 
138     ShowWindow(hwnd, iCmdShow);
139     UpdateWindow(hwnd);
140 
141     hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR1));
142 
143     DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIFFICULTY), hwnd, DifficultyDlgProc);
144 
145     while (GetMessage(&msg, NULL,0,0))
146     {
147         if (!TranslateAccelerator(hwnd, hAccelTable, &msg))
148         {
149             TranslateMessage(&msg);
150             DispatchMessage(&msg);
151         }
152     }
153     return msg.wParam;
154 }
155 
156 LRESULT CALLBACK CardImageWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
157 {
158     PCARDBACK pCardBack = (PCARDBACK)GetWindowLongPtr(hwnd, GWLP_USERDATA);
159     static WNDPROC hOldProc = NULL;
160 
161     if (!pCardBack)
162         return FALSE;
163 
164     if (!hOldProc)
165         hOldProc = pCardBack->hOldProc;
166 
167     switch (msg)
168     {
169         case WM_PAINT:
170         {
171             HDC hdc;
172             PAINTSTRUCT ps;
173             HPEN hPen, hOldPen;
174             HBRUSH hBrush, hOldBrush;
175             RECT rc;
176 
177             hdc = BeginPaint(hwnd, &ps);
178 
179             if (pCardBack->bSelected)
180             {
181                 hPen = CreatePen(PS_SOLID, 2, RGB(0,0,0));
182             }
183             else
184             {
185                 DWORD Face = GetSysColor(COLOR_3DFACE);
186                 hPen = CreatePen(PS_SOLID, 2, Face);
187             }
188 
189             GetClientRect(hwnd, &rc);
190             hBrush = (HBRUSH)GetStockObject(NULL_BRUSH);
191             hOldPen = (HPEN)SelectObject(hdc, hPen);
192             hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
193 
194             Rectangle(hdc, rc.left+1, rc.top+1, rc.right, rc.bottom);
195 
196             StretchBlt(hdc,
197                        2,
198                        2,
199                        CARDBACK_OPTIONS_WIDTH,
200                        CARDBACK_OPTIONS_HEIGHT,
201                        __hdcCardBitmaps,
202                        pCardBack->hdcNum * __cardwidth,
203                        0,
204                        __cardwidth,
205                        __cardheight,
206                        SRCCOPY);
207 
208             SelectObject(hdc, hOldPen);
209             SelectObject(hdc, hOldBrush);
210 
211             EndPaint(hwnd, &ps);
212 
213             break;
214     }
215 
216     case WM_LBUTTONDOWN:
217         pCardBack->bSelected = pCardBack->bSelected ? FALSE : TRUE;
218         break;
219     }
220 
221     return CallWindowProc(hOldProc, hwnd, msg, wParam, lParam);
222 }
223 
224 
225 INT_PTR CALLBACK CardBackDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
226 {
227     static PCARDBACK pCardBacks = NULL;
228 
229     switch (uMsg)
230     {
231         case WM_INITDIALOG:
232         {
233             INT i, c;
234             SIZE_T size = sizeof(CARDBACK) * NUM_CARDBACKS;
235 
236             pCardBacks = (PCARDBACK)HeapAlloc(GetProcessHeap(), 0, size);
237 
238             if (!pCardBacks)
239                 return FALSE;
240 
241             for (i = 0, c = CARDBACK_START; c <= CARDBACK_END; i++, c++)
242             {
243                 pCardBacks[i].hSelf = GetDlgItem(hDlg, c);
244                 pCardBacks[i].bSelected = FALSE;
245                 pCardBacks[i].hdcNum = CARDBACK_RES_START + i;
246                 pCardBacks[i].imgNum = i + 1;
247                 pCardBacks[i].hOldProc = (WNDPROC)SetWindowLongPtr(pCardBacks[i].hSelf,
248                                                                    GWLP_WNDPROC,
249                                                                    (LONG_PTR)CardImageWndProc);
250 
251                 SetWindowLongPtr(pCardBacks[i].hSelf, GWLP_USERDATA, (LONG_PTR)&pCardBacks[i]);
252             }
253 
254             return TRUE;
255         }
256 
257         case WM_COMMAND:
258             if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
259             {
260                 INT i, num = 0;
261                 for (i = 0; i < NUM_CARDBACKS; i++)
262                 {
263                     if (pCardBacks[i].bSelected)
264                     {
265                         num = pCardBacks[i].imgNum;
266                     }
267                 }
268 
269                 EndDialog(hDlg, LOWORD(wParam) == IDOK ? num : FALSE);
270                 HeapFree(GetProcessHeap(), 0, pCardBacks);
271                 return TRUE;
272             }
273 
274             if (HIWORD(wParam) == STN_CLICKED)
275             {
276                 INT i;
277                 RECT rc;
278                 for (i = 0; i < NUM_CARDBACKS; i++)
279                 {
280                     pCardBacks[i].bSelected = pCardBacks[i].hSelf == (HWND)lParam;
281 
282                     GetClientRect(pCardBacks[i].hSelf, &rc);
283                     InvalidateRect(pCardBacks[i].hSelf, &rc, TRUE);
284                 }
285 
286                 break;
287             }
288     }
289 
290     return FALSE;
291 }
292 
293 
294 VOID ShowDeckOptionsDlg(HWND hwnd)
295 {
296     INT cardBack;
297 
298     if ((cardBack = DialogBox(hInstance, MAKEINTRESOURCE(IDD_CARDBACK), hwnd, CardBackDlgProc)))
299     {
300         SpiderWnd.SetBackCardIdx(CARDBACK_RES_START + (cardBack - 1));
301         SpiderWnd.Redraw();
302     }
303 }
304 
305 LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
306 {
307     static int nWidth, nHeight;
308 
309     switch (iMsg)
310     {
311         case WM_CREATE:
312         {
313             // For now, the Help dialog is disabled because of lacking of HTML Help support
314             EnableMenuItem(GetMenu(hwnd), IDM_HELP_CONTENTS, MF_BYCOMMAND | MF_GRAYED);
315 
316             SpiderWnd.Create(hwnd, 0, WS_CHILD | WS_VISIBLE, 0, 0, 100, 100);
317             dwDifficulty = IDC_DIF_ONECOLOR;
318             CreateSpider();
319 
320             SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOZORDER);
321 
322             dwAppStartTime = GetTickCount();
323 
324             return 0;
325         }
326 
327         case WM_DESTROY:
328             PostQuitMessage(0);
329             return 0;
330 
331         case WM_SIZE:
332             nWidth  = LOWORD(lParam);
333             nHeight = HIWORD(lParam);
334 
335             MoveWindow(SpiderWnd, 0, 0, nWidth, nHeight, TRUE);
336             return 0;
337 
338         case WM_GETMINMAXINFO:
339         {
340             MINMAXINFO *mmi;
341 
342             mmi = (MINMAXINFO *)lParam;
343             mmi->ptMinTrackSize.x = NUM_STACKS * __cardwidth + (NUM_STACKS + 3) * X_BORDER + 12; // Border left and right of 6px
344             mmi->ptMinTrackSize.y = GetSystemMetrics(SM_CYCAPTION) +
345                                     GetSystemMetrics(SM_CYMENU) +
346                                     2 * Y_BORDER +
347                                     3 * __cardheight +
348                                     6 * yRowStackCardOffset;
349             return 0;
350         }
351 
352         case WM_COMMAND:
353             switch (LOWORD(wParam))
354             {
355                 case IDM_GAME_NEW:
356                     NewGame();
357                     return 0;
358 
359                 case IDM_GAME_DECK:
360                     ShowDeckOptionsDlg(hwnd);
361                     return 0;
362 
363                 case IDM_HELP_CONTENTS:
364                     WinHelp(hwnd, szHelpPath, HELP_CONTENTS, 0);//HELP_KEY, (DWORD)"How to play");
365                     return 0;
366 
367                 case IDM_HELP_ABOUT:
368                     MessageBox(hwnd, MsgAbout, szAppName, MB_OK|MB_ICONINFORMATION);
369                     return 0;
370 
371                 case IDM_GAME_EXIT:
372                     PostMessage(hwnd, WM_CLOSE, 0, 0);
373                     return 0;
374             }
375 
376             return 0;
377 
378         case WM_CLOSE:
379             if (fGameStarted == false)
380             {
381                 DestroyWindow(hwnd);
382                 return 0;
383             }
384             else
385             {
386                 int ret;
387 
388                 ret = MessageBox(hwnd, MsgQuit, szAppName, MB_YESNO|MB_ICONQUESTION);
389                 if (ret == IDYES)
390                 {
391                     WinHelp(hwnd, szHelpPath, HELP_QUIT, 0);
392                     DestroyWindow(hwnd);
393                 }
394             }
395             return 0;
396     }
397     return DefWindowProc (hwnd, iMsg, wParam, lParam);
398 }
399 
400