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