changes from H.G. Muller; version 4.3.13
[xboard.git] / winboard / wevalgraph.c
index 2896a23..997bd17 100644 (file)
-/*
- * 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
+ * Evaluation graph\r
+ *\r
+ * Author: Alessandro Scotti (Dec 2005)\r
+ *\r
+ * ------------------------------------------------------------------------\r
+ * This program 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 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU 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, write to the Free Software\r
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
+ * ------------------------------------------------------------------------\r
+ */\r
+#include "config.h"\r
+\r
+#include <windows.h> /* required for all Windows applications */\r
+#include <richedit.h>\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <malloc.h>\r
+#include <commdlg.h>\r
+#include <dlgs.h>\r
+\r
+#include "common.h"\r
+#include "winboard.h"\r
+#include "frontend.h"\r
+#include "backend.h"\r
+\r
+#include "wsnap.h"\r
+\r
+VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
+VOID EvalGraphPopUp();\r
+VOID EvalGraphPopDown();\r
+BOOL EvalGraphIsUp();\r
+\r
+#define WM_REFRESH_GRAPH    (WM_USER + 1)\r
+\r
+/* Imports from backend.c */\r
+char * SavePart(char *str);\r
+\r
+/* Imports from winboard.c */\r
+extern HWND evalGraphDialog;\r
+extern BOOLEAN evalGraphDialogUp;\r
+\r
+extern HINSTANCE hInst;\r
+extern HWND hwndMain;\r
+\r
+extern WindowPlacement wpEvalGraph;\r
+\r
+/* Module globals */\r
+static ChessProgramStats_Move * currPvInfo;\r
+static int currFirst = 0;\r
+static int currLast = 0;\r
+static int currCurrent = -1;\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 int nWidthPB = 0;\r
+static int nHeightPB = 0;\r
+static HPEN hpenDotted = NULL;\r
+static HPEN hpenBlueDotted = NULL;\r
+static HPEN hpenBold[2] = { NULL, NULL };\r
+static HBRUSH hbrHist[2] = { NULL, NULL };\r
+\r
+static int MarginX = 18;\r
+static int MarginW = 4;\r
+static int MarginH = 4;\r
+\r
+#define MIN_HIST_WIDTH  4\r
+#define MAX_HIST_WIDTH  10\r
+\r
+static int GetPvScore( int index )\r
+{\r
+    int score = currPvInfo[ index ].score;\r
+\r
+    if( index & 1 ) score = -score; /* Flip score for black */\r
+\r
+    return score;\r
+}\r
+\r
+static VOID DrawLine( int x1, int y1, int x2, int y2 )\r
+{\r
+    MoveToEx( hdcPB, x1, y1, NULL );\r
+\r
+    LineTo( hdcPB, x2, y2 );\r
+}\r
+\r
+static VOID DrawLineEx( int x1, int y1, int x2, int y2 )\r
+{\r
+    POINT stPT;\r
+\r
+    MoveToEx( hdcPB, x1, y1, &stPT );\r
+\r
+    LineTo( hdcPB, x2, y2 );\r
+\r
+    MoveToEx( hdcPB, stPT.x, stPT.y, NULL );\r
+}\r
+\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
+/*\r
+    For a centipawn value, this function returns the height of the corresponding\r
+    histogram, centered on the reference axis.\r
+\r
+    Note: height can be negative!\r
+*/\r
+static int GetValueY( int value )\r
+{\r
+    if( value < -700 ) value = -700;\r
+    if( value > +700 ) value = +700;\r
+\r
+    return (nHeightPB / 2) - (int)(value * (nHeightPB - 2*MarginH) / 1400.0);\r
+}\r
+\r
+static VOID DrawAxisSegmentHoriz( int value, BOOL drawValue )\r
+{\r
+    int y = GetValueY( value*100 );\r
+\r
+    SelectObject( hdcPB, GetStockObject(BLACK_PEN) );\r
+    DrawLine( MarginX, y, MarginX + MarginW, y );\r
+    SelectObject( hdcPB, hpenDotted );\r
+    DrawLine( MarginX + MarginW, y, nWidthPB - MarginW, y );\r
+\r
+    if( drawValue ) {\r
+        SIZE stSize;\r
+        char buf[8];\r
+        int cbBuf;\r
+\r
+        if( value > 0 ) {\r
+            buf[0] = '+';\r
+            itoa( value, buf+1, 10 );\r
+        }\r
+        else {\r
+            itoa( value, buf, 10 );\r
+        }\r
+\r
+        cbBuf = strlen( buf );\r
+\r
+        GetTextExtentPoint32( hdcPB, buf, cbBuf, &stSize );\r
+\r
+        TextOut( hdcPB, MarginX - stSize.cx - 2, y - stSize.cy / 2, buf, cbBuf );\r
+    }\r
+}\r
+\r
+static VOID DrawAxis()\r
+{\r
+    int cy = nHeightPB / 2;\r
+    \r
+    SelectObject( hdcPB, GetStockObject(NULL_BRUSH) );\r
+\r
+    SetBkMode( hdcPB, TRANSPARENT );\r
+\r
+    DrawAxisSegmentHoriz( +5, TRUE );\r
+    DrawAxisSegmentHoriz( +3, FALSE );\r
+    DrawAxisSegmentHoriz( +1, FALSE );\r
+    DrawAxisSegmentHoriz(  0, TRUE );\r
+    DrawAxisSegmentHoriz( -1, FALSE );\r
+    DrawAxisSegmentHoriz( -3, FALSE );\r
+    DrawAxisSegmentHoriz( -5, TRUE );\r
+\r
+    SelectObject( hdcPB, GetStockObject(BLACK_PEN) );\r
+\r
+    DrawLine( MarginX + MarginW, cy, nWidthPB - MarginW, cy );\r
+    DrawLine( MarginX + MarginW, MarginH, MarginX + MarginW, nHeightPB - MarginH );\r
+}\r
+\r
+static VOID DrawHistogram( int x, int y, int width, int value, int side )\r
+{\r
+    RECT rc;\r
+\r
+    if( value > -25 && value < +25 ) return;\r
+\r
+    rc.left = x;\r
+    rc.right = rc.left + width + 1;\r
+\r
+    if( value > 0 ) {\r
+        rc.top = GetValueY( value );\r
+        rc.bottom = y+1;\r
+    }\r
+    else {\r
+        rc.top = y;\r
+        rc.bottom = GetValueY( value ) + 1;\r
+    }\r
+\r
+\r
+    if( width == MIN_HIST_WIDTH ) {\r
+        rc.right--;\r
+        FillRect( hdcPB, &rc, hbrHist[side] );\r
+    }\r
+    else {\r
+        SelectObject( hdcPB, hbrHist[side] );\r
+        Rectangle( hdcPB, rc.left, rc.top, rc.right, rc.bottom );\r
+    }\r
+}\r
+\r
+static VOID DrawSeparator( int index, int x )\r
+{\r
+    if( index > 0 ) {\r
+        if( index == currCurrent ) {\r
+            HPEN hp = SelectObject( hdcPB, hpenBlueDotted );\r
+            DrawLineEx( x, MarginH, x, nHeightPB - MarginH );\r
+            SelectObject( hdcPB, hp );\r
+        }\r
+        else if( (index % 20) == 0 ) {\r
+            HPEN hp = SelectObject( hdcPB, hpenDotted );\r
+            DrawLineEx( x, MarginH, x, nHeightPB - MarginH );\r
+            SelectObject( hdcPB, hp );\r
+        }\r
+    }\r
+}\r
+\r
+/* Actually draw histogram as a diagram, cause there's too much data */\r
+static VOID DrawHistogramAsDiagram( int cy, int paint_width, int hist_count )\r
+{\r
+    double step;\r
+    int i;\r
+\r
+    /* Rescale the graph every few moves (as opposed to every move) */\r
+    hist_count -= hist_count % 8;\r
+    hist_count += 8;\r
+    hist_count /= 2;\r
+\r
+    step = (double) paint_width / (hist_count + 1);\r
+\r
+    for( i=0; i<2; i++ ) {\r
+        int index = currFirst;\r
+        int side = (currCurrent + i + 1) & 1; /* Draw current side last */\r
+        double x = MarginX + MarginW;\r
+\r
+        if( (index & 1) != side ) {\r
+            x += step / 2;\r
+            index++;\r
+        }\r
+\r
+        SelectObject( hdcPB, hpenBold[side] );\r
+\r
+        MoveToEx( hdcPB, (int) x, cy, NULL );\r
+\r
+        index += 2;\r
+\r
+        while( index < currLast ) {\r
+            x += step;\r
+\r
+            DrawSeparator( index, (int) x );\r
+\r
+            /* Extend line up to current point */\r
+            if( currPvInfo[index].depth > 0 ) {\r
+                LineTo( hdcPB, (int) x, GetValueY( GetPvScore(index) ) );\r
+            }\r
+\r
+            index += 2;\r
+        }\r
+    }\r
+}\r
+\r
+static VOID DrawHistogramFull( int cy, int hist_width, int hist_count )\r
+{\r
+    int i;\r
+\r
+    SelectObject( hdcPB, GetStockObject(BLACK_PEN) );\r
+\r
+    for( i=0; i<hist_count; i++ ) {\r
+        int index = currFirst + i;\r
+        int x = MarginX + MarginW + index * hist_width;\r
+\r
+        /* Draw a separator every 10 moves */\r
+        DrawSeparator( index, x );\r
+\r
+        /* Draw histogram */\r
+        if( currPvInfo[i].depth > 0 ) {\r
+            DrawHistogram( x, cy, hist_width, GetPvScore(index), index & 1 );\r
+        }\r
+    }\r
+}\r
+\r
+typedef struct {\r
+    int cy;\r
+    int hist_width;\r
+    int hist_count;\r
+    int paint_width;\r
+} VisualizationData;\r
+\r
+static BOOL InitVisualization( VisualizationData * vd )\r
+{\r
+    BOOL result = FALSE;\r
+\r
+    vd->cy = nHeightPB / 2;\r
+    vd->hist_width = MIN_HIST_WIDTH;\r
+    vd->hist_count = currLast - currFirst;\r
+    vd->paint_width = nWidthPB - MarginX - 2*MarginW;\r
+\r
+    if( vd->hist_count > 0 ) {\r
+        result = TRUE;\r
+\r
+        /* Compute width */\r
+        vd->hist_width = vd->paint_width / vd->hist_count;\r
+\r
+        if( vd->hist_width > MAX_HIST_WIDTH ) vd->hist_width = MAX_HIST_WIDTH;\r
+\r
+        vd->hist_width -= vd->hist_width % 2;\r
+    }\r
+\r
+    return result;\r
+}\r
+\r
+static VOID DrawHistograms()\r
+{\r
+    VisualizationData vd;\r
+\r
+    if( InitVisualization( &vd ) ) {\r
+        if( vd.hist_width < MIN_HIST_WIDTH ) {\r
+            DrawHistogramAsDiagram( vd.cy, vd.paint_width, vd.hist_count );\r
+        }\r
+        else {\r
+            DrawHistogramFull( vd.cy, vd.hist_width, vd.hist_count );\r
+        }\r
+    }\r
+}\r
+\r
+static int GetMoveIndexFromPoint( int x, int y )\r
+{\r
+    int result = -1;\r
+    int start_x = MarginX + MarginW;\r
+    VisualizationData vd;\r
+\r
+    if( x >= start_x && InitVisualization( &vd ) ) {\r
+        /* Almost an hack here... we duplicate some of the paint logic */\r
+        if( vd.hist_width < MIN_HIST_WIDTH ) {\r
+            double step;\r
+\r
+            vd.hist_count -= vd.hist_count % 8;\r
+            vd.hist_count += 8;\r
+            vd.hist_count /= 2;\r
+\r
+            step = (double) vd.paint_width / (vd.hist_count + 1);\r
+            step /= 2;\r
+\r
+            result = (int) (0.5 + (double) (x - start_x) / step);\r
+        }\r
+        else {\r
+            result = (x - start_x) / vd.hist_width;\r
+        }\r
+    }\r
+\r
+    if( result >= currLast ) {\r
+        result = -1;\r
+    }\r
+\r
+    return result;\r
+}\r
+\r
+static VOID DrawBackground()\r
+{\r
+    HBRUSH hbr;\r
+    RECT rc;\r
+\r
+    hbr = CreateBrush( BS_SOLID, GetSysColor( COLOR_3DFACE ) );\r
+\r
+    rc.left = 0;\r
+    rc.top = 0;\r
+    rc.right = nWidthPB;\r
+    rc.bottom = nHeightPB;\r
+\r
+    FillRect( hdcPB, &rc, hbr );\r
+\r
+    DeleteObject( hbr );\r
+}\r
+\r
+static VOID PaintEvalGraph( 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( hpenDotted == NULL ) {\r
+            hpenDotted = CreatePen( PS_DOT, 0, RGB(0xA0,0xA0,0xA0) );\r
+            hpenBlueDotted = CreatePen( PS_DOT, 0, RGB(0x00,0x00,0xFF) );\r
+            hpenBold[0] = CreatePen( PS_SOLID, 2, crWhite );\r
+            hpenBold[1] = CreatePen( PS_SOLID, 2, crBlack );\r
+            hbrHist[0] = CreateBrush( BS_SOLID, crWhite );\r
+            hbrHist[1] = CreateBrush( BS_SOLID, crBlack );\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
+    /* Draw */\r
+    DrawBackground();\r
+    DrawAxis();\r
+    DrawHistograms();\r
+\r
+    /* Copy bitmap into destination DC */\r
+    BitBlt( hDC, 0, 0, width, height, hdcPB, 0, 0, SRCCOPY );\r
+}\r
+\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
+        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
+        PaintEvalGraph( hDlg, hDC );\r
+        EndPaint( hDlg, &stPS );\r
+        break;\r
+\r
+    case WM_REFRESH_GRAPH:\r
+        hDC = GetDC( hDlg );\r
+        PaintEvalGraph( hDlg, hDC );\r
+        ReleaseDC( hDlg, hDC );\r
+        break;\r
+\r
+    case WM_LBUTTONDBLCLK:\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
+#if 0\r
+    case WM_NCLBUTTONDBLCLK:\r
+        if( wParam == HTCAPTION ) {\r
+            int index;\r
+            POINT mouse_xy;\r
+            POINTS pts = MAKEPOINTS(lParam);\r
+\r
+            mouse_xy.x = pts.x;\r
+            mouse_xy.y = pts.y;\r
+            ScreenToClient( hDlg, &mouse_xy );\r
+\r
+            index = GetMoveIndexFromPoint( mouse_xy.x, mouse_xy.y );\r
+\r
+            if( index >= 0 && index < currLast ) {\r
+                ToNrEvent( index + 1 );\r
+            }\r
+        }\r
+        break;\r
+\r
+    case WM_NCHITTEST:\r
+        {\r
+            LRESULT res = DefWindowProc( hDlg, message, wParam, lParam );\r
+\r
+            if( res == HTCLIENT ) res = HTCAPTION;\r
+\r
+            SetWindowLong( hDlg, DWL_MSGRESULT, res );\r
+\r
+            return TRUE;\r
+        }\r
+        break;\r
+#endif\r
+\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
+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
+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
+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
+\r
+BOOL EvalGraphIsUp()\r
+{\r
+    return evalGraphDialogUp;\r
+}\r