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