Fix multi-leg promotions
[xboard.git] / winboard / wevalgraph.c
1 /*\r
2  * wevalgraph.c - Evaluation graph front-end part\r
3  *\r
4  * Author: Alessandro Scotti (Dec 2005)\r
5  *\r
6  * Copyright 2005 Alessandro Scotti\r
7  *\r
8  * Enhancements Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015,\r
9  * 2016 Free Software Foundation, Inc.\r
10  *\r
11  * ------------------------------------------------------------------------\r
12  *\r
13  * GNU XBoard is free software: you can redistribute it and/or modify\r
14  * it under the terms of the GNU General Public License as published by\r
15  * the Free Software Foundation, either version 3 of the License, or (at\r
16  * your option) any later version.\r
17  *\r
18  * GNU XBoard is distributed in the hope that it will be useful, but\r
19  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
21  * General Public License for more details.\r
22  *\r
23  * You should have received a copy of the GNU General Public License\r
24  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
25  *\r
26  *------------------------------------------------------------------------\r
27  ** See the file ChangeLog for a revision history.  */\r
28 \r
29 // code refactored by HGM to obtain front-end / back-end separation\r
30 \r
31 #include "config.h"\r
32 \r
33 #include <windows.h>\r
34 #include <commdlg.h>\r
35 #include <dlgs.h>\r
36 #include <stdio.h>\r
37 \r
38 #include "common.h"\r
39 #include "frontend.h"\r
40 #include "backend.h"\r
41 #include "winboard.h"\r
42 #include "evalgraph.h"\r
43 #include "wsnap.h"\r
44 \r
45 #define WM_REFRESH_GRAPH    (WM_USER + 1)\r
46 \r
47 /* Module globals */\r
48 static BOOLEAN evalGraphDialogUp;\r
49 \r
50 static COLORREF crWhite = RGB( 0xFF, 0xFF, 0xB0 );\r
51 static COLORREF crBlack = RGB( 0xAD, 0x5D, 0x3D );\r
52 \r
53 static HDC hdcPB = NULL;\r
54 static HBITMAP hbmPB = NULL;\r
55 static HPEN pens[PEN_ANY+1]; // [HGM] put all pens in one array\r
56 static HBRUSH hbrHist[3] = { NULL, NULL, NULL };\r
57 \r
58 Boolean EvalGraphIsUp()\r
59 {\r
60     return evalGraphDialogUp;\r
61 }\r
62 \r
63 // [HGM] front-end, added as wrapper to avoid use of LineTo and MoveToEx in other routines (so they can be back-end) \r
64 void DrawSegment( int x, int y, int *lastX, int *lastY, int penType )\r
65 {\r
66     POINT stPt;\r
67     if(penType == PEN_NONE) MoveToEx( hdcPB, x, y, &stPt ); else {\r
68         HPEN hp = SelectObject( hdcPB, pens[penType] );\r
69         LineTo( hdcPB, x, y );\r
70         SelectObject( hdcPB, hp );\r
71     }\r
72     if(lastX != NULL) { *lastX = stPt.x; *lastY = stPt.y; }\r
73 }\r
74 \r
75 // front-end wrapper for drawing functions to do rectangles\r
76 void DrawRectangle( int left, int top, int right, int bottom, int side, int style )\r
77 {\r
78     HPEN hp = SelectObject( hdcPB, pens[PEN_BLACK] );\r
79     RECT rc;\r
80 \r
81     rc.top = top; rc.left = left; rc.bottom = bottom; rc.right = right;\r
82     if(style == FILLED)\r
83         FillRect( hdcPB, &rc, hbrHist[side] );\r
84     else {\r
85         SelectObject( hdcPB, hbrHist[side] );\r
86         Rectangle( hdcPB, left, top, right, bottom );\r
87     }\r
88     SelectObject( hdcPB, hp );\r
89 }\r
90 \r
91 // front-end wrapper for putting text in graph\r
92 void DrawEvalText(char *buf, int cbBuf, int y)\r
93 {\r
94         SIZE stSize;\r
95         SetBkMode( hdcPB, TRANSPARENT );\r
96         GetTextExtentPoint32( hdcPB, buf, cbBuf, &stSize );\r
97         TextOut( hdcPB, MarginX - stSize.cx - 2, y - stSize.cy / 2, buf, cbBuf );\r
98 }\r
99 \r
100 // front-end\r
101 static HBRUSH CreateBrush( UINT style, COLORREF color )\r
102 {\r
103     LOGBRUSH stLB;\r
104 \r
105     stLB.lbStyle = style;\r
106     stLB.lbColor = color;\r
107     stLB.lbHatch = 0;\r
108 \r
109     return CreateBrushIndirect( &stLB );\r
110 }\r
111 \r
112 // front-end. Create pens, device context and buffer bitmap for global use, copy result to display\r
113 // The back-end part n the middle has been taken out and moed to PainEvalGraph()\r
114 static VOID DisplayEvalGraph( HWND hWnd, HDC hDC )\r
115 {\r
116     RECT rcClient;\r
117     int width;\r
118     int height;\r
119 \r
120     /* Get client area */\r
121     GetClientRect( hWnd, &rcClient );\r
122 \r
123     width = rcClient.right - rcClient.left;\r
124     height = rcClient.bottom - rcClient.top;\r
125 \r
126     /* Create or recreate paint box if needed */\r
127     if( hbmPB == NULL || width != nWidthPB || height != nHeightPB ) {\r
128         if( pens[PEN_DOTTED] == NULL ) {\r
129             pens[PEN_BLACK]      = GetStockObject(BLACK_PEN);\r
130             pens[PEN_DOTTED]     = CreatePen( PS_DOT, 0, RGB(0xA0,0xA0,0xA0) );\r
131             pens[PEN_BLUEDOTTED] = CreatePen( PS_DOT, 0, RGB(0x00,0x00,0xFF) );\r
132             pens[PEN_BOLDWHITE]  = CreatePen( PS_SOLID, 2, crWhite );\r
133             pens[PEN_BOLDBLACK]  = CreatePen( PS_SOLID, 2, crBlack );\r
134             hbrHist[0] = CreateBrush( BS_SOLID, crWhite );\r
135             hbrHist[1] = CreateBrush( BS_SOLID, crBlack );\r
136             hbrHist[2] = CreateBrush( BS_SOLID, GetSysColor( COLOR_3DFACE ) ); // background\r
137         }\r
138 \r
139         if( hdcPB != NULL ) {\r
140             DeleteDC( hdcPB );\r
141             hdcPB = NULL;\r
142         }\r
143 \r
144         if( hbmPB != NULL ) {\r
145             DeleteObject( hbmPB );\r
146             hbmPB = NULL;\r
147         }\r
148 \r
149         hdcPB = CreateCompatibleDC( hDC );\r
150 \r
151         nWidthPB = width;\r
152         nHeightPB = height;\r
153         hbmPB = CreateCompatibleBitmap( hDC, nWidthPB, nHeightPB );\r
154 \r
155         SelectObject( hdcPB, hbmPB );\r
156     }\r
157 \r
158     // back-end painting; calls back front-end primitives for lines, rectangles and text\r
159     PaintEvalGraph();\r
160     SetWindowText(hWnd, MakeEvalTitle(differentialView ? T_("Blunder Graph") : T_("Evaluation Graph")));\r
161 \r
162     /* Copy bitmap into destination DC */\r
163     BitBlt( hDC, 0, 0, nWidthPB, nHeightPB, hdcPB, 0, 0, SRCCOPY );\r
164 }\r
165 \r
166 // Note: Once the eval graph is opened, this window-proc lives forever; een closing the\r
167 // eval-graph window merely hides it. On opening we re-initialize it, though, so it could\r
168 // as well hae been destroyed. While it is open it processes the REFRESH_GRAPH commands.\r
169 LRESULT CALLBACK EvalGraphProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )\r
170 {\r
171     static SnapData sd;\r
172 \r
173     PAINTSTRUCT stPS;\r
174     HDC hDC;\r
175 \r
176     switch (message) {\r
177     case WM_INITDIALOG:\r
178         Translate(hDlg, DLG_EvalGraph);\r
179         if( evalGraphDialog == NULL ) {\r
180             evalGraphDialog = hDlg;\r
181 \r
182             RestoreWindowPlacement( hDlg, &wpEvalGraph ); /* Restore window placement */\r
183         }\r
184 \r
185         return FALSE;\r
186 \r
187     case WM_COMMAND:\r
188         switch (LOWORD(wParam)) {\r
189         case IDOK:\r
190           EndDialog(hDlg, TRUE);\r
191           return TRUE;\r
192 \r
193         case IDCANCEL:\r
194           EndDialog(hDlg, FALSE);\r
195           return TRUE;\r
196 \r
197         default:\r
198           break;\r
199         }\r
200 \r
201         break;\r
202 \r
203     case WM_ERASEBKGND:\r
204         return TRUE;\r
205 \r
206     case WM_PAINT:\r
207         hDC = BeginPaint( hDlg, &stPS );\r
208         DisplayEvalGraph( hDlg, hDC );\r
209         EndPaint( hDlg, &stPS );\r
210         break;\r
211 \r
212     case WM_MOUSEWHEEL:\r
213         if((short)HIWORD(wParam) < 0) appData.zoom++;\r
214         if((short)HIWORD(wParam) > 0 && appData.zoom > 1)  appData.zoom--;\r
215         goto paint;\r
216     case WM_RBUTTONDOWN:\r
217         differentialView = !differentialView;\r
218     case WM_REFRESH_GRAPH:\r
219     paint:\r
220         hDC = GetDC( hDlg );\r
221         DisplayEvalGraph( hDlg, hDC );\r
222         ReleaseDC( hDlg, hDC );\r
223         break;\r
224 \r
225     case WM_LBUTTONDOWN:\r
226         if( wParam == 0 || wParam == MK_LBUTTON ) {\r
227             int index = GetMoveIndexFromPoint( LOWORD(lParam), HIWORD(lParam) );\r
228 \r
229             if( index >= 0 && index < currLast ) {\r
230                 ToNrEvent( index + 1 );\r
231             }\r
232         }\r
233         return TRUE;\r
234 \r
235     case WM_SIZE:\r
236         InvalidateRect( hDlg, NULL, FALSE );\r
237         break;\r
238 \r
239     case WM_GETMINMAXINFO:\r
240         {\r
241             MINMAXINFO * mmi = (MINMAXINFO *) lParam;\r
242         \r
243             mmi->ptMinTrackSize.x = 100;\r
244             mmi->ptMinTrackSize.y = 100;\r
245         }\r
246         break;\r
247 \r
248     /* Support for captionless window */\r
249     case WM_CLOSE:\r
250         EvalGraphPopDown();\r
251         break;\r
252 \r
253     case WM_ENTERSIZEMOVE:\r
254         return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
255 \r
256     case WM_SIZING:\r
257         return OnSizing( &sd, hDlg, wParam, lParam );\r
258 \r
259     case WM_MOVING:\r
260         return OnMoving( &sd, hDlg, wParam, lParam );\r
261 \r
262     case WM_EXITSIZEMOVE:\r
263         return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
264     }\r
265 \r
266     return FALSE;\r
267 }\r
268 \r
269 // creates the eval graph, or unhides it.\r
270 VOID EvalGraphPopUp()\r
271 {\r
272   FARPROC lpProc;\r
273   \r
274   CheckMenuItem(GetMenu(hwndMain), IDM_ShowEvalGraph, MF_CHECKED);\r
275 \r
276   if( evalGraphDialog ) {\r
277     SendMessage( evalGraphDialog, WM_INITDIALOG, 0, 0 );\r
278 \r
279     if( ! evalGraphDialogUp ) {\r
280         ShowWindow(evalGraphDialog, SW_SHOW);\r
281     }\r
282   }\r
283   else {\r
284     crWhite = appData.evalHistColorWhite;\r
285     crBlack = appData.evalHistColorBlack;\r
286 \r
287     lpProc = MakeProcInstance( (FARPROC) EvalGraphProc, hInst );\r
288 \r
289     /* Note to self: dialog must have the WS_VISIBLE style set, otherwise it's not shown! */\r
290     CreateDialog( hInst, MAKEINTRESOURCE(DLG_EvalGraph), hwndMain, (DLGPROC)lpProc );\r
291 \r
292     FreeProcInstance(lpProc);\r
293   }\r
294 \r
295   evalGraphDialogUp = TRUE;\r
296 }\r
297 \r
298 // Note that this hides the window. It could as well have destroyed it.\r
299 VOID EvalGraphPopDown()\r
300 {\r
301   CheckMenuItem(GetMenu(hwndMain), IDM_ShowEvalGraph, MF_UNCHECKED);\r
302 \r
303   if( evalGraphDialog ) {\r
304       ShowWindow(evalGraphDialog, SW_HIDE);\r
305   }\r
306 \r
307   evalGraphDialogUp = FALSE;\r
308 }\r
309 \r
310 // This function is the interface to the back-end. It is currently called through the front-end,\r
311 // though, where it shares the HistorySet() wrapper with MoveHistorySet(). Once all front-ends\r
312 // support the eval graph, it would be more logical to call it directly from the back-end.\r
313 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo )\r
314 {\r
315     /* [AS] Danger! For now we rely on the pvInfo parameter being a static variable! */\r
316 \r
317     currFirst = first;\r
318     currLast = last;\r
319     currCurrent = current;\r
320     currPvInfo = pvInfo;\r
321 \r
322     if( evalGraphDialog ) {\r
323         SendMessage( evalGraphDialog, WM_REFRESH_GRAPH, 0, 0 );\r
324     }\r
325 }\r