Fix compile time warnings and a couple other cleanups.
[cboard.git] / src / history.c
blob859f80e6ad373fb89595e32b3f20cdc4ce7de869
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2002-2006 Ben Kibbey <bjk@arbornet.org>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <limits.h>
22 #include <string.h>
23 #include <ctype.h>
24 #include <errno.h>
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
30 #ifdef HAVE_MENU_H
31 #include <menu.h>
32 #endif
34 #include "common.h"
35 #include "colors.h"
36 #include "history.h"
38 void free_historydata(HISTORY **history, int idx, int total)
40 int i;
41 HISTORY *h = *history;
43 if (total) {
44 for (i = idx; i < total; i++) {
45 if (h[i].comment)
46 free(h[i].comment);
50 if (idx)
51 h = Realloc(h, (idx) * sizeof(HISTORY));
53 *history = h;
54 return;
57 static int init_nag()
59 FILE *fp;
60 char line[LINE_MAX];
61 int i = 0;
63 if ((fp = fopen(config.nagfile, "r")) == NULL) {
64 cmessage(ERROR, ANYKEY, "%s: %s", config.nagfile, strerror(errno));
65 return 1;
68 while (!feof(fp)) {
69 if (fscanf(fp, " %[^\n] ", line) == 1) {
70 nags = Realloc(nags, (i + 2) * sizeof(struct nag_s));
71 nags[i].line = strdup(line);
72 i++;
76 if (nags)
77 nags[i].line = NULL;
78 return 0;
81 static void view_nag(void *arg)
83 int idx = (int)arg;
84 char buf[80];
85 char line[LINE_MAX] = {0};
86 int i = 0;
88 snprintf(buf, sizeof(buf), "Viewing NAG for \"%s\"",
89 game[gindex].history[idx].move);
91 if (!nags) {
92 if (init_nag())
93 return;
96 for (i = 0; i < MAX_PGN_NAG; i++) {
97 if (!game[gindex].history[idx].nag[i])
98 break;
100 strncat(line, nags[game[gindex].history[idx].nag[i] - 1].line,
101 sizeof(line));
102 strncat(line, "\n", sizeof(line));
105 line[strlen(line) - 1] = 0;
106 message(buf, ANYKEY, "%s", line);
107 return;
110 void view_annotation(int idx)
112 char buf[MAX_PGN_MOVE_LEN + strlen(ANNOTATION_VIEW_TITLE) + 4];
113 int nag = 0, comment = 0;
115 if (idx < 0 || idx > game[gindex].htotal)
116 return;
118 if (game[gindex].history[idx].comment &&
119 game[gindex].history[idx].comment[0])
120 comment++;
122 if (game[gindex].history[idx].nag[0])
123 nag++;
125 if (!nag && !comment)
126 return;
128 snprintf(buf, sizeof(buf), "%s \"%s\"", ANNOTATION_VIEW_TITLE,
129 game[gindex].history[idx].move);
131 if (comment)
132 show_message(buf, (nag) ? "Any other key to continue" : ANYKEY,
133 (nag) ? "Press 'n' to view NAG" : NULL,
134 (nag) ? view_nag : NULL, (nag) ? (void *)idx : NULL,
135 (nag) ? 'n' : 0, "%s", game[gindex].history[idx].comment);
136 else
137 show_message(buf, "Any other key to continue", "Press 'n' to view NAG",
138 view_nag, (void *)idx, 'n', "%s",
139 "No annotations for this move");
141 return;
144 int get_history_by_index(int n, HISTORY *h)
146 if (n < 0 || n > game[gindex].htotal - 1)
147 return 1;
149 *h = game[gindex].history[n];
150 return 0;
153 void reset_history()
155 game[gindex].hindex = game[gindex].htotal = 0;
156 return;
159 char *history_edit_nag(void *arg)
161 WINDOW *win, *subw;
162 PANEL *panel;
163 ITEM **mitems = NULL;
164 MENU *menu;
165 int i = 0, n;
166 int itemcount = 0;
167 int rows, cols;
168 char *mbuf = NULL;
169 int idx = (int)arg;
171 if (!nags) {
172 if (init_nag())
173 return NULL;
176 i = 0;
177 mitems = Realloc(mitems, (i + 2) * sizeof(ITEM));
178 mitems[i++] = new_item(NONE, NULL);
180 for (n = 0; nags[n].line; n++, i++) {
181 mitems = Realloc(mitems, (i + 2) * sizeof(ITEM));
182 mitems[i] = new_item(nags[n].line, NULL);
185 mitems[i] = NULL;
186 menu = new_menu(mitems);
187 scale_menu(menu, &rows, &cols);
189 win = newwin(rows + 4, cols + 2, CALCPOSY(rows) - 2, CALCPOSX(cols));
190 set_menu_win(menu, win);
191 subw = derwin(win, rows, cols, 2, 1);
192 set_menu_sub(menu, subw);
193 set_menu_fore(menu, A_REVERSE);
194 set_menu_grey(menu, A_NORMAL);
195 set_menu_mark(menu, NULL);
196 set_menu_spacing(menu, 0, 0, 0);
197 menu_opts_off(menu, O_NONCYCLIC|O_SHOWDESC|O_ONEVALUE);
198 post_menu(menu);
199 panel = new_panel(win);
200 cbreak();
201 noecho();
202 keypad(win, TRUE);
203 set_menu_pattern(menu, mbuf);
204 wbkgd(win, CP_MESSAGE_WINDOW);
205 draw_window_title(win, NAG_EDIT_TITLE, cols + 2, CP_HISTORY_TITLE,
206 CP_HISTORY_BORDER);
208 for (i = 0; i < MAX_PGN_NAG; i++) {
209 if (game[gindex].history[idx].nag[i] &&
210 game[gindex].history[idx].nag[i] <= item_count(menu)) {
211 set_item_value(mitems[game[gindex].history[idx].nag[i]], TRUE);
212 set_current_item(menu, mitems[game[gindex].history[idx].nag[i]]);
213 itemcount++;
217 while (1) {
218 int c;
219 char *tmp;
220 char buf[cols - 4];
222 wattron(win, A_REVERSE);
224 for (c = 1; c < (cols + 2) - 1; c++)
225 mvwprintw(win, rows + 2, c, " ");
227 c = item_index(current_item(menu)) + 1;
229 snprintf(buf, sizeof(buf), "Item %i of %i (%i of %i selected) %s", c,
230 item_count(menu), itemcount, MAX_PGN_NAG, NAG_EDIT_PROMPT);
231 draw_prompt(win, rows + 2, cols + 2, buf, CP_MESSAGE_PROMPT);
233 wattroff(win, A_REVERSE);
235 if (!itemcount) {
236 for (i = 0; mitems[i]; i++)
237 set_item_value(mitems[i], FALSE);
239 set_item_value(mitems[0], TRUE);
241 else
242 set_item_value(mitems[0], FALSE);
244 /* This nl() statement needs to be here because NL is recognized
245 * for some reason after the first selection.
247 nl();
248 update_panels();
249 doupdate();
251 c = wgetch(win);
253 switch (c) {
254 int found;
256 case CTRL('G'):
257 help(NAG_EDIT_HELP, ANYKEY, naghelp);
258 break;
259 case KEY_RIGHT:
260 if (!itemcount)
261 break;
263 found = 0;
265 for (i = item_index(current_item(menu)) + 1; mitems[i]; i++) {
266 if (item_value(mitems[i]) == TRUE) {
267 found = i;
268 break;
272 if (!found) {
273 for (i = 0; mitems[i]; i++) {
274 if (item_value(mitems[i]) == TRUE) {
275 found = i;
276 break;
281 set_current_item(menu, mitems[found]);
282 break;
283 case KEY_LEFT:
284 if (!itemcount)
285 break;
287 found = 0;
289 for (i = item_index(current_item(menu)) - 1; i > 0; i--) {
290 if (item_value(mitems[i]) == TRUE) {
291 found = i;
292 break;
296 if (!found) {
297 for (i = item_count(menu) - 1; i > 0; i--) {
298 if (item_value(mitems[i]) == TRUE) {
299 found = i;
300 break;
305 set_current_item(menu, mitems[found]);
306 break;
307 case KEY_HOME:
308 menu_driver(menu, REQ_FIRST_ITEM);
309 break;
310 case KEY_END:
311 menu_driver(menu, REQ_LAST_ITEM);
312 break;
313 case KEY_UP:
314 menu_driver(menu, REQ_UP_ITEM);
315 break;
316 case KEY_DOWN:
317 menu_driver(menu, REQ_DOWN_ITEM);
318 break;
319 case KEY_PPAGE:
320 case CTRL('P'):
321 if (menu_driver(menu, REQ_SCR_UPAGE) == E_REQUEST_DENIED)
322 menu_driver(menu, REQ_FIRST_ITEM);
323 break;
324 case KEY_NPAGE:
325 case CTRL('N'):
326 if (menu_driver(menu, REQ_SCR_DPAGE) == E_REQUEST_DENIED)
327 menu_driver(menu, REQ_LAST_ITEM);
328 break;
329 case ' ':
330 if (item_index(current_item(menu)) == 0 &&
331 item_value(current_item(menu)) == FALSE) {
332 itemcount = 0;
333 break;
336 if (item_value(current_item(menu)) == TRUE) {
337 set_item_value(current_item(menu), FALSE);
338 itemcount--;
340 else {
341 if (itemcount + 1 > MAX_PGN_NAG)
342 break;
344 set_item_value(current_item(menu), TRUE);
345 itemcount++;
348 SET_FLAG(game[gindex].flags, GF_MODIFIED);
349 break;
350 case '\n':
351 goto gotitem;
352 break;
353 case KEY_ESCAPE:
354 goto done;
355 break;
356 default:
357 tmp = menu_pattern(menu);
359 if (tmp && tmp[strlen(tmp) - 1] != c) {
360 menu_driver(menu, REQ_CLEAR_PATTERN);
361 menu_driver(menu, c);
363 else {
364 if (menu_driver(menu, REQ_NEXT_MATCH) == E_NO_MATCH)
365 menu_driver(menu, c);
368 break;
372 gotitem:
373 for (i = 0; i < MAX_PGN_NAG; i++)
374 game[gindex].history[idx].nag[i] = 0;
376 for (i = 0, n = 0; mitems[i] && n < MAX_PGN_NAG; i++) {
377 if (item_value(mitems[i]) == TRUE)
378 game[gindex].history[idx].nag[n++] = i;
381 update_all();
383 done:
384 unpost_menu(menu);
385 free_menu(menu);
387 for (i = 0; mitems[i]; i++)
388 free_item(mitems[i]);
390 free(mitems);
391 del_panel(panel);
392 delwin(subw);
393 delwin(win);
394 return NULL;
397 void add_to_history(HISTORY **h, int *n, int *t, const char *str)
399 HISTORY *history = *h;
400 int idx = *n;
402 history = Realloc(history, (idx + 2) * sizeof(HISTORY));
403 memset(&history[idx], 0, sizeof(HISTORY));
404 strncpy(history[idx].move, str, sizeof(history[idx].move));
405 memset(&history[++idx], 0, sizeof(HISTORY));
407 *n = *t = idx;
408 *h = history;
409 return;
412 void parse_history_move(BOARD b, int idx)
414 int i = 0;
415 int flags = 0;
417 game[gindex].bcaptures = game[gindex].wcaptures = 0;
418 status.turn = game[gindex].openingside;
420 if (TEST_FLAG(game[gindex].flags, GF_PERROR))
421 SET_FLAG(flags, GF_PERROR);
423 if (TEST_FLAG(game[gindex].flags, GF_MODIFIED))
424 SET_FLAG(flags, GF_MODIFIED);
426 if (TEST_FLAG(game[gindex].flags, GF_DELETE))
427 SET_FLAG(flags, GF_DELETE);
429 if (TEST_FLAG(game[gindex].flags, GF_GAMEOVER))
430 SET_FLAG(flags, GF_GAMEOVER);
432 game[gindex].flags = flags;
433 game[gindex].ply = 0;
435 init_board(b);
437 /* FIXME Move numbers and turns. */
438 if (game[gindex].fentag)
439 parse_fen_line(b, game[gindex].tag[game[gindex].fentag].value);
441 for (i = 0; i < idx; i++) {
442 HISTORY h;
444 if (get_history_by_index(i, &h))
445 break;
447 if (parse_move_text(b, h.move)) {
448 cmessage(NULL, ANYKEY, "%s \"%s\"", E_INVALID_MOVE, h.move);
449 break;
452 switch_turn();
455 if (!status.notify && !status.mode == MODE_HISTORY)
456 update_status_notify("%s", GAME_HELP_PROMPT);
458 return;
461 /* FIXME castling */
462 static void cursor_from_history(int idx, int *r, int *c)
464 char *p;
465 int len;
467 p = game[gindex].history[idx].move;
469 if (!p)
470 return;
472 len = strlen(p);
474 if (*p == 'O') {
475 if (len <= 4)
476 *c = 7;
477 else
478 *c = 3;
480 *r = (status.turn == WHITE) ? 8 : 1;
481 return;
484 while (!isdigit(*p))
485 p--;
487 *r = ROWTOINT(*p--);
488 *c = COLTOINT(*p);
490 return;
493 void history_previous(BOARD b, int n, int *r, int *c)
495 if (game[gindex].hindex - n < 0) {
496 if ((n == 2 && movestep == 2) || (n == 1 && movestep == 1))
497 game[gindex].hindex = game[gindex].htotal;
498 else
499 game[gindex].hindex = 0;
501 else
502 game[gindex].hindex -= n;
504 cursor_from_history(game[gindex].hindex, r, c);
505 parse_history_move(b, game[gindex].hindex);
506 return;
509 void history_next(BOARD b, int n, int *r, int *c)
511 if (game[gindex].hindex + n > game[gindex].htotal) {
512 if ((n == 2 && movestep == 2) || (n == 1 && movestep == 1))
513 game[gindex].hindex = 0;
514 else
515 game[gindex].hindex = game[gindex].htotal;
517 else
518 game[gindex].hindex += n;
520 cursor_from_history(game[gindex].hindex, r, c);
521 parse_history_move(b, game[gindex].hindex);
522 return;
525 void init_history(BOARD b)
527 status.mode = MODE_HISTORY;
528 parse_history_move(b, game[gindex].hindex);
529 update_status_window();
530 return;