0f0b53e70e08d06bdfdf52c3c4b532551e2f5afb
[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, 2012 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, page;
99
100 char gameListTranslations[] =
101   "<Btn1Down>: LoadSelectedProc(100) Set() \n \
102    <Btn1Up>(2): LoadSelectedProc(0) \n \
103    <Key>Home: LoadSelectedProc(-2) \n \
104    <Key>End: LoadSelectedProc(2) \n \
105    Ctrl<Key>Up: LoadSelectedProc(-3) \n \
106    Ctrl<Key>Down: LoadSelectedProc(3) \n \
107    <Key>Up: LoadSelectedProc(-1) \n \
108    <Key>Down: LoadSelectedProc(1) \n \
109    <Key>Left: LoadSelectedProc(-1) \n \
110    <Key>Right: LoadSelectedProc(1) \n \
111    <Key>Return: LoadSelectedProc(0) \n";
112 char filterTranslations[] =
113   "<Key>Return: SetFilterProc() \n";
114
115 char *dummyList[] = { N_("no games matched your request"), NULL };
116
117 typedef struct {
118     Widget shell;
119     Position x, y;
120     Dimension w, h;
121     Boolean up;
122     FILE *fp;
123     char *filename;
124     char **strings;
125 } GameListClosure;
126 static GameListClosure *glc = NULL;
127
128 Widget
129 GameListCreate (char *name, XtCallbackProc callback, 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     XtOverrideTranslations(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 extern int soughtCounts[];
328 extern Board soughtBoard;
329
330 static int
331 GameListPrepare (int byPos)
332 {   // [HGM] filter: put in separate routine, to make callable from call-back
333     int nstrings;
334     ListGame *lg;
335     char **st, *line;
336     TimeMark t, t2;
337
338     GetTimeMark(&t);
339     if(st = glc->strings) while(*st) free(*st++);
340     nstrings = ((ListGame *) gameList.tailPred)->number;
341     glc->strings = (char **) malloc((nstrings + 1) * sizeof(char *));
342     st = glc->strings;
343     lg = (ListGame *) gameList.head;
344     listLength = wins = losses = draws = 0;
345     if(byPos) InitSearch();
346     while (nstrings--) {
347         int pos = -1;
348         line = GameListLine(lg->number, &lg->gameInfo);
349         if((filterString[0] == NULLCHAR || SearchPattern( line, filterString )) && (!byPos || (pos=GameContainsPosition(glc->fp, lg)) >= 0) ) {
350             *st++ = line; // [HGM] filter: make adding line conditional.
351             listLength++;
352             if( lg->gameInfo.result == WhiteWins ) wins++; else
353             if( lg->gameInfo.result == BlackWins ) losses++; else
354             if( lg->gameInfo.result == GameIsDrawn ) draws++;
355         }
356         if(lg->number % 2000 == 0) {
357             char buf[MSG_SIZ];
358             snprintf(buf, MSG_SIZ, _("Scanning through games (%d)"), lg->number);
359             DisplayTitle(buf);
360         }
361         lg->position = pos;
362         lg = (ListGame *) lg->node.succ;
363      }
364 GetTimeMark(&t2);printf("GameListPrepare %ld msec\n", SubtractTimeMarks(&t2,&t));
365      DisplayTitle("XBoard");
366     *st = NULL;
367     return listLength;
368 }
369
370 static char *list[1003];
371 int listEnd;
372
373 static void
374 GameListReplace (int page)
375 {
376   // filter: put in separate routine, to make callable from call-back
377   Widget listwidg;
378   Arg arg;
379   char buf[MSG_SIZ], *p, **st=list;
380   int i;
381
382   if(page) *st++ = _("previous page"); else if(listLength > 1000) *st++ = "";
383   for(i=0; i<1000; i++) if( !(*st++ = glc->strings[page+i]) ) { st--; break; }
384   listEnd = st - list;
385   if(page + 1000 <= listLength) *st++ = _("next page");
386   *st = NULL;
387
388   listwidg = XtNameToWidget(glc->shell, "*form.viewport.list");
389   XtSetArg(arg, XtNlist, listLength ? list : dummyList); // empty list displays message
390   XawListChange(listwidg, list, 0, 0, True);
391   XtSetValues(listwidg, &arg, 1);
392   XawListHighlight(listwidg, 0);
393   snprintf(buf, MSG_SIZ, _("%s - %d/%d games (%d-%d-%d)"), glc->filename, listLength, ((ListGame *) gameList.tailPred)->number, wins, losses, draws);
394   XtSetArg(arg, XtNtitle, buf);
395   XtSetValues(glc->shell, &arg, 1);
396 }
397
398 void
399 GameListCallback (Widget w, XtPointer client_data, XtPointer call_data)
400 {
401     String name;
402     Arg args[16];
403     int j;
404     Widget listwidg;
405     GameListClosure *glc = (GameListClosure *) client_data;
406     XawListReturnStruct *rs;
407     int index;
408
409     j = 0;
410     XtSetArg(args[j], XtNlabel, &name);  j++;
411     XtGetValues(w, args, j);
412
413     if (strcmp(name, _("close")) == 0) {
414         GameListPopDown();
415         return;
416     }
417     if (strcmp(name, _("thresholds")) == 0) {
418         LoadOptionsProc();
419         return;
420     }
421     listwidg = XtNameToWidget(glc->shell, "*form.viewport.list");
422     rs = XawListShowCurrent(listwidg);
423     if (strcmp(name, _("load")) == 0) {
424         index = rs->list_index;
425         if (index < 0) {
426             DisplayError(_("No game selected"), 0);
427             return;
428         }
429     } else if (strcmp(name, _("next")) == 0) {
430         index = rs->list_index + 1;
431         if (index >= listLength || !list[index]) {
432             DisplayError(_("Can't go forward any further"), 0);
433             return;
434         }
435         XawListHighlight(listwidg, index);
436     } else if (strcmp(name, _("prev")) == 0) {
437         index = rs->list_index - 1;
438         if (index < 0) {
439             DisplayError(_("Can't back up any further"), 0);
440             return;
441         }
442         XawListHighlight(listwidg, index);
443     } else if (strcmp(name, _("apply")) == 0 ||
444                strcmp(name, _("find position")) == 0) {
445         String text;
446         j = 0;
447         XtSetArg(args[j], XtNstring, &text);  j++;
448         XtGetValues(filterText, args, j);
449         safeStrCpy(filterString, text, sizeof(filterString)/sizeof(filterString[0]));
450         XawListHighlight(listwidg, 0);
451         GameListPrepare(strcmp(name, _("find position")) == 0); GameListReplace(0);
452         return;
453     }
454 #if 1
455     index = atoi(list[index])-1; // [HGM] filter: read true index from sequence nr of line
456     if (cmailMsgLoaded) {
457         CmailLoadGame(glc->fp, index + 1, glc->filename, True);
458     } else {
459         LoadGame(glc->fp, index + 1, glc->filename, True);
460     }
461 #else
462     printf("This code should have been unreachable. Please report bug!\n");
463 #endif
464 }
465
466 void
467 GameListPopUp (FILE *fp, char *filename)
468 {
469     Arg args[16];
470     int j;
471     char **st;
472
473     if (glc == NULL) {
474         glc = (GameListClosure *) calloc(1, sizeof(GameListClosure));
475         glc->x = glc->y = -1;
476         glc->filename = NULL;
477         glc->shell = NULL;
478     }
479
480     GameListPrepare(False); // [HGM] filter: code put in separate routine
481
482     glc->fp = fp;
483
484     if (glc->filename != NULL) free(glc->filename);
485     glc->filename = StrSave(filename);
486
487     if (glc->shell == NULL) {
488         glc->shell = GameListCreate(filename, GameListCallback, glc);
489     } else {
490         j = 0;
491         XtSetArg(args[j], XtNiconName, (XtArgVal) filename);  j++;
492 //      XtSetArg(args[j], XtNtitle, (XtArgVal) filename);  j++;
493         XtSetValues(glc->shell, args, j);
494     }
495     page = 0;
496     GameListReplace(0); // [HGM] filter: code put in separate routine, and also called to set title
497
498     XtPopup(glc->shell, XtGrabNone);
499     glc->up = True;
500     j = 0;
501     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
502     XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Game List"),
503                 args, j);
504 }
505
506 void
507 GameListDestroy ()
508 {
509     if (glc == NULL) return;
510     GameListPopDown();
511     if (glc->strings != NULL) {
512         char **st;
513         st = glc->strings;
514         while (*st) {
515             free(*st++);
516         }
517         free(glc->strings);
518     }
519     free(glc);
520     glc = NULL;
521 }
522
523 void
524 ShowGameListProc ()
525 {
526     Arg args[16];
527     int j;
528
529     if (glc == NULL) {
530         DisplayError(_("There is no game list"), 0);
531         return;
532     }
533     if (glc->up) {
534         GameListPopDown();
535         return;
536     }
537     XtPopup(glc->shell, XtGrabNone);
538     glc->up = True;
539     j = 0;
540     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
541     XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Game List"),
542                 args, j);
543     GameListHighlight(lastLoadGameNumber);
544 }
545
546 void
547 LoadSelectedProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
548 {
549     Widget listwidg;
550     XawListReturnStruct *rs;
551     int index, direction = atoi(prms[0]);
552 printf("action(%d)\n",direction);
553     if (glc == NULL || listLength == 0) return;
554     listwidg = XtNameToWidget(glc->shell, "*form.viewport.list");
555     if(direction == 100) { XtSetKeyboardFocus(glc->shell, listwidg); return; }
556     rs = XawListShowCurrent(listwidg);
557     index = rs->list_index;
558     if (index < 0) return;
559     if(page && index == 0) {
560         page -= 1000;
561         if(page < 0) page = 0; // safety
562         GameListReplace(page);
563         return;
564     }
565     if(index == 1001) {
566         page += 1000;
567         GameListReplace(page);
568         return;
569     }
570
571     if(direction != 0) {
572         int doLoad = abs(direction) > 2;
573         if(doLoad) direction /= 3;
574         index += direction;
575         if(direction == -2) index = 0;
576         if(direction == 2) index = listEnd-1;
577         if(index < 0 || index >= listEnd) return;
578         XawListHighlight(listwidg, index);
579         if(!doLoad) return;
580     }
581     index = atoi(list[index])-1; // [HGM] filter: read true index from sequence nr of line
582     if (cmailMsgLoaded) {
583         CmailLoadGame(glc->fp, index + 1, glc->filename, True);
584     } else {
585         LoadGame(glc->fp, index + 1, glc->filename, True);
586         XSync(xDisplay, False);
587         XSetInputFocus(xDisplay, XtWindow(boardWidget), RevertToPointerRoot, CurrentTime);
588     }
589 }
590
591 void
592 SetFilterProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
593 {
594         Arg args[16];
595         String name;
596         Widget list;
597         int j = 0;
598         XtSetArg(args[j], XtNstring, &name);  j++;
599         XtGetValues(filterText, args, j);
600         safeStrCpy(filterString, name, sizeof(filterString)/sizeof(filterString[0]));
601         GameListPrepare(False); GameListReplace(0);
602         list = XtNameToWidget(glc->shell, "*form.viewport.list");
603         XawListHighlight(list, 0);
604         j = 0;
605         XtSetArg(args[j], XtNdisplayCaret, False); j++;
606         XtSetValues(filterText, args, j);
607         XtSetKeyboardFocus(glc->shell, list);
608 }
609
610 void
611 GameListPopDown ()
612 {
613     Arg args[16];
614     int j;
615
616     if (glc == NULL) return;
617     j = 0;
618     XtSetArg(args[j], XtNx, &glc->x); j++;
619     XtSetArg(args[j], XtNy, &glc->y); j++;
620     XtSetArg(args[j], XtNheight, &glc->h); j++;
621     XtSetArg(args[j], XtNwidth, &glc->w); j++;
622     XtGetValues(glc->shell, args, j);
623     wpGameList.x = glc->x - 4;
624     wpGameList.y = glc->y - 23;
625     wpGameList.width = glc->w;
626     wpGameList.height = glc->h;
627     XtPopdown(glc->shell);
628     XtSetKeyboardFocus(shellWidget, formWidget);
629     glc->up = False;
630     j = 0;
631     XtSetArg(args[j], XtNleftBitmap, None); j++;
632     XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Game List"),
633                 args, j);
634 }
635
636 void
637 GameListHighlight (int index)
638 {
639     Widget listwidg;
640     int i=0; char **st;
641     if (glc == NULL || !glc->up) return;
642     listwidg = XtNameToWidget(glc->shell, "*form.viewport.list");
643     st = list;
644     while(*st && atoi(*st)<index) st++,i++;
645     XawListHighlight(listwidg, i);
646 }
647
648 Boolean
649 GameListIsUp ()
650 {
651     return glc && glc->up;
652 }
653
654 int
655 SaveGameListAsText (FILE *f)
656 {
657     ListGame * lg = (ListGame *) gameList.head;
658     int nItem;
659
660     if( !glc || ((ListGame *) gameList.tailPred)->number <= 0 ) {
661       DisplayError(_("Game list not loaded or empty"), 0);
662         return False;
663     }
664
665     /* Copy the list into the global memory block */
666     if( f != NULL ) {
667  
668         lg = (ListGame *) gameList.head;
669
670         for (nItem = 0; nItem < ((ListGame *) gameList.tailPred)->number; nItem++){
671             char * st = GameListLineFull(lg->number, &lg->gameInfo);
672             char *line = GameListLine(lg->number, &lg->gameInfo);
673             if(filterString[0] == NULLCHAR || SearchPattern( line, filterString ) )
674                     fprintf( f, "%s\n", st );
675             free(st); free(line);
676             lg = (ListGame *) lg->node.succ;
677         }
678
679         fclose(f);
680         return True;
681     }
682     return False;
683 }
684