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 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, boardWidget, menuBarWidget;
104 extern Window xBoardWindow;
105 extern int squareSize;
106 extern Pixmap xMarkPixmap, wIconPixmap, bIconPixmap;
107 extern char *layoutName;
108
109 Pixmap icons[8]; // [HGM] this front-end array translates back-end icon indicator to handle
110 Widget outputField[2][7]; // [HGM] front-end array to translate output field to window handle
111
112 /* Imports from backend.c */
113
114 /* Imports from xboard.c */
115 extern Arg layoutArgs[2], formArgs[2], messageArgs[4];
116 extern GC coordGC;
117
118 //extern WindowPlacement wpEvalGraph;
119
120 Position evalGraphX = -1, evalGraphY = -1;
121 Dimension evalGraphW, evalGraphH;
122 Widget evalGraphShell;
123 static int evalGraphDialogUp;
124
125 /* Module variables */
126
127 char *crWhite = "#FFFFB0";
128 char *crBlack = "#AD5D3D";
129 static Display *yDisplay;
130 static Window eGraphWindow;
131
132 static GC pens[6]; // [HGM] put all pens in one array
133 static GC hbrHist[3];
134
135 #if 0
136 static HDC hdcPB = NULL;
137 static HBITMAP hbmPB = NULL;
138 #endif
139
140 // [HGM] front-end, added as wrapper to avoid use of LineTo and MoveToEx in other routines (so they can be back-end) 
141 void DrawSegment( int x, int y, int *lastX, int *lastY, int penType )
142 {
143 static curX, curY;
144     if(penType != PEN_NONE)
145       XDrawLine(yDisplay, eGraphWindow, pens[penType], curX, curY, x, y);
146     if(lastX != NULL) { *lastX = curX; *lastY = curY; }
147     curX = x; curY = y;
148 }
149
150 // front-end wrapper for drawing functions to do rectangles
151 void DrawRectangle( int left, int top, int right, int bottom, int side, int style )
152 {
153     XFillRectangle(yDisplay, eGraphWindow, hbrHist[side], left, top, right-left, bottom-top);
154     if(style != FILLED)
155       XDrawRectangle(yDisplay, eGraphWindow, pens[PEN_BLACK], left, top, right-left-1, bottom-top-1);
156 }
157
158 // front-end wrapper for putting text in graph
159 void DrawEvalText(char *buf, int cbBuf, int y)
160 {
161     // the magic constants 7 and 5 should really be derived from the font size somehow
162     XDrawString(yDisplay, eGraphWindow, coordGC, MarginX - 2 - 7*cbBuf, y+5, buf, cbBuf);
163 }
164
165 // front-end
166 static Pixel MakeColor(char *color )
167 {
168     XrmValue vFrom, vTo;
169
170     vFrom.addr = (caddr_t) color;
171     vFrom.size = strlen(color);
172     XtConvert(evalGraphShell, XtRString, &vFrom, XtRPixel, &vTo);
173     // test for NULL?
174
175     return *(Pixel *) vTo.addr;
176 }
177
178 static GC CreateGC(int width, char *fg, char *bg, int style)
179 {
180     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
181       | GCBackground | GCFunction | GCPlaneMask;
182     XGCValues gc_values;
183
184     gc_values.plane_mask = AllPlanes;
185     gc_values.line_width = width;
186     gc_values.line_style = style;
187     gc_values.function = GXcopy;
188
189     gc_values.foreground = MakeColor(fg);
190     gc_values.background = MakeColor(bg);
191
192     return XtGetGC(evalGraphShell, value_mask, &gc_values);
193 }
194
195 // front-end. Create pens, device context and buffer bitmap for global use, copy result to display
196 // The back-end part n the middle has been taken out and moed to PainEvalGraph()
197 static void DisplayEvalGraph()
198 {
199     int j;
200     int width;
201     int height;
202     Dimension w, h;
203     Arg args[6];
204
205     /* Get client area */
206     j = 0;
207     XtSetArg(args[j], XtNwidth, &w); j++;
208     XtSetArg(args[j], XtNheight, &h); j++;
209     XtGetValues(evalGraphShell, args, j);
210     width = w;
211     height = h;
212
213     /* Create or recreate paint box if needed */
214     if( width != nWidthPB || height != nHeightPB ) {
215
216         nWidthPB = width;
217         nHeightPB = height;
218     }
219
220     // back-end painting; calls back front-end primitives for lines, rectangles and text
221     PaintEvalGraph();
222
223     XSync(yDisplay, False);
224 }
225
226 static void InitializeEvalGraph()
227 { int i;    XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
228       | GCBackground | GCFunction | GCPlaneMask;
229     XGCValues gc_values;
230 //    GC copyInvertedGC;
231
232     pens[PEN_BLACK]     = CreateGC(1, "black", "black", LineSolid);
233     pens[PEN_DOTTED]    = CreateGC(1, "#A0A0A0", "#A0A0A0", LineOnOffDash);
234     pens[PEN_BLUEDOTTED] = CreateGC(1, "#0000FF", "#0000FF", LineOnOffDash);
235     pens[PEN_BOLD]      = CreateGC(3, crWhite, crWhite, LineSolid);
236     pens[PEN_BOLD+1]    = CreateGC(3, crBlack, crBlack, LineSolid);
237     hbrHist[0] = CreateGC(3, crWhite, crWhite, LineSolid);
238     hbrHist[1] = CreateGC(3, crBlack, crBlack, LineSolid);
239     hbrHist[2] = CreateGC(3, "#E0E0F0", "#E0E0F0", LineSolid);; // background (a bit blueish, for contrst with yellow curve)
240 }
241
242 void EvalClick(widget, unused, event)
243      Widget widget;
244      caddr_t unused;
245      XEvent *event;
246 {
247         if( widget && event->type == ButtonPress ) {
248             int index = GetMoveIndexFromPoint( event->xbutton.x, event->xbutton.y );
249
250             if( index >= 0 && index < currLast ) {
251                 ToNrEvent( index + 1 );
252             }
253         }
254 }
255
256 // This (cloned from EventProc in xboard.c) is needed as event handler, to prevent
257 // the graph being wiped out after covering / uncovering by other windows.
258 void EvalEventProc(widget, unused, event)
259      Widget widget;
260      caddr_t unused;
261      XEvent *event;
262 {
263     if (!XtIsRealized(widget))
264       return;
265
266     switch (event->type) {
267       case Expose:
268         if (event->xexpose.count > 0) return;  /* no clipping is done */
269         DisplayEvalGraph();
270         break;
271       default:
272         return;
273     }
274 }
275 // The following routines are mutated clones of the commentPopUp routines
276
277 Widget EvalGraphCreate(name)
278      char *name;
279 {
280     Arg args[16];
281     Widget shell, layout, form, form2, edit;
282     Dimension bw_width, bw_height;
283     int j;
284
285     // get board width
286     j = 0;
287     XtSetArg(args[j], XtNwidth,  &bw_width);  j++;
288     XtSetArg(args[j], XtNheight, &bw_height);  j++;
289     XtGetValues(boardWidget, args, j);
290
291     // define form within layout within shell.
292     j = 0;
293     XtSetArg(args[j], XtNresizable, True);  j++;
294     shell =
295 #if TOPLEVEL 
296       //     XtCreatePopupShell(name, topLevelShellWidgetClass,
297 #else
298       //      XtCreatePopupShell(name, transientShellWidgetClass,
299 #endif
300       //                         shellWidget, args, j);
301 //    layout =
302 //      XtCreateManagedWidget(layoutName, formWidgetClass, shell,
303 //                          layoutArgs, XtNumber(layoutArgs));
304     // divide window vertically into two equal parts, by creating two forms
305     form =
306       XtCreateManagedWidget("form", formWidgetClass, layout,
307                             formArgs, XtNumber(formArgs));
308     // make sure width is known in advance, for better placement of child widgets
309     j = 0;
310     XtSetArg(args[j], XtNwidth,     (XtArgVal) bw_width-16); j++;
311     XtSetArg(args[j], XtNheight,    (XtArgVal) bw_height/4); j++;
312     XtSetValues(shell, args, j);
313
314     XtRealizeWidget(shell);
315
316     if(wpEvalGraph.width > 0) {
317       evalGraphW = wpEvalGraph.width;
318       evalGraphH = wpEvalGraph.height;
319       evalGraphX = wpEvalGraph.x;
320       evalGraphY = wpEvalGraph.y;
321     }
322
323     if (evalGraphX == -1) {
324         int xx, yy;
325         Window junk;
326         Dimension pw_height;
327         Dimension ew_height;
328         evalGraphH = bw_height/4;
329         evalGraphW = bw_width-16;
330
331         //      XSync(xDisplay, False);
332 #ifdef NOTDEF
333         /* This code seems to tickle an X bug if it is executed too soon
334            after xboard starts up.  The coordinates get transformed as if
335            the main window was positioned at (0, 0).
336            */
337 //      XtTranslateCoords(shellWidget,
338 //                        (bw_width - evalGraphW) / 2, 0 - evalGraphH / 2,
339 //                        &evalGraphX, &evalGraphY);
340 #else  /*!NOTDEF*/
341 //        XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
342 //                            RootWindowOfScreen(XtScreen(shellWidget)),
343 //                            (bw_width - evalGraphW) / 2, 0 - evalGraphH / 2,
344 //                            &xx, &yy, &junk);
345         evalGraphX = xx;
346         evalGraphY = yy;
347 #endif /*!NOTDEF*/
348         if (evalGraphY < 0) evalGraphY = 0; /*avoid positioning top offscreen*/
349     }
350     j = 0;
351     XtSetArg(args[j], XtNheight, evalGraphH);  j++;
352     XtSetArg(args[j], XtNwidth, evalGraphW);  j++;
353     XtSetArg(args[j], XtNx, evalGraphX);  j++;
354     XtSetArg(args[j], XtNy, evalGraphY);  j++;
355     XtSetValues(shell, args, j);
356 //    XtSetKeyboardFocus(shell, edit);
357
358     yDisplay = XtDisplay(shell);
359     eGraphWindow = XtWindow(form);
360     XtAddEventHandler(form, ExposureMask, False,
361                       (XtEventHandler) EvalEventProc, NULL);
362     XtAddEventHandler(form, ButtonPressMask, False,
363                       (XtEventHandler) EvalClick, NULL);
364
365     return shell;
366 }
367
368 void 
369 EvalGraphPopUp()
370 {
371     Arg args[16];
372     int j;
373     Widget edit;
374     static int  needInit = TRUE;
375     static char *title = _("Evaluation graph");
376
377     if (evalGraphShell == NULL) {
378
379         evalGraphShell =
380           EvalGraphCreate(title);
381         XtRealizeWidget(evalGraphShell);
382         //      CatchDeleteWindow(evalGraphShell, "EvalGraphPopDown");
383         if( needInit ) {
384             InitializeEvalGraph();
385             needInit = FALSE;
386         }
387     } else {
388         j = 0;
389         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
390         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
391         XtSetValues(evalGraphShell, args, j);
392     }
393
394     XtPopup(evalGraphShell, XtGrabNone);
395     XSync(yDisplay, False);
396
397     j=0;
398     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
399     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Show Evaluation Graph"),
400                 args, j);
401
402     evalGraphDialogUp = True;
403 //    ShowThinkingEvent(); // [HGM] thinking: might need to prompt engine for thinking output
404 }
405
406 void EvalGraphPopDown()
407 {
408     Arg args[16];
409     int j;
410
411     if (!evalGraphDialogUp) return;
412     j = 0;
413     XtSetArg(args[j], XtNx, &evalGraphX); j++;
414     XtSetArg(args[j], XtNy, &evalGraphY); j++;
415     XtSetArg(args[j], XtNwidth, &evalGraphW); j++;
416     XtSetArg(args[j], XtNheight, &evalGraphH); j++;
417     XtGetValues(evalGraphShell, args, j);
418     wpEvalGraph.x = evalGraphX - 4;
419     wpEvalGraph.y = evalGraphY - 23;
420     wpEvalGraph.width = evalGraphW;
421     wpEvalGraph.height = evalGraphH;
422     XtPopdown(evalGraphShell);
423     //    XSync(xDisplay, False);
424     j=0;
425     XtSetArg(args[j], XtNleftBitmap, None); j++;
426     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Show Evaluation Graph"),
427                 args, j);
428
429     evalGraphDialogUp = False;
430 //    ShowThinkingEvent(); // [HGM] thinking: might need to shut off thinking output
431 }
432
433 Boolean EvalGraphIsUp()
434 {
435     return evalGraphDialogUp;
436 }
437
438 int EvalGraphDialogExists()
439 {
440     return evalGraphShell != NULL;
441 }
442
443 void
444 EvalGraphProc(w, event, prms, nprms)
445      Widget w;
446      XEvent *event;
447      String *prms;
448      Cardinal *nprms;
449 {
450   if (evalGraphDialogUp) {
451     EvalGraphPopDown();
452   } else {
453     EvalGraphPopUp();
454   }
455 }
456 // This function is the interface to the back-end. It is currently called through the front-end,
457 // though, where it shares the HistorySet() wrapper with MoveHistorySet(). Once all front-ends
458 // support the eval graph, it would be more logical to call it directly from the back-end.
459 void EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo )
460 {
461     /* [AS] Danger! For now we rely on the pvInfo parameter being a static variable! */
462
463     currFirst = first;
464     currLast = last;
465     currCurrent = current;
466     currPvInfo = pvInfo;
467
468     if( evalGraphShell ) {
469         DisplayEvalGraph();
470     }
471 }