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