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