4 * Author: Alessandro Scotti (Dec 2005)
\r
6 * Copyright 2005 Alessandro Scotti
\r
8 * ------------------------------------------------------------------------
\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
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
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
23 *------------------------------------------------------------------------
\r
24 ** See the file ChangeLog for a revision history. */
\r
28 #include <windows.h> /* required for all Windows applications */
\r
29 #include <richedit.h>
\r
33 #include <commdlg.h>
\r
37 #include "winboard.h"
\r
38 #include "frontend.h"
\r
39 #include "backend.h"
\r
43 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );
\r
44 VOID EvalGraphPopUp();
\r
45 VOID EvalGraphPopDown();
\r
46 BOOL EvalGraphIsUp();
\r
48 #define WM_REFRESH_GRAPH (WM_USER + 1)
\r
50 /* Imports from backend.c */
\r
51 char * SavePart(char *str);
\r
53 /* Imports from winboard.c */
\r
54 extern HWND evalGraphDialog;
\r
55 extern BOOLEAN evalGraphDialogUp;
\r
57 extern HINSTANCE hInst;
\r
58 extern HWND hwndMain;
\r
60 extern WindowPlacement wpEvalGraph;
\r
62 /* Module globals */
\r
63 static ChessProgramStats_Move * currPvInfo;
\r
64 static int currFirst = 0;
\r
65 static int currLast = 0;
\r
66 static int currCurrent = -1;
\r
68 static COLORREF crWhite = RGB( 0xFF, 0xFF, 0xB0 );
\r
69 static COLORREF crBlack = RGB( 0xAD, 0x5D, 0x3D );
\r
71 static HDC hdcPB = NULL;
\r
72 static HBITMAP hbmPB = NULL;
\r
73 static int nWidthPB = 0;
\r
74 static int nHeightPB = 0;
\r
75 static HPEN hpenDotted = NULL;
\r
76 static HPEN hpenBlueDotted = NULL;
\r
77 static HPEN hpenBold[2] = { NULL, NULL };
\r
78 static HBRUSH hbrHist[2] = { NULL, NULL };
\r
80 static int MarginX = 18;
\r
81 static int MarginW = 4;
\r
82 static int MarginH = 4;
\r
84 #define MIN_HIST_WIDTH 4
\r
85 #define MAX_HIST_WIDTH 10
\r
87 static int GetPvScore( int index )
\r
89 int score = currPvInfo[ index ].score;
\r
91 if( index & 1 ) score = -score; /* Flip score for black */
\r
96 static VOID DrawLine( int x1, int y1, int x2, int y2 )
\r
98 MoveToEx( hdcPB, x1, y1, NULL );
\r
100 LineTo( hdcPB, x2, y2 );
\r
103 static VOID DrawLineEx( int x1, int y1, int x2, int y2 )
\r
107 MoveToEx( hdcPB, x1, y1, &stPT );
\r
109 LineTo( hdcPB, x2, y2 );
\r
111 MoveToEx( hdcPB, stPT.x, stPT.y, NULL );
\r
114 static HBRUSH CreateBrush( UINT style, COLORREF color )
\r
118 stLB.lbStyle = style;
\r
119 stLB.lbColor = color;
\r
122 return CreateBrushIndirect( &stLB );
\r
126 For a centipawn value, this function returns the height of the corresponding
\r
127 histogram, centered on the reference axis.
\r
129 Note: height can be negative!
\r
131 static int GetValueY( int value )
\r
133 if( value < -700 ) value = -700;
\r
134 if( value > +700 ) value = +700;
\r
136 return (nHeightPB / 2) - (int)(value * (nHeightPB - 2*MarginH) / 1400.0);
\r
139 static VOID DrawAxisSegmentHoriz( int value, BOOL drawValue )
\r
141 int y = GetValueY( value*100 );
\r
143 SelectObject( hdcPB, GetStockObject(BLACK_PEN) );
\r
144 DrawLine( MarginX, y, MarginX + MarginW, y );
\r
145 SelectObject( hdcPB, hpenDotted );
\r
146 DrawLine( MarginX + MarginW, y, nWidthPB - MarginW, y );
\r
150 char buf[MSG_SIZ], *b = buf;
\r
153 if( value > 0 ) *b++ = '+';
\r
154 sprintf(b, "%d", value);
\r
156 cbBuf = strlen( buf );
\r
157 GetTextExtentPoint32( hdcPB, buf, cbBuf, &stSize );
\r
158 TextOut( hdcPB, MarginX - stSize.cx - 2, y - stSize.cy / 2, buf, cbBuf );
\r
162 static VOID DrawAxis()
\r
164 int cy = nHeightPB / 2;
\r
166 SelectObject( hdcPB, GetStockObject(NULL_BRUSH) );
\r
168 SetBkMode( hdcPB, TRANSPARENT );
\r
170 DrawAxisSegmentHoriz( +5, TRUE );
\r
171 DrawAxisSegmentHoriz( +3, FALSE );
\r
172 DrawAxisSegmentHoriz( +1, FALSE );
\r
173 DrawAxisSegmentHoriz( 0, TRUE );
\r
174 DrawAxisSegmentHoriz( -1, FALSE );
\r
175 DrawAxisSegmentHoriz( -3, FALSE );
\r
176 DrawAxisSegmentHoriz( -5, TRUE );
\r
178 SelectObject( hdcPB, GetStockObject(BLACK_PEN) );
\r
180 DrawLine( MarginX + MarginW, cy, nWidthPB - MarginW, cy );
\r
181 DrawLine( MarginX + MarginW, MarginH, MarginX + MarginW, nHeightPB - MarginH );
\r
184 static VOID DrawHistogram( int x, int y, int width, int value, int side )
\r
188 if( value > -25 && value < +25 ) return;
\r
191 rc.right = rc.left + width + 1;
\r
194 rc.top = GetValueY( value );
\r
199 rc.bottom = GetValueY( value ) + 1;
\r
203 if( width == MIN_HIST_WIDTH ) {
\r
205 FillRect( hdcPB, &rc, hbrHist[side] );
\r
208 SelectObject( hdcPB, hbrHist[side] );
\r
209 Rectangle( hdcPB, rc.left, rc.top, rc.right, rc.bottom );
\r
213 static VOID DrawSeparator( int index, int x )
\r
216 if( index == currCurrent ) {
\r
217 HPEN hp = SelectObject( hdcPB, hpenBlueDotted );
\r
218 DrawLineEx( x, MarginH, x, nHeightPB - MarginH );
\r
219 SelectObject( hdcPB, hp );
\r
221 else if( (index % 20) == 0 ) {
\r
222 HPEN hp = SelectObject( hdcPB, hpenDotted );
\r
223 DrawLineEx( x, MarginH, x, nHeightPB - MarginH );
\r
224 SelectObject( hdcPB, hp );
\r
229 /* Actually draw histogram as a diagram, cause there's too much data */
\r
230 static VOID DrawHistogramAsDiagram( int cy, int paint_width, int hist_count )
\r
235 /* Rescale the graph every few moves (as opposed to every move) */
\r
236 hist_count -= hist_count % 8;
\r
240 step = (double) paint_width / (hist_count + 1);
\r
242 for( i=0; i<2; i++ ) {
\r
243 int index = currFirst;
\r
244 int side = (currCurrent + i + 1) & 1; /* Draw current side last */
\r
245 double x = MarginX + MarginW;
\r
247 if( (index & 1) != side ) {
\r
252 SelectObject( hdcPB, hpenBold[side] );
\r
254 MoveToEx( hdcPB, (int) x, cy, NULL );
\r
258 while( index < currLast ) {
\r
261 DrawSeparator( index, (int) x );
\r
263 /* Extend line up to current point */
\r
264 if( currPvInfo[index].depth > 0 ) {
\r
265 LineTo( hdcPB, (int) x, GetValueY( GetPvScore(index) ) );
\r
273 static VOID DrawHistogramFull( int cy, int hist_width, int hist_count )
\r
277 SelectObject( hdcPB, GetStockObject(BLACK_PEN) );
\r
279 for( i=0; i<hist_count; i++ ) {
\r
280 int index = currFirst + i;
\r
281 int x = MarginX + MarginW + index * hist_width;
\r
283 /* Draw a separator every 10 moves */
\r
284 DrawSeparator( index, x );
\r
286 /* Draw histogram */
\r
287 if( currPvInfo[i].depth > 0 ) {
\r
288 DrawHistogram( x, cy, hist_width, GetPvScore(index), index & 1 );
\r
298 } VisualizationData;
\r
300 static BOOL InitVisualization( VisualizationData * vd )
\r
302 BOOL result = FALSE;
\r
304 vd->cy = nHeightPB / 2;
\r
305 vd->hist_width = MIN_HIST_WIDTH;
\r
306 vd->hist_count = currLast - currFirst;
\r
307 vd->paint_width = nWidthPB - MarginX - 2*MarginW;
\r
309 if( vd->hist_count > 0 ) {
\r
312 /* Compute width */
\r
313 vd->hist_width = vd->paint_width / vd->hist_count;
\r
315 if( vd->hist_width > MAX_HIST_WIDTH ) vd->hist_width = MAX_HIST_WIDTH;
\r
317 vd->hist_width -= vd->hist_width % 2;
\r
323 static VOID DrawHistograms()
\r
325 VisualizationData vd;
\r
327 if( InitVisualization( &vd ) ) {
\r
328 if( vd.hist_width < MIN_HIST_WIDTH ) {
\r
329 DrawHistogramAsDiagram( vd.cy, vd.paint_width, vd.hist_count );
\r
332 DrawHistogramFull( vd.cy, vd.hist_width, vd.hist_count );
\r
337 static int GetMoveIndexFromPoint( int x, int y )
\r
340 int start_x = MarginX + MarginW;
\r
341 VisualizationData vd;
\r
343 if( x >= start_x && InitVisualization( &vd ) ) {
\r
344 /* Almost an hack here... we duplicate some of the paint logic */
\r
345 if( vd.hist_width < MIN_HIST_WIDTH ) {
\r
348 vd.hist_count -= vd.hist_count % 8;
\r
349 vd.hist_count += 8;
\r
350 vd.hist_count /= 2;
\r
352 step = (double) vd.paint_width / (vd.hist_count + 1);
\r
355 result = (int) (0.5 + (double) (x - start_x) / step);
\r
358 result = (x - start_x) / vd.hist_width;
\r
362 if( result >= currLast ) {
\r
369 static VOID DrawBackground()
\r
374 hbr = CreateBrush( BS_SOLID, GetSysColor( COLOR_3DFACE ) );
\r
378 rc.right = nWidthPB;
\r
379 rc.bottom = nHeightPB;
\r
381 FillRect( hdcPB, &rc, hbr );
\r
383 DeleteObject( hbr );
\r
386 static VOID PaintEvalGraph( HWND hWnd, HDC hDC )
\r
392 /* Get client area */
\r
393 GetClientRect( hWnd, &rcClient );
\r
395 width = rcClient.right - rcClient.left;
\r
396 height = rcClient.bottom - rcClient.top;
\r
398 /* Create or recreate paint box if needed */
\r
399 if( hbmPB == NULL || width != nWidthPB || height != nHeightPB ) {
\r
400 if( hpenDotted == NULL ) {
\r
401 hpenDotted = CreatePen( PS_DOT, 0, RGB(0xA0,0xA0,0xA0) );
\r
402 hpenBlueDotted = CreatePen( PS_DOT, 0, RGB(0x00,0x00,0xFF) );
\r
403 hpenBold[0] = CreatePen( PS_SOLID, 2, crWhite );
\r
404 hpenBold[1] = CreatePen( PS_SOLID, 2, crBlack );
\r
405 hbrHist[0] = CreateBrush( BS_SOLID, crWhite );
\r
406 hbrHist[1] = CreateBrush( BS_SOLID, crBlack );
\r
409 if( hdcPB != NULL ) {
\r
414 if( hbmPB != NULL ) {
\r
415 DeleteObject( hbmPB );
\r
419 hdcPB = CreateCompatibleDC( hDC );
\r
422 nHeightPB = height;
\r
423 hbmPB = CreateCompatibleBitmap( hDC, nWidthPB, nHeightPB );
\r
425 SelectObject( hdcPB, hbmPB );
\r
433 /* Copy bitmap into destination DC */
\r
434 BitBlt( hDC, 0, 0, width, height, hdcPB, 0, 0, SRCCOPY );
\r
437 LRESULT CALLBACK EvalGraphProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
\r
439 static SnapData sd;
\r
445 case WM_INITDIALOG:
\r
446 if( evalGraphDialog == NULL ) {
\r
447 evalGraphDialog = hDlg;
\r
449 RestoreWindowPlacement( hDlg, &wpEvalGraph ); /* Restore window placement */
\r
455 switch (LOWORD(wParam)) {
\r
457 EndDialog(hDlg, TRUE);
\r
461 EndDialog(hDlg, FALSE);
\r
470 case WM_ERASEBKGND:
\r
474 hDC = BeginPaint( hDlg, &stPS );
\r
475 PaintEvalGraph( hDlg, hDC );
\r
476 EndPaint( hDlg, &stPS );
\r
479 case WM_REFRESH_GRAPH:
\r
480 hDC = GetDC( hDlg );
\r
481 PaintEvalGraph( hDlg, hDC );
\r
482 ReleaseDC( hDlg, hDC );
\r
485 case WM_LBUTTONDBLCLK:
\r
486 if( wParam == 0 || wParam == MK_LBUTTON ) {
\r
487 int index = GetMoveIndexFromPoint( LOWORD(lParam), HIWORD(lParam) );
\r
489 if( index >= 0 && index < currLast ) {
\r
490 ToNrEvent( index + 1 );
\r
496 InvalidateRect( hDlg, NULL, FALSE );
\r
499 case WM_GETMINMAXINFO:
\r
501 MINMAXINFO * mmi = (MINMAXINFO *) lParam;
\r
503 mmi->ptMinTrackSize.x = 100;
\r
504 mmi->ptMinTrackSize.y = 100;
\r
508 /* Support for captionless window */
\r
510 case WM_NCLBUTTONDBLCLK:
\r
511 if( wParam == HTCAPTION ) {
\r
514 POINTS pts = MAKEPOINTS(lParam);
\r
516 mouse_xy.x = pts.x;
\r
517 mouse_xy.y = pts.y;
\r
518 ScreenToClient( hDlg, &mouse_xy );
\r
520 index = GetMoveIndexFromPoint( mouse_xy.x, mouse_xy.y );
\r
522 if( index >= 0 && index < currLast ) {
\r
523 ToNrEvent( index + 1 );
\r
530 LRESULT res = DefWindowProc( hDlg, message, wParam, lParam );
\r
532 if( res == HTCLIENT ) res = HTCAPTION;
\r
534 SetWindowLong( hDlg, DWL_MSGRESULT, res );
\r
542 EvalGraphPopDown();
\r
545 case WM_ENTERSIZEMOVE:
\r
546 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
549 return OnSizing( &sd, hDlg, wParam, lParam );
\r
552 return OnMoving( &sd, hDlg, wParam, lParam );
\r
554 case WM_EXITSIZEMOVE:
\r
555 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
561 VOID EvalGraphPopUp()
\r
565 CheckMenuItem(GetMenu(hwndMain), IDM_ShowEvalGraph, MF_CHECKED);
\r
567 if( evalGraphDialog ) {
\r
568 SendMessage( evalGraphDialog, WM_INITDIALOG, 0, 0 );
\r
570 if( ! evalGraphDialogUp ) {
\r
571 ShowWindow(evalGraphDialog, SW_SHOW);
\r
575 crWhite = appData.evalHistColorWhite;
\r
576 crBlack = appData.evalHistColorBlack;
\r
578 lpProc = MakeProcInstance( (FARPROC) EvalGraphProc, hInst );
\r
580 /* Note to self: dialog must have the WS_VISIBLE style set, otherwise it's not shown! */
\r
581 CreateDialog( hInst, MAKEINTRESOURCE(DLG_EvalGraph), hwndMain, (DLGPROC)lpProc );
\r
583 FreeProcInstance(lpProc);
\r
586 evalGraphDialogUp = TRUE;
\r
589 VOID EvalGraphPopDown()
\r
591 CheckMenuItem(GetMenu(hwndMain), IDM_ShowEvalGraph, MF_UNCHECKED);
\r
593 if( evalGraphDialog ) {
\r
594 ShowWindow(evalGraphDialog, SW_HIDE);
\r
597 evalGraphDialogUp = FALSE;
\r
600 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo )
\r
602 /* [AS] Danger! For now we rely on the pvInfo parameter being a static variable! */
\r
606 currCurrent = current;
\r
607 currPvInfo = pvInfo;
\r
609 if( evalGraphDialog ) {
\r
610 SendMessage( evalGraphDialog, WM_REFRESH_GRAPH, 0, 0 );
\r
614 BOOL EvalGraphIsUp()
\r
616 return evalGraphDialogUp;
\r