new developer release
[xboard.git] / xevalgraph.c
1 /*
2  * Evaluation graph
3  *
4  * Author: Alessandro Scotti (Dec 2005)
5  * Translated to X by H.G.Muller (Nov 2009)
6  *
7  * Copyright 2005 Alessandro Scotti
8  *
9  * Enhancements Copyright 2009, 2010 Free Software Foundation, Inc.
10  *
11  * ------------------------------------------------------------------------
12  *
13  * GNU XBoard is free software: you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation, either version 3 of the License, or (at
16  * your option) any later version.
17  *
18  * GNU XBoard is distributed in the hope that it will be useful, but
19  * WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21  * General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program. If not, see http://www.gnu.org/licenses/.
25  *
26  * ------------------------------------------------------------------------
27  ** See the file ChangeLog for a revision history.  */
28
29 #include "config.h"
30
31 #include <stdio.h>
32 #include <ctype.h>
33 #include <errno.h>
34 #include <sys/types.h>
35
36 #if STDC_HEADERS
37 # include <stdlib.h>
38 # include <string.h>
39 #else /* not STDC_HEADERS */
40 extern char *getenv();
41 # if HAVE_STRING_H
42 #  include <string.h>
43 # else /* not HAVE_STRING_H */
44 #  include <strings.h>
45 # endif /* not HAVE_STRING_H */
46 #endif /* not STDC_HEADERS */
47
48 #if HAVE_UNISTD_H
49 # include <unistd.h>
50 #endif
51
52 #include <X11/Intrinsic.h>
53 #include <X11/StringDefs.h>
54 #include <X11/Shell.h>
55 #include <X11/Xaw/Dialog.h>
56 #include <X11/Xaw/Form.h>
57 #include <X11/Xaw/List.h>
58 #include <X11/Xaw/Label.h>
59 #include <X11/Xaw/SimpleMenu.h>
60 #include <X11/Xaw/SmeBSB.h>
61 #include <X11/Xaw/SmeLine.h>
62 #include <X11/Xaw/Box.h>
63 #include <X11/Xaw/Paned.h>
64 #include <X11/Xaw/MenuButton.h>
65 #include <X11/cursorfont.h>
66 #include <X11/Xaw/Text.h>
67 #include <X11/Xaw/AsciiText.h>
68 #include <X11/Xaw/Viewport.h>
69
70 #include "common.h"
71 #include "frontend.h"
72 #include "backend.h"
73 #include "xboard.h"
74 #include "evalgraph.h"
75 #include "gettext.h"
76
77 #ifdef ENABLE_NLS
78 # define  _(s) gettext (s)
79 # define N_(s) gettext_noop (s)
80 #else
81 # define  _(s) (s)
82 # define N_(s)  s
83 #endif
84
85 #include <X11/xpm.h>
86
87 // [HGM] pixmaps of some ICONS used in the engine-outut window
88 #include "pixmaps/WHITE_14.xpm"
89 #include "pixmaps/BLACK_14.xpm"
90 #include "pixmaps/CLEAR_14.xpm"
91 #include "pixmaps/UNKNOWN_14.xpm"
92 #include "pixmaps/THINKING_14.xpm"
93 #include "pixmaps/PONDER_14.xpm"
94 #include "pixmaps/ANALYZING_14.xpm"
95
96 #ifdef SNAP
97 #include "wsnap.h"
98 #endif
99
100 #define _LL_ 100
101
102 // imports from xboard.c
103 extern Widget formWidget, shellWidget, boardWidget, menuBarWidget;
104 extern Display *xDisplay;
105 extern Window xBoardWindow;
106 extern int squareSize;
107 extern Pixmap xMarkPixmap, wIconPixmap, bIconPixmap;
108 extern char *layoutName;
109
110 Pixmap icons[8]; // [HGM] this front-end array translates back-end icon indicator to handle
111 Widget outputField[2][7]; // [HGM] front-end array to translate output field to window handle
112
113 /* Imports from backend.c */
114
115 /* Imports from xboard.c */
116 extern Arg layoutArgs[2], formArgs[2], messageArgs[4];
117 extern GC coordGC;
118
119 //extern WindowPlacement wpEvalGraph;
120
121 Position evalGraphX = -1, evalGraphY = -1;
122 Dimension evalGraphW, evalGraphH;
123 Widget evalGraphShell;
124 static int evalGraphDialogUp;
125
126 /* Module variables */
127
128 char *crWhite = "#FFFFB0";
129 char *crBlack = "#AD5D3D";
130 static Display *yDisplay;
131 static Window eGraphWindow;
132
133 static GC pens[6]; // [HGM] put all pens in one array
134 static GC hbrHist[3];
135
136 #if 0
137 static HDC hdcPB = NULL;
138 static HBITMAP hbmPB = NULL;
139 #endif
140
141 // [HGM] front-end, added as wrapper to avoid use of LineTo and MoveToEx in other routines (so they can be back-end) 
142 void DrawSegment( int x, int y, int *lastX, int *lastY, int penType )
143 {
144 static curX, curY;
145     if(penType != PEN_NONE)
146       XDrawLine(yDisplay, eGraphWindow, pens[penType], curX, curY, x, y);
147     if(lastX != NULL) { *lastX = curX; *lastY = curY; }
148     curX = x; curY = y;
149 }
150
151 // front-end wrapper for drawing functions to do rectangles
152 void DrawRectangle( int left, int top, int right, int bottom, int side, int style )
153 {
154     XFillRectangle(yDisplay, eGraphWindow, hbrHist[side], left, top, right-left, bottom-top);
155     if(style != FILLED)
156       XDrawRectangle(yDisplay, eGraphWindow, pens[PEN_BLACK], left, top, right-left-1, bottom-top-1);
157 }
158
159 // front-end wrapper for putting text in graph
160 void DrawEvalText(char *buf, int cbBuf, int y)
161 {
162     // the magic constants 7 and 5 should really be derived from the font size somehow
163     XDrawString(yDisplay, eGraphWindow, coordGC, MarginX - 2 - 7*cbBuf, y+5, buf, cbBuf);
164 }
165
166 // front-end
167 static Pixel MakeColor(char *color )
168 {
169     XrmValue vFrom, vTo;
170
171     vFrom.addr = (caddr_t) color;
172     vFrom.size = strlen(color);
173     XtConvert(evalGraphShell, XtRString, &vFrom, XtRPixel, &vTo);
174     // test for NULL?
175
176     return *(Pixel *) vTo.addr;
177 }
178
179 static GC CreateGC(int width, char *fg, char *bg, int style)
180 {
181     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
182       | GCBackground | GCFunction | GCPlaneMask;
183     XGCValues gc_values;
184
185     gc_values.plane_mask = AllPlanes;
186     gc_values.line_width = width;
187     gc_values.line_style = style;
188     gc_values.function = GXcopy;
189
190     gc_values.foreground = MakeColor(fg);
191     gc_values.background = MakeColor(bg);
192
193     return XtGetGC(evalGraphShell, value_mask, &gc_values);
194 }
195
196 // front-end. Create pens, device context and buffer bitmap for global use, copy result to display
197 // The back-end part n the middle has been taken out and moed to PainEvalGraph()
198 static void DisplayEvalGraph()
199 {
200     int j;
201     int width;
202     int height;
203     Dimension w, h;
204     Arg args[6];
205
206     /* Get client area */
207     j = 0;
208     XtSetArg(args[j], XtNwidth, &w); j++;
209     XtSetArg(args[j], XtNheight, &h); j++;
210     XtGetValues(evalGraphShell, args, j);
211     width = w;
212     height = h;
213
214     /* Create or recreate paint box if needed */
215     if( width != nWidthPB || height != nHeightPB ) {
216
217         nWidthPB = width;
218         nHeightPB = height;
219     }
220
221     // back-end painting; calls back front-end primitives for lines, rectangles and text
222     PaintEvalGraph();
223
224     XSync(yDisplay, False);
225 }
226
227 static void InitializeEvalGraph()
228 { int i;    XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
229       | GCBackground | GCFunction | GCPlaneMask;
230     XGCValues gc_values;
231 //    GC copyInvertedGC;
232
233     pens[PEN_BLACK]     = CreateGC(1, "black", "black", LineSolid);
234     pens[PEN_DOTTED]    = CreateGC(1, "#A0A0A0", "#A0A0A0", LineOnOffDash);
235     pens[PEN_BLUEDOTTED] = CreateGC(1, "#0000FF", "#0000FF", LineOnOffDash);
236     pens[PEN_BOLD]      = CreateGC(3, crWhite, crWhite, LineSolid);
237     pens[PEN_BOLD+1]    = CreateGC(3, crBlack, crBlack, LineSolid);
238     hbrHist[0] = CreateGC(3, crWhite, crWhite, LineSolid);
239     hbrHist[1] = CreateGC(3, crBlack, crBlack, LineSolid);
240     hbrHist[2] = CreateGC(3, "#E0E0F0", "#E0E0F0", LineSolid);; // background (a bit blueish, for contrst with yellow curve)
241 }
242
243 void EvalClick(widget, unused, event)
244      Widget widget;
245      caddr_t unused;
246      XEvent *event;
247 {
248         if( widget && event->type == ButtonPress ) {
249             int index = GetMoveIndexFromPoint( event->xbutton.x, event->xbutton.y );
250
251             if( index >= 0 && index < currLast ) {
252                 ToNrEvent( index + 1 );
253             }
254         }
255 }
256
257 // This (cloned from EventProc in xboard.c) is needed as event handler, to prevent
258 // the graph being wiped out after covering / uncovering by other windows.
259 void EvalEventProc(widget, unused, event)
260      Widget widget;
261      caddr_t unused;
262      XEvent *event;
263 {
264     if (!XtIsRealized(widget))
265       return;
266
267     switch (event->type) {
268       case Expose:
269         if (event->xexpose.count > 0) return;  /* no clipping is done */
270         DisplayEvalGraph();
271         break;
272       default:
273         return;
274     }
275 }
276 // The following routines are mutated clones of the commentPopUp routines
277
278 Widget EvalGraphCreate(name)
279      char *name;
280 {
281     Arg args[16];
282     Widget shell, layout, form, form2, edit;
283     Dimension bw_width, bw_height;
284     int j;
285
286     // get board width
287     j = 0;
288     XtSetArg(args[j], XtNwidth,  &bw_width);  j++;
289     XtSetArg(args[j], XtNheight, &bw_height);  j++;
290     XtGetValues(boardWidget, args, j);
291
292     // define form within layout within shell.
293     j = 0;
294     XtSetArg(args[j], XtNresizable, True);  j++;
295     shell =
296 #if TOPLEVEL 
297      XtCreatePopupShell(name, topLevelShellWidgetClass,
298 #else
299       XtCreatePopupShell(name, transientShellWidgetClass,
300 #endif
301                          shellWidget, args, j);
302     layout =
303       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
304                             layoutArgs, XtNumber(layoutArgs));
305     // divide window vertically into two equal parts, by creating two forms
306     form =
307       XtCreateManagedWidget("form", formWidgetClass, layout,
308                             formArgs, XtNumber(formArgs));
309     // make sure width is known in advance, for better placement of child widgets
310     j = 0;
311     XtSetArg(args[j], XtNwidth,     (XtArgVal) bw_width-16); j++;
312     XtSetArg(args[j], XtNheight,    (XtArgVal) bw_height/4); j++;
313     XtSetValues(shell, args, j);
314
315     XtRealizeWidget(shell);
316
317     if(wpEvalGraph.width > 0) {
318       evalGraphW = wpEvalGraph.width;
319       evalGraphH = wpEvalGraph.height;
320       evalGraphX = wpEvalGraph.x;
321       evalGraphY = wpEvalGraph.y;
322     }
323
324     if (evalGraphX == -1) {
325         int xx, yy;
326         Window junk;
327         Dimension pw_height;
328         Dimension ew_height;
329         evalGraphH = bw_height/4;
330         evalGraphW = bw_width-16;
331
332         XSync(xDisplay, False);
333 #ifdef NOTDEF
334         /* This code seems to tickle an X bug if it is executed too soon
335            after xboard starts up.  The coordinates get transformed as if
336            the main window was positioned at (0, 0).
337            */
338         XtTranslateCoords(shellWidget,
339                           (bw_width - evalGraphW) / 2, 0 - evalGraphH / 2,
340                           &evalGraphX, &evalGraphY);
341 #else  /*!NOTDEF*/
342         XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
343                               RootWindowOfScreen(XtScreen(shellWidget)),
344                               (bw_width - evalGraphW) / 2, 0 - evalGraphH / 2,
345                               &xx, &yy, &junk);
346         evalGraphX = xx;
347         evalGraphY = yy;
348 #endif /*!NOTDEF*/
349         if (evalGraphY < 0) evalGraphY = 0; /*avoid positioning top offscreen*/
350     }
351     j = 0;
352     XtSetArg(args[j], XtNheight, evalGraphH);  j++;
353     XtSetArg(args[j], XtNwidth, evalGraphW);  j++;
354     XtSetArg(args[j], XtNx, evalGraphX);  j++;
355     XtSetArg(args[j], XtNy, evalGraphY);  j++;
356     XtSetValues(shell, args, j);
357 //    XtSetKeyboardFocus(shell, edit);
358
359     yDisplay = XtDisplay(shell);
360     eGraphWindow = XtWindow(form);
361     XtAddEventHandler(form, ExposureMask, False,
362                       (XtEventHandler) EvalEventProc, NULL);
363     XtAddEventHandler(form, ButtonPressMask, False,
364                       (XtEventHandler) EvalClick, NULL);
365
366     return shell;
367 }
368
369 void 
370 EvalGraphPopUp()
371 {
372     Arg args[16];
373     int j;
374     Widget edit;
375     static int  needInit = TRUE;
376     static char *title = _("Evaluation graph");
377
378     if (evalGraphShell == NULL) {
379
380         evalGraphShell =
381           EvalGraphCreate(title);
382         XtRealizeWidget(evalGraphShell);
383         CatchDeleteWindow(evalGraphShell, "EvalGraphPopDown");
384         if( needInit ) {
385             InitializeEvalGraph();
386             needInit = FALSE;
387         }
388     } else {
389         j = 0;
390         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
391         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
392         XtSetValues(evalGraphShell, args, j);
393     }
394
395     XtPopup(evalGraphShell, XtGrabNone);
396     XSync(yDisplay, False);
397
398     j=0;
399     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
400     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Show Evaluation Graph"),
401                 args, j);
402
403     evalGraphDialogUp = True;
404 //    ShowThinkingEvent(); // [HGM] thinking: might need to prompt engine for thinking output
405 }
406
407 void EvalGraphPopDown()
408 {
409     Arg args[16];
410     int j;
411
412     if (!evalGraphDialogUp) return;
413     j = 0;
414     XtSetArg(args[j], XtNx, &evalGraphX); j++;
415     XtSetArg(args[j], XtNy, &evalGraphY); j++;
416     XtSetArg(args[j], XtNwidth, &evalGraphW); j++;
417     XtSetArg(args[j], XtNheight, &evalGraphH); j++;
418     XtGetValues(evalGraphShell, args, j);
419     wpEvalGraph.x = evalGraphX - 4;
420     wpEvalGraph.y = evalGraphY - 23;
421     wpEvalGraph.width = evalGraphW;
422     wpEvalGraph.height = evalGraphH;
423     XtPopdown(evalGraphShell);
424     XSync(xDisplay, False);
425     j=0;
426     XtSetArg(args[j], XtNleftBitmap, None); j++;
427     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Show Evaluation Graph"),
428                 args, j);
429
430     evalGraphDialogUp = False;
431 //    ShowThinkingEvent(); // [HGM] thinking: might need to shut off thinking output
432 }
433
434 Boolean EvalGraphIsUp()
435 {
436     return evalGraphDialogUp;
437 }
438
439 int EvalGraphDialogExists()
440 {
441     return evalGraphShell != NULL;
442 }
443
444 void
445 EvalGraphProc(w, event, prms, nprms)
446      Widget w;
447      XEvent *event;
448      String *prms;
449      Cardinal *nprms;
450 {
451   if (evalGraphDialogUp) {
452     EvalGraphPopDown();
453   } else {
454     EvalGraphPopUp();
455   }
456 }
457 // This function is the interface to the back-end. It is currently called through the front-end,
458 // though, where it shares the HistorySet() wrapper with MoveHistorySet(). Once all front-ends
459 // support the eval graph, it would be more logical to call it directly from the back-end.
460 void EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo )
461 {
462     /* [AS] Danger! For now we rely on the pvInfo parameter being a static variable! */
463
464     currFirst = first;
465     currLast = last;
466     currCurrent = current;
467     currPvInfo = pvInfo;
468
469     if( evalGraphShell ) {
470         DisplayEvalGraph();
471     }
472 }