06ebf4a210c94156485bd1c27c1785dbdb2c7924
[xboard.git] / xgamelist.c
1 /*
2  * xgamelist.c -- Game list window, part of X front end for XBoard
3  *
4  * Copyright 1995, 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/cursorfont.h>
50 #if USE_XAW3D
51 #include <X11/Xaw3d/Dialog.h>
52 #include <X11/Xaw3d/Form.h>
53 #include <X11/Xaw3d/List.h>
54 #include <X11/Xaw3d/Label.h>
55 #include <X11/Xaw3d/SimpleMenu.h>
56 #include <X11/Xaw3d/SmeBSB.h>
57 #include <X11/Xaw3d/SmeLine.h>
58 #include <X11/Xaw3d/Box.h>
59 #include <X11/Xaw3d/MenuButton.h>
60 #include <X11/Xaw3d/Text.h>
61 #include <X11/Xaw3d/AsciiText.h>
62 #include <X11/Xaw3d/Viewport.h>
63 #else
64 #include <X11/Xaw/Dialog.h>
65 #include <X11/Xaw/Form.h>
66 #include <X11/Xaw/List.h>
67 #include <X11/Xaw/Label.h>
68 #include <X11/Xaw/SimpleMenu.h>
69 #include <X11/Xaw/SmeBSB.h>
70 #include <X11/Xaw/SmeLine.h>
71 #include <X11/Xaw/Box.h>
72 #include <X11/Xaw/MenuButton.h>
73 #include <X11/Xaw/Text.h>
74 #include <X11/Xaw/AsciiText.h>
75 #include <X11/Xaw/Viewport.h>
76 #endif
77
78 #include "common.h"
79 #include "frontend.h"
80 #include "backend.h"
81 #include "xboard.h"
82 #include "xgamelist.h"
83 #include "gettext.h"
84
85 #ifdef ENABLE_NLS
86 # define  _(s) gettext (s)
87 # define N_(s) gettext_noop (s)
88 #else
89 # define  _(s) (s)
90 # define N_(s)  s
91 #endif
92
93
94 void SetFocus P((Widget w, XtPointer data, XEvent *event, Boolean *b));
95
96 static Widget filterText;
97 static char filterString[MSG_SIZ];
98 static int listLength, wins, losses, draws;
99
100 char gameListTranslations[] =
101   "<Btn1Up>(2): LoadSelectedProc(0) \n \
102    <Key>Home: LoadSelectedProc(-2) \n \
103    <Key>End: LoadSelectedProc(2) \n \
104    <Key>Up: LoadSelectedProc(-1) \n \
105    <Key>Down: LoadSelectedProc(1) \n \
106    <Key>Left: LoadSelectedProc(-1) \n \
107    <Key>Right: LoadSelectedProc(1) \n \
108    <Key>Return: LoadSelectedProc(0) \n";
109 char filterTranslations[] =
110   "<Key>Return: SetFilterProc() \n";
111
112 char *dummyList[] = { N_("no games matched your request"), NULL };
113
114 typedef struct {
115     Widget shell;
116     Position x, y;
117     Dimension w, h;
118     Boolean up;
119     FILE *fp;
120     char *filename;
121     char **strings;
122 } GameListClosure;
123 static GameListClosure *glc = NULL;
124
125 Widget
126 GameListCreate(name, callback, client_data)
127      char *name;
128      XtCallbackProc callback;
129      XtPointer client_data;
130 {
131     Arg args[16];
132     Widget shell, form, viewport, listwidg, layout, label;
133     Widget b_load, b_loadprev, b_loadnext, b_close, b_filter;
134     Dimension fw_width;
135     int j;
136     GameListClosure *glc = (GameListClosure *) client_data;
137
138     j = 0;
139     XtSetArg(args[j], XtNwidth, &fw_width);  j++;
140     XtGetValues(formWidget, args, j);
141
142     j = 0;
143     XtSetArg(args[j], XtNresizable, True);  j++;
144     XtSetArg(args[j], XtNallowShellResize, True);  j++;
145 #if TOPLEVEL
146     shell = gameListShell =
147       XtCreatePopupShell(name, topLevelShellWidgetClass,
148                          shellWidget, args, j);
149 #else
150     shell = gameListShell =
151       XtCreatePopupShell(name, transientShellWidgetClass,
152                          shellWidget, args, j);
153 #endif
154     layout =
155       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
156                             layoutArgs, XtNumber(layoutArgs));
157     j = 0;
158     XtSetArg(args[j], XtNborderWidth, 0); j++;
159     form =
160       XtCreateManagedWidget("form", formWidgetClass, layout, args, j);
161
162     j = 0;
163     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
164     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
165     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
166     XtSetArg(args[j], XtNright, XtChainRight);  j++;
167     XtSetArg(args[j], XtNresizable, False);  j++;
168     XtSetArg(args[j], XtNwidth, fw_width);  j++;
169     XtSetArg(args[j], XtNallowVert, True); j++;
170     viewport =
171       XtCreateManagedWidget("viewport", viewportWidgetClass, form, args, j);
172
173     j = 0;
174 //    XtSetArg(args[j], XtNlist, glc->strings);  j++;
175     XtSetArg(args[j], XtNdefaultColumns, 1);  j++;
176     XtSetArg(args[j], XtNforceColumns, True);  j++;
177     XtSetArg(args[j], XtNverticalList, True);  j++;
178     listwidg =
179       XtCreateManagedWidget("list", listWidgetClass, viewport, args, j);
180     XawListHighlight(listwidg, 0);
181     XtAugmentTranslations(listwidg,
182                           XtParseTranslationTable(gameListTranslations));
183
184     j = 0;
185     XtSetArg(args[j], XtNfromVert, viewport);  j++;
186     XtSetArg(args[j], XtNtop, XtChainBottom); j++;
187     XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
188     XtSetArg(args[j], XtNleft, XtChainLeft); j++;
189     XtSetArg(args[j], XtNright, XtChainLeft); j++;
190     b_load =
191       XtCreateManagedWidget(_("thresholds"), commandWidgetClass, form, args, j);
192     XtAddCallback(b_load, XtNcallback, callback, client_data);
193
194     j = 0;
195     XtSetArg(args[j], XtNfromVert, viewport);  j++;
196     XtSetArg(args[j], XtNfromHoriz, b_load);  j++;
197     XtSetArg(args[j], XtNtop, XtChainBottom); j++;
198     XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
199     XtSetArg(args[j], XtNleft, XtChainLeft); j++;
200     XtSetArg(args[j], XtNright, XtChainLeft); j++;
201     b_loadprev =
202       XtCreateManagedWidget(_("find position"), commandWidgetClass, form, args, j);
203     XtAddCallback(b_loadprev, XtNcallback, callback, client_data);
204 #if 1
205     j = 0;
206     XtSetArg(args[j], XtNfromVert, viewport);  j++;
207     XtSetArg(args[j], XtNfromHoriz, b_loadprev);  j++;
208     XtSetArg(args[j], XtNtop, XtChainBottom); j++;
209     XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
210     XtSetArg(args[j], XtNleft, XtChainLeft); j++;
211     XtSetArg(args[j], XtNright, XtChainLeft); j++;
212     b_loadnext =
213       XtCreateManagedWidget(_("next"), commandWidgetClass, form, args, j);
214     XtAddCallback(b_loadnext, XtNcallback, callback, client_data);
215 #else
216     b_loadnext = b_loadprev;
217 #endif
218     j = 0;
219     XtSetArg(args[j], XtNfromVert, viewport);  j++;
220     XtSetArg(args[j], XtNfromHoriz, b_loadnext);  j++;
221     XtSetArg(args[j], XtNtop, XtChainBottom); j++;
222     XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
223     XtSetArg(args[j], XtNleft, XtChainLeft); j++;
224     XtSetArg(args[j], XtNright, XtChainLeft); j++;
225     b_close =
226       XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
227     XtAddCallback(b_close, XtNcallback, callback, client_data);
228
229     j = 0;
230     XtSetArg(args[j], XtNfromVert, viewport);  j++;
231     XtSetArg(args[j], XtNfromHoriz, b_close);  j++;
232     XtSetArg(args[j], XtNtop, XtChainBottom); j++;
233     XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
234     XtSetArg(args[j], XtNleft, XtChainLeft); j++;
235     XtSetArg(args[j], XtNright, XtChainLeft); j++;
236     XtSetArg(args[j], XtNborderWidth, 0); j++;
237     label =
238       XtCreateManagedWidget(_("Filter:"), labelWidgetClass, form, args, j);
239
240     j = 0;
241     XtSetArg(args[j], XtNfromVert, viewport);  j++;
242     XtSetArg(args[j], XtNfromHoriz, label);  j++;
243     XtSetArg(args[j], XtNtop, XtChainBottom); j++;
244     XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
245     XtSetArg(args[j], XtNleft, XtChainLeft); j++;
246     XtSetArg(args[j], XtNright, XtChainRight); j++;
247     XtSetArg(args[j], XtNwidth, fw_width - 275 - squareSize); j++;
248     XtSetArg(args[j], XtNstring, filterString);  j++;
249     XtSetArg(args[j], XtNdisplayCaret, False);  j++;
250     XtSetArg(args[j], XtNresizable, True);  j++;
251 //    XtSetArg(args[j], XtNwidth, bw_width);  j++; /*force wider than buttons*/
252     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
253     XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
254     XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
255     filterText =
256       XtCreateManagedWidget(_("filtertext"), asciiTextWidgetClass, form, args, j);
257     XtAddEventHandler(filterText, ButtonPressMask, False, SetFocus, (XtPointer) shell);
258     XtOverrideTranslations(filterText,
259                           XtParseTranslationTable(filterTranslations));
260
261     j = 0;
262     XtSetArg(args[j], XtNfromVert, viewport);  j++;
263     XtSetArg(args[j], XtNfromHoriz, filterText);  j++;
264     XtSetArg(args[j], XtNtop, XtChainBottom); j++;
265     XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
266     XtSetArg(args[j], XtNleft, XtChainRight); j++;
267     XtSetArg(args[j], XtNright, XtChainRight); j++;
268     b_filter =
269       XtCreateManagedWidget(_("apply"), commandWidgetClass, form, args, j);
270     XtAddCallback(b_filter, XtNcallback, callback, client_data);
271
272
273     if(wpGameList.width > 0) {
274         glc->x = wpGameList.x;
275         glc->y = wpGameList.y;
276         glc->w = wpGameList.width;
277         glc->h = wpGameList.height;
278     }
279
280     if (glc->x == -1) {
281         Position y1;
282         Dimension h1;
283         int xx, yy;
284         Window junk;
285
286         j = 0;
287         XtSetArg(args[j], XtNheight, &h1); j++;
288         XtSetArg(args[j], XtNy, &y1); j++;
289         XtGetValues(boardWidget, args, j);
290         glc->w = fw_width * 3/4;
291         glc->h = squareSize * 3;
292
293         XSync(xDisplay, False);
294 #ifdef NOTDEF
295         /* This code seems to tickle an X bug if it is executed too soon
296            after xboard starts up.  The coordinates get transformed as if
297            the main window was positioned at (0, 0).
298         */
299         XtTranslateCoords(shellWidget, (fw_width - glc->w) / 2,
300                           y1 + (h1 - glc->h + appData.borderYoffset) / 2,
301                           &glc->x, &glc->y);
302 #else /*!NOTDEF*/
303         XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
304                               RootWindowOfScreen(XtScreen(shellWidget)),
305                               (fw_width - glc->w) / 2,
306                               y1 + (h1 - glc->h + appData.borderYoffset) / 2,
307                               &xx, &yy, &junk);
308         glc->x = xx;
309         glc->y = yy;
310 #endif /*!NOTDEF*/
311         if (glc->y < 0) glc->y = 0; /*avoid positioning top offscreen*/
312     }
313     j = 0;
314     XtSetArg(args[j], XtNheight, glc->h);  j++;
315     XtSetArg(args[j], XtNwidth, glc->w);  j++;
316     XtSetArg(args[j], XtNx, glc->x - appData.borderXoffset);  j++;
317     XtSetArg(args[j], XtNy, glc->y - appData.borderYoffset);  j++;
318     XtSetValues(shell, args, j);
319
320     XtRealizeWidget(shell);
321     CatchDeleteWindow(shell, "GameListPopDown");
322     XtSetKeyboardFocus(shell, listwidg);
323
324     return shell;
325 }
326
327 static int
328 GameListPrepare(int byPos)
329 {   // [HGM] filter: put in separate routine, to make callable from call-back
330     int nstrings;
331     ListGame *lg;
332     char **st, *line;
333
334     if(st = glc->strings) while(*st) free(*st++);
335     nstrings = ((ListGame *) gameList.tailPred)->number;
336     glc->strings = (char **) malloc((nstrings + 1) * sizeof(char *));
337     st = glc->strings;
338     lg = (ListGame *) gameList.head;
339     listLength = wins = losses = draws = 0;
340     while (nstrings--) {
341         int pos = -1;
342         line = GameListLine(lg->number, &lg->gameInfo);
343         if((filterString[0] == NULLCHAR || SearchPattern( line, filterString )) && (!byPos || (pos=GameContainsPosition(glc->fp, lg)) >= 0) ) {
344             if(listLength < 1500) *st++ = line; // [HGM] filter: make adding line conditional. *** X listboxes have severe size limit ***
345             listLength++;
346             if( lg->gameInfo.result == WhiteWins ) wins++; else
347             if( lg->gameInfo.result == BlackWins ) losses++; else
348             if( lg->gameInfo.result == GameIsDrawn ) draws++;
349         }
350         if(lg->number % 2000 == 0) {
351             char buf[MSG_SIZ];
352             snprintf(buf, MSG_SIZ, _("Scanning through games (%d)"), lg->number);
353             DisplayTitle(buf);
354         }
355         lg->position = pos;
356         lg = (ListGame *) lg->node.succ;
357      }
358      DisplayTitle("XBoard");
359     *st = NULL;
360     return listLength;
361 }
362
363 static void
364 GameListReplace()
365 {
366   // filter: put in separate routine, to make callable from call-back
367   Widget listwidg;
368   Arg arg;
369   char buf[MSG_SIZ],*p;
370
371   listwidg = XtNameToWidget(glc->shell, "*form.viewport.list");
372   XtSetArg(arg, XtNlist, listLength ? glc->strings : dummyList); // empty list displays message
373   XawListChange(listwidg, glc->strings, 0, 0, True);
374   XtSetValues(listwidg, &arg, 1);
375   XawListHighlight(listwidg, 0);
376   snprintf(buf, MSG_SIZ, "%s- %d/%d games (%d-%d-%d)", glc->filename, listLength, ((ListGame *) gameList.tailPred)->number, wins, losses, draws);
377   XtSetArg(arg, XtNtitle, buf);
378   XtSetValues(glc->shell, &arg, 1);
379 }
380
381 void
382 GameListCallback(w, client_data, call_data)
383      Widget w;
384      XtPointer client_data, call_data;
385 {
386     String name;
387     Arg args[16];
388     int j;
389     Widget listwidg;
390     GameListClosure *glc = (GameListClosure *) client_data;
391     XawListReturnStruct *rs;
392     int index;
393
394     j = 0;
395     XtSetArg(args[j], XtNlabel, &name);  j++;
396     XtGetValues(w, args, j);
397
398     if (strcmp(name, _("close")) == 0) {
399         GameListPopDown();
400         return;
401     }
402     if (strcmp(name, _("thresholds")) == 0) {
403         LoadOptionsProc();
404         return;
405     }
406     listwidg = XtNameToWidget(glc->shell, "*form.viewport.list");
407     rs = XawListShowCurrent(listwidg);
408     if (strcmp(name, _("load")) == 0) {
409         index = rs->list_index;
410         if (index < 0) {
411             DisplayError(_("No game selected"), 0);
412             return;
413         }
414     } else if (strcmp(name, _("next")) == 0) {
415         index = rs->list_index + 1;
416         if (index >= listLength) {
417             DisplayError(_("Can't go forward any further"), 0);
418             return;
419         }
420         XawListHighlight(listwidg, index);
421     } else if (strcmp(name, _("prev")) == 0) {
422         index = rs->list_index - 1;
423         if (index < 0) {
424             DisplayError(_("Can't back up any further"), 0);
425             return;
426         }
427         XawListHighlight(listwidg, index);
428     } else if (strcmp(name, _("apply")) == 0 ||
429                strcmp(name, _("find position")) == 0) {
430         String text;
431         j = 0;
432         XtSetArg(args[j], XtNstring, &text);  j++;
433         XtGetValues(filterText, args, j);
434         safeStrCpy(filterString, text, sizeof(filterString)/sizeof(filterString[0]));
435         XawListHighlight(listwidg, 0);
436         GameListPrepare(strcmp(name, _("find position")) == 0); GameListReplace();
437         return;
438     }
439 #if 1
440     index = atoi(glc->strings[index])-1; // [HGM] filter: read true index from sequence nr of line
441     if (cmailMsgLoaded) {
442         CmailLoadGame(glc->fp, index + 1, glc->filename, True);
443     } else {
444         LoadGame(glc->fp, index + 1, glc->filename, True);
445     }
446 #else
447     printf("This code should have been unreachable. Please report bug!\n");
448 #endif
449 }
450
451 void
452 GameListPopUp(fp, filename)
453      FILE *fp;
454      char *filename;
455 {
456     Arg args[16];
457     int j;
458     char **st;
459
460     if (glc == NULL) {
461         glc = (GameListClosure *) calloc(1, sizeof(GameListClosure));
462         glc->x = glc->y = -1;
463     }
464
465     GameListPrepare(False); // [HGM] filter: code put in separate routine
466
467     glc->fp = fp;
468
469     if (glc->filename != NULL) free(glc->filename);
470     glc->filename = StrSave(filename);
471
472
473     if (glc->shell == NULL) {
474         glc->shell = GameListCreate(filename, GameListCallback, glc);
475     } else {
476         j = 0;
477         XtSetArg(args[j], XtNiconName, (XtArgVal) filename);  j++;
478 //      XtSetArg(args[j], XtNtitle, (XtArgVal) filename);  j++;
479         XtSetValues(glc->shell, args, j);
480     }
481     GameListReplace(); // [HGM] filter: code put in separate routine, and also called to set title
482
483     XtPopup(glc->shell, XtGrabNone);
484     glc->up = True;
485     j = 0;
486     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
487     XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Game List"),
488                 args, j);
489 }
490
491 void
492 GameListDestroy()
493 {
494     if (glc == NULL) return;
495     GameListPopDown();
496     if (glc->strings != NULL) {
497         char **st;
498         st = glc->strings;
499         while (*st) {
500             free(*st++);
501         }
502         free(glc->strings);
503     }
504     free(glc);
505     glc = NULL;
506 }
507
508 void
509 ShowGameListProc(w, event, prms, nprms)
510      Widget w;
511      XEvent *event;
512      String *prms;
513      Cardinal *nprms;
514 {
515     Arg args[16];
516     int j;
517
518     if (glc == NULL) {
519         DisplayError(_("There is no game list"), 0);
520         return;
521     }
522     if (glc->up) {
523         GameListPopDown();
524         return;
525     }
526     XtPopup(glc->shell, XtGrabNone);
527     glc->up = True;
528     j = 0;
529     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
530     XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Game List"),
531                 args, j);
532     GameListHighlight(lastLoadGameNumber);
533 }
534
535 void
536 LoadSelectedProc(w, event, prms, nprms)
537      Widget w;
538      XEvent *event;
539      String *prms;
540      Cardinal *nprms;
541 {
542     Widget listwidg;
543     XawListReturnStruct *rs;
544     int index, direction = atoi(prms[0]);
545
546     if (glc == NULL || listLength == 0) return;
547     listwidg = XtNameToWidget(glc->shell, "*form.viewport.list");
548     rs = XawListShowCurrent(listwidg);
549     index = rs->list_index;
550     if (index < 0) return;
551     if(direction != 0) {
552         index += direction;
553         if(direction == -2) index = 0;
554         if(direction == 2) index = listLength-1;
555         if(index < 0 || index >= listLength) return;
556         XawListHighlight(listwidg, index);
557         return;
558     }
559     index = atoi(glc->strings[index])-1; // [HGM] filter: read true index from sequence nr of line
560     if (cmailMsgLoaded) {
561         CmailLoadGame(glc->fp, index + 1, glc->filename, True);
562     } else {
563         LoadGame(glc->fp, index + 1, glc->filename, True);
564     }
565 }
566
567 void
568 SetFilterProc(w, event, prms, nprms)
569      Widget w;
570      XEvent *event;
571      String *prms;
572      Cardinal *nprms;
573 {
574         Arg args[16];
575         String name;
576         Widget list;
577         int j = 0;
578         XtSetArg(args[j], XtNstring, &name);  j++;
579         XtGetValues(filterText, args, j);
580         safeStrCpy(filterString, name, sizeof(filterString)/sizeof(filterString[0]));
581         GameListPrepare(False); GameListReplace();
582         list = XtNameToWidget(glc->shell, "*form.viewport.list");
583         XawListHighlight(list, 0);
584         j = 0;
585         XtSetArg(args[j], XtNdisplayCaret, False); j++;
586         XtSetValues(filterText, args, j);
587         XtSetKeyboardFocus(glc->shell, list);
588 }
589
590 void
591 GameListPopDown()
592 {
593     Arg args[16];
594     int j;
595
596     if (glc == NULL) return;
597     j = 0;
598     XtSetArg(args[j], XtNx, &glc->x); j++;
599     XtSetArg(args[j], XtNy, &glc->y); j++;
600     XtSetArg(args[j], XtNheight, &glc->h); j++;
601     XtSetArg(args[j], XtNwidth, &glc->w); j++;
602     XtGetValues(glc->shell, args, j);
603     wpGameList.x = glc->x - 4;
604     wpGameList.y = glc->y - 23;
605     wpGameList.width = glc->w;
606     wpGameList.height = glc->h;
607     XtPopdown(glc->shell);
608     XtSetKeyboardFocus(shellWidget, formWidget);
609     glc->up = False;
610     j = 0;
611     XtSetArg(args[j], XtNleftBitmap, None); j++;
612     XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Game List"),
613                 args, j);
614 }
615
616 void
617 GameListHighlight(index)
618      int index;
619 {
620     Widget listwidg;
621     int i=0; char **st;
622     if (glc == NULL || !glc->up) return;
623     listwidg = XtNameToWidget(glc->shell, "*form.viewport.list");
624     st = glc->strings;
625     while(*st && atoi(*st)<index) st++,i++;
626     XawListHighlight(listwidg, i);
627 }
628
629 Boolean
630 GameListIsUp()
631 {
632     return glc && glc->up;
633 }
634
635 int SaveGameListAsText(FILE *f)
636 {
637     ListGame * lg = (ListGame *) gameList.head;
638     int nItem;
639
640     if( !glc || ((ListGame *) gameList.tailPred)->number <= 0 ) {
641         DisplayError("Game list not loaded or empty", 0);
642         return False;
643     }
644
645     /* Copy the list into the global memory block */
646     if( f != NULL ) {
647  
648         lg = (ListGame *) gameList.head;
649
650         for (nItem = 0; nItem < ((ListGame *) gameList.tailPred)->number; nItem++){
651             char * st = GameListLineFull(lg->number, &lg->gameInfo);
652             char *line = GameListLine(lg->number, &lg->gameInfo);
653             if(filterString[0] == NULLCHAR || SearchPattern( line, filterString ) )
654                     fprintf( f, "%s\n", st );
655             free(st); free(line);
656             lg = (ListGame *) lg->node.succ;
657         }
658
659         fclose(f);
660         return True;
661     }
662     return False;
663 }
664 //--------------------------------- Game-List options dialog ------------------------------------------
665
666 Widget gameListOptShell, listwidg;
667
668 char *strings[20];
669 int stringPtr;
670
671 void GLT_ClearList()
672 {
673     strings[0] = NULL;
674     stringPtr = 0;
675 }
676
677 void GLT_AddToList(char *name)
678 {
679     strings[stringPtr++] = name;
680     strings[stringPtr] = NULL;
681 }
682
683 Boolean GLT_GetFromList(int index, char *name)
684 {
685   safeStrCpy(name, strings[index], MSG_SIZ);
686   return TRUE;
687 }
688
689 void GLT_DeSelectList()
690 {
691     XawListChange(listwidg, strings, 0, 0, True);
692     XawListHighlight(listwidg, 0);
693 }
694
695 void
696 GameListOptionsPopDown()
697 {
698   if (gameListOptShell == NULL) return;
699
700   XtPopdown(gameListOptShell);
701   XtDestroyWidget(gameListOptShell);
702   gameListOptShell = 0;
703   XtSetKeyboardFocus(shellWidget, formWidget);
704 }
705
706 void
707 GameListOptionsCallback(w, client_data, call_data)
708      Widget w;
709      XtPointer client_data, call_data;
710 {
711     String name;
712     Arg args[16];
713     int j;
714     Widget listwidg;
715     XawListReturnStruct *rs;
716     int index;
717     char *p;
718
719     j = 0;
720     XtSetArg(args[j], XtNlabel, &name);  j++;
721     XtGetValues(w, args, j);
722
723     if (strcmp(name, _("OK")) == 0) {
724         GLT_ParseList();
725         appData.gameListTags = strdup(lpUserGLT);
726         GameListOptionsPopDown();
727         return;
728     } else
729     if (strcmp(name, _("cancel")) == 0) {
730         GameListOptionsPopDown();
731         return;
732     }
733     listwidg = XtNameToWidget(gameListOptShell, "*form.list");
734     rs = XawListShowCurrent(listwidg);
735     index = rs->list_index;
736     if (index < 0) {
737         DisplayError(_("No tag selected"), 0);
738         return;
739     }
740     p = strings[index];
741     if (strcmp(name, _("down")) == 0) {
742         if(index >= strlen(GLT_ALL_TAGS)) return;
743         strings[index] = strings[index+1];
744         strings[++index] = p;
745     } else
746     if (strcmp(name, _("up")) == 0) {
747         if(index == 0) return;
748         strings[index] = strings[index-1];
749         strings[--index] = p;
750     } else
751     if (strcmp(name, _("factory")) == 0) {
752       safeStrCpy(lpUserGLT, GLT_DEFAULT_TAGS, LPUSERGLT_SIZE);
753       GLT_TagsToList(lpUserGLT);
754       index = 0;
755     }
756     XawListHighlight(listwidg, index);
757 }
758
759 Widget
760 GameListOptionsCreate()
761 {
762     Arg args[16];
763     Widget shell, form, viewport, layout;
764     Widget b_load, b_loadprev, b_loadnext, b_close, b_cancel;
765     Dimension fw_width;
766     XtPointer client_data = NULL;
767     int j;
768
769     j = 0;
770     XtSetArg(args[j], XtNwidth, &fw_width);  j++;
771     XtGetValues(formWidget, args, j);
772
773     j = 0;
774     XtSetArg(args[j], XtNresizable, True);  j++;
775     XtSetArg(args[j], XtNallowShellResize, True);  j++;
776     shell = gameListOptShell =
777       XtCreatePopupShell("Game-list options", transientShellWidgetClass,
778                          shellWidget, args, j);
779     layout =
780       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
781                             layoutArgs, XtNumber(layoutArgs));
782     j = 0;
783     XtSetArg(args[j], XtNborderWidth, 0); j++;
784     form =
785       XtCreateManagedWidget("form", formWidgetClass, layout, args, j);
786
787     j = 0;
788     XtSetArg(args[j], XtNdefaultColumns, 1);  j++;
789     XtSetArg(args[j], XtNforceColumns, True);  j++;
790     XtSetArg(args[j], XtNverticalList, True);  j++;
791     listwidg = viewport =
792       XtCreateManagedWidget("list", listWidgetClass, form, args, j);
793     XawListHighlight(listwidg, 0);
794 //    XtAugmentTranslations(listwidg,
795 //                        XtParseTranslationTable(gameListOptTranslations));
796
797     j = 0;
798     XtSetArg(args[j], XtNfromVert, viewport);  j++;
799     XtSetArg(args[j], XtNtop, XtChainBottom); j++;
800     XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
801     XtSetArg(args[j], XtNleft, XtChainLeft); j++;
802     XtSetArg(args[j], XtNright, XtChainLeft); j++;
803     b_load =
804       XtCreateManagedWidget(_("factory"), commandWidgetClass, form, args, j);
805     XtAddCallback(b_load, XtNcallback, GameListOptionsCallback, client_data);
806
807     j = 0;
808     XtSetArg(args[j], XtNfromVert, viewport);  j++;
809     XtSetArg(args[j], XtNfromHoriz, b_load);  j++;
810     XtSetArg(args[j], XtNtop, XtChainBottom); j++;
811     XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
812     XtSetArg(args[j], XtNleft, XtChainLeft); j++;
813     XtSetArg(args[j], XtNright, XtChainLeft); j++;
814     b_loadprev =
815       XtCreateManagedWidget(_("up"), commandWidgetClass, form, args, j);
816     XtAddCallback(b_loadprev, XtNcallback, GameListOptionsCallback, client_data);
817
818     j = 0;
819     XtSetArg(args[j], XtNfromVert, viewport);  j++;
820     XtSetArg(args[j], XtNfromHoriz, b_loadprev);  j++;
821     XtSetArg(args[j], XtNtop, XtChainBottom); j++;
822     XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
823     XtSetArg(args[j], XtNleft, XtChainLeft); j++;
824     XtSetArg(args[j], XtNright, XtChainLeft); j++;
825     b_loadnext =
826       XtCreateManagedWidget(_("down"), commandWidgetClass, form, args, j);
827     XtAddCallback(b_loadnext, XtNcallback, GameListOptionsCallback, client_data);
828
829     j = 0;
830     XtSetArg(args[j], XtNfromVert, viewport);  j++;
831     XtSetArg(args[j], XtNfromHoriz, b_loadnext);  j++;
832     XtSetArg(args[j], XtNtop, XtChainBottom); j++;
833     XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
834     XtSetArg(args[j], XtNleft, XtChainLeft); j++;
835     XtSetArg(args[j], XtNright, XtChainLeft); j++;
836     b_cancel =
837       XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
838     XtAddCallback(b_cancel, XtNcallback, GameListOptionsCallback, client_data);
839
840     j = 0;
841     XtSetArg(args[j], XtNfromVert, viewport);  j++;
842     XtSetArg(args[j], XtNfromHoriz, b_cancel);  j++;
843     XtSetArg(args[j], XtNtop, XtChainBottom); j++;
844     XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
845     XtSetArg(args[j], XtNleft, XtChainLeft); j++;
846     XtSetArg(args[j], XtNright, XtChainLeft); j++;
847     b_close =
848       XtCreateManagedWidget(_("OK"), commandWidgetClass, form, args, j);
849     XtAddCallback(b_close, XtNcallback, GameListOptionsCallback, client_data);
850
851     safeStrCpy(lpUserGLT, appData.gameListTags, LPUSERGLT_SIZE);
852     GLT_TagsToList(lpUserGLT);
853
854     XtRealizeWidget(shell);
855     CatchDeleteWindow(shell, "GameListOptionsPopDown");
856
857     return shell;
858 }
859
860 void
861 GameListOptionsPopUp(Widget w, XEvent *event, String *prms, Cardinal *nprms)
862 {
863   if (gameListOptShell == NULL)
864     gameListOptShell = GameListOptionsCreate();
865
866   XtPopup(gameListOptShell, XtGrabNone);
867 }
868
869