Update copyright year to 2015.
[cboard.git] / src / tags.c
blob20e78f6bd0e3af033d7e11a7054267af1db9b2a1
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2002-2015 Ben Kibbey <bjk@luxsci.net>
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 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/time.h>
27 #include <time.h>
28 #include <limits.h>
29 #include <errno.h>
31 #ifdef HAVE_STRINGS_H
32 #include <strings.h>
33 #endif
35 #include "common.h"
36 #include "conf.h"
37 #include "colors.h"
38 #include "strings.h"
39 #include "window.h"
40 #include "input.h"
41 #include "misc.h"
42 #include "message.h"
43 #include "menu.h"
44 #include "keys.h"
46 static struct country_codes {
47 char code[4];
48 char country[64];
49 } *ccodes;
51 static int init_country_codes()
53 FILE *fp;
54 char line[LINE_MAX], *s;
55 int cindex = 0;
57 if ((fp = fopen(config.ccfile, "r")) == NULL) {
58 cmessage(ERROR_STR, ANY_KEY_STR, "%s: %s", config.ccfile, strerror(errno));
59 return 1;
62 while ((s = fgets(line, sizeof(line), fp)) != NULL) {
63 char *tmp;
65 if ((tmp = strsep(&s, " ")) == NULL)
66 continue;
68 s = trim(s);
69 tmp = trim(tmp);
71 if (!s || !tmp)
72 continue;
74 ccodes = Realloc(ccodes, (cindex + 2) * sizeof(struct country_codes));
75 strncpy(ccodes[cindex].code, tmp, sizeof(ccodes[cindex].code));
76 ccodes[cindex].code[sizeof(ccodes[cindex].code)-1] = 0;
77 strncpy(ccodes[cindex].country, s, sizeof(ccodes[cindex].country));
78 ccodes[cindex].country[sizeof(ccodes[cindex].country)-1] = 0;
79 cindex++;
82 memset(&ccodes[cindex], '\0', sizeof(struct country_codes));
83 fclose(fp);
84 return 0;
87 static struct menu_item_s **get_cc_items(WIN *win)
89 int i;
90 struct menu_input_s *m = win->data;
91 struct menu_item_s **items = m->items;
93 if (items) {
94 for (i = 0; items[i]; i++)
95 free(items[i]);
98 for (i = 0; ccodes[i].code[0]; i++) {
99 items = Realloc(items, (i+2) * sizeof(struct menu_item_s *));
100 items[i] = Malloc(sizeof(struct menu_item_s));
101 items[i]->name = ccodes[i].code;
102 items[i]->value = ccodes[i].country;
103 items[i]->selected = 0;
106 items[i] = NULL;
107 m->total = i;
108 m->items = items;
109 m->nofree = 1;
110 return items;
113 static void do_cc_help(struct menu_input_s *m)
115 message(_("Country Code Keys"), ANY_KEY_STR, "%s",
117 " UP/DOWN - previous/next menu item\n"
118 " HOME/END - first/last menu item\n"
119 " PGDN/PGUP - next/previous page\n"
120 " a-zA-Z0-9 - jump to item\n"
121 " ENTER - select item\n"
122 " ESCAPE - cancel"
126 static void do_cc_abort(struct menu_input_s *m)
128 pushkey = -1;
131 static void do_cc_finalize(WIN *win)
133 struct input_s *in = win->data;
134 TAG *t = (TAG *)in->arg;
136 set_field_buffer(in->fields[0], 0, t->value);
139 static void do_cc_save(struct menu_input_s *m)
141 struct input_s *in = m->data;
142 TAG *t = (TAG *)in->arg;
143 int len = strlen(m->items[m->selected]->name);
145 t->value = Realloc(t->value, len + 1);
146 strcpy(t->value, m->items[m->selected]->name);
147 pushkey = -1;
150 static void cc_print(WIN *win)
152 struct menu_input_s *m = win->data;
154 mvwprintw(win->w, m->print_line, 1, "%s %-*s", m->item->name,
155 win->cols - 6, m->item->value);
158 static void country_codes(void *arg)
160 struct menu_key_s **keys = NULL;
162 if (!ccodes) {
163 if (init_country_codes())
164 return;
167 add_menu_key(&keys, KEY_F(1), do_cc_help);
168 add_menu_key(&keys, KEY_ESCAPE, do_cc_abort);
169 add_menu_key(&keys, '\n', do_cc_save);
170 construct_menu(0, 0, -1, -1, _("Country Codes"), 0, get_cc_items, keys, arg,
171 cc_print, do_cc_finalize);
172 return;
175 void add_custom_tags(TAG ***t)
177 int i;
178 int total = pgn_tag_total(config.tag);
180 if (!config.tag)
181 return;
183 for (i = 0; i < total; i++)
184 pgn_tag_add(t, config.tag[i]->name, config.tag[i]->value);
186 pgn_tag_sort(*t);
189 static struct menu_item_s **get_tag_items(WIN *win)
191 int i, n;
192 struct menu_input_s *m = win->data;
193 struct menu_item_s **items = m->items;
194 TAG **t = (m->data) ? m->data : gp->tag;
196 if (items) {
197 for (i = 0; items[i]; i++)
198 free(items[i]);
201 n = pgn_tag_total(t);
203 for (i = 0; i < n; i++) {
204 items = Realloc(items, (i+2) * sizeof(struct menu_item_s *));
205 items[i] = Malloc(sizeof(struct menu_item_s));
206 items[i]->name = t[i]->name;
207 items[i]->value = t[i]->value;
208 items[i]->selected = 0;
211 items[i] = NULL;
212 m->total = i;
213 m->items = items;
214 m->nofree = 1;
215 return items;
218 static void edit_tag_add_fen(struct menu_input_s *m)
220 TAG **t = m->data;
221 struct userdata_s *d = gp->data;
222 char *fen = pgn_game_to_fen(gp, d->b);
224 pgn_tag_add(&t, "FEN", fen);
225 free (fen);
226 m->data = t;
229 static void edit_tag_abort(struct menu_input_s *m)
231 TAG **t = m->data;
233 pgn_tag_free(t);
234 m->data = NULL;
235 pushkey = -1;
236 update_status_notify(gp, _("Tag edit aborted."));
239 static void edit_tag_add_finalize(WIN *w)
241 struct input_data_s *in = w->data;
242 char *name = in->moredata;
243 char *value = in->str;
244 struct menu_input_s *m = in->data;
245 TAG **t = m->data;
246 char buf[32];
247 struct tm *tm;
248 time_t now;
249 char *tmp;
250 int count;
252 if (!value || !*value) {
253 if (strcasecmp(name, "Round") == 0)
254 value = "-";
255 else if (strcasecmp(name, "Result") == 0)
256 value = "*";
257 else if (strcasecmp(name, "Date") == 0) {
258 time(&now);
259 tm = localtime(&now);
260 strftime(buf, sizeof(buf), PGN_TIME_FORMAT, tm);
261 value = buf;
263 else
264 value = "?";
267 tmp = trim_multi(value);
268 count = pgn_tag_total(t);
269 pgn_tag_add(&t, name, tmp);
270 free(tmp);
271 m->data = t;
273 if (count != pgn_tag_total(t))
274 m->selected = m->total;
276 if (in->str)
277 free(in->str);
279 free(name);
280 free(in);
281 pushkey = REFRESH_MENU;
284 static void set_menu_stuff(TAG **t, char *name, char **init, int *type,
285 int *lines, input_func **func, wint_t *key, char **eprompt, void **arg)
287 int n;
288 char *p;
290 if ((n = pgn_tag_find(t, name)) != -1) {
291 p = t[n]->value;
292 *init = p;
295 if (strcasecmp(name, "Date") == 0) {
296 *type = FIELD_TYPE_PGN_DATE;
297 *lines = 1;
299 else if (strcasecmp(name, "Site") == 0) {
300 *func = country_codes;
301 *key = CTRL_KEY('t');
302 *eprompt = _("Type CTRL-t for country codes");
303 *arg = t[n];
305 else if (strcasecmp(name, "Round") == 0) {
306 *type = FIELD_TYPE_PGN_ROUND;
307 *lines = 1;
309 else if (strcasecmp(name, "Result") == 0) {
310 *type = FIELD_TYPE_PGN_RESULT;
311 *lines = 1;
315 static void edit_tag_value(struct menu_input_s *m)
317 char buf[COLS - 4];
318 struct input_data_s *in = Calloc(1, sizeof(struct input_data_s));
319 char *init = NULL;
320 TAG **t = m->data;
321 char *name;
322 int type = -1;
323 input_func *func = NULL;
324 wint_t key = 0;
325 char *eprompt = NULL;
326 void *arg = NULL;
327 int lines = MAX_PGN_LINE_LEN / INPUT_WIDTH;
328 wchar_t *wc;
330 in->data = m;
331 name = strdup(t[m->selected]->name);
332 in->moredata = name;
333 in->efunc = edit_tag_add_finalize;
334 wc = str_to_wchar (name);
335 snprintf(buf, sizeof(buf), "%s \"%ls\"", _("Editing Tag"), wc);
336 free (wc);
337 set_menu_stuff(t, name, &init, &type, &lines, &func, &key, &eprompt, &arg);
338 construct_input(buf, init, lines, 0, eprompt, func, arg, key, in, -1, type);
341 static void edit_tag_add_name_finalize(WIN *w)
343 struct input_data_s *in = w->data;
344 struct input_data_s *inv;
345 struct menu_input_s *m = in->data;
346 TAG **t = m->data;
347 char buf[COLS - 4];
348 char *init = NULL;
349 char *name;
350 wint_t key = 0;
351 int type = -1;
352 input_func *func = NULL;
353 char *eprompt = NULL;
354 int lines = MAX_PGN_LINE_LEN / INPUT_WIDTH;
355 void *arg = NULL;
356 wchar_t *wc;
358 if (!in->str)
359 return;
361 name = strdup(in->str);
362 inv = Calloc(1, sizeof(struct input_data_s));
363 inv->efunc = edit_tag_add_finalize;
364 inv->data = in->data;
365 inv->moredata = name;
366 free(in->str);
367 free(in);
368 wc = str_to_wchar (name);
369 snprintf(buf, sizeof(buf), "%s \"%ls\"", _("Editing Tag"), wc);
370 free (wc);
371 set_menu_stuff(t, name, &init, &type, &lines, &func, &key, &eprompt, &arg);
372 construct_input(buf, init, lines, 0, eprompt, func, arg, key, inv, -1, type);
375 static void edit_tag_add(struct menu_input_s *m)
377 struct input_data_s *in = Calloc(1, sizeof(struct input_s));
379 in->data = m;
380 in->efunc = edit_tag_add_name_finalize;
381 construct_input(_("New Tag Name"), NULL, 1, 1, NULL, NULL, NULL, 0, in, -1,
382 FIELD_TYPE_PGN_TAG_NAME);
385 static void edit_tag_remove(struct menu_input_s *m)
387 TAG **data = NULL;
388 TAG **t = m->data;
389 int i, n = pgn_tag_total(t);
391 if (m->selected < 7) {
392 cmessage(NULL, ANY_KEY_STR, "%s", _ ("Cannot remove the Seven Tag Roster"));
393 return;
396 for (i = 0; i < n; i++) {
397 if (i == m->selected)
398 continue;
400 pgn_tag_add(&data, t[i]->name, t[i]->value);
403 pgn_tag_free(t);
404 m->data = data;
405 m->update = 1;
408 static void edit_tag_add_custom(struct menu_input_s *m)
410 TAG **t = m->data;
412 add_custom_tags(&t);
413 m->data = t;
416 static void edit_tag_save(struct menu_input_s *m)
418 TAG **t = m->data;
419 struct userdata_s *d = gp->data;
421 if (!m->data)
422 return;
424 pgn_tag_free(gp->tag);
425 pgn_tag_sort(t);
426 gp->tag = t;
427 pushkey = -1;
428 SET_FLAG(d->flags, CF_MODIFIED);
431 * In case of editing a FEN tag. Must not be MODE_PLAY. Also updates the
432 * games ply count for the fifty move draw rule.
434 if (d->mode != MODE_PLAY)
435 pgn_board_update(gp, d->b, gp->hindex);
438 static void edit_tag_help(struct menu_input_s *m)
440 message(_("Tag Editing Keys"), ANY_KEY_STR,
442 " UP/DOWN - previous/next menu item\n"
443 " HOME/END - first/last menu item\n"
444 " PGDN/PGUP - next/previous page\n"
445 " a-zA-Z0-9 - jump to item\n"
446 " ENTER - edit select item\n"
447 " CTRL-a - add an entry\n"
448 " CTRL-f - add FEN tag from current position\n"
449 " CTRL-r - remove selected entry\n"
450 " CTRL-t - add custom tags\n"
451 " CTRL-x - quit with changes\n"
452 " ESCAPE - quit without changes"
456 static void view_tag_help(struct menu_input_s *m)
458 message(_("Tag Viewing Keys"), ANY_KEY_STR,
460 " UP/DOWN - previous/next menu item\n"
461 " HOME/END - first/last menu item\n"
462 " PGDN/PGUP - next/previous page\n"
463 " a-zA-Z0-9 - jump to item\n"
464 " ENTER - view selected item\n"
465 " ESCAPE - cancel"
469 static void view_tag_quit(struct menu_input_s *m)
471 pushkey = -1;
474 static void view_tag_value(struct menu_input_s *m)
476 struct menu_item_s *item = m->items[m->selected];
477 char buf[COLS - 4];
478 wchar_t *wc = str_to_wchar (item->name);
480 snprintf(buf, sizeof(buf), "%s \"%ls\"", _("Viewing Tag"), wc);
481 free (wc);
482 construct_message(buf, ANY_KEY_STR, 0, 1, NULL,
483 NULL, NULL, NULL, 0, 0, item->value);
486 static void tag_print(WIN *win)
488 int i, len = 0, n;
489 struct menu_input_s *m = win->data;
490 wchar_t *wc;
492 for (i = 0; m->items[i]; i++) {
493 wc = translate_tag_name (m->items[i]->name);
494 n = wcslen (wc);
495 free (wc);
496 if (len < n)
497 len = n;
500 wc = translate_tag_name(m->item->name);
501 mvwprintw(win->w, m->print_line, 1, "%ls", wc);
503 for (n = wcslen(wc) + 1; n <= len; n++)
504 mvwprintw(win->w, m->print_line, n, "%c", '.');
506 free (wc);
507 mvwprintw(win->w, m->print_line, n, ": ");
508 i = win->cols - n - 3;
509 wc = str_etc (m->item->value, i, 0);
510 mvwprintw(win->w, m->print_line, n + 2, "%-*ls", i, wc);
511 free (wc);
513 if (m->update) {
514 wmove(stdscr, 0, 0);
515 wclrtobot(stdscr);
516 update_all(gp);
517 m->update = 0;
521 void edit_tags(GAME g, BOARD b, int edit)
523 struct menu_key_s **keys = NULL;
524 TAG **data = NULL;
525 int i;
527 if (edit) {
528 for (i = 0; gp->tag[i]; i++)
529 pgn_tag_add(&data, gp->tag[i]->name, gp->tag[i]->value);
531 add_menu_key(&keys, '\n', edit_tag_value);
532 add_menu_key(&keys, CTRL_KEY('f'), edit_tag_add_fen);
533 add_menu_key(&keys, CTRL_KEY('a'), edit_tag_add);
534 add_menu_key(&keys, CTRL_KEY('r'), edit_tag_remove);
535 add_menu_key(&keys, CTRL_KEY('t'), edit_tag_add_custom);
536 add_menu_key(&keys, CTRL_KEY('x'), edit_tag_save);
537 add_menu_key(&keys, KEY_ESCAPE, edit_tag_abort);
538 add_menu_key(&keys, KEY_F(1), edit_tag_help);
540 else {
541 add_menu_key(&keys, '\n', view_tag_value);
542 data = gp->tag;
543 add_menu_key(&keys, KEY_ESCAPE, view_tag_quit);
544 add_menu_key(&keys, KEY_F(1), view_tag_help);
547 construct_menu(0, 0, -1, -1,
548 (edit) ? _("Editing Roster Tags") : _("Viewing Roster Tags"),
549 0, get_tag_items, keys, data, tag_print, NULL);