fixed crash in history window
[xboard.git] / xhistory.c
1 /*\r
2  * xhistory.c -- Move list window, part of X front end for XBoard\r
3  * $Id$\r
4  *\r
5  * Copyright 2000,2009 Free Software Foundation, Inc.\r
6  * ------------------------------------------------------------------------\r
7  *\r
8  * GNU XBoard is free software: you can redistribute it and/or modify\r
9  * it under the terms of the GNU General Public License as published by\r
10  * the Free Software Foundation, either version 3 of the License, or (at\r
11  * your option) any later version.\r
12  *\r
13  * GNU XBoard is distributed in the hope that it will be useful, but\r
14  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
16  * General Public License for more details.\r
17  *\r
18  * You should have received a copy of the GNU General Public License\r
19  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
20  *\r
21  *------------------------------------------------------------------------\r
22  ** See the file ChangeLog for a revision history.  */\r
23 \r
24 #include "config.h"\r
25 \r
26 #include <stdio.h>\r
27 #include <ctype.h>\r
28 #include <errno.h>\r
29 #include <sys/types.h>\r
30 \r
31 #if STDC_HEADERS\r
32 # include <stdlib.h>\r
33 # include <string.h>\r
34 #else /* not STDC_HEADERS */\r
35 extern char *getenv();\r
36 # if HAVE_STRING_H\r
37 #  include <string.h>\r
38 # else /* not HAVE_STRING_H */\r
39 #  include <strings.h>\r
40 # endif /* not HAVE_STRING_H */\r
41 #endif /* not STDC_HEADERS */\r
42 \r
43 #if HAVE_UNISTD_H\r
44 # include <unistd.h>\r
45 #endif\r
46 \r
47 #include <X11/Intrinsic.h>\r
48 #include <X11/StringDefs.h>\r
49 #include <X11/Shell.h>\r
50 #include <X11/Xaw/Dialog.h>\r
51 #include <X11/Xaw/Form.h>\r
52 #include <X11/Xaw/List.h>\r
53 #include <X11/Xaw/Label.h>\r
54 #include <X11/Xaw/SimpleMenu.h>\r
55 #include <X11/Xaw/SmeBSB.h>\r
56 #include <X11/Xaw/SmeLine.h>\r
57 #include <X11/Xaw/Box.h>\r
58 #include <X11/Xaw/Paned.h>\r
59 #include <X11/Xaw/MenuButton.h>\r
60 #include <X11/cursorfont.h>\r
61 #include <X11/Xaw/Text.h>\r
62 #include <X11/Xaw/AsciiText.h>\r
63 #include <X11/Xaw/Viewport.h>\r
64 \r
65 #include "common.h"\r
66 #include "frontend.h"\r
67 #include "backend.h"\r
68 #include "xboard.h"\r
69 #include "xhistory.h"\r
70 #include "gettext.h"\r
71 \r
72 #ifdef ENABLE_NLS\r
73 # define  _(s) gettext (s)\r
74 # define N_(s) gettext_noop (s)\r
75 #else\r
76 # define  _(s) (s)\r
77 # define N_(s)  s\r
78 #endif\r
79 \r
80 #define _LL_ 100\r
81 \r
82 extern Widget formWidget, shellWidget, boardWidget, menuBarWidget;\r
83 extern Display *xDisplay;\r
84 extern int squareSize;\r
85 extern Pixmap xMarkPixmap;\r
86 extern char *layoutName;\r
87 \r
88 struct History{\r
89   String *Nr,*white,*black;\r
90   int     aNr;  /* space actually alocated */\r
91   Widget mvn,mvw,mvb,vbox,viewport,sh;\r
92   char Up;\r
93 };\r
94 \r
95 struct History *hist=0;\r
96 String dots=" ... ";\r
97 Position gameHistoryX, gameHistoryY;\r
98 \r
99 void\r
100 HistoryPopDown(w, client_data, call_data)\r
101      Widget w;\r
102      XtPointer client_data, call_data;\r
103 {\r
104   Arg args[16];\r
105   int j;\r
106   if(hist) {\r
107     // [HGM] remember old position\r
108     j = 0;\r
109     XtSetArg(args[j], XtNx, &gameHistoryX);  j++;\r
110     XtSetArg(args[j], XtNy, &gameHistoryY);  j++;\r
111     XtGetValues(hist->sh, args, j);\r
112 \r
113     XtPopdown(hist->sh);\r
114     hist->Up=False;\r
115   }\r
116   j=0;\r
117   XtSetArg(args[j], XtNleftBitmap, None); j++;\r
118   XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Show Move History"),\r
119                 args, j);\r
120 }\r
121 \r
122 void HistoryMoveProc(Widget w, XtPointer closure, XtPointer call_data)\r
123 {\r
124     int to;\r
125     XawListReturnStruct *R = (XawListReturnStruct *) call_data;\r
126     if (w == hist->mvn || w == hist->mvw) {\r
127       to=2*R->list_index-1;\r
128       ToNrEvent(to);\r
129     }\r
130     else if (w == hist->mvb) {\r
131       to=2*R->list_index;\r
132       ToNrEvent(to);\r
133     }\r
134 }\r
135 \r
136 void HistoryAlloc(int len){\r
137   int i;\r
138   if(hist){\r
139     free(hist->Nr[0]);free(hist->white[0]);free(hist->black[0]);\r
140     free(hist->Nr);free(hist->white);free(hist->black);\r
141   }\r
142   else{\r
143     hist=(struct History*)malloc(sizeof(struct History));\r
144   }\r
145     hist->aNr=len;\r
146     hist->Nr=(String*)malloc(hist->aNr*sizeof(String*));\r
147     hist->white=(String*)malloc(hist->aNr*sizeof(String*));\r
148     hist->black=(String*)malloc(hist->aNr*sizeof(String*));\r
149 \r
150     hist->Nr[0]=(String)malloc(hist->aNr*6);\r
151     hist->white[0]=(String)malloc(hist->aNr*MOVE_LEN);\r
152     hist->black[0]=(String)malloc(hist->aNr*MOVE_LEN);\r
153 \r
154       sprintf(hist->Nr[0],"    ");\r
155       sprintf(hist->white[0],_("White "));\r
156       sprintf(hist->black[0],_("Black "));\r
157     for(i=1;i<hist->aNr;i++){\r
158       hist->Nr[i]= hist->Nr[i-1]+6;\r
159       hist->white[i]= hist->white[i-1]+MOVE_LEN;\r
160       hist->black[i]= hist->black[i-1]+MOVE_LEN;\r
161       sprintf(hist->Nr[i],"%i.",i);\r
162       sprintf(hist->white[i],"-----");\r
163       sprintf(hist->black[i],"-----");\r
164      }\r
165 }\r
166 \r
167 \r
168 #if 1\r
169 /* Find empty space inside vbox form widget and redistribute it amongst\r
170    the list widgets inside it. */\r
171 /* This version sort of works */\r
172 void\r
173 HistoryFill()\r
174 {\r
175   Dimension w, bw;\r
176   long extra;\r
177   Position x, x1, x2;\r
178   int j, dd;\r
179   Arg args[16];\r
180 \r
181   j = 0;\r
182   XtSetArg(args[j], XtNx, &x);  j++;\r
183   XtSetArg(args[j], XtNwidth, &w);  j++;\r
184   XtSetArg(args[j], XtNborderWidth, &bw);  j++;\r
185   XtGetValues(hist->mvb, args, j);\r
186   x1 = x + w + 2*bw;\r
187 \r
188   j = 0;\r
189   XtSetArg(args[j], XtNwidth, &w);  j++;\r
190   XtSetArg(args[j], XtNdefaultDistance, &dd);  j++;\r
191   XtGetValues(hist->vbox, args, j);\r
192   x2 = w - dd;\r
193 \r
194   extra = x2 - x1;\r
195   if (extra < 0) {\r
196     extra = -((-extra)/2);\r
197   } else {\r
198     extra = extra/2;\r
199   }\r
200 \r
201   j = 0;\r
202   XtSetArg(args[j], XtNwidth, &w);  j++;\r
203   XtGetValues(hist->mvw, args, j);\r
204   w += extra;\r
205   j = 0;\r
206   XtSetArg(args[j], XtNwidth, w);  j++;\r
207   XtSetValues(hist->mvw, args, j);\r
208 \r
209   j = 0;\r
210   XtSetArg(args[j], XtNwidth, &w);  j++;\r
211   XtGetValues(hist->mvb, args, j);\r
212   w += extra;\r
213   j = 0;\r
214   XtSetArg(args[j], XtNwidth, w);  j++;\r
215   XtSetValues(hist->mvb, args, j);\r
216 }\r
217 #else\r
218 /* Find empty space inside vbox form widget and redistribute it amongst\r
219    the list widgets inside it. */\r
220 /* This version doesn't work */\r
221 void\r
222 HistoryFill()\r
223 {\r
224   Arg args[16];\r
225   Dimension fw, niw, wiw, biw, nbw, wbw, bbw;\r
226   int j, nl, wl, bl, fdd;\r
227   long extra;\r
228 \r
229   j = 0;\r
230   XtSetArg(args[j], XtNwidth, &fw);  j++;\r
231   XtSetArg(args[j], XtNdefaultDistance, &fdd);  j++;\r
232   XtGetValues(hist->vbox, args, j);\r
233 \r
234   j = 0;\r
235   XtSetArg(args[j], XtNlongest, &nl);  j++;\r
236   XtSetArg(args[j], XtNinternalWidth, &niw);  j++;\r
237   XtSetArg(args[j], XtNborderWidth, &nbw);  j++;\r
238   XtGetValues(hist->mvn, args, j);\r
239 \r
240   j = 0;\r
241   XtSetArg(args[j], XtNlongest, &wl);  j++;\r
242   XtSetArg(args[j], XtNinternalWidth, &wiw);  j++;\r
243   XtSetArg(args[j], XtNborderWidth, &wbw);  j++;\r
244   XtGetValues(hist->mvw, args, j);\r
245 \r
246   j = 0;\r
247   XtSetArg(args[j], XtNlongest, &bl);  j++;\r
248   XtSetArg(args[j], XtNinternalWidth, &biw);  j++;\r
249   XtSetArg(args[j], XtNborderWidth, &bbw);  j++;\r
250   XtGetValues(hist->mvb, args, j);\r
251 \r
252   extra = fw - 4*fdd -\r
253     nl - 1 - 2*niw - 2*nbw - wl - 2*wiw - 2*wbw - bl - 2*biw - 2*bbw;\r
254   if (extra < 0) extra = 0;\r
255 \r
256   j = 0;\r
257   XtSetArg(args[j], XtNwidth, nl + 1 + 2*niw);  j++;\r
258   XtSetValues(hist->mvn, args, j);\r
259 \r
260   j = 0;\r
261   XtSetArg(args[j], XtNwidth, wl + 2*wiw + extra/2);  j++;\r
262   XtSetValues(hist->mvw, args, j);\r
263 \r
264   j = 0;\r
265   XtSetArg(args[j], XtNwidth, bl + 2*biw + extra/2);  j++;\r
266   XtSetValues(hist->mvb, args, j);\r
267 }\r
268 #endif\r
269 \r
270 void HistorySet(char movelist[][2*MOVE_LEN],int first,int last,int current){\r
271   int i,b,m;\r
272   if(hist){\r
273     if(last >= hist->aNr) HistoryAlloc(last+_LL_);\r
274     for(i=0;i<last;i++) {\r
275       if((i%2)==0) {\r
276         if(movelist[i][0]) {\r
277           char* p = strchr(movelist[i], ' ');\r
278           if (p) {\r
279             strncpy(hist->white[i/2+1], movelist[i], p-movelist[i]);\r
280             hist->white[i/2+1][p-movelist[i]] = NULLCHAR;\r
281           } else {\r
282             strcpy(hist->white[i/2+1],movelist[i]);\r
283           }\r
284         } else {\r
285           strcpy(hist->white[i/2+1],dots);\r
286         }\r
287       } else {\r
288         if(movelist[i][0]) {\r
289           char* p = strchr(movelist[i], ' ');\r
290           if (p) {\r
291             strncpy(hist->black[i/2+1], movelist[i], p-movelist[i]);\r
292             hist->black[i/2+1][p-movelist[i]] = NULLCHAR;\r
293           } else {\r
294             strcpy(hist->black[i/2+1],movelist[i]);\r
295           }\r
296         } else {\r
297           strcpy(hist->black[i/2+1],"");\r
298         }\r
299       }\r
300     }\r
301     strcpy(hist->black[last/2+1],"");\r
302     b=first/2;\r
303     m=(last+3)/2-b;\r
304     XawFormDoLayout(hist->vbox, False);\r
305     XawListChange(hist->mvn,hist->Nr+b,m,0,True);\r
306     XawListChange(hist->mvw,hist->white+b,m,0,True);\r
307     XawListChange(hist->mvb,hist->black+b,m,0,True);\r
308     HistoryFill();\r
309     XawFormDoLayout(hist->vbox, True);\r
310     if(current<0){\r
311       XawListUnhighlight(hist->mvw);\r
312       XawListUnhighlight(hist->mvb);\r
313     }\r
314     else if((current%2)==0){\r
315       XawListHighlight(hist->mvw, current/2+1);\r
316       XawListUnhighlight(hist->mvb);\r
317     }\r
318     else{\r
319       XawListUnhighlight(hist->mvw);\r
320       if(current) XawListHighlight(hist->mvb, current/2+1);\r
321       else XawListUnhighlight(hist->mvb);\r
322     }\r
323   }\r
324 }\r
325 \r
326 Widget HistoryCreate()\r
327 {\r
328     Arg args[16];\r
329     int i,j;\r
330 \r
331     Widget layout,form,b_close;\r
332     String trstr=\r
333              "<Key>Up: BackwardProc() \n \\r
334              <Key>Left: BackwardProc() \n \\r
335              <Key>Down: ForwardProc() \n \\r
336              <Key>Right: ForwardProc() \n";\r
337     /*--- allocate memory for move-strings ---*/\r
338     HistoryAlloc(_LL_);\r
339 \r
340     /*-------- create the widgets ---------------*/\r
341     j = 0;\r
342     XtSetArg(args[j], XtNresizable, True);  j++;\r
343     XtSetArg(args[j], XtNallowShellResize, True);  j++;\r
344 #if TOPLEVEL\r
345     hist->sh =\r
346       XtCreatePopupShell(_("Move list"), topLevelShellWidgetClass,\r
347                          shellWidget, args, j);\r
348 #else\r
349     hist->sh =\r
350       XtCreatePopupShell(_("Move list"), transientShellWidgetClass,\r
351                          shellWidget, args, j);\r
352 #endif\r
353     j = 0;\r
354     XtSetArg(args[j], XtNborderWidth, 0); j++;\r
355     XtSetArg(args[j], XtNdefaultDistance, 0);  j++;\r
356       layout =\r
357       XtCreateManagedWidget(layoutName, formWidgetClass, hist->sh,\r
358                             args, j);\r
359 \r
360     j = 0;\r
361     XtSetArg(args[j], XtNborderWidth, 0); j++;\r
362     XtSetArg(args[j], XtNresizable, True);  j++;\r
363 \r
364     form =\r
365       XtCreateManagedWidget("form", formWidgetClass, layout, args, j);\r
366      j=0;\r
367 \r
368     j = 0;\r
369 \r
370     XtSetArg(args[j], XtNtop, XtChainTop);  j++;\r
371     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;\r
372     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;\r
373     XtSetArg(args[j], XtNright, XtChainRight);  j++;\r
374 \r
375     XtSetArg(args[j], XtNborderWidth, 1); j++;\r
376     XtSetArg(args[j], XtNresizable, False);  j++;\r
377     XtSetArg(args[j], XtNallowVert, True); j++;\r
378     XtSetArg(args[j], XtNallowHoriz, True);  j++;\r
379     XtSetArg(args[j], XtNforceBars, False); j++;\r
380     XtSetArg(args[j], XtNheight, 280); j++;\r
381     hist->viewport =\r
382       XtCreateManagedWidget("viewport", viewportWidgetClass,\r
383                             form, args, j);\r
384     j=0;\r
385     XtSetArg(args[j], XtNborderWidth, 0); j++;\r
386     XtSetArg(args[j], XtNorientation,XtorientHorizontal);j++;\r
387     hist->vbox =\r
388       XtCreateManagedWidget("vbox", formWidgetClass, hist->viewport, args, j);\r
389 \r
390     j=0;\r
391     XtSetArg(args[j], XtNtop, XtChainTop);  j++;\r
392     XtSetArg(args[j], XtNbottom, XtChainTop);  j++;\r
393     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;\r
394     XtSetArg(args[j], XtNright, XtChainLeft);  j++;\r
395 \r
396     XtSetArg(args[j], XtNdefaultColumns, 1);  j++;\r
397     XtSetArg(args[j], XtNforceColumns, True);  j++;\r
398     XtSetArg(args[j], XtNverticalList, True);  j++;\r
399     XtSetArg(args[j], XtNborderWidth, 0); j++;\r
400     XtSetArg(args[j], XtNresizable,True);j++;\r
401     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;\r
402     hist->mvn = XtCreateManagedWidget("movesn", listWidgetClass,\r
403                                       hist->vbox, args, j);\r
404     XtAddCallback(hist->mvn, XtNcallback, HistoryMoveProc, (XtPointer) hist);\r
405 \r
406     j=0;\r
407     XtSetArg(args[j], XtNtop, XtChainTop);  j++;\r
408     XtSetArg(args[j], XtNbottom, XtChainTop);  j++;\r
409     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;\r
410     XtSetArg(args[j], XtNright, XtRubber);  j++;\r
411 \r
412     XtSetArg(args[j], XtNdefaultColumns, 1);  j++;\r
413     XtSetArg(args[j], XtNforceColumns, True);  j++;\r
414     XtSetArg(args[j], XtNverticalList, True);  j++;\r
415     XtSetArg(args[j], XtNborderWidth, 0); j++;\r
416     XtSetArg(args[j], XtNresizable,True);j++;\r
417     XtSetArg(args[j], XtNfromHoriz, hist->mvn);  j++;\r
418     hist->mvw = XtCreateManagedWidget("movesw", listWidgetClass,\r
419                                       hist->vbox, args, j);\r
420     XtAddCallback(hist->mvw, XtNcallback, HistoryMoveProc, (XtPointer) hist);\r
421 \r
422     j=0;\r
423     XtSetArg(args[j], XtNtop, XtChainTop);  j++;\r
424     XtSetArg(args[j], XtNbottom, XtChainTop);  j++;\r
425     XtSetArg(args[j], XtNleft, XtRubber);  j++;\r
426     XtSetArg(args[j], XtNright,  XtRubber);  j++;\r
427 \r
428     XtSetArg(args[j], XtNdefaultColumns, 1);  j++;\r
429     XtSetArg(args[j], XtNforceColumns, True);  j++;\r
430     XtSetArg(args[j], XtNverticalList, True);  j++;\r
431     XtSetArg(args[j], XtNborderWidth, 0); j++;\r
432     XtSetArg(args[j], XtNresizable,True);j++;\r
433     XtSetArg(args[j], XtNfromHoriz, hist->mvw);  j++;\r
434     hist->mvb = XtCreateManagedWidget("movesb", listWidgetClass,\r
435                                       hist->vbox, args, j);\r
436     XtAddCallback(hist->mvb, XtNcallback, HistoryMoveProc, (XtPointer) hist);\r
437 \r
438     j=0;\r
439     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;\r
440     XtSetArg(args[j], XtNtop, XtChainBottom);  j++;\r
441     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;\r
442     XtSetArg(args[j], XtNright, XtChainLeft);  j++;\r
443     XtSetArg(args[j], XtNfromVert, hist->viewport);  j++;\r
444     b_close= XtCreateManagedWidget(_("Close"), commandWidgetClass,\r
445                                    form, args, j);\r
446     XtAddCallback(b_close, XtNcallback, HistoryPopDown, (XtPointer) 0);\r
447 \r
448     XtAugmentTranslations(hist->sh,XtParseTranslationTable (trstr));\r
449 \r
450     XtRealizeWidget(hist->sh);\r
451     CatchDeleteWindow(hist->sh, "HistoryPopDown");\r
452 \r
453     for(i=1;i<hist->aNr;i++){\r
454       strcpy(hist->white[i],dots);\r
455       strcpy(hist->black[i],"");\r
456      }\r
457 \r
458     return hist->sh;\r
459 }\r
460 \r
461 void\r
462 HistoryPopUp()\r
463 {\r
464   Arg args[16];\r
465   int j;\r
466 \r
467   if(!hist) HistoryCreate();\r
468 \r
469   XtPopup(hist->sh, XtGrabNone);\r
470 \r
471   // [HGM] restore old position\r
472   j = 0;\r
473   XtSetArg(args[j], XtNx, gameHistoryX);  j++;\r
474   XtSetArg(args[j], XtNy, gameHistoryY);  j++;\r
475   XtSetValues(hist->sh, args, j);\r
476 \r
477   j=0;\r
478   XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;\r
479   XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Show Move History"),\r
480                 args, j);\r
481   hist->Up=True;\r
482 }\r
483 \r
484 \r
485 void\r
486 HistoryShowProc(w, event, prms, nprms)\r
487      Widget w;\r
488      XEvent *event;\r
489      String *prms;\r
490      Cardinal *nprms;\r
491 {\r
492   if (!hist) {\r
493     HistoryCreate();\r
494     HistoryPopUp();\r
495   } else if (hist->Up) {\r
496     HistoryPopDown(0,0,0);\r
497   } else {\r
498     HistoryPopUp();\r
499   }\r
500   ToNrEvent(currentMove);\r
501 }\r
502 \r