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