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