2 * ngamelist.c -- Game list window, Xt-independent front-end code for XBoard
4 * Copyright 1995, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free Software Foundation, Inc.
5 * ------------------------------------------------------------------------
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.
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.
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/. *
20 *------------------------------------------------------------------------
21 ** See the file ChangeLog for a revision history. */
28 #include <sys/types.h>
33 #else /* not STDC_HEADERS */
34 extern char *getenv();
37 # else /* not HAVE_STRING_H */
39 # endif /* not HAVE_STRING_H */
40 #endif /* not STDC_HEADERS */
54 # define _(s) gettext (s)
55 # define N_(s) gettext_noop (s)
62 static char filterString[MSG_SIZ];
63 static int listLength, wins, losses, draws, page;
74 static GameListClosure *glc = NULL;
76 static char *filterPtr;
77 static char *list[1003];
80 static int GameListPrepare P((int byPos, int narrow));
81 static void GameListReplace P((int page));
82 static void GL_Button P((int n));
84 static Option gamesOptions[] = {
85 { 200, LR|TB, 400, NULL, (void*) list, NULL, NULL, ListBox, "", &appData.gameListFont },
86 { 0, 0, 100, NULL, (void*) &filterPtr, "", NULL, TextBox, "" },
87 { 4, SAME_ROW, 0, NULL, (void*) &GL_Button, NULL, NULL, Button, N_("find position") },
88 { 2, SAME_ROW, 0, NULL, (void*) &GL_Button, NULL, NULL, Button, N_("narrow") }, // buttons referred to by ID in value (=first) field!
89 { 3, SAME_ROW, 0, NULL, (void*) &GL_Button, NULL, NULL, Button, N_("thresholds") },
90 { 9, SAME_ROW, 0, NULL, (void*) &GL_Button, NULL, NULL, Button, N_("tags") },
91 { 5, SAME_ROW, 0, NULL, (void*) &GL_Button, NULL, NULL, Button, N_("next") },
92 { 6, SAME_ROW, 0, NULL, (void*) &GL_Button, NULL, NULL, Button, N_("close") },
93 { 0, SAME_ROW | NO_OK, 0, NULL, NULL, "", NULL, EndMark , "" }
100 n = gamesOptions[n].value; // use marker in option rather than n itself, for more easy adding/deletng of buttons
101 if (n == 6) { // close
102 PopDown(GameListDlg);
105 if (n == 3) { // thresholds
106 LoadOptionsPopUp(GameListDlg);
109 if (n == 9) { // tags
110 GameListOptionsPopUp(GameListDlg);
113 index = SelectedListBoxItem(&gamesOptions[0]);
114 if (n == 7) { // load
116 DisplayError(_("No game selected"), 0);
119 } else if (n == 5) { // next
121 if (index >= listLength || !list[index]) {
122 DisplayError(_("Can't go forward any further"), 0);
125 HighlightWithScroll(&gamesOptions[0], index, listEnd);
126 } else if (n == 8) { // prev
129 DisplayError(_("Can't back up any further"), 0);
132 HighlightWithScroll(&gamesOptions[0], index, listEnd);
133 } else if (n == 2 || // narrow
134 n == 4) { // find position
136 GetWidgetText(&gamesOptions[1], &text);
137 safeStrCpy(filterString, text, sizeof(filterString)/sizeof(filterString[0]));
138 GameListPrepare(True, n == 2); GameListReplace(0);
142 index = atoi(list[index])-1; // [HGM] filter: read true index from sequence nr of line
143 if (cmailMsgLoaded) {
144 CmailLoadGame(glc->fp, index + 1, glc->filename, True);
146 LoadGame(glc->fp, index + 1, glc->filename, True);
151 GameListCreate (char *name)
154 if(new = GenericPopUp(gamesOptions, name, GameListDlg, BoardWindow, NONMODAL, appData.topLevel))
155 AddHandler(&gamesOptions[1], GameListDlg, 4),
156 AddHandler(&gamesOptions[0], GameListDlg, 5);
157 FocusOnWidget(&gamesOptions[0], GameListDlg);
162 GameListPrepare (int byPos, int narrow)
163 { // [HGM] filter: put in separate routine, to make callable from call-back
170 if(st = glc->strings) while(*st) free(*st++);
171 nstrings = ((ListGame *) gameList.tailPred)->number;
172 glc->strings = (char **) malloc((nstrings + 1) * sizeof(char *));
174 lg = (ListGame *) gameList.head;
175 listLength = wins = losses = draws = 0;
176 if(byPos) InitSearch();
179 if(!narrow || lg->position >= 0) { // only consider already selected positions when narrowing
180 line = GameListLine(lg->number, &lg->gameInfo);
181 if((filterString[0] == NULLCHAR || SearchPattern( line, filterString )) && (!byPos || (pos=GameContainsPosition(glc->fp, lg)) >= 0) ) {
182 *st++ = line; // [HGM] filter: make adding line conditional.
184 if( lg->gameInfo.result == WhiteWins ) wins++; else
185 if( lg->gameInfo.result == BlackWins ) losses++; else
186 if( lg->gameInfo.result == GameIsDrawn ) draws++;
187 if(!byPos) pos = 0; // indicate selected
190 if(lg->number % 2000 == 0) {
192 snprintf(buf, MSG_SIZ, _("Scanning through games (%d)"), lg->number);
193 DisplayTitle(buf); DoEvents();
196 lg = (ListGame *) lg->node.succ;
198 if(appData.debugMode) { GetTimeMark(&t2);printf("GameListPrepare %ld msec\n", SubtractTimeMarks(&t2,&t)); }
199 DisplayTitle("XBoard");
205 GameListReplace (int page)
207 // filter: put in separate routine, to make callable from call-back
208 char buf[MSG_SIZ], **st=list;
211 if(page) *st++ = _("previous page"); else if(listLength > 1000) *st++ = "";
212 for(i=0; i<1000; i++) if( !(*st++ = glc->strings[page+i]) ) { st--; break; }
214 if(page + 1000 <= listLength) *st++ = _("next page");
217 LoadListBox(&gamesOptions[0], _("no games matched your request"), -1, -1);
218 HighlightWithScroll(&gamesOptions[0], listEnd > 1000, listEnd);
219 snprintf(buf, MSG_SIZ, _("%s - %d/%d games (%d-%d-%d)"), glc->filename, listLength, ((ListGame *) gameList.tailPred)->number, wins, losses, draws);
220 SetDialogTitle(GameListDlg, buf);
226 if(!DialogExists(GameListDlg)) return;
227 GameListPrepare(False, False);
232 GameListPopUp (FILE *fp, char *filename)
235 glc = (GameListClosure *) calloc(1, sizeof(GameListClosure));
236 glc->x = glc->y = -1;
237 glc->filename = NULL;
240 GameListPrepare(False, False); // [HGM] filter: code put in separate routine
244 if (glc->filename != NULL) free(glc->filename);
245 glc->filename = StrSave(filename);
247 if (!GameListCreate(filename))
248 SetIconName(GameListDlg, filename);
251 GameListReplace(0); // [HGM] filter: code put in separate routine, and also called to set title
252 MarkMenu("View.GameList", GameListDlg);
253 EnableNamedMenuItem("File.SaveSelected", TRUE);
259 return glc ? glc->fp : NULL;
265 if (glc == NULL) return;
266 EnableNamedMenuItem("File.SaveSelected", FALSE);
267 PopDown(GameListDlg);
268 if (glc->strings != NULL) {
284 DisplayError(_("There is no game list"), 0);
287 if (shellUp[GameListDlg]) {
288 PopDown(GameListDlg);
291 GenericPopUp(NULL, NULL, GameListDlg, BoardWindow, NONMODAL, appData.topLevel); // first two args ignored when shell exists!
292 MarkMenu("View.GameList", GameListDlg);
293 GameListHighlight(lastLoadGameNumber);
297 GameListClicks (int direction)
301 if (glc == NULL || listLength == 0) return 1;
302 if(direction == 100) { FocusOnWidget(&gamesOptions[0], GameListDlg); return 1; }
303 index = SelectedListBoxItem(&gamesOptions[0]);
305 if (index < 0) return 1;
306 if(page && (index == 0 && direction < 1 || direction == -4)) {
308 if(page < 0) page = 0; // safety
309 GameListReplace(page);
312 if(index == 1001 && direction >= 0 || listEnd == 1001 && direction == 4) {
314 GameListReplace(page);
319 int doLoad = abs(direction) == 3;
320 if(doLoad) direction /= 3;
322 if(direction < -1) index = 0;
323 if(direction > 1) index = listEnd-1;
324 if(index < 0 || index >= listEnd) return 1;
325 HighlightWithScroll(&gamesOptions[0], index, listEnd);
326 if(!doLoad) return 1;
328 index = atoi(list[index])-1; // [HGM] filter: read true index from sequence nr of line
329 if (cmailMsgLoaded) {
330 CmailLoadGame(glc->fp, index + 1, glc->filename, True);
332 LoadGame(glc->fp, index + 1, glc->filename, True);
341 GetWidgetText(&gamesOptions[1], &name);
342 safeStrCpy(filterString, name, sizeof(filterString)/sizeof(filterString[0]));
343 GameListPrepare(False, False); GameListReplace(0);
344 UnCaret(); // filter text-edit
345 FocusOnWidget(&gamesOptions[0], GameListDlg); // listbox
349 GameListHighlight (int index)
352 if (!shellUp[GameListDlg]) return;
354 while(*st && atoi(*st)<index) st++,i++;
355 HighlightWithScroll(&gamesOptions[0], i, listEnd);
359 SaveGameListAsText (FILE *f)
361 ListGame * lg = (ListGame *) gameList.head;
364 if( !glc || ((ListGame *) gameList.tailPred)->number <= 0 ) {
365 DisplayError(_("Game list not loaded or empty"), 0);
369 /* Copy the list into the global memory block */
372 lg = (ListGame *) gameList.head;
374 for (nItem = 0; nItem < ((ListGame *) gameList.tailPred)->number; nItem++){
375 char * st = GameListLineFull(lg->number, &lg->gameInfo);
376 char *line = GameListLine(lg->number, &lg->gameInfo);
377 if(filterString[0] == NULLCHAR || SearchPattern( line, filterString ) )
378 fprintf( f, "%s\n", st );
379 free(st); free(line);
380 lg = (ListGame *) lg->node.succ;