wordpad: Add recent file list.
[wine] / programs / winemine / main.c
1 /*
2  * WineMine (main.c)
3  *
4  * Copyright 2000 Joshua Thielen
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #define WIN32_LEAN_AND_MEAN
22
23 #include <string.h>
24 #include <time.h>
25 #include <windows.h>
26 #include <stdlib.h>
27 #include "main.h"
28 #include "resource.h"
29
30 #include <wine/debug.h>
31
32 WINE_DEFAULT_DEBUG_CHANNEL(winemine);
33
34 static const DWORD wnd_style = WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX;
35 static const char* registry_key = "Software\\Microsoft\\WinMine";
36
37
38 int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow )
39 {
40     MSG msg;
41     WNDCLASS wc;
42     HWND hWnd;
43     HACCEL haccel;
44     char appname[20];
45
46     LoadString( hInst, IDS_APPNAME, appname, sizeof(appname));
47
48     wc.style = 0;
49     wc.lpfnWndProc = MainProc;
50     wc.cbClsExtra = 0;
51     wc.cbWndExtra = 0;
52     wc.hInstance = hInst;
53     wc.hIcon = LoadIcon( hInst, "WINEMINE" );
54     wc.hCursor = LoadCursor( 0, IDI_APPLICATION );
55     wc.hbrBackground = (HBRUSH) GetStockObject( BLACK_BRUSH );
56     wc.lpszMenuName = "MENU_WINEMINE";
57     wc.lpszClassName = appname;
58
59     if (!RegisterClass(&wc)) ExitProcess(1);
60     hWnd = CreateWindow( appname, appname,
61         wnd_style,
62         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
63         0, 0, hInst, NULL );
64
65     if (!hWnd) ExitProcess(1);
66
67     ShowWindow( hWnd, cmdshow );
68     UpdateWindow( hWnd );
69
70     haccel = LoadAccelerators( hInst, MAKEINTRESOURCE(IDA_WINEMINE) );
71     SetTimer( hWnd, ID_TIMER, 1000, NULL );
72
73     while( GetMessage(&msg, 0, 0, 0) ) {
74         if (!TranslateAccelerator( hWnd, haccel, &msg ))
75             TranslateMessage( &msg );
76
77         DispatchMessage( &msg );
78     }
79     return msg.wParam;
80 }
81
82 LRESULT WINAPI MainProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
83 {
84     HDC hdc;
85     PAINTSTRUCT ps;
86     HMENU hMenu;
87     static BOARD board;
88
89     switch( msg ) {
90     case WM_CREATE:
91         board.hInst = ((LPCREATESTRUCT) lParam)->hInstance;
92         board.hWnd = hWnd;
93         InitBoard( &board );
94         CreateBoard( &board );
95         return 0;
96
97     case WM_PAINT:
98       {
99         HDC hMemDC;
100
101         WINE_TRACE("WM_PAINT\n");
102         hdc = BeginPaint( hWnd, &ps );
103         hMemDC = CreateCompatibleDC( hdc );
104
105         DrawBoard( hdc, hMemDC, &ps, &board );
106
107         DeleteDC( hMemDC );
108         EndPaint( hWnd, &ps );
109
110         return 0;
111       }
112
113     case WM_MOVE:
114         WINE_TRACE("WM_MOVE\n");
115         board.pos.x = (short)LOWORD(lParam);
116         board.pos.y = (short)HIWORD(lParam);
117         return 0;
118
119     case WM_DESTROY:
120         SaveBoard( &board );
121         DestroyBoard( &board );
122         PostQuitMessage( 0 );
123         return 0;
124
125     case WM_TIMER:
126         if( board.status == PLAYING ) {
127             board.time++;
128             RedrawWindow( hWnd, &board.timer_rect, 0,
129                           RDW_INVALIDATE | RDW_UPDATENOW );
130         }
131         return 0;
132
133     case WM_LBUTTONDOWN:
134         WINE_TRACE("WM_LBUTTONDOWN\n");
135         if( wParam & MK_RBUTTON )
136             msg = WM_MBUTTONDOWN;
137         TestBoard( hWnd, &board, (short)LOWORD(lParam), (short)HIWORD(lParam), msg );
138         SetCapture( hWnd );
139         return 0;
140
141     case WM_LBUTTONUP:
142         WINE_TRACE("WM_LBUTTONUP\n");
143         if( wParam & MK_RBUTTON )
144             msg = WM_MBUTTONUP;
145         TestBoard( hWnd, &board, (short)LOWORD(lParam), (short)HIWORD(lParam), msg );
146         ReleaseCapture();
147         return 0;
148
149     case WM_RBUTTONDOWN:
150         WINE_TRACE("WM_RBUTTONDOWN\n");
151         if( wParam & MK_LBUTTON ) {
152             board.press.x = 0;
153             board.press.y = 0;
154             msg = WM_MBUTTONDOWN;
155         }
156         TestBoard( hWnd, &board, (short)LOWORD(lParam), (short)HIWORD(lParam), msg );
157         return 0;
158
159     case WM_RBUTTONUP:
160         WINE_TRACE("WM_RBUTTONUP\n");
161         if( wParam & MK_LBUTTON )
162             msg = WM_MBUTTONUP;
163         TestBoard( hWnd, &board, (short)LOWORD(lParam), (short)HIWORD(lParam), msg );
164         return 0;
165
166     case WM_MBUTTONDOWN:
167         WINE_TRACE("WM_MBUTTONDOWN\n");
168         TestBoard( hWnd, &board, (short)LOWORD(lParam), (short)HIWORD(lParam), msg );
169         return 0;
170
171     case WM_MBUTTONUP:
172         WINE_TRACE("WM_MBUTTONUP\n");
173         TestBoard( hWnd, &board, (short)LOWORD(lParam), (short)HIWORD(lParam), msg );
174         return 0;
175
176     case WM_MOUSEMOVE:
177     {
178         if( (wParam & MK_LBUTTON) && (wParam & MK_RBUTTON) ) {
179             msg = WM_MBUTTONDOWN;
180         }
181         else if( wParam & MK_LBUTTON ) {
182             msg = WM_LBUTTONDOWN;
183         }
184         else {
185             return 0;
186         }
187
188         TestBoard( hWnd, &board, (short)LOWORD(lParam), (short)HIWORD(lParam),  msg );
189
190         return 0;
191     }
192
193     case WM_COMMAND:
194         switch(LOWORD(wParam)) {
195         case IDM_NEW:
196             CreateBoard( &board );
197             return 0;
198
199         case IDM_MARKQ:
200             hMenu = GetMenu( hWnd );
201             board.IsMarkQ = !board.IsMarkQ;
202             if( board.IsMarkQ )
203                 CheckMenuItem( hMenu, IDM_MARKQ, MF_CHECKED );
204             else
205                 CheckMenuItem( hMenu, IDM_MARKQ, MF_UNCHECKED );
206             return 0;
207
208         case IDM_BEGINNER:
209             SetDifficulty( &board, BEGINNER );
210             CreateBoard( &board );
211             return 0;
212
213         case IDM_ADVANCED:
214             SetDifficulty( &board, ADVANCED );
215             CreateBoard( &board );
216             return 0;
217
218         case IDM_EXPERT:
219             SetDifficulty( &board, EXPERT );
220             CreateBoard( &board );
221             return 0;
222
223         case IDM_CUSTOM:
224             SetDifficulty( &board, CUSTOM );
225             CreateBoard( &board );
226             return 0;
227
228         case IDM_EXIT:
229             SendMessage( hWnd, WM_CLOSE, 0, 0);
230             return 0;
231
232         case IDM_TIMES:
233             DialogBoxParam( board.hInst, "DLG_TIMES", hWnd,
234                     TimesDlgProc, (LPARAM) &board);
235             return 0;
236
237         case IDM_ABOUT:
238             DialogBox( board.hInst, "DLG_ABOUT", hWnd, AboutDlgProc );
239             return 0;
240         default:
241             WINE_TRACE("Unknown WM_COMMAND command message received\n");
242             break;
243         }
244     }
245     return( DefWindowProc( hWnd, msg, wParam, lParam ));
246 }
247
248 void InitBoard( BOARD *p_board )
249 {
250     HMENU hMenu;
251
252     p_board->hMinesBMP = LoadBitmap( p_board->hInst, "mines");
253     p_board->hFacesBMP = LoadBitmap( p_board->hInst, "faces");
254     p_board->hLedsBMP = LoadBitmap( p_board->hInst, "leds");
255
256     LoadBoard( p_board );
257
258     hMenu = GetMenu( p_board->hWnd );
259     CheckMenuItem( hMenu, IDM_BEGINNER + (unsigned) p_board->difficulty,
260             MF_CHECKED );
261     if( p_board->IsMarkQ )
262         CheckMenuItem( hMenu, IDM_MARKQ, MF_CHECKED );
263     else
264         CheckMenuItem( hMenu, IDM_MARKQ, MF_UNCHECKED );
265     CheckLevel( p_board );
266 }
267
268 void LoadBoard( BOARD *p_board )
269 {
270     DWORD size;
271     DWORD type;
272     HKEY hkey;
273     char data[16];
274     char key_name[8];
275     unsigned i;
276
277     RegOpenKeyEx( HKEY_CURRENT_USER, registry_key,
278             0, KEY_QUERY_VALUE, &hkey );
279
280     size = sizeof( p_board->pos.x );
281     if( !RegQueryValueEx( hkey, "Xpos", NULL, (LPDWORD) &type,
282             (LPBYTE) &p_board->pos.x, (LPDWORD) &size ) == ERROR_SUCCESS )
283         p_board->pos.x = 0;
284
285     size = sizeof( p_board->pos.y );
286     if( !RegQueryValueEx( hkey, "Ypos", NULL, (LPDWORD) &type,
287             (LPBYTE) &p_board->pos.y, (LPDWORD) &size ) == ERROR_SUCCESS )
288         p_board->pos.y = 0;
289
290     size = sizeof( p_board->rows );
291     if( !RegQueryValueEx( hkey, "Height", NULL, (LPDWORD) &type,
292             (LPBYTE) &p_board->rows, (LPDWORD) &size ) == ERROR_SUCCESS )
293         p_board->rows = BEGINNER_ROWS;
294
295     size = sizeof( p_board->cols );
296     if( !RegQueryValueEx( hkey, "Width", NULL, (LPDWORD) &type,
297             (LPBYTE) &p_board->cols, (LPDWORD) &size ) == ERROR_SUCCESS )
298         p_board->cols = BEGINNER_COLS;
299
300     size = sizeof( p_board->mines );
301     if( !RegQueryValueEx( hkey, "Mines", NULL, (LPDWORD) &type,
302             (LPBYTE) &p_board->mines, (LPDWORD) &size ) == ERROR_SUCCESS )
303         p_board->mines = BEGINNER_MINES;
304
305     size = sizeof( p_board->difficulty );
306     if( !RegQueryValueEx( hkey, "Difficulty", NULL, (LPDWORD) &type,
307             (LPBYTE) &p_board->difficulty, (LPDWORD) &size ) == ERROR_SUCCESS )
308         p_board->difficulty = BEGINNER;
309
310     size = sizeof( p_board->IsMarkQ );
311     if( !RegQueryValueEx( hkey, "Mark", NULL, (LPDWORD) &type,
312             (LPBYTE) &p_board->IsMarkQ, (LPDWORD) &size ) == ERROR_SUCCESS )
313         p_board->IsMarkQ = TRUE;
314
315     for( i = 0; i < 3; i++ ) {
316         wsprintf( key_name, "Name%d", i+1 );
317         size = sizeof( data );
318         if( RegQueryValueEx( hkey, key_name, NULL, (LPDWORD) &type,
319                 (LPBYTE) data,
320                 (LPDWORD) &size ) == ERROR_SUCCESS )
321                     lstrcpynA( p_board->best_name[i], data, sizeof(p_board->best_name[i]) );
322         else
323             LoadString( p_board->hInst, IDS_NOBODY, p_board->best_name[i], 16 );
324     }
325
326     for( i = 0; i < 3; i++ ) {
327         wsprintf( key_name, "Time%d", i+1 );
328         size = sizeof( p_board->best_time[i] );
329         if( !RegQueryValueEx( hkey, key_name, NULL, (LPDWORD) &type,
330                 (LPBYTE) &p_board->best_time[i],
331                 (LPDWORD) &size ) == ERROR_SUCCESS )
332             p_board->best_time[i] = 999;
333     }
334     RegCloseKey( hkey );
335 }
336
337 void SaveBoard( BOARD *p_board )
338 {
339     HKEY hkey;
340     unsigned i;
341     char data[16];
342     char key_name[8];
343
344     if( RegCreateKeyEx( HKEY_CURRENT_USER, registry_key,
345                 0, NULL,
346                 REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
347                 &hkey, NULL ) != ERROR_SUCCESS)
348         return;
349
350     RegSetValueEx( hkey, "Xpos", 0, REG_DWORD, (LPBYTE) &p_board->pos.x, sizeof(p_board->pos.x) );
351     RegSetValueEx( hkey, "Ypos", 0, REG_DWORD, (LPBYTE) &p_board->pos.y, sizeof(p_board->pos.y) );
352     RegSetValueEx( hkey, "Difficulty", 0, REG_DWORD, (LPBYTE) &p_board->difficulty, sizeof(p_board->difficulty) );
353     RegSetValueEx( hkey, "Height", 0, REG_DWORD, (LPBYTE) &p_board->rows, sizeof(p_board->rows) );
354     RegSetValueEx( hkey, "Width", 0, REG_DWORD, (LPBYTE) &p_board->cols, sizeof(p_board->cols) );
355     RegSetValueEx( hkey, "Mines", 0, REG_DWORD, (LPBYTE) &p_board->mines, sizeof(p_board->mines) );
356     RegSetValueEx( hkey, "Mark", 0, REG_DWORD, (LPBYTE) &p_board->IsMarkQ, sizeof(p_board->IsMarkQ) );
357
358     for( i = 0; i < 3; i++ ) {
359         wsprintf( key_name, "Name%u", i+1 );
360         lstrcpyn( data, p_board->best_name[i], sizeof( data ) );
361         RegSetValueEx( hkey, key_name, 0, REG_SZ, (LPBYTE) data, strlen(data)+1 );
362     }
363
364     for( i = 0; i < 3; i++ ) {
365         wsprintf( key_name, "Time%u", i+1 );
366         RegSetValueEx( hkey, key_name, 0, REG_DWORD, (LPBYTE) &p_board->best_time[i], sizeof(p_board->best_time[i]) );
367     }
368     RegCloseKey( hkey );
369 }
370
371 void DestroyBoard( BOARD *p_board )
372 {
373     DeleteObject( p_board->hFacesBMP );
374     DeleteObject( p_board->hLedsBMP );
375     DeleteObject( p_board->hMinesBMP );
376 }
377
378 void SetDifficulty( BOARD *p_board, DIFFICULTY difficulty )
379 {
380     HMENU hMenu;
381
382     if ( difficulty == CUSTOM )
383         if (DialogBoxParam( p_board->hInst, "DLG_CUSTOM", p_board->hWnd,
384                     CustomDlgProc, (LPARAM) p_board) != 0)
385            return;
386
387     hMenu = GetMenu( p_board->hWnd );
388     CheckMenuItem( hMenu, IDM_BEGINNER + p_board->difficulty, MF_UNCHECKED );
389     p_board->difficulty = difficulty;
390     CheckMenuItem( hMenu, IDM_BEGINNER + difficulty, MF_CHECKED );
391
392     switch( difficulty ) {
393     case BEGINNER:
394         p_board->cols = BEGINNER_COLS;
395         p_board->rows = BEGINNER_ROWS;
396         p_board->mines = BEGINNER_MINES;
397         break;
398
399     case ADVANCED:
400         p_board->cols = ADVANCED_COLS;
401         p_board->rows = ADVANCED_ROWS;
402         p_board->mines = ADVANCED_MINES;
403         break;
404
405     case EXPERT:
406         p_board->cols = EXPERT_COLS;
407         p_board->rows = EXPERT_ROWS;
408
409         p_board->mines = EXPERT_MINES;
410         break;
411
412     case CUSTOM:
413         break;
414     }
415 }
416
417 static void ShiftBetween(LONG* x, LONG* y, LONG a, LONG b)
418 {
419     if (*x < a) {
420         *y += a - *x;
421         *x = a;
422     }
423     else if (*y > b) {
424         *x -= *y - b;
425         *y = b;
426     }
427 }
428
429 static void MoveOnScreen(RECT* rect)
430 {
431     HMONITOR hMonitor;
432     MONITORINFO mi;
433
434     /* find the nearest monitor ... */
435     hMonitor = MonitorFromRect(rect, MONITOR_DEFAULTTONEAREST);
436
437     /* ... and move it into the work area (ie excluding task bar)*/
438     mi.cbSize = sizeof(mi);
439     GetMonitorInfo(hMonitor, &mi);
440
441     ShiftBetween(&rect->left, &rect->right, mi.rcWork.left, mi.rcWork.right);
442     ShiftBetween(&rect->top, &rect->bottom, mi.rcWork.top, mi.rcWork.bottom);
443 }
444
445 void CreateBoard( BOARD *p_board )
446 {
447     int left, top, bottom, right;
448     RECT wnd_rect;
449
450     p_board->mb = MB_NONE;
451     p_board->boxes_left = p_board->cols * p_board->rows - p_board->mines;
452     p_board->num_flags = 0;
453
454     CreateBoxes( p_board );
455
456     p_board->width = p_board->cols * MINE_WIDTH + BOARD_WMARGIN * 2;
457
458     p_board->height = p_board->rows * MINE_HEIGHT + LED_HEIGHT
459         + BOARD_HMARGIN * 3;
460
461     /* setting the mines rectangle boundary */
462     left = BOARD_WMARGIN;
463     top = BOARD_HMARGIN * 2 + LED_HEIGHT;
464     right = left + p_board->cols * MINE_WIDTH;
465     bottom = top + p_board->rows * MINE_HEIGHT;
466     SetRect( &p_board->mines_rect, left, top, right, bottom );
467
468     /* setting the face rectangle boundary */
469     left = p_board->width / 2 - FACE_WIDTH / 2;
470     top = BOARD_HMARGIN;
471     right = left + FACE_WIDTH;
472     bottom = top + FACE_HEIGHT;
473     SetRect( &p_board->face_rect, left, top, right, bottom );
474
475     /* setting the timer rectangle boundary */
476     left = BOARD_WMARGIN;
477     top = BOARD_HMARGIN;
478     right = left + LED_WIDTH * 3;
479     bottom = top + LED_HEIGHT;
480     SetRect( &p_board->timer_rect, left, top, right, bottom );
481
482     /* setting the counter rectangle boundary */
483     left =  p_board->width - BOARD_WMARGIN - LED_WIDTH * 3;
484     top = BOARD_HMARGIN;
485     right = p_board->width - BOARD_WMARGIN;
486     bottom = top + LED_HEIGHT;
487     SetRect( &p_board->counter_rect, left, top, right, bottom );
488
489     p_board->status = WAITING;
490     p_board->face_bmp = SMILE_BMP;
491     p_board->time = 0;
492
493     wnd_rect.left   = p_board->pos.x;
494     wnd_rect.right  = p_board->pos.x + p_board->width;
495     wnd_rect.top    = p_board->pos.y;
496     wnd_rect.bottom = p_board->pos.y + p_board->height;
497     AdjustWindowRect(&wnd_rect, wnd_style, TRUE);
498
499     /* Make sure the window is completely on the screen */
500     MoveOnScreen(&wnd_rect);
501     MoveWindow( p_board->hWnd, wnd_rect.left, wnd_rect.top,
502                 wnd_rect.right - wnd_rect.left,
503                 wnd_rect.bottom - wnd_rect.top,
504                 TRUE );
505     RedrawWindow( p_board->hWnd, NULL, 0,
506                   RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);
507 }
508
509
510 void CheckLevel( BOARD *p_board )
511 {
512     if( p_board->rows < BEGINNER_ROWS )
513         p_board->rows = BEGINNER_ROWS;
514
515     if( p_board->rows > MAX_ROWS )
516         p_board->rows = MAX_ROWS;
517
518     if( p_board->cols < BEGINNER_COLS )
519         p_board->cols = BEGINNER_COLS;
520
521     if( p_board->cols > MAX_COLS )
522         p_board->cols = MAX_COLS;
523
524     if( p_board->mines < BEGINNER_MINES )
525         p_board->mines = BEGINNER_MINES;
526
527     if( p_board->mines > p_board->cols * p_board->rows - 1 )
528         p_board->mines = p_board->cols * p_board->rows - 1;
529 }
530
531
532 void CreateBoxes( BOARD *p_board )
533 {
534     int i, j;
535     unsigned col, row;
536
537     srand( (unsigned) time( NULL ) );
538
539     /* Create the boxes...
540      * We actually create them with an empty border,
541      * so special care doesn't have to be taken on the edges
542      */
543
544     for( col = 0; col <= p_board->cols + 1; col++ )
545       for( row = 0; row <= p_board->rows + 1; row++ ) {
546         p_board->box[col][row].IsPressed = FALSE;
547         p_board->box[col][row].IsMine = FALSE;
548         p_board->box[col][row].FlagType = NORMAL;
549         p_board->box[col][row].NumMines = 0;
550       }
551
552     /* create mines */
553     i = 0;
554     while( (unsigned) i < p_board->mines ) {
555         col = (int) (p_board->cols * (float) rand() / RAND_MAX + 1);
556         row = (int) (p_board->rows * (float) rand() / RAND_MAX + 1);
557
558         if( !p_board->box[col][row].IsMine ) {
559             i++;
560             p_board->box[col][row].IsMine = TRUE;
561         }
562     }
563
564     /*
565      * Now we label the remaining boxes with the
566      * number of mines surrounding them.
567      */
568
569     for( col = 1; col < p_board->cols + 1; col++ )
570     for( row = 1; row < p_board->rows + 1; row++ ) {
571         for( i = -1; i <= 1; i++ )
572         for( j = -1; j <= 1; j++ ) {
573             if( p_board->box[col + i][row + j].IsMine ) {
574                 p_board->box[col][row].NumMines++ ;
575             }
576         }
577     }
578 }
579
580 void DrawMines ( HDC hdc, HDC hMemDC, BOARD *p_board )
581 {
582     HGDIOBJ hOldObj;
583     unsigned col, row;
584     hOldObj = SelectObject (hMemDC, p_board->hMinesBMP);
585
586     for( row = 1; row <= p_board->rows; row++ ) {
587       for( col = 1; col <= p_board->cols; col++ ) {
588         DrawMine( hdc, hMemDC, p_board, col, row, FALSE );
589       }
590     }
591     SelectObject( hMemDC, hOldObj );
592 }
593
594 void DrawMine( HDC hdc, HDC hMemDC, BOARD *p_board, unsigned col, unsigned row, BOOL IsPressed )
595 {
596     MINEBMP_OFFSET offset = BOX_BMP;
597
598     if( col == 0 || col > p_board->cols || row == 0 || row > p_board->rows )
599            return;
600
601     if( p_board->status == GAMEOVER ) {
602         if( p_board->box[col][row].IsMine ) {
603             switch( p_board->box[col][row].FlagType ) {
604             case FLAG:
605                 offset = FLAG_BMP;
606                 break;
607             case COMPLETE:
608                 offset = EXPLODE_BMP;
609                 break;
610             case QUESTION:
611                 /* fall through */
612             case NORMAL:
613                 offset = MINE_BMP;
614             }
615         } else {
616             switch( p_board->box[col][row].FlagType ) {
617             case QUESTION:
618                 offset = QUESTION_BMP;
619                 break;
620             case FLAG:
621                 offset = WRONG_BMP;
622                 break;
623             case NORMAL:
624                 offset = BOX_BMP;
625                 break;
626             case COMPLETE:
627                 /* Do nothing */
628                 break;
629             default:
630                 WINE_TRACE("Unknown FlagType during game over in DrawMine\n");
631                 break;
632             }
633         }
634     } else {    /* WAITING or PLAYING */
635         switch( p_board->box[col][row].FlagType ) {
636         case QUESTION:
637             if( !IsPressed )
638                 offset = QUESTION_BMP;
639             else
640                 offset = QPRESS_BMP;
641             break;
642         case FLAG:
643             offset = FLAG_BMP;
644             break;
645         case NORMAL:
646             if( !IsPressed )
647                 offset = BOX_BMP;
648             else
649                 offset = MPRESS_BMP;
650             break;
651         case COMPLETE:
652             /* Do nothing */
653             break;
654         default:
655             WINE_TRACE("Unknown FlagType while playing in DrawMine\n");
656             break;
657         }
658     }
659
660     if( p_board->box[col][row].FlagType == COMPLETE
661         && !p_board->box[col][row].IsMine )
662           offset = (MINEBMP_OFFSET) p_board->box[col][row].NumMines;
663
664     BitBlt( hdc,
665             (col - 1) * MINE_WIDTH + p_board->mines_rect.left,
666             (row - 1) * MINE_HEIGHT + p_board->mines_rect.top,
667             MINE_WIDTH, MINE_HEIGHT,
668             hMemDC, 0, offset * MINE_HEIGHT, SRCCOPY );
669 }
670
671 void DrawLeds( HDC hdc, HDC hMemDC, BOARD *p_board, int number, int x, int y )
672 {
673     HGDIOBJ hOldObj;
674     unsigned led[3], i;
675     int count;
676
677     count = number;
678     if( count < 1000 ) {
679         if( count >= 0 ) {
680             led[0] = count / 100 ;
681             count -= led[0] * 100;
682         }
683         else {
684             led[0] = 10; /* negative sign */
685             count = -count;
686         }
687         led[1] = count / 10;
688         count -= led[1] * 10;
689         led[2] = count;
690     }
691     else {
692         for( i = 0; i < 3; i++ )
693             led[i] = 10;
694     }
695
696     /* use unlit led if not playing */
697     if( p_board->status == WAITING )
698         for( i = 0; i < 3; i++ )
699             led[i] = 11;
700
701     hOldObj = SelectObject (hMemDC, p_board->hLedsBMP);
702
703     for( i = 0; i < 3; i++ ) {
704         BitBlt( hdc,
705             i * LED_WIDTH + x,
706             y,
707             LED_WIDTH,
708             LED_HEIGHT,
709             hMemDC,
710             0,
711             led[i] * LED_HEIGHT,
712             SRCCOPY);
713     }
714
715     SelectObject( hMemDC, hOldObj );
716 }
717
718
719 void DrawFace( HDC hdc, HDC hMemDC, BOARD *p_board )
720 {
721     HGDIOBJ hOldObj;
722
723     hOldObj = SelectObject (hMemDC, p_board->hFacesBMP);
724
725     BitBlt( hdc,
726         p_board->face_rect.left,
727         p_board->face_rect.top,
728         FACE_WIDTH,
729         FACE_HEIGHT,
730         hMemDC, 0, p_board->face_bmp * FACE_HEIGHT, SRCCOPY);
731
732     SelectObject( hMemDC, hOldObj );
733 }
734
735
736 void DrawBoard( HDC hdc, HDC hMemDC, PAINTSTRUCT *ps, BOARD *p_board )
737 {
738     RECT tmp_rect;
739
740     if( IntersectRect( &tmp_rect, &ps->rcPaint, &p_board->counter_rect ) )
741         DrawLeds( hdc, hMemDC, p_board, p_board->mines - p_board->num_flags,
742                   p_board->counter_rect.left,
743                   p_board->counter_rect.top );
744
745     if( IntersectRect( &tmp_rect, &ps->rcPaint, &p_board->timer_rect ) )
746         DrawLeds( hdc, hMemDC, p_board, p_board->time,
747                   p_board->timer_rect.left,
748                   p_board->timer_rect.top );
749
750     if( IntersectRect( &tmp_rect, &ps->rcPaint, &p_board->face_rect ) )
751         DrawFace( hdc, hMemDC, p_board );
752
753     if( IntersectRect( &tmp_rect, &ps->rcPaint, &p_board->mines_rect ) )
754         DrawMines( hdc, hMemDC, p_board );
755 }
756
757
758 void TestBoard( HWND hWnd, BOARD *p_board, int x, int y, int msg )
759 {
760     POINT pt;
761     unsigned col,row;
762
763     pt.x = x;
764     pt.y = y;
765
766     if( PtInRect( &p_board->mines_rect, pt ) && p_board->status != GAMEOVER
767     && p_board->status != WON )
768         TestMines( p_board, pt, msg );
769     else {
770         UnpressBoxes( p_board,
771             p_board->press.x,
772             p_board->press.y );
773         p_board->press.x = 0;
774         p_board->press.y = 0;
775     }
776
777     if( p_board->boxes_left == 0 ) {
778         p_board->status = WON;
779
780         if (p_board->num_flags < p_board->mines) {
781             for( row = 1; row <= p_board->rows; row++ ) {
782                 for( col = 1; col <= p_board->cols; col++ ) {
783                     if (p_board->box[col][row].IsMine && p_board->box[col][row].FlagType != FLAG)
784                         p_board->box[col][row].FlagType = FLAG;
785                 }
786             }
787
788             p_board->num_flags = p_board->mines;
789
790             RedrawWindow( p_board->hWnd, NULL, 0,
791                 RDW_INVALIDATE | RDW_UPDATENOW );
792         }
793
794         if( p_board->difficulty != CUSTOM &&
795                     p_board->time < p_board->best_time[p_board->difficulty] ) {
796             p_board->best_time[p_board->difficulty] = p_board->time;
797
798             DialogBoxParam( p_board->hInst, "DLG_CONGRATS", hWnd,
799                     CongratsDlgProc, (LPARAM) p_board);
800
801             DialogBoxParam( p_board->hInst, "DLG_TIMES", hWnd,
802                     TimesDlgProc, (LPARAM) p_board);
803         }
804     }
805     TestFace( p_board, pt, msg );
806 }
807
808 void TestMines( BOARD *p_board, POINT pt, int msg )
809 {
810     BOOL draw = TRUE;
811     int col, row;
812
813     col = (pt.x - p_board->mines_rect.left) / MINE_WIDTH + 1;
814     row = (pt.y - p_board->mines_rect.top ) / MINE_HEIGHT + 1;
815
816     switch ( msg ) {
817     case WM_LBUTTONDOWN:
818         if( p_board->press.x != col || p_board->press.y != row ) {
819             UnpressBox( p_board,
820                     p_board->press.x, p_board->press.y );
821             p_board->press.x = col;
822             p_board->press.y = row;
823             PressBox( p_board, col, row );
824         }
825         draw = FALSE;
826         break;
827
828     case WM_LBUTTONUP:
829         if( p_board->press.x != col || p_board->press.y != row )
830             UnpressBox( p_board,
831                     p_board->press.x, p_board->press.y );
832         p_board->press.x = 0;
833         p_board->press.y = 0;
834         if( p_board->box[col][row].FlagType != FLAG )
835             p_board->status = PLAYING;
836         CompleteBox( p_board, col, row );
837         break;
838
839     case WM_MBUTTONDOWN:
840         PressBoxes( p_board, col, row );
841         draw = FALSE;
842         break;
843
844     case WM_MBUTTONUP:
845         if( p_board->press.x != col || p_board->press.y != row )
846             UnpressBoxes( p_board,
847                     p_board->press.x, p_board->press.y );
848         p_board->press.x = 0;
849         p_board->press.y = 0;
850         CompleteBoxes( p_board, col, row );
851         break;
852
853     case WM_RBUTTONDOWN:
854         AddFlag( p_board, col, row );
855         p_board->status = PLAYING;
856         break;
857     default:
858         WINE_TRACE("Unknown message type received in TestMines\n");
859         break;
860     }
861
862     if( draw )
863     {
864         RedrawWindow( p_board->hWnd, NULL, 0,
865             RDW_INVALIDATE | RDW_UPDATENOW );
866     }
867 }
868
869
870 void TestFace( BOARD *p_board, POINT pt, int msg )
871 {
872     if( p_board->status == PLAYING || p_board->status == WAITING ) {
873         if( msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN )
874             p_board->face_bmp = OOH_BMP;
875         else p_board->face_bmp = SMILE_BMP;
876     }
877     else if( p_board->status == GAMEOVER )
878         p_board->face_bmp = DEAD_BMP;
879     else if( p_board->status == WON )
880             p_board->face_bmp = COOL_BMP;
881
882     if( PtInRect( &p_board->face_rect, pt ) ) {
883         if( msg == WM_LBUTTONDOWN )
884             p_board->face_bmp = SPRESS_BMP;
885
886         if( msg == WM_LBUTTONUP )
887             CreateBoard( p_board );
888     }
889
890     RedrawWindow( p_board->hWnd, &p_board->face_rect, 0,
891         RDW_INVALIDATE | RDW_UPDATENOW );
892 }
893
894
895 void CompleteBox( BOARD *p_board, unsigned col, unsigned row )
896 {
897     int i, j;
898
899     if( p_board->box[col][row].FlagType != COMPLETE &&
900             p_board->box[col][row].FlagType != FLAG &&
901             col > 0 && col < p_board->cols + 1 &&
902             row > 0 && row < p_board->rows + 1 ) {
903         p_board->box[col][row].FlagType = COMPLETE;
904
905         if( p_board->box[col][row].IsMine ) {
906             p_board->face_bmp = DEAD_BMP;
907             p_board->status = GAMEOVER;
908         }
909         else if( p_board->status != GAMEOVER )
910             p_board->boxes_left--;
911
912         if( p_board->box[col][row].NumMines == 0 )
913         {
914             for( i = -1; i <= 1; i++ )
915             for( j = -1; j <= 1; j++ )
916                 CompleteBox( p_board, col + i, row + j  );
917         }
918     }
919 }
920
921
922 void CompleteBoxes( BOARD *p_board, unsigned col, unsigned row )
923 {
924     unsigned numFlags = 0;
925     int i, j;
926
927     if( p_board->box[col][row].FlagType == COMPLETE ) {
928         for( i = -1; i <= 1; i++ )
929           for( j = -1; j <= 1; j++ ) {
930             if( p_board->box[col+i][row+j].FlagType == FLAG )
931                 numFlags++;
932           }
933
934         if( numFlags == p_board->box[col][row].NumMines ) {
935             for( i = -1; i <= 1; i++ )
936               for( j = -1; j <= 1; j++ ) {
937                 if( p_board->box[col+i][row+j].FlagType != FLAG )
938                     CompleteBox( p_board, col+i, row+j );
939               }
940         }
941     }
942 }
943
944
945 void AddFlag( BOARD *p_board, unsigned col, unsigned row )
946 {
947     if( p_board->box[col][row].FlagType != COMPLETE ) {
948         switch( p_board->box[col][row].FlagType ) {
949         case FLAG:
950             if( p_board->IsMarkQ )
951                 p_board->box[col][row].FlagType = QUESTION;
952             else
953                 p_board->box[col][row].FlagType = NORMAL;
954             p_board->num_flags--;
955             break;
956
957         case QUESTION:
958             p_board->box[col][row].FlagType = NORMAL;
959             break;
960
961         default:
962             p_board->box[col][row].FlagType = FLAG;
963             p_board->num_flags++;
964         }
965     }
966 }
967
968
969 void PressBox( BOARD *p_board, unsigned col, unsigned row )
970 {
971     HDC hdc;
972     HGDIOBJ hOldObj;
973     HDC hMemDC;
974
975     hdc = GetDC( p_board->hWnd );
976     hMemDC = CreateCompatibleDC( hdc );
977     hOldObj = SelectObject (hMemDC, p_board->hMinesBMP);
978
979     DrawMine( hdc, hMemDC, p_board, col, row, TRUE );
980
981     SelectObject( hMemDC, hOldObj );
982     DeleteDC( hMemDC );
983     ReleaseDC( p_board->hWnd, hdc );
984 }
985
986
987 void PressBoxes( BOARD *p_board, unsigned col, unsigned row )
988 {
989     int i, j;
990
991     for( i = -1; i <= 1; i++ )
992       for( j = -1; j <= 1; j++ ) {
993         p_board->box[col + i][row + j].IsPressed = TRUE;
994         PressBox( p_board, col + i, row + j );
995     }
996
997     for( i = -1; i <= 1; i++ )
998       for( j = -1; j <= 1; j++ ) {
999         if( !p_board->box[p_board->press.x + i][p_board->press.y + j].IsPressed )
1000             UnpressBox( p_board, p_board->press.x + i, p_board->press.y + j );
1001     }
1002
1003     for( i = -1; i <= 1; i++ )
1004       for( j = -1; j <= 1; j++ ) {
1005         p_board->box[col + i][row + j].IsPressed = FALSE;
1006         PressBox( p_board, col + i, row + j );
1007     }
1008
1009     p_board->press.x = col;
1010     p_board->press.y = row;
1011 }
1012
1013
1014 void UnpressBox( BOARD *p_board, unsigned col, unsigned row )
1015 {
1016     HDC hdc;
1017     HGDIOBJ hOldObj;
1018     HDC hMemDC;
1019
1020     hdc = GetDC( p_board->hWnd );
1021     hMemDC = CreateCompatibleDC( hdc );
1022     hOldObj = SelectObject( hMemDC, p_board->hMinesBMP );
1023
1024     DrawMine( hdc, hMemDC, p_board, col, row, FALSE );
1025
1026     SelectObject( hMemDC, hOldObj );
1027     DeleteDC( hMemDC );
1028     ReleaseDC( p_board->hWnd, hdc );
1029 }
1030
1031
1032 void UnpressBoxes( BOARD *p_board, unsigned col, unsigned row )
1033 {
1034     int i, j;
1035
1036     for( i = -1; i <= 1; i++ )
1037       for( j = -1; j <= 1; j++ ) {
1038         UnpressBox( p_board, col + i, row + j );
1039       }
1040 }