-/*
- * Evaluation graph
- *
- * Author: Alessandro Scotti (Dec 2005)
- *
- * ------------------------------------------------------------------------
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- * ------------------------------------------------------------------------
- */
-#include "config.h"
-
-#include <windows.h> /* required for all Windows applications */
-#include <richedit.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <malloc.h>
-#include <commdlg.h>
-#include <dlgs.h>
-
-#include "common.h"
-#include "winboard.h"
-#include "frontend.h"
-#include "backend.h"
-
-#include "wsnap.h"
-
-VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );
-VOID EvalGraphPopUp();
-VOID EvalGraphPopDown();
-BOOL EvalGraphIsUp();
-
-#define WM_REFRESH_GRAPH (WM_USER + 1)
-
-/* Imports from backend.c */
-char * SavePart(char *str);
-
-/* Imports from winboard.c */
-extern HWND evalGraphDialog;
-extern BOOLEAN evalGraphDialogUp;
-
-extern HINSTANCE hInst;
-extern HWND hwndMain;
-
-extern WindowPlacement wpEvalGraph;
-
-/* Module globals */
-static ChessProgramStats_Move * currPvInfo;
-static int currFirst = 0;
-static int currLast = 0;
-static int currCurrent = -1;
-
-static COLORREF crWhite = RGB( 0xFF, 0xFF, 0xB0 );
-static COLORREF crBlack = RGB( 0xAD, 0x5D, 0x3D );
-
-static HDC hdcPB = NULL;
-static HBITMAP hbmPB = NULL;
-static int nWidthPB = 0;
-static int nHeightPB = 0;
-static HPEN hpenDotted = NULL;
-static HPEN hpenBlueDotted = NULL;
-static HPEN hpenBold[2] = { NULL, NULL };
-static HBRUSH hbrHist[2] = { NULL, NULL };
-
-static int MarginX = 18;
-static int MarginW = 4;
-static int MarginH = 4;
-
-#define MIN_HIST_WIDTH 4
-#define MAX_HIST_WIDTH 10
-
-static int GetPvScore( int index )
-{
- int score = currPvInfo[ index ].score;
-
- if( index & 1 ) score = -score; /* Flip score for black */
-
- return score;
-}
-
-static VOID DrawLine( int x1, int y1, int x2, int y2 )
-{
- MoveToEx( hdcPB, x1, y1, NULL );
-
- LineTo( hdcPB, x2, y2 );
-}
-
-static VOID DrawLineEx( int x1, int y1, int x2, int y2 )
-{
- POINT stPT;
-
- MoveToEx( hdcPB, x1, y1, &stPT );
-
- LineTo( hdcPB, x2, y2 );
-
- MoveToEx( hdcPB, stPT.x, stPT.y, NULL );
-}
-
-static HBRUSH CreateBrush( UINT style, COLORREF color )
-{
- LOGBRUSH stLB;
-
- stLB.lbStyle = style;
- stLB.lbColor = color;
- stLB.lbHatch = 0;
-
- return CreateBrushIndirect( &stLB );
-}
-
-/*
- For a centipawn value, this function returns the height of the corresponding
- histogram, centered on the reference axis.
-
- Note: height can be negative!
-*/
-static int GetValueY( int value )
-{
- if( value < -700 ) value = -700;
- if( value > +700 ) value = +700;
-
- return (nHeightPB / 2) - (int)(value * (nHeightPB - 2*MarginH) / 1400.0);
-}
-
-static VOID DrawAxisSegmentHoriz( int value, BOOL drawValue )
-{
- int y = GetValueY( value*100 );
-
- SelectObject( hdcPB, GetStockObject(BLACK_PEN) );
- DrawLine( MarginX, y, MarginX + MarginW, y );
- SelectObject( hdcPB, hpenDotted );
- DrawLine( MarginX + MarginW, y, nWidthPB - MarginW, y );
-
- if( drawValue ) {
- SIZE stSize;
- char buf[8];
- int cbBuf;
-
- if( value > 0 ) {
- buf[0] = '+';
- itoa( value, buf+1, 10 );
- }
- else {
- itoa( value, buf, 10 );
- }
-
- cbBuf = strlen( buf );
-
- GetTextExtentPoint32( hdcPB, buf, cbBuf, &stSize );
-
- TextOut( hdcPB, MarginX - stSize.cx - 2, y - stSize.cy / 2, buf, cbBuf );
- }
-}
-
-static VOID DrawAxis()
-{
- int cy = nHeightPB / 2;
-
- SelectObject( hdcPB, GetStockObject(NULL_BRUSH) );
-
- SetBkMode( hdcPB, TRANSPARENT );
-
- DrawAxisSegmentHoriz( +5, TRUE );
- DrawAxisSegmentHoriz( +3, FALSE );
- DrawAxisSegmentHoriz( +1, FALSE );
- DrawAxisSegmentHoriz( 0, TRUE );
- DrawAxisSegmentHoriz( -1, FALSE );
- DrawAxisSegmentHoriz( -3, FALSE );
- DrawAxisSegmentHoriz( -5, TRUE );
-
- SelectObject( hdcPB, GetStockObject(BLACK_PEN) );
-
- DrawLine( MarginX + MarginW, cy, nWidthPB - MarginW, cy );
- DrawLine( MarginX + MarginW, MarginH, MarginX + MarginW, nHeightPB - MarginH );
-}
-
-static VOID DrawHistogram( int x, int y, int width, int value, int side )
-{
- RECT rc;
-
- if( value > -25 && value < +25 ) return;
-
- rc.left = x;
- rc.right = rc.left + width + 1;
-
- if( value > 0 ) {
- rc.top = GetValueY( value );
- rc.bottom = y+1;
- }
- else {
- rc.top = y;
- rc.bottom = GetValueY( value ) + 1;
- }
-
-
- if( width == MIN_HIST_WIDTH ) {
- rc.right--;
- FillRect( hdcPB, &rc, hbrHist[side] );
- }
- else {
- SelectObject( hdcPB, hbrHist[side] );
- Rectangle( hdcPB, rc.left, rc.top, rc.right, rc.bottom );
- }
-}
-
-static VOID DrawSeparator( int index, int x )
-{
- if( index > 0 ) {
- if( index == currCurrent ) {
- HPEN hp = SelectObject( hdcPB, hpenBlueDotted );
- DrawLineEx( x, MarginH, x, nHeightPB - MarginH );
- SelectObject( hdcPB, hp );
- }
- else if( (index % 20) == 0 ) {
- HPEN hp = SelectObject( hdcPB, hpenDotted );
- DrawLineEx( x, MarginH, x, nHeightPB - MarginH );
- SelectObject( hdcPB, hp );
- }
- }
-}
-
-/* Actually draw histogram as a diagram, cause there's too much data */
-static VOID DrawHistogramAsDiagram( int cy, int paint_width, int hist_count )
-{
- double step;
- int i;
-
- /* Rescale the graph every few moves (as opposed to every move) */
- hist_count -= hist_count % 8;
- hist_count += 8;
- hist_count /= 2;
-
- step = (double) paint_width / (hist_count + 1);
-
- for( i=0; i<2; i++ ) {
- int index = currFirst;
- int side = (currCurrent + i + 1) & 1; /* Draw current side last */
- double x = MarginX + MarginW;
-
- if( (index & 1) != side ) {
- x += step / 2;
- index++;
- }
-
- SelectObject( hdcPB, hpenBold[side] );
-
- MoveToEx( hdcPB, (int) x, cy, NULL );
-
- index += 2;
-
- while( index < currLast ) {
- x += step;
-
- DrawSeparator( index, (int) x );
-
- /* Extend line up to current point */
- if( currPvInfo[index].depth > 0 ) {
- LineTo( hdcPB, (int) x, GetValueY( GetPvScore(index) ) );
- }
-
- index += 2;
- }
- }
-}
-
-static VOID DrawHistogramFull( int cy, int hist_width, int hist_count )
-{
- int i;
-
- SelectObject( hdcPB, GetStockObject(BLACK_PEN) );
-
- for( i=0; i<hist_count; i++ ) {
- int index = currFirst + i;
- int x = MarginX + MarginW + index * hist_width;
-
- /* Draw a separator every 10 moves */
- DrawSeparator( index, x );
-
- /* Draw histogram */
- if( currPvInfo[i].depth > 0 ) {
- DrawHistogram( x, cy, hist_width, GetPvScore(index), index & 1 );
- }
- }
-}
-
-typedef struct {
- int cy;
- int hist_width;
- int hist_count;
- int paint_width;
-} VisualizationData;
-
-static BOOL InitVisualization( VisualizationData * vd )
-{
- BOOL result = FALSE;
-
- vd->cy = nHeightPB / 2;
- vd->hist_width = MIN_HIST_WIDTH;
- vd->hist_count = currLast - currFirst;
- vd->paint_width = nWidthPB - MarginX - 2*MarginW;
-
- if( vd->hist_count > 0 ) {
- result = TRUE;
-
- /* Compute width */
- vd->hist_width = vd->paint_width / vd->hist_count;
-
- if( vd->hist_width > MAX_HIST_WIDTH ) vd->hist_width = MAX_HIST_WIDTH;
-
- vd->hist_width -= vd->hist_width % 2;
- }
-
- return result;
-}
-
-static VOID DrawHistograms()
-{
- VisualizationData vd;
-
- if( InitVisualization( &vd ) ) {
- if( vd.hist_width < MIN_HIST_WIDTH ) {
- DrawHistogramAsDiagram( vd.cy, vd.paint_width, vd.hist_count );
- }
- else {
- DrawHistogramFull( vd.cy, vd.hist_width, vd.hist_count );
- }
- }
-}
-
-static int GetMoveIndexFromPoint( int x, int y )
-{
- int result = -1;
- int start_x = MarginX + MarginW;
- VisualizationData vd;
-
- if( x >= start_x && InitVisualization( &vd ) ) {
- /* Almost an hack here... we duplicate some of the paint logic */
- if( vd.hist_width < MIN_HIST_WIDTH ) {
- double step;
-
- vd.hist_count -= vd.hist_count % 8;
- vd.hist_count += 8;
- vd.hist_count /= 2;
-
- step = (double) vd.paint_width / (vd.hist_count + 1);
- step /= 2;
-
- result = (int) (0.5 + (double) (x - start_x) / step);
- }
- else {
- result = (x - start_x) / vd.hist_width;
- }
- }
-
- if( result >= currLast ) {
- result = -1;
- }
-
- return result;
-}
-
-static VOID DrawBackground()
-{
- HBRUSH hbr;
- RECT rc;
-
- hbr = CreateBrush( BS_SOLID, GetSysColor( COLOR_3DFACE ) );
-
- rc.left = 0;
- rc.top = 0;
- rc.right = nWidthPB;
- rc.bottom = nHeightPB;
-
- FillRect( hdcPB, &rc, hbr );
-
- DeleteObject( hbr );
-}
-
-static VOID PaintEvalGraph( HWND hWnd, HDC hDC )
-{
- RECT rcClient;
- int width;
- int height;
-
- /* Get client area */
- GetClientRect( hWnd, &rcClient );
-
- width = rcClient.right - rcClient.left;
- height = rcClient.bottom - rcClient.top;
-
- /* Create or recreate paint box if needed */
- if( hbmPB == NULL || width != nWidthPB || height != nHeightPB ) {
- if( hpenDotted == NULL ) {
- hpenDotted = CreatePen( PS_DOT, 0, RGB(0xA0,0xA0,0xA0) );
- hpenBlueDotted = CreatePen( PS_DOT, 0, RGB(0x00,0x00,0xFF) );
- hpenBold[0] = CreatePen( PS_SOLID, 2, crWhite );
- hpenBold[1] = CreatePen( PS_SOLID, 2, crBlack );
- hbrHist[0] = CreateBrush( BS_SOLID, crWhite );
- hbrHist[1] = CreateBrush( BS_SOLID, crBlack );
- }
-
- if( hdcPB != NULL ) {
- DeleteDC( hdcPB );
- hdcPB = NULL;
- }
-
- if( hbmPB != NULL ) {
- DeleteObject( hbmPB );
- hbmPB = NULL;
- }
-
- hdcPB = CreateCompatibleDC( hDC );
-
- nWidthPB = width;
- nHeightPB = height;
- hbmPB = CreateCompatibleBitmap( hDC, nWidthPB, nHeightPB );
-
- SelectObject( hdcPB, hbmPB );
- }
-
- /* Draw */
- DrawBackground();
- DrawAxis();
- DrawHistograms();
-
- /* Copy bitmap into destination DC */
- BitBlt( hDC, 0, 0, width, height, hdcPB, 0, 0, SRCCOPY );
-}
-
-LRESULT CALLBACK EvalGraphProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
-{
- static SnapData sd;
-
- PAINTSTRUCT stPS;
- HDC hDC;
-
- switch (message) {
- case WM_INITDIALOG:
- if( evalGraphDialog == NULL ) {
- evalGraphDialog = hDlg;
-
- RestoreWindowPlacement( hDlg, &wpEvalGraph ); /* Restore window placement */
- }
-
- return FALSE;
-
- case WM_COMMAND:
- switch (LOWORD(wParam)) {
- case IDOK:
- EndDialog(hDlg, TRUE);
- return TRUE;
-
- case IDCANCEL:
- EndDialog(hDlg, FALSE);
- return TRUE;
-
- default:
- break;
- }
-
- break;
-
- case WM_ERASEBKGND:
- return TRUE;
-
- case WM_PAINT:
- hDC = BeginPaint( hDlg, &stPS );
- PaintEvalGraph( hDlg, hDC );
- EndPaint( hDlg, &stPS );
- break;
-
- case WM_REFRESH_GRAPH:
- hDC = GetDC( hDlg );
- PaintEvalGraph( hDlg, hDC );
- ReleaseDC( hDlg, hDC );
- break;
-
- case WM_LBUTTONDBLCLK:
- if( wParam == 0 || wParam == MK_LBUTTON ) {
- int index = GetMoveIndexFromPoint( LOWORD(lParam), HIWORD(lParam) );
-
- if( index >= 0 && index < currLast ) {
- ToNrEvent( index + 1 );
- }
- }
- return TRUE;
-
- case WM_SIZE:
- InvalidateRect( hDlg, NULL, FALSE );
- break;
-
- case WM_GETMINMAXINFO:
- {
- MINMAXINFO * mmi = (MINMAXINFO *) lParam;
-
- mmi->ptMinTrackSize.x = 100;
- mmi->ptMinTrackSize.y = 100;
- }
- break;
-
- /* Support for captionless window */
-#if 0
- case WM_NCLBUTTONDBLCLK:
- if( wParam == HTCAPTION ) {
- int index;
- POINT mouse_xy;
- POINTS pts = MAKEPOINTS(lParam);
-
- mouse_xy.x = pts.x;
- mouse_xy.y = pts.y;
- ScreenToClient( hDlg, &mouse_xy );
-
- index = GetMoveIndexFromPoint( mouse_xy.x, mouse_xy.y );
-
- if( index >= 0 && index < currLast ) {
- ToNrEvent( index + 1 );
- }
- }
- break;
-
- case WM_NCHITTEST:
- {
- LRESULT res = DefWindowProc( hDlg, message, wParam, lParam );
-
- if( res == HTCLIENT ) res = HTCAPTION;
-
- SetWindowLong( hDlg, DWL_MSGRESULT, res );
-
- return TRUE;
- }
- break;
-#endif
-
- case WM_CLOSE:
- EvalGraphPopDown();
- break;
-
- case WM_ENTERSIZEMOVE:
- return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
-
- case WM_SIZING:
- return OnSizing( &sd, hDlg, wParam, lParam );
-
- case WM_MOVING:
- return OnMoving( &sd, hDlg, wParam, lParam );
-
- case WM_EXITSIZEMOVE:
- return OnExitSizeMove( &sd, hDlg, wParam, lParam );
- }
-
- return FALSE;
-}
-
-VOID EvalGraphPopUp()
-{
- FARPROC lpProc;
-
- CheckMenuItem(GetMenu(hwndMain), IDM_ShowEvalGraph, MF_CHECKED);
-
- if( evalGraphDialog ) {
- SendMessage( evalGraphDialog, WM_INITDIALOG, 0, 0 );
-
- if( ! evalGraphDialogUp ) {
- ShowWindow(evalGraphDialog, SW_SHOW);
- }
- }
- else {
- crWhite = appData.evalHistColorWhite;
- crBlack = appData.evalHistColorBlack;
-
- lpProc = MakeProcInstance( (FARPROC) EvalGraphProc, hInst );
-
- /* Note to self: dialog must have the WS_VISIBLE style set, otherwise it's not shown! */
- CreateDialog( hInst, MAKEINTRESOURCE(DLG_EvalGraph), hwndMain, (DLGPROC)lpProc );
-
- FreeProcInstance(lpProc);
- }
-
- evalGraphDialogUp = TRUE;
-}
-
-VOID EvalGraphPopDown()
-{
- CheckMenuItem(GetMenu(hwndMain), IDM_ShowEvalGraph, MF_UNCHECKED);
-
- if( evalGraphDialog ) {
- ShowWindow(evalGraphDialog, SW_HIDE);
- }
-
- evalGraphDialogUp = FALSE;
-}
-
-VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo )
-{
- /* [AS] Danger! For now we rely on the pvInfo parameter being a static variable! */
-
- currFirst = first;
- currLast = last;
- currCurrent = current;
- currPvInfo = pvInfo;
-
- if( evalGraphDialog ) {
- SendMessage( evalGraphDialog, WM_REFRESH_GRAPH, 0, 0 );
- }
-}
-
-BOOL EvalGraphIsUp()
-{
- return evalGraphDialogUp;
-}
+/*\r
+ * wevalgraph.c - Evaluation graph front-end part\r
+ *\r
+ * Author: Alessandro Scotti (Dec 2005)\r
+ *\r
+ * Copyright 2005 Alessandro Scotti\r
+ *\r
+ * Enhancements Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.\r
+ *\r
+ * ------------------------------------------------------------------------\r
+ *\r
+ * GNU XBoard is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation, either version 3 of the License, or (at\r
+ * your option) any later version.\r
+ *\r
+ * GNU XBoard is distributed in the hope that it will be useful, but\r
+ * WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program. If not, see http://www.gnu.org/licenses/. *\r
+ *\r
+ *------------------------------------------------------------------------\r
+ ** See the file ChangeLog for a revision history. */\r
+\r
+// code refactored by HGM to obtain front-end / back-end separation\r
+\r
+#include "config.h"\r
+\r
+#include <windows.h>\r
+#include <commdlg.h>\r
+#include <dlgs.h>\r
+#include <stdio.h>\r
+\r
+#include "common.h"\r
+#include "frontend.h"\r
+#include "backend.h"\r
+#include "winboard.h"\r
+#include "evalgraph.h"\r
+#include "wsnap.h"\r
+\r
+#define WM_REFRESH_GRAPH (WM_USER + 1)\r
+\r
+/* Module globals */\r
+static BOOLEAN evalGraphDialogUp;\r
+\r
+static COLORREF crWhite = RGB( 0xFF, 0xFF, 0xB0 );\r
+static COLORREF crBlack = RGB( 0xAD, 0x5D, 0x3D );\r
+\r
+static HDC hdcPB = NULL;\r
+static HBITMAP hbmPB = NULL;\r
+static HPEN pens[PEN_ANY+1]; // [HGM] put all pens in one array\r
+static HBRUSH hbrHist[3] = { NULL, NULL, NULL };\r
+\r
+Boolean EvalGraphIsUp()\r
+{\r
+ return evalGraphDialogUp;\r
+}\r
+\r
+// [HGM] front-end, added as wrapper to avoid use of LineTo and MoveToEx in other routines (so they can be back-end) \r
+void DrawSegment( int x, int y, int *lastX, int *lastY, int penType )\r
+{\r
+ POINT stPt;\r
+ if(penType == PEN_NONE) MoveToEx( hdcPB, x, y, &stPt ); else {\r
+ HPEN hp = SelectObject( hdcPB, pens[penType] );\r
+ LineTo( hdcPB, x, y );\r
+ SelectObject( hdcPB, hp );\r
+ }\r
+ if(lastX != NULL) { *lastX = stPt.x; *lastY = stPt.y; }\r
+}\r
+\r
+// front-end wrapper for drawing functions to do rectangles\r
+void DrawRectangle( int left, int top, int right, int bottom, int side, int style )\r
+{\r
+ HPEN hp = SelectObject( hdcPB, pens[PEN_BLACK] );\r
+ RECT rc;\r
+\r
+ rc.top = top; rc.left = left; rc.bottom = bottom; rc.right = right;\r
+ if(style == FILLED)\r
+ FillRect( hdcPB, &rc, hbrHist[side] );\r
+ else {\r
+ SelectObject( hdcPB, hbrHist[side] );\r
+ Rectangle( hdcPB, left, top, right, bottom );\r
+ }\r
+ SelectObject( hdcPB, hp );\r
+}\r
+\r
+// front-end wrapper for putting text in graph\r
+void DrawEvalText(char *buf, int cbBuf, int y)\r
+{\r
+ SIZE stSize;\r
+ SetBkMode( hdcPB, TRANSPARENT );\r
+ GetTextExtentPoint32( hdcPB, buf, cbBuf, &stSize );\r
+ TextOut( hdcPB, MarginX - stSize.cx - 2, y - stSize.cy / 2, buf, cbBuf );\r
+}\r
+\r
+// front-end\r
+static HBRUSH CreateBrush( UINT style, COLORREF color )\r
+{\r
+ LOGBRUSH stLB;\r
+\r
+ stLB.lbStyle = style;\r
+ stLB.lbColor = color;\r
+ stLB.lbHatch = 0;\r
+\r
+ return CreateBrushIndirect( &stLB );\r
+}\r
+\r
+// front-end. Create pens, device context and buffer bitmap for global use, copy result to display\r
+// The back-end part n the middle has been taken out and moed to PainEvalGraph()\r
+static VOID DisplayEvalGraph( HWND hWnd, HDC hDC )\r
+{\r
+ RECT rcClient;\r
+ int width;\r
+ int height;\r
+\r
+ /* Get client area */\r
+ GetClientRect( hWnd, &rcClient );\r
+\r
+ width = rcClient.right - rcClient.left;\r
+ height = rcClient.bottom - rcClient.top;\r
+\r
+ /* Create or recreate paint box if needed */\r
+ if( hbmPB == NULL || width != nWidthPB || height != nHeightPB ) {\r
+ if( pens[PEN_DOTTED] == NULL ) {\r
+ pens[PEN_BLACK] = GetStockObject(BLACK_PEN);\r
+ pens[PEN_DOTTED] = CreatePen( PS_DOT, 0, RGB(0xA0,0xA0,0xA0) );\r
+ pens[PEN_BLUEDOTTED] = CreatePen( PS_DOT, 0, RGB(0x00,0x00,0xFF) );\r
+ pens[PEN_BOLDWHITE] = CreatePen( PS_SOLID, 2, crWhite );\r
+ pens[PEN_BOLDBLACK] = CreatePen( PS_SOLID, 2, crBlack );\r
+ hbrHist[0] = CreateBrush( BS_SOLID, crWhite );\r
+ hbrHist[1] = CreateBrush( BS_SOLID, crBlack );\r
+ hbrHist[2] = CreateBrush( BS_SOLID, GetSysColor( COLOR_3DFACE ) ); // background\r
+ }\r
+\r
+ if( hdcPB != NULL ) {\r
+ DeleteDC( hdcPB );\r
+ hdcPB = NULL;\r
+ }\r
+\r
+ if( hbmPB != NULL ) {\r
+ DeleteObject( hbmPB );\r
+ hbmPB = NULL;\r
+ }\r
+\r
+ hdcPB = CreateCompatibleDC( hDC );\r
+\r
+ nWidthPB = width;\r
+ nHeightPB = height;\r
+ hbmPB = CreateCompatibleBitmap( hDC, nWidthPB, nHeightPB );\r
+\r
+ SelectObject( hdcPB, hbmPB );\r
+ }\r
+\r
+ // back-end painting; calls back front-end primitives for lines, rectangles and text\r
+ PaintEvalGraph();\r
+ SetWindowText(hWnd, MakeEvalTitle(differentialView ? T_("Blunder Graph") : T_("Evaluation Graph")));\r
+\r
+ /* Copy bitmap into destination DC */\r
+ BitBlt( hDC, 0, 0, nWidthPB, nHeightPB, hdcPB, 0, 0, SRCCOPY );\r
+}\r
+\r
+// Note: Once the eval graph is opened, this window-proc lives forever; een closing the\r
+// eval-graph window merely hides it. On opening we re-initialize it, though, so it could\r
+// as well hae been destroyed. While it is open it processes the REFRESH_GRAPH commands.\r
+LRESULT CALLBACK EvalGraphProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )\r
+{\r
+ static SnapData sd;\r
+\r
+ PAINTSTRUCT stPS;\r
+ HDC hDC;\r
+\r
+ switch (message) {\r
+ case WM_INITDIALOG:\r
+ Translate(hDlg, DLG_EvalGraph);\r
+ if( evalGraphDialog == NULL ) {\r
+ evalGraphDialog = hDlg;\r
+\r
+ RestoreWindowPlacement( hDlg, &wpEvalGraph ); /* Restore window placement */\r
+ }\r
+\r
+ return FALSE;\r
+\r
+ case WM_COMMAND:\r
+ switch (LOWORD(wParam)) {\r
+ case IDOK:\r
+ EndDialog(hDlg, TRUE);\r
+ return TRUE;\r
+\r
+ case IDCANCEL:\r
+ EndDialog(hDlg, FALSE);\r
+ return TRUE;\r
+\r
+ default:\r
+ break;\r
+ }\r
+\r
+ break;\r
+\r
+ case WM_ERASEBKGND:\r
+ return TRUE;\r
+\r
+ case WM_PAINT:\r
+ hDC = BeginPaint( hDlg, &stPS );\r
+ DisplayEvalGraph( hDlg, hDC );\r
+ EndPaint( hDlg, &stPS );\r
+ break;\r
+\r
+ case WM_MOUSEWHEEL:\r
+ if((short)HIWORD(wParam) < 0) appData.zoom++;\r
+ if((short)HIWORD(wParam) > 0 && appData.zoom > 1) appData.zoom--;\r
+ goto paint;\r
+ case WM_RBUTTONDOWN:\r
+ differentialView = !differentialView;\r
+ case WM_REFRESH_GRAPH:\r
+ paint:\r
+ hDC = GetDC( hDlg );\r
+ DisplayEvalGraph( hDlg, hDC );\r
+ ReleaseDC( hDlg, hDC );\r
+ break;\r
+\r
+ case WM_LBUTTONDOWN:\r
+ if( wParam == 0 || wParam == MK_LBUTTON ) {\r
+ int index = GetMoveIndexFromPoint( LOWORD(lParam), HIWORD(lParam) );\r
+\r
+ if( index >= 0 && index < currLast ) {\r
+ ToNrEvent( index + 1 );\r
+ }\r
+ }\r
+ return TRUE;\r
+\r
+ case WM_SIZE:\r
+ InvalidateRect( hDlg, NULL, FALSE );\r
+ break;\r
+\r
+ case WM_GETMINMAXINFO:\r
+ {\r
+ MINMAXINFO * mmi = (MINMAXINFO *) lParam;\r
+ \r
+ mmi->ptMinTrackSize.x = 100;\r
+ mmi->ptMinTrackSize.y = 100;\r
+ }\r
+ break;\r
+\r
+ /* Support for captionless window */\r
+ case WM_CLOSE:\r
+ EvalGraphPopDown();\r
+ break;\r
+\r
+ case WM_ENTERSIZEMOVE:\r
+ return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
+\r
+ case WM_SIZING:\r
+ return OnSizing( &sd, hDlg, wParam, lParam );\r
+\r
+ case WM_MOVING:\r
+ return OnMoving( &sd, hDlg, wParam, lParam );\r
+\r
+ case WM_EXITSIZEMOVE:\r
+ return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+// creates the eval graph, or unhides it.\r
+VOID EvalGraphPopUp()\r
+{\r
+ FARPROC lpProc;\r
+ \r
+ CheckMenuItem(GetMenu(hwndMain), IDM_ShowEvalGraph, MF_CHECKED);\r
+\r
+ if( evalGraphDialog ) {\r
+ SendMessage( evalGraphDialog, WM_INITDIALOG, 0, 0 );\r
+\r
+ if( ! evalGraphDialogUp ) {\r
+ ShowWindow(evalGraphDialog, SW_SHOW);\r
+ }\r
+ }\r
+ else {\r
+ crWhite = appData.evalHistColorWhite;\r
+ crBlack = appData.evalHistColorBlack;\r
+\r
+ lpProc = MakeProcInstance( (FARPROC) EvalGraphProc, hInst );\r
+\r
+ /* Note to self: dialog must have the WS_VISIBLE style set, otherwise it's not shown! */\r
+ CreateDialog( hInst, MAKEINTRESOURCE(DLG_EvalGraph), hwndMain, (DLGPROC)lpProc );\r
+\r
+ FreeProcInstance(lpProc);\r
+ }\r
+\r
+ evalGraphDialogUp = TRUE;\r
+}\r
+\r
+// Note that this hides the window. It could as well have destroyed it.\r
+VOID EvalGraphPopDown()\r
+{\r
+ CheckMenuItem(GetMenu(hwndMain), IDM_ShowEvalGraph, MF_UNCHECKED);\r
+\r
+ if( evalGraphDialog ) {\r
+ ShowWindow(evalGraphDialog, SW_HIDE);\r
+ }\r
+\r
+ evalGraphDialogUp = FALSE;\r
+}\r
+\r
+// This function is the interface to the back-end. It is currently called through the front-end,\r
+// though, where it shares the HistorySet() wrapper with MoveHistorySet(). Once all front-ends\r
+// support the eval graph, it would be more logical to call it directly from the back-end.\r
+VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo )\r
+{\r
+ /* [AS] Danger! For now we rely on the pvInfo parameter being a static variable! */\r
+\r
+ currFirst = first;\r
+ currLast = last;\r
+ currCurrent = current;\r
+ currPvInfo = pvInfo;\r
+\r
+ if( evalGraphDialog ) {\r
+ SendMessage( evalGraphDialog, WM_REFRESH_GRAPH, 0, 0 );\r
+ }\r
+}\r