Add -trueColors option
[xboard.git] / xevalgraph.c
index 5c8f1f0..f3cac1f 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Copyright 2005 Alessandro Scotti
  *
- * Enhancements Copyright 2009, 2010 Free Software Foundation, Inc.
+ * Enhancements Copyright 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
  *
  * ------------------------------------------------------------------------
  *
@@ -67,11 +67,18 @@ extern char *getenv();
 #include <X11/Xaw/AsciiText.h>
 #include <X11/Xaw/Viewport.h>
 
+#include <cairo/cairo.h>
+#include <cairo/cairo-xlib.h>
+
 #include "common.h"
 #include "frontend.h"
 #include "backend.h"
+#include "dialogs.h"
+#include "menus.h"
 #include "xboard.h"
 #include "evalgraph.h"
+#include "xevalgraph.h"
+#include "draw.h"
 #include "gettext.h"
 
 #ifdef ENABLE_NLS
@@ -90,358 +97,233 @@ extern char *getenv();
 
 #define _LL_ 100
 
-// imports from xboard.c
-extern Widget formWidget, shellWidget, boardWidget, menuBarWidget;
-extern Display *xDisplay;
-extern Window xBoardWindow;
-extern int squareSize;
-extern Pixmap xMarkPixmap, wIconPixmap, bIconPixmap;
-extern char *layoutName;
-
 Pixmap icons[8]; // [HGM] this front-end array translates back-end icon indicator to handle
 Widget outputField[2][7]; // [HGM] front-end array to translate output field to window handle
-
-/* Imports from backend.c */
-
-/* Imports from xboard.c */
-extern Arg layoutArgs[2], formArgs[2], messageArgs[4];
-extern GC coordGC;
+static char *title = N_("Evaluation graph");
 
 //extern WindowPlacement wpEvalGraph;
 
 Position evalGraphX = -1, evalGraphY = -1;
 Dimension evalGraphW, evalGraphH;
-Widget evalGraphShell;
-static int evalGraphDialogUp;
 
 /* Module variables */
 
 char *crWhite = "#FFFFB0";
 char *crBlack = "#AD5D3D";
-static Display *yDisplay;
-static Window eGraphWindow;
+Option *disp;
 
-static GC pens[6]; // [HGM] put all pens in one array
-static GC hbrHist[3];
+static Option *EvalCallback P((int button, int x, int y));
 
-#if 0
-static HDC hdcPB = NULL;
-static HBITMAP hbmPB = NULL;
-#endif
+static void
+ChoosePen(cairo_t *cr, int i)
+{
+  switch(i) {
+    case PEN_BLACK:
+      SetPen(cr, 1.0, "#000000", 0);
+      break;
+    case PEN_DOTTED:
+      SetPen(cr, 1.0, "#A0A0A0", 1);
+      break;
+    case PEN_BLUEDOTTED:
+      SetPen(cr, 1.0, "#0000FF", 1);
+      break;
+    case PEN_BOLDWHITE:
+      SetPen(cr, 3.0, crWhite, 0);
+      break;
+    case PEN_BOLDBLACK:
+      SetPen(cr, 3.0, crBlack, 0);
+      break;
+    case PEN_BACKGD:
+      SetPen(cr, 3.0, "#E0E0F0", 0);
+      break;
+  }
+}
 
 // [HGM] front-end, added as wrapper to avoid use of LineTo and MoveToEx in other routines (so they can be back-end)
-void DrawSegment( int x, int y, int *lastX, int *lastY, int penType )
+void
+DrawSegment (int x, int y, int *lastX, int *lastY, enum PEN penType)
 {
   static int curX, curY;
 
-  if(penType != PEN_NONE)
-    XDrawLine(yDisplay, eGraphWindow, pens[penType], curX, curY, x, y);
+  if(penType != PEN_NONE) {
+    cairo_t *cr = cairo_create(DRAWABLE(disp));
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+    cairo_move_to (cr, curX, curY);
+    cairo_line_to (cr, x,y);
+    ChoosePen(cr, penType);
+    cairo_stroke (cr);
+    cairo_destroy (cr);
+  }
+
   if(lastX != NULL) { *lastX = curX; *lastY = curY; }
   curX = x; curY = y;
 }
 
 // front-end wrapper for drawing functions to do rectangles
-void DrawRectangle( int left, int top, int right, int bottom, int side, int style )
-{
-    XFillRectangle(yDisplay, eGraphWindow, hbrHist[side], left, top, right-left, bottom-top);
-    if(style != FILLED)
-      XDrawRectangle(yDisplay, eGraphWindow, pens[PEN_BLACK], left, top, right-left-1, bottom-top-1);
-}
-
-// front-end wrapper for putting text in graph
-void DrawEvalText(char *buf, int cbBuf, int y)
-{
-    // the magic constants 7 and 5 should really be derived from the font size somehow
-    XDrawString(yDisplay, eGraphWindow, coordGC, MarginX - 2 - 7*cbBuf, y+5, buf, cbBuf);
-}
-
-// front-end
-static Pixel MakeColor(char *color )
+void
+DrawRectangle (int left, int top, int right, int bottom, int side, int style)
 {
-    XrmValue vFrom, vTo;
+  cairo_t *cr;
+
+  cr = cairo_create (DRAWABLE(disp));
+  cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+  cairo_rectangle (cr, left, top, right-left, bottom-top);
+  switch(side)
+    {
+    case 0: ChoosePen(cr, PEN_BOLDWHITE); break;
+    case 1: ChoosePen(cr, PEN_BOLDBLACK); break;
+    case 2: ChoosePen(cr, PEN_BACKGD); break;
+    }
+  cairo_fill (cr);
 
-    vFrom.addr = (caddr_t) color;
-    vFrom.size = strlen(color);
-    XtConvert(evalGraphShell, XtRString, &vFrom, XtRPixel, &vTo);
-    // test for NULL?
+  if(style != FILLED)
+    {
+      cairo_rectangle (cr, left, top, right-left-1, bottom-top-1);
+      ChoosePen(cr, PEN_BLACK);
+      cairo_stroke (cr);
+    }
 
-    return *(Pixel *) vTo.addr;
+  cairo_destroy(cr);
 }
 
-static GC CreateGC(int width, char *fg, char *bg, int style)
+// front-end wrapper for putting text in graph
+void
+DrawEvalText (char *buf, int cbBuf, int y)
 {
-    XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
-      | GCBackground | GCFunction | GCPlaneMask;
-    XGCValues gc_values;
+    // the magic constants 8 and 5 should really be derived from the font size somehow
+  cairo_text_extents_t extents;
+  cairo_t *cr = cairo_create(DRAWABLE(disp));
 
-    gc_values.plane_mask = AllPlanes;
-    gc_values.line_width = width;
-    gc_values.line_style = style;
-    gc_values.function = GXcopy;
+  /* GTK-TODO this has to go into the font-selection */
+  cairo_select_font_face (cr, "Sans",
+                         CAIRO_FONT_SLANT_NORMAL,
+                         CAIRO_FONT_WEIGHT_NORMAL);
+  cairo_set_font_size (cr, 12.0);
 
-    gc_values.foreground = MakeColor(fg);
-    gc_values.background = MakeColor(bg);
 
-    return XtGetGC(evalGraphShell, value_mask, &gc_values);
-}
-
-// front-end. Create pens, device context and buffer bitmap for global use, copy result to display
-// The back-end part n the middle has been taken out and moed to PainEvalGraph()
-static void DisplayEvalGraph()
-{
-    int j;
-    int width;
-    int height;
-    Dimension w, h;
-    Arg args[6];
-
-    /* Get client area */
-    j = 0;
-    XtSetArg(args[j], XtNwidth, &w); j++;
-    XtSetArg(args[j], XtNheight, &h); j++;
-    XtGetValues(evalGraphShell, args, j);
-    width = w;
-    height = h;
-
-    /* Create or recreate paint box if needed */
-    if( width != nWidthPB || height != nHeightPB ) {
-
-        nWidthPB = width;
-        nHeightPB = height;
-    }
+  cairo_text_extents (cr, buf, &extents);
 
-    // back-end painting; calls back front-end primitives for lines, rectangles and text
-    PaintEvalGraph();
+  cairo_move_to (cr, MarginX - 2 - 8*cbBuf, y+5);
+  cairo_text_path (cr, buf);
+  cairo_set_source_rgb (cr, 0.0, 0.0, 0);
+  cairo_fill_preserve (cr);
+  cairo_set_source_rgb (cr, 0, 1.0, 0);
+  cairo_set_line_width (cr, 0.1);
+  cairo_stroke (cr);
 
-    XSync(yDisplay, False);
+  /* free memory */
+  cairo_destroy (cr);
 }
 
-static void InitializeEvalGraph()
+static int initDone = FALSE;
+
+static void
+InitializeEvalGraph (Option *opt, int w, int h)
 {
-  pens[PEN_BLACK]      = CreateGC(1, "black", "black", LineSolid);
-  pens[PEN_DOTTED]     = CreateGC(1, "#A0A0A0", "#A0A0A0", LineOnOffDash);
-  pens[PEN_BLUEDOTTED] = CreateGC(1, "#0000FF", "#0000FF", LineOnOffDash);
-  pens[PEN_BOLD]       = CreateGC(3, crWhite, crWhite, LineSolid);
-  pens[PEN_BOLD+1]     = CreateGC(3, crBlack, crBlack, LineSolid);
-  hbrHist[0] = CreateGC(3, crWhite, crWhite, LineSolid);
-  hbrHist[1] = CreateGC(3, crBlack, crBlack, LineSolid);
-  hbrHist[2] = CreateGC(3, "#E0E0F0", "#E0E0F0", LineSolid);; // background (a bit blueish, for contrst with yellow curve)
+  if(w == 0) {
+    Arg args[10];
+    XtSetArg(args[0], XtNwidth, &evalGraphW);
+    XtSetArg(args[1], XtNheight, &evalGraphH);
+    XtGetValues(opt->handle, args, 2);
+    nWidthPB = evalGraphW; nHeightPB = evalGraphH;
+  } else nWidthPB = w, nHeightPB = h;
+
+  initDone = TRUE;
 }
 
-void EvalClick(widget, unused, event)
-     Widget widget;
-     caddr_t unused;
-     XEvent *event;
+// The following stuff is really back-end (but too little to bother with a separate file)
+
+static void
+EvalClick (int x, int y)
 {
-        if( widget && event->type == ButtonPress ) {
-            int index = GetMoveIndexFromPoint( event->xbutton.x, event->xbutton.y );
+    int index = GetMoveIndexFromPoint( x, y );
 
-            if( index >= 0 && index < currLast ) {
-                ToNrEvent( index + 1 );
-            }
-        }
+    if( index >= 0 && index < currLast ) ToNrEvent( index + 1 );
 }
 
-// This (cloned from EventProc in xboard.c) is needed as event handler, to prevent
-// the graph being wiped out after covering / uncovering by other windows.
-void EvalEventProc(widget, unused, event)
-     Widget widget;
-     caddr_t unused;
-     XEvent *event;
-{
-    if (!XtIsRealized(widget))
-      return;
-
-    switch (event->type) {
-      case Expose:
-       if (event->xexpose.count > 0) return;  /* no clipping is done */
-       DisplayEvalGraph();
-       break;
-      default:
-       return;
-    }
+static Option graphOptions[] = {
+{ 150, 0x9C, 300, NULL, (void*) &EvalCallback, NULL, NULL, Graph , "" },
+{ 0, 2, 0, NULL, NULL, "", NULL, EndMark , "" }
+};
+
+static void
+DisplayEvalGraph ()
+{   // back-end painting; calls back front-end primitives for lines, rectangles and text
+    char *t = MakeEvalTitle(_(title));
+    nWidthPB = disp->max; nHeightPB = disp->value;
+    if(t != title && nWidthPB < 340) t = MakeEvalTitle(nWidthPB < 240 ? "" : _("Eval"));
+    PaintEvalGraph();
+    GraphExpose(graphOptions, 0, 0, nWidthPB, nHeightPB);
+    SetDialogTitle(EvalGraphDlg, t);
 }
-// The following routines are mutated clones of the commentPopUp routines
 
-Widget EvalGraphCreate(name)
-     char *name;
+static Option *
+EvalCallback (int button, int x, int y)
 {
-    Arg args[16];
-    Widget shell, layout, form;
-    Dimension bw_width, bw_height;
-    int j;
-
-    // get board width
-    j = 0;
-    XtSetArg(args[j], XtNwidth,  &bw_width);  j++;
-    XtSetArg(args[j], XtNheight, &bw_height);  j++;
-    XtGetValues(boardWidget, args, j);
-
-    // define form within layout within shell.
-    j = 0;
-    XtSetArg(args[j], XtNresizable, True);  j++;
-    shell =
-#if TOPLEVEL
-     XtCreatePopupShell(name, topLevelShellWidgetClass,
-#else
-      XtCreatePopupShell(name, transientShellWidgetClass,
-#endif
-                        shellWidget, args, j);
-    layout =
-      XtCreateManagedWidget(layoutName, formWidgetClass, shell,
-                           layoutArgs, XtNumber(layoutArgs));
-    // divide window vertically into two equal parts, by creating two forms
-    form =
-      XtCreateManagedWidget("form", formWidgetClass, layout,
-                           formArgs, XtNumber(formArgs));
-    // make sure width is known in advance, for better placement of child widgets
-    j = 0;
-    XtSetArg(args[j], XtNwidth,     (XtArgVal) bw_width-16); j++;
-    XtSetArg(args[j], XtNheight,    (XtArgVal) bw_height/4); j++;
-    XtSetValues(shell, args, j);
-
-    XtRealizeWidget(shell);
-
-    if(wpEvalGraph.width > 0) {
-      evalGraphW = wpEvalGraph.width;
-      evalGraphH = wpEvalGraph.height;
-      evalGraphX = wpEvalGraph.x;
-      evalGraphY = wpEvalGraph.y;
-    }
-
-    if (evalGraphX == -1) {
-       int xx, yy;
-       Window junk;
-       evalGraphH = bw_height/4;
-       evalGraphW = bw_width-16;
-
-       XSync(xDisplay, False);
-#ifdef NOTDEF
-       /* This code seems to tickle an X bug if it is executed too soon
-          after xboard starts up.  The coordinates get transformed as if
-          the main window was positioned at (0, 0).
-          */
-       XtTranslateCoords(shellWidget,
-                         (bw_width - evalGraphW) / 2, 0 - evalGraphH / 2,
-                         &evalGraphX, &evalGraphY);
-#else  /*!NOTDEF*/
-        XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
-                             RootWindowOfScreen(XtScreen(shellWidget)),
-                             (bw_width - evalGraphW) / 2, 0 - evalGraphH / 2,
-                             &xx, &yy, &junk);
-       evalGraphX = xx;
-       evalGraphY = yy;
-#endif /*!NOTDEF*/
-       if (evalGraphY < 0) evalGraphY = 0; /*avoid positioning top offscreen*/
+    if(!initDone) return NULL;
+
+    switch(button) {
+       case 10: // expose event
+           /* Create or recreate paint box if needed */
+           if(x != nWidthPB || y != nHeightPB) {
+               InitializeEvalGraph(&graphOptions[0], x, y);
+           }
+           nWidthPB = x;
+           nHeightPB = y;
+           DisplayEvalGraph();
+           break;
+       case 1: EvalClick(x, y); // left button
+       default: break; // other buttons ignored
     }
-    j = 0;
-    XtSetArg(args[j], XtNheight, evalGraphH);  j++;
-    XtSetArg(args[j], XtNwidth, evalGraphW);  j++;
-    XtSetArg(args[j], XtNx, evalGraphX);  j++;
-    XtSetArg(args[j], XtNy, evalGraphY);  j++;
-    XtSetValues(shell, args, j);
-
-    yDisplay = XtDisplay(shell);
-    eGraphWindow = XtWindow(form);
-    XtAddEventHandler(form, ExposureMask, False,
-                     (XtEventHandler) EvalEventProc, NULL);
-    XtAddEventHandler(form, ButtonPressMask, False,
-                     (XtEventHandler) EvalClick, NULL);
-
-    return shell;
+    return NULL; // no context menu!
 }
 
 void
-EvalGraphPopUp()
+EvalGraphPopUp ()
 {
-    Arg args[16];
-    int j;
-    static int  needInit = TRUE;
-    static char *title = _("Evaluation graph");
-
-    if (evalGraphShell == NULL) {
-
-       evalGraphShell =
-         EvalGraphCreate(title);
-       XtRealizeWidget(evalGraphShell);
-       CatchDeleteWindow(evalGraphShell, "EvalGraphPopDown");
-       if( needInit ) {
-           InitializeEvalGraph();
-           needInit = FALSE;
-       }
+    if (GenericPopUp(graphOptions, _(title), EvalGraphDlg, BoardWindow, NONMODAL, 1)) {
+       InitializeEvalGraph(&graphOptions[0], 0, 0); // first time: add callbacks and initialize pens
+       disp = graphOptions;
     } else {
-       j = 0;
-       XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
-       XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
-       XtSetValues(evalGraphShell, args, j);
+       SetDialogTitle(EvalGraphDlg, _(title));
+       SetIconName(EvalGraphDlg, _(title));
     }
 
-    XtPopup(evalGraphShell, XtGrabNone);
-    XSync(yDisplay, False);
+    MarkMenu("View.EvaluationGraph", EvalGraphDlg);
 
-    j=0;
-    XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
-    XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Evaluation Graph"),
-               args, j);
-
-    evalGraphDialogUp = True;
 //    ShowThinkingEvent(); // [HGM] thinking: might need to prompt engine for thinking output
 }
 
-void EvalGraphPopDown()
+void
+EvalGraphPopDown ()
 {
-    Arg args[16];
-    int j;
-
-    if (!evalGraphDialogUp) return;
-    j = 0;
-    XtSetArg(args[j], XtNx, &evalGraphX); j++;
-    XtSetArg(args[j], XtNy, &evalGraphY); j++;
-    XtSetArg(args[j], XtNwidth, &evalGraphW); j++;
-    XtSetArg(args[j], XtNheight, &evalGraphH); j++;
-    XtGetValues(evalGraphShell, args, j);
-    wpEvalGraph.x = evalGraphX - 4;
-    wpEvalGraph.y = evalGraphY - 23;
-    wpEvalGraph.width = evalGraphW;
-    wpEvalGraph.height = evalGraphH;
-    XtPopdown(evalGraphShell);
-    XSync(xDisplay, False);
-    j=0;
-    XtSetArg(args[j], XtNleftBitmap, None); j++;
-    XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Evaluation Graph"),
-               args, j);
-
-    evalGraphDialogUp = False;
+    PopDown(EvalGraphDlg);
+
 //    ShowThinkingEvent(); // [HGM] thinking: might need to shut off thinking output
 }
 
-Boolean EvalGraphIsUp()
+Boolean
+EvalGraphIsUp ()
 {
-    return evalGraphDialogUp;
+    return shellUp[EvalGraphDlg];
 }
 
-int EvalGraphDialogExists()
+int
+EvalGraphDialogExists ()
 {
-    return evalGraphShell != NULL;
+    return DialogExists(EvalGraphDlg);
 }
 
 void
-EvalGraphProc(w, event, prms, nprms)
-     Widget w;
-     XEvent *event;
-     String *prms;
-     Cardinal *nprms;
+EvalGraphProc ()
 {
-  if (evalGraphDialogUp) {
-    EvalGraphPopDown();
-  } else {
-    EvalGraphPopUp();
-  }
+  if (!PopDown(EvalGraphDlg)) EvalGraphPopUp();
 }
-// This function is the interface to the back-end. It is currently called through the front-end,
-// though, where it shares the HistorySet() wrapper with MoveHistorySet(). Once all front-ends
-// support the eval graph, it would be more logical to call it directly from the back-end.
-void EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo )
+
+// This function is the interface to the back-end.
+
+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! */
 
@@ -450,7 +332,8 @@ void EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pv
     currCurrent = current;
     currPvInfo = pvInfo;
 
-    if( evalGraphShell ) {
+    if( DialogExists(EvalGraphDlg) ) {
         DisplayEvalGraph();
     }
 }
+