Print progress during load / selection of game
[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         if(listLength % 2000 == 0) {
345             char buf[MSG_SIZ];
346             snprintf(buf, MSG_SIZ, _("Reading game file (%d)"), listLength);
347             DisplayTitle(buf);
348         }
349         lg->position = pos;
350         lg = (ListGame *) lg->node.succ;
351      }
352      DisplayTitle("WinBoard");
353     *st = NULL;
354     return listLength;
355 }
356
357 static void
358 GameListReplace()
359 {
360   // filter: put in separate routine, to make callable from call-back
361   Widget listwidg;
362
363   listwidg = XtNameToWidget(glc->shell, "*form.viewport.list");
364   XawListChange(listwidg, glc->strings, 0, 0, True);
365   XawListHighlight(listwidg, 0);
366 }
367
368 void
369 GameListCallback(w, client_data, call_data)
370      Widget w;
371      XtPointer client_data, call_data;
372 {
373     String name;
374     Arg args[16];
375     int j;
376     Widget listwidg;
377     GameListClosure *glc = (GameListClosure *) client_data;
378     XawListReturnStruct *rs;
379     int index;
380
381     j = 0;
382     XtSetArg(args[j], XtNlabel, &name);  j++;
383     XtGetValues(w, args, j);
384
385     if (strcmp(name, _("close")) == 0) {
386         GameListPopDown();
387         return;
388     }
389     if (strcmp(name, _("thresholds")) == 0) {
390         LoadOptionsProc();
391         return;
392     }
393     if (strcmp(name, _("find position")) == 0) {
394         if(GameListPrepare(True)) GameListReplace(); // crashes on empty list...
395         return;
396     }
397     listwidg = XtNameToWidget(glc->shell, "*form.viewport.list");
398     rs = XawListShowCurrent(listwidg);
399     if (strcmp(name, _("load")) == 0) {
400         index = rs->list_index;
401         if (index < 0) {
402             DisplayError(_("No game selected"), 0);
403             return;
404         }
405     } else if (strcmp(name, _("next")) == 0) {
406         index = rs->list_index + 1;
407         if (index >= listLength) {
408             DisplayError(_("Can't go forward any further"), 0);
409             return;
410         }
411         XawListHighlight(listwidg, index);
412     } else if (strcmp(name, _("prev")) == 0) {
413         index = rs->list_index - 1;
414         if (index < 0) {
415             DisplayError(_("Can't back up any further"), 0);
416             return;
417         }
418         XawListHighlight(listwidg, index);
419     } else if (strcmp(name, _("apply")) == 0) {
420         String name;
421         j = 0;
422         XtSetArg(args[j], XtNstring, &name);  j++;
423         XtGetValues(filterText, args, j);
424         safeStrCpy(filterString, name, sizeof(filterString)/sizeof(filterString[0]));
425         XawListHighlight(listwidg, 0);
426         if(GameListPrepare(False)) GameListReplace(); // crashes on empty list...
427         return;
428     }
429 #if 1
430     index = atoi(glc->strings[index])-1; // [HGM] filter: read true index from sequence nr of line
431     if (cmailMsgLoaded) {
432         CmailLoadGame(glc->fp, index + 1, glc->filename, True);
433     } else {
434         LoadGame(glc->fp, index + 1, glc->filename, True);
435     }
436 #else
437     printf("This code should have been unreachable. Please report bug!\n");
438 #endif
439 }
440
441 void
442 GameListPopUp(fp, filename)
443      FILE *fp;
444      char *filename;
445 {
446     Arg args[16];
447     int j;
448     char **st;
449
450     if (glc == NULL) {
451         glc = (GameListClosure *) calloc(1, sizeof(GameListClosure));
452         glc->x = glc->y = -1;
453     }
454
455     if (glc->strings != NULL) {
456         st = glc->strings;
457         while (*st) {
458             free(*st++);
459         }
460         free(glc->strings);
461     }
462
463     GameListPrepare(False); // [HGM] filter: code put in separate routine
464
465     glc->fp = fp;
466
467     if (glc->filename != NULL) free(glc->filename);
468     glc->filename = StrSave(filename);
469
470
471     if (glc->shell == NULL) {
472         glc->shell = GameListCreate(filename, GameListCallback, glc);
473     } else {
474         GameListReplace(); // [HGM] filter: code put in separate routine
475         j = 0;
476         XtSetArg(args[j], XtNiconName, (XtArgVal) filename);  j++;
477         XtSetArg(args[j], XtNtitle, (XtArgVal) filename);  j++;
478         XtSetValues(glc->shell, args, j);
479     }
480
481     XtPopup(glc->shell, XtGrabNone);
482     glc->up = True;
483     j = 0;
484     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
485     XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Game List"),
486                 args, j);
487 }
488
489 void
490 GameListDestroy()
491 {
492     if (glc == NULL) return;
493     GameListPopDown();
494     if (glc->strings != NULL) {
495         char **st;
496         st = glc->strings;
497         while (*st) {
498             free(*st++);
499         }
500         free(glc->strings);
501     }
502     free(glc);
503     glc = NULL;
504 }
505
506 void
507 ShowGameListProc(w, event, prms, nprms)
508      Widget w;
509      XEvent *event;
510      String *prms;
511      Cardinal *nprms;
512 {
513     Arg args[16];
514     int j;
515
516     if (glc == NULL) {
517         DisplayError(_("There is no game list"), 0);
518         return;
519     }
520     if (glc->up) {
521         GameListPopDown();
522         return;
523     }
524     XtPopup(glc->shell, XtGrabNone);
525     glc->up = True;
526     j = 0;
527     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
528     XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Game List"),
529                 args, j);
530     GameListHighlight(lastLoadGameNumber);
531 }
532
533 void
534 LoadSelectedProc(w, event, prms, nprms)
535      Widget w;
536      XEvent *event;
537      String *prms;
538      Cardinal *nprms;
539 {
540     Widget listwidg;
541     XawListReturnStruct *rs;
542     int index, direction = atoi(prms[0]);
543
544     if (glc == NULL) return;
545     listwidg = XtNameToWidget(glc->shell, "*form.viewport.list");
546     rs = XawListShowCurrent(listwidg);
547     index = rs->list_index;
548     if (index < 0) return;
549     if(direction != 0) {
550         index += direction;
551         if(direction == -2) index = 0;
552         if(direction == 2) index = listLength-1;
553         if(index < 0 || index >= listLength) return;
554         XawListHighlight(listwidg, index);
555         return;
556     }
557     index = atoi(glc->strings[index])-1; // [HGM] filter: read true index from sequence nr of line
558     if (cmailMsgLoaded) {
559         CmailLoadGame(glc->fp, index + 1, glc->filename, True);
560     } else {
561         LoadGame(glc->fp, index + 1, glc->filename, True);
562     }
563 }
564
565 void
566 SetFilterProc(w, event, prms, nprms)
567      Widget w;
568      XEvent *event;
569      String *prms;
570      Cardinal *nprms;
571 {
572         Arg args[16];
573         String name;
574         Widget list;
575         int j = 0;
576         XtSetArg(args[j], XtNstring, &name);  j++;
577         XtGetValues(filterText, args, j);
578         safeStrCpy(filterString, name, sizeof(filterString)/sizeof(filterString[0]));
579         if(GameListPrepare(False)) GameListReplace(); // crashes on empty list...
580         list = XtNameToWidget(glc->shell, "*form.viewport.list");
581         XawListHighlight(list, 0);
582         j = 0;
583         XtSetArg(args[j], XtNdisplayCaret, False); j++;
584         XtSetValues(filterText, args, j);
585         XtSetKeyboardFocus(glc->shell, list);
586 }
587
588 void
589 GameListPopDown()
590 {
591     Arg args[16];
592     int j;
593
594     if (glc == NULL) return;
595     j = 0;
596     XtSetArg(args[j], XtNx, &glc->x); j++;
597     XtSetArg(args[j], XtNy, &glc->y); j++;
598     XtSetArg(args[j], XtNheight, &glc->h); j++;
599     XtSetArg(args[j], XtNwidth, &glc->w); j++;
600     XtGetValues(glc->shell, args, j);
601     wpGameList.x = glc->x - 4;
602     wpGameList.y = glc->y - 23;
603     wpGameList.width = glc->w;
604     wpGameList.height = glc->h;
605     XtPopdown(glc->shell);
606     XtSetKeyboardFocus(shellWidget, formWidget);
607     glc->up = False;
608     j = 0;
609     XtSetArg(args[j], XtNleftBitmap, None); j++;
610     XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Game List"),
611                 args, j);
612 }
613
614 void
615 GameListHighlight(index)
616      int index;
617 {
618     Widget listwidg;
619     int i=0; char **st;
620     if (glc == NULL || !glc->up) return;
621     listwidg = XtNameToWidget(glc->shell, "*form.viewport.list");
622     st = glc->strings;
623     while(*st && atoi(*st)<index) st++,i++;
624     XawListHighlight(listwidg, i);
625 }
626
627 Boolean
628 GameListIsUp()
629 {
630     return glc && glc->up;
631 }
632
633 int SaveGameListAsText(FILE *f)
634 {
635     ListGame * lg = (ListGame *) gameList.head;
636     int nItem;
637
638     if( !glc || ((ListGame *) gameList.tailPred)->number <= 0 ) {
639         DisplayError("Game list not loaded or empty", 0);
640         return False;
641     }
642
643     /* Copy the list into the global memory block */
644     if( f != NULL ) {
645  
646         lg = (ListGame *) gameList.head;
647
648         for (nItem = 0; nItem < ((ListGame *) gameList.tailPred)->number; nItem++){
649             char * st = GameListLineFull(lg->number, &lg->gameInfo);
650             char *line = GameListLine(lg->number, &lg->gameInfo);
651             if(filterString[0] == NULLCHAR || SearchPattern( line, filterString ) )
652                     fprintf( f, "%s\n", st );
653             free(st); free(line);
654             lg = (ListGame *) lg->node.succ;
655         }
656
657         fclose(f);
658         return True;
659     }
660     return False;
661 }
662 //--------------------------------- Game-List options dialog ------------------------------------------
663
664 Widget gameListOptShell, listwidg;
665
666 char *strings[20];
667 int stringPtr;
668
669 void GLT_ClearList()
670 {
671     strings[0] = NULL;
672     stringPtr = 0;
673 }
674
675 void GLT_AddToList(char *name)
676 {
677     strings[stringPtr++] = name;
678     strings[stringPtr] = NULL;
679 }
680
681 Boolean GLT_GetFromList(int index, char *name)
682 {
683   safeStrCpy(name, strings[index], MSG_SIZ);
684   return TRUE;
685 }
686
687 void GLT_DeSelectList()
688 {
689     XawListChange(listwidg, strings, 0, 0, True);
690     XawListHighlight(listwidg, 0);
691 }
692
693 void
694 GameListOptionsPopDown()
695 {
696   if (gameListOptShell == NULL) return;
697
698   XtPopdown(gameListOptShell);
699   XtDestroyWidget(gameListOptShell);
700   gameListOptShell = 0;
701   XtSetKeyboardFocus(shellWidget, formWidget);
702 }
703
704 void
705 GameListOptionsCallback(w, client_data, call_data)
706      Widget w;
707      XtPointer client_data, call_data;
708 {
709     String name;
710     Arg args[16];
711     int j;
712     Widget listwidg;
713     XawListReturnStruct *rs;
714     int index;
715     char *p;
716
717     j = 0;
718     XtSetArg(args[j], XtNlabel, &name);  j++;
719     XtGetValues(w, args, j);
720
721     if (strcmp(name, _("OK")) == 0) {
722         GLT_ParseList();
723         appData.gameListTags = strdup(lpUserGLT);
724         GameListOptionsPopDown();
725         return;
726     } else
727     if (strcmp(name, _("cancel")) == 0) {
728         GameListOptionsPopDown();
729         return;
730     }
731     listwidg = XtNameToWidget(gameListOptShell, "*form.list");
732     rs = XawListShowCurrent(listwidg);
733     index = rs->list_index;
734     if (index < 0) {
735         DisplayError(_("No tag selected"), 0);
736         return;
737     }
738     p = strings[index];
739     if (strcmp(name, _("down")) == 0) {
740         if(index >= strlen(GLT_ALL_TAGS)) return;
741         strings[index] = strings[index+1];
742         strings[++index] = p;
743     } else
744     if (strcmp(name, _("up")) == 0) {
745         if(index == 0) return;
746         strings[index] = strings[index-1];
747         strings[--index] = p;
748     } else
749     if (strcmp(name, _("factory")) == 0) {
750       safeStrCpy(lpUserGLT, GLT_DEFAULT_TAGS, LPUSERGLT_SIZE);
751       GLT_TagsToList(lpUserGLT);
752       index = 0;
753     }
754     XawListHighlight(listwidg, index);
755 }
756
757 Widget
758 GameListOptionsCreate()
759 {
760     Arg args[16];
761     Widget shell, form, viewport, layout;
762     Widget b_load, b_loadprev, b_loadnext, b_close, b_cancel;
763     Dimension fw_width;
764     XtPointer client_data = NULL;
765     int j;
766
767     j = 0;
768     XtSetArg(args[j], XtNwidth, &fw_width);  j++;
769     XtGetValues(formWidget, args, j);
770
771     j = 0;
772     XtSetArg(args[j], XtNresizable, True);  j++;
773     XtSetArg(args[j], XtNallowShellResize, True);  j++;
774     shell = gameListOptShell =
775       XtCreatePopupShell("Game-list options", transientShellWidgetClass,
776                          shellWidget, args, j);
777     layout =
778       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
779                             layoutArgs, XtNumber(layoutArgs));
780     j = 0;
781     XtSetArg(args[j], XtNborderWidth, 0); j++;
782     form =
783       XtCreateManagedWidget("form", formWidgetClass, layout, args, j);
784
785     j = 0;
786     XtSetArg(args[j], XtNdefaultColumns, 1);  j++;
787     XtSetArg(args[j], XtNforceColumns, True);  j++;
788     XtSetArg(args[j], XtNverticalList, True);  j++;
789     listwidg = viewport =
790       XtCreateManagedWidget("list", listWidgetClass, form, args, j);
791     XawListHighlight(listwidg, 0);
792 //    XtAugmentTranslations(listwidg,
793 //                        XtParseTranslationTable(gameListOptTranslations));
794
795     j = 0;
796     XtSetArg(args[j], XtNfromVert, viewport);  j++;
797     XtSetArg(args[j], XtNtop, XtChainBottom); j++;
798     XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
799     XtSetArg(args[j], XtNleft, XtChainLeft); j++;
800     XtSetArg(args[j], XtNright, XtChainLeft); j++;
801     b_load =
802       XtCreateManagedWidget(_("factory"), commandWidgetClass, form, args, j);
803     XtAddCallback(b_load, XtNcallback, GameListOptionsCallback, client_data);
804
805     j = 0;
806     XtSetArg(args[j], XtNfromVert, viewport);  j++;
807     XtSetArg(args[j], XtNfromHoriz, b_load);  j++;
808     XtSetArg(args[j], XtNtop, XtChainBottom); j++;
809     XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
810     XtSetArg(args[j], XtNleft, XtChainLeft); j++;
811     XtSetArg(args[j], XtNright, XtChainLeft); j++;
812     b_loadprev =
813       XtCreateManagedWidget(_("up"), commandWidgetClass, form, args, j);
814     XtAddCallback(b_loadprev, XtNcallback, GameListOptionsCallback, client_data);
815
816     j = 0;
817     XtSetArg(args[j], XtNfromVert, viewport);  j++;
818     XtSetArg(args[j], XtNfromHoriz, b_loadprev);  j++;
819     XtSetArg(args[j], XtNtop, XtChainBottom); j++;
820     XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
821     XtSetArg(args[j], XtNleft, XtChainLeft); j++;
822     XtSetArg(args[j], XtNright, XtChainLeft); j++;
823     b_loadnext =
824       XtCreateManagedWidget(_("down"), commandWidgetClass, form, args, j);
825     XtAddCallback(b_loadnext, XtNcallback, GameListOptionsCallback, client_data);
826
827     j = 0;
828     XtSetArg(args[j], XtNfromVert, viewport);  j++;
829     XtSetArg(args[j], XtNfromHoriz, b_loadnext);  j++;
830     XtSetArg(args[j], XtNtop, XtChainBottom); j++;
831     XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
832     XtSetArg(args[j], XtNleft, XtChainLeft); j++;
833     XtSetArg(args[j], XtNright, XtChainLeft); j++;
834     b_cancel =
835       XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
836     XtAddCallback(b_cancel, XtNcallback, GameListOptionsCallback, client_data);
837
838     j = 0;
839     XtSetArg(args[j], XtNfromVert, viewport);  j++;
840     XtSetArg(args[j], XtNfromHoriz, b_cancel);  j++;
841     XtSetArg(args[j], XtNtop, XtChainBottom); j++;
842     XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
843     XtSetArg(args[j], XtNleft, XtChainLeft); j++;
844     XtSetArg(args[j], XtNright, XtChainLeft); j++;
845     b_close =
846       XtCreateManagedWidget(_("OK"), commandWidgetClass, form, args, j);
847     XtAddCallback(b_close, XtNcallback, GameListOptionsCallback, client_data);
848
849     safeStrCpy(lpUserGLT, appData.gameListTags, LPUSERGLT_SIZE);
850     GLT_TagsToList(lpUserGLT);
851
852     XtRealizeWidget(shell);
853     CatchDeleteWindow(shell, "GameListOptionsPopDown");
854
855     return shell;
856 }
857
858 void
859 GameListOptionsPopUp(Widget w, XEvent *event, String *prms, Cardinal *nprms)
860 {
861   if (gameListOptShell == NULL)
862     gameListOptShell = GameListOptionsCreate();
863
864   XtPopup(gameListOptShell, XtGrabNone);
865 }
866
867