Separate off drawing routines from xboard.c
[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 <cairo/cairo.h>
71 #include <cairo/cairo-xlib.h>
72
73 #include "common.h"
74 #include "frontend.h"
75 #include "backend.h"
76 #include "dialogs.h"
77 #include "menus.h"
78 #include "xboard.h"
79 #include "evalgraph.h"
80 #include "xevalgraph.h"
81 #include "gettext.h"
82
83 #ifdef ENABLE_NLS
84 # define  _(s) gettext (s)
85 # define N_(s) gettext_noop (s)
86 #else
87 # define  _(s) (s)
88 # define N_(s)  s
89 #endif
90
91 #include <X11/xpm.h>
92
93 #ifdef SNAP
94 #include "wsnap.h"
95 #endif
96
97 #define _LL_ 100
98
99 Pixmap icons[8]; // [HGM] this front-end array translates back-end icon indicator to handle
100 Widget outputField[2][7]; // [HGM] front-end array to translate output field to window handle
101 static char *title = N_("Evaluation graph");
102
103 //extern WindowPlacement wpEvalGraph;
104
105 Position evalGraphX = -1, evalGraphY = -1;
106 Dimension evalGraphW, evalGraphH;
107
108 /* Module variables */
109
110 char *crWhite = "#FFFFB0";
111 char *crBlack = "#AD5D3D";
112 static Window eGraphWindow;
113 static cairo_surface_t *cs;
114
115 static Option *EvalCallback P((int button, int x, int y));
116
117 static void
118 ChoosePen(cairo_t *cr, int i)
119 {
120   switch(i) {
121     case PEN_BLACK:
122       SetPen(cr, 1.0, "#000000", 0);
123       break;
124     case PEN_DOTTED:
125       SetPen(cr, 1.0, "#A0A0A0", 1);
126       break;
127     case PEN_BLUEDOTTED:
128       SetPen(cr, 1.0, "#0000FF", 1);
129       break;
130     case PEN_BOLDWHITE:
131       SetPen(cr, 3.0, crWhite, 0);
132       break;
133     case PEN_BOLDBLACK:
134       SetPen(cr, 3.0, crBlack, 0);
135       break;
136     case PEN_BACKGD:
137       SetPen(cr, 3.0, "#E0E0F0", 0);
138       break;
139   }
140 }
141
142 // [HGM] front-end, added as wrapper to avoid use of LineTo and MoveToEx in other routines (so they can be back-end)
143 void
144 DrawSegment (int x, int y, int *lastX, int *lastY, enum PEN penType)
145 {
146   static int curX, curY;
147
148   if(penType != PEN_NONE) {
149     cairo_t *cr = cairo_create(cs);
150     cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
151     cairo_move_to (cr, curX, curY);
152     cairo_line_to (cr, x,y);
153     ChoosePen(cr, penType);
154     cairo_stroke (cr);
155     cairo_destroy (cr);
156   }
157
158   if(lastX != NULL) { *lastX = curX; *lastY = curY; }
159   curX = x; curY = y;
160 }
161
162 // front-end wrapper for drawing functions to do rectangles
163 void
164 DrawRectangle (int left, int top, int right, int bottom, int side, int style)
165 {
166   cairo_t *cr;
167
168   cr = cairo_create (cs);
169   cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
170   cairo_rectangle (cr, left, top, right-left, bottom-top);
171   switch(side)
172     {
173     case 0: ChoosePen(cr, PEN_BOLDWHITE); break;
174     case 1: ChoosePen(cr, PEN_BOLDBLACK); break;
175     case 2: ChoosePen(cr, PEN_BACKGD); break;
176     }
177   cairo_fill (cr);
178
179   if(style != FILLED)
180     {
181       cairo_rectangle (cr, left, top, right-left-1, bottom-top-1);
182       ChoosePen(cr, PEN_BLACK);
183       cairo_stroke (cr);
184     }
185
186   cairo_destroy(cr);
187 }
188
189 // front-end wrapper for putting text in graph
190 void
191 DrawEvalText (char *buf, int cbBuf, int y)
192 {
193     // the magic constants 8 and 5 should really be derived from the font size somehow
194   cairo_text_extents_t extents;
195   cairo_t *cr = cairo_create(cs);
196
197   /* GTK-TODO this has to go into the font-selection */
198   cairo_select_font_face (cr, "Sans",
199                           CAIRO_FONT_SLANT_NORMAL,
200                           CAIRO_FONT_WEIGHT_NORMAL);
201   cairo_set_font_size (cr, 12.0);
202
203
204   cairo_text_extents (cr, buf, &extents);
205
206   cairo_move_to (cr, MarginX - 2 - 8*cbBuf, y+5);
207   cairo_text_path (cr, buf);
208   cairo_set_source_rgb (cr, 0.0, 0.0, 0);
209   cairo_fill_preserve (cr);
210   cairo_set_source_rgb (cr, 0, 1.0, 0);
211   cairo_set_line_width (cr, 0.1);
212   cairo_stroke (cr);
213
214   /* free memory */
215   cairo_destroy (cr);
216 }
217
218 static int initDone = FALSE;
219
220 static void
221 InitializeEvalGraph (Option *opt, int w, int h)
222 {
223   eGraphWindow = XtWindow(opt->handle);
224
225   if(w == 0) {
226     Arg args[10];
227     XtSetArg(args[0], XtNwidth, &evalGraphW);
228     XtSetArg(args[1], XtNheight, &evalGraphH);
229     XtGetValues(opt->handle, args, 2);
230     nWidthPB = evalGraphW; nHeightPB = evalGraphH;
231   } else nWidthPB = w, nHeightPB = h;
232
233   if(cs) cairo_surface_destroy(cs);
234   cs=cairo_xlib_surface_create(xDisplay, eGraphWindow, DefaultVisual(xDisplay, 0), nWidthPB, nHeightPB);
235
236   initDone = TRUE;
237 }
238
239 // The following stuff is really back-end (but too little to bother with a separate file)
240
241 static void
242 DisplayEvalGraph ()
243 {   // back-end painting; calls back front-end primitives for lines, rectangles and text
244     char *t = MakeEvalTitle(_(title));
245     if(t != title && nWidthPB < 340) t = MakeEvalTitle(nWidthPB < 240 ? "" : _("Eval"));
246     PaintEvalGraph();
247     SetDialogTitle(EvalGraphDlg, t);
248 }
249
250 static void
251 EvalClick (int x, int y)
252 {
253     int index = GetMoveIndexFromPoint( x, y );
254
255     if( index >= 0 && index < currLast ) ToNrEvent( index + 1 );
256 }
257
258 static Option graphOptions[] = {
259 { 150, 0x9C, 300, NULL, (void*) &EvalCallback, NULL, NULL, Graph , "" },
260 { 0, 2, 0, NULL, NULL, "", NULL, EndMark , "" }
261 };
262
263 static Option *
264 EvalCallback (int button, int x, int y)
265 {
266     if(!initDone) return NULL;
267
268     switch(button) {
269         case 10: // expose event
270             /* Create or recreate paint box if needed */
271             if(x != nWidthPB || y != nHeightPB) {
272                 InitializeEvalGraph(&graphOptions[0], x, y);
273             }
274             nWidthPB = x;
275             nHeightPB = y;
276             DisplayEvalGraph();
277             break;
278         case 1: EvalClick(x, y); // left button
279         default: break; // other buttons ignored
280     }
281     return NULL; // no context menu!
282 }
283
284 void
285 EvalGraphPopUp ()
286 {
287     if (GenericPopUp(graphOptions, _(title), EvalGraphDlg, BoardWindow, NONMODAL, 1)) {
288         InitializeEvalGraph(&graphOptions[0], 0, 0); // first time: add callbacks and initialize pens
289     } else {
290         SetDialogTitle(EvalGraphDlg, _(title));
291         SetIconName(EvalGraphDlg, _(title));
292     }
293
294     MarkMenu("View.EvaluationGraph", EvalGraphDlg);
295
296 //    ShowThinkingEvent(); // [HGM] thinking: might need to prompt engine for thinking output
297 }
298
299 void
300 EvalGraphPopDown ()
301 {
302     PopDown(EvalGraphDlg);
303
304 //    ShowThinkingEvent(); // [HGM] thinking: might need to shut off thinking output
305 }
306
307 Boolean
308 EvalGraphIsUp ()
309 {
310     return shellUp[EvalGraphDlg];
311 }
312
313 int
314 EvalGraphDialogExists ()
315 {
316     return DialogExists(EvalGraphDlg);
317 }
318
319 void
320 EvalGraphProc ()
321 {
322   if (!PopDown(EvalGraphDlg)) EvalGraphPopUp();
323 }
324
325 // This function is the interface to the back-end.
326
327 void
328 EvalGraphSet (int first, int last, int current, ChessProgramStats_Move * pvInfo)
329 {
330     /* [AS] Danger! For now we rely on the pvInfo parameter being a static variable! */
331
332     currFirst = first;
333     currLast = last;
334     currCurrent = current;
335     currPvInfo = pvInfo;
336
337     if( DialogExists(EvalGraphDlg) ) {
338         DisplayEvalGraph();
339     }
340 }
341