Update copyright year.
[cboard.git] / src / tags.c
blobdeaf3f85124e3aabdebc93bedb77a4a58d0112e9
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2002-2024 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"
45 #include "rcfile.h"
47 static struct country_codes
49 char code[4];
50 char country[64];
51 } *ccodes;
53 static int
54 init_country_codes ()
56 FILE *fp;
57 char line[LINE_MAX], *s;
58 int cindex = 0;
60 if ((fp = fopen (config.ccfile, "r")) == NULL)
62 cmessage (ERROR_STR, ANY_KEY_STR, "%s: %s", config.ccfile,
63 strerror (errno));
64 return 1;
67 while ((s = fgets (line, sizeof (line), fp)) != NULL)
69 char *tmp;
71 if ((tmp = strsep (&s, " ")) == NULL)
72 continue;
74 s = trim (s);
75 tmp = trim (tmp);
77 if (!s || !tmp)
78 continue;
80 ccodes = Realloc (ccodes, (cindex + 2) * sizeof (struct country_codes));
81 strncpy (ccodes[cindex].code, tmp, sizeof (ccodes[cindex].code));
82 ccodes[cindex].code[sizeof (ccodes[cindex].code) - 1] = 0;
83 strncpy (ccodes[cindex].country, s, sizeof (ccodes[cindex].country));
84 ccodes[cindex].country[sizeof (ccodes[cindex].country) - 1] = 0;
85 cindex++;
88 memset (&ccodes[cindex], '\0', sizeof (struct country_codes));
89 fclose (fp);
90 return 0;
93 static struct menu_item_s **
94 get_cc_items (WIN * win)
96 int i;
97 struct menu_input_s *m = win->data;
98 struct menu_item_s **items = m->items;
100 if (items)
102 for (i = 0; items[i]; i++)
103 free (items[i]);
106 for (i = 0; ccodes[i].code[0]; i++)
108 items = Realloc (items, (i + 2) * sizeof (struct menu_item_s *));
109 items[i] = Malloc (sizeof (struct menu_item_s));
110 items[i]->name = ccodes[i].code;
111 items[i]->value = ccodes[i].country;
112 items[i]->selected = 0;
115 if (items)
116 items[i] = NULL;
118 m->total = i;
119 m->items = items;
120 m->nofree = 1;
121 return items;
124 static void
125 do_cc_help (struct menu_input_s *m)
127 message (_("Country Code Keys"), ANY_KEY_STR, "%s",
128 _(" UP/DOWN - previous/next menu item\n"
129 " HOME/END - first/last menu item\n"
130 " PGDN/PGUP - next/previous page\n"
131 " a-zA-Z0-9 - jump to item\n"
132 " ENTER - select item\n" " ESCAPE - cancel"));
135 static void
136 do_cc_abort (struct menu_input_s *m)
138 pushkey = -1;
141 static void
142 do_cc_finalize (WIN * win)
144 struct input_s *in = win->data;
145 TAG *t = (TAG *) in->arg;
147 set_field_buffer (in->fields[0], 0, t->value);
150 static void
151 do_cc_save (struct menu_input_s *m)
153 struct input_s *in = m->data;
154 TAG *t = (TAG *) in->arg;
155 int len = strlen (m->items[m->selected]->name);
157 t->value = Realloc (t->value, len + 1);
158 strcpy (t->value, m->items[m->selected]->name);
159 pushkey = -1;
162 static void
163 cc_print (WIN * win)
165 struct menu_input_s *m = win->data;
167 mvwprintw (win->w, m->print_line, 1, "%s %-*s", m->item->name,
168 win->cols - 6, m->item->value);
171 static void
172 country_codes (void *arg)
174 struct menu_key_s **keys = NULL;
176 if (!ccodes)
178 if (init_country_codes ())
179 return;
182 add_menu_key (&keys, keycode_lookup (global_keys, do_global_help),
183 do_cc_help);
184 add_menu_key (&keys, KEY_ESCAPE, do_cc_abort);
185 add_menu_key (&keys, '\n', do_cc_save);
186 construct_menu (0, 0, -1, -1, _("Country Codes"), 0, get_cc_items, keys,
187 arg, cc_print, do_cc_finalize, NULL);
188 return;
191 void
192 add_custom_tags (TAG *** t)
194 int i;
195 int total = pgn_tag_total (config.tag);
197 if (!config.tag)
198 return;
200 for (i = 0; i < total; i++)
201 pgn_tag_add (t, config.tag[i]->name, config.tag[i]->value);
203 pgn_tag_sort (*t);
206 static struct menu_item_s **
207 get_tag_items (WIN * win)
209 int i, n;
210 struct menu_input_s *m = win->data;
211 struct menu_item_s **items = m->items;
212 TAG **t = (m->data) ? m->data : gp->tag;
214 if (items)
216 for (i = 0; items[i]; i++)
217 free (items[i]);
220 n = pgn_tag_total (t);
222 for (i = 0; i < n; i++)
224 items = Realloc (items, (i + 2) * sizeof (struct menu_item_s *));
225 items[i] = Malloc (sizeof (struct menu_item_s));
226 items[i]->name = t[i]->name;
227 items[i]->value = t[i]->value;
228 items[i]->selected = 0;
231 if (items)
232 items[i] = NULL;
234 m->total = i;
235 m->items = items;
236 m->nofree = 1;
237 return items;
240 static void
241 edit_tag_add_fen (struct menu_input_s *m)
243 TAG **t = m->data;
244 struct userdata_s *d = gp->data;
245 char *fen = pgn_game_to_fen (gp, d->b);
247 pgn_tag_add (&t, (char *) "FEN", fen);
248 free (fen);
249 m->data = t;
252 static void
253 edit_tag_abort (struct menu_input_s *m)
255 TAG **t = m->data;
257 pgn_tag_free (t);
258 m->data = NULL;
259 pushkey = -1;
260 update_status_notify (gp, _("Tag edit aborted."));
263 static void
264 edit_tag_add_finalize (WIN * w)
266 struct input_data_s *in = w->data;
267 char *name = in->moredata;
268 char *value = in->str;
269 struct menu_input_s *m = in->data;
270 TAG **t = m->data;
271 char buf[32];
272 struct tm *tm;
273 time_t now;
274 char *tmp;
275 int count;
277 if (!value || !*value)
279 if (strcasecmp (name, "Round") == 0)
280 value = (char *) "-";
281 else if (strcasecmp (name, "Result") == 0)
282 value = (char *) "*";
283 else if (strcasecmp (name, "Date") == 0)
285 time (&now);
286 tm = localtime (&now);
287 strftime (buf, sizeof (buf), PGN_TIME_FORMAT, tm);
288 value = buf;
290 else
291 value = (char *) "?";
294 tmp = trim_multi (value);
295 count = pgn_tag_total (t);
296 pgn_tag_add (&t, name, tmp);
297 free (tmp);
298 m->data = t;
300 if (count != pgn_tag_total (t))
301 m->selected = m->total;
303 if (in->str)
304 free (in->str);
306 free (name);
307 free (in);
308 pushkey = REFRESH_MENU;
311 static void
312 set_menu_stuff (TAG ** t, char *name, char **init, int *type,
313 int *lines, input_func ** func, wint_t * key, char **eprompt,
314 void **arg)
316 int n;
317 char *p;
319 if ((n = pgn_tag_find (t, name)) != -1)
321 p = t[n]->value;
322 *init = p;
325 if (strcasecmp (name, "Date") == 0)
327 *type = FIELD_TYPE_PGN_DATE;
328 *lines = 1;
330 else if (strcasecmp (name, "Site") == 0)
332 *func = country_codes;
333 *key = CTRL_KEY ('t');
334 *eprompt = _("Type CTRL-t for country codes");
335 *arg = t[n];
337 else if (strcasecmp (name, "Round") == 0)
339 *type = FIELD_TYPE_PGN_ROUND;
340 *lines = 1;
342 else if (strcasecmp (name, "Result") == 0)
344 *type = FIELD_TYPE_PGN_RESULT;
345 *lines = 1;
349 static void
350 edit_tag_value (struct menu_input_s *m)
352 char buf[COLS - 4];
353 struct input_data_s *in = Calloc (1, sizeof (struct input_data_s));
354 char *init = NULL;
355 TAG **t = m->data;
356 char *name;
357 int type = -1;
358 input_func *func = NULL;
359 wint_t key = 0;
360 char *eprompt = NULL;
361 void *arg = NULL;
362 int lines = MAX_PGN_LINE_LEN / INPUT_WIDTH;
363 wchar_t *wc;
365 in->data = m;
366 name = strdup (t[m->selected]->name);
367 in->moredata = name;
368 in->efunc = edit_tag_add_finalize;
369 wc = str_to_wchar (name);
370 snprintf (buf, sizeof (buf), "%s \"%ls\"", _("Editing Tag"), wc);
371 free (wc);
372 set_menu_stuff (t, name, &init, &type, &lines, &func, &key, &eprompt, &arg);
373 construct_input (buf, init, lines, 0, eprompt, func, arg, key, in, -1,
374 NULL, type);
377 static void
378 edit_tag_add_name_finalize (WIN * w)
380 struct input_data_s *in = w->data;
381 struct input_data_s *inv;
382 struct menu_input_s *m = in->data;
383 TAG **t = m->data;
384 char buf[COLS - 4];
385 char *init = NULL;
386 char *name;
387 wint_t key = 0;
388 int type = -1;
389 input_func *func = NULL;
390 char *eprompt = NULL;
391 int lines = MAX_PGN_LINE_LEN / INPUT_WIDTH;
392 void *arg = NULL;
393 wchar_t *wc;
395 if (!in->str)
396 return;
398 name = strdup (in->str);
399 inv = Calloc (1, sizeof (struct input_data_s));
400 inv->efunc = edit_tag_add_finalize;
401 inv->data = in->data;
402 inv->moredata = name;
403 free (in->str);
404 free (in);
405 wc = str_to_wchar (name);
406 snprintf (buf, sizeof (buf), "%s \"%ls\"", _("Editing Tag"), wc);
407 free (wc);
408 set_menu_stuff (t, name, &init, &type, &lines, &func, &key, &eprompt, &arg);
409 construct_input (buf, init, lines, 0, eprompt, func, arg, key, inv, -1,
410 NULL, type);
413 static void
414 edit_tag_add (struct menu_input_s *m)
416 struct input_data_s *in = Calloc (1, sizeof (struct input_s));
418 in->data = m;
419 in->efunc = edit_tag_add_name_finalize;
420 construct_input (_("New Tag Name"), NULL, 1, 1, NULL, NULL, NULL, 0, in, -1,
421 NULL, FIELD_TYPE_PGN_TAG_NAME);
424 static void
425 edit_tag_remove (struct menu_input_s *m)
427 TAG **data = NULL;
428 TAG **t = m->data;
429 int i, n = pgn_tag_total (t);
431 if (m->selected < 7)
433 cmessage (NULL, ANY_KEY_STR, "%s",
434 _("Cannot remove the Seven Tag Roster"));
435 return;
438 for (i = 0; i < n; i++)
440 if (i == m->selected)
441 continue;
443 pgn_tag_add (&data, t[i]->name, t[i]->value);
446 pgn_tag_free (t);
447 m->data = data;
448 m->update = 1;
451 static void
452 edit_tag_add_custom (struct menu_input_s *m)
454 TAG **t = m->data;
456 add_custom_tags (&t);
457 m->data = t;
460 static void
461 edit_tag_save (struct menu_input_s *m)
463 TAG **t = m->data;
464 struct userdata_s *d = gp->data;
466 if (!m->data)
467 return;
469 pgn_tag_free (gp->tag);
470 pgn_tag_sort (t);
471 gp->tag = t;
472 pushkey = -1;
473 SET_FLAG (d->flags, CF_MODIFIED);
476 * In case of editing a FEN tag. Must not be MODE_PLAY. Also updates the
477 * games ply count for the fifty move draw rule.
479 if (d->mode != MODE_PLAY)
480 pgn_board_update (gp, d->b, gp->hindex);
483 static void
484 edit_tag_help (struct menu_input_s *m)
486 message (_("Tag Editing Keys"), ANY_KEY_STR,
487 _(" UP/DOWN - previous/next menu item\n"
488 " HOME/END - first/last menu item\n"
489 " PGDN/PGUP - next/previous page\n"
490 " a-zA-Z0-9 - jump to item\n"
491 " ENTER - edit select item\n"
492 " CTRL-a - add an entry\n"
493 " CTRL-f - add FEN tag from current position\n"
494 " CTRL-r - remove selected entry\n"
495 " CTRL-t - add custom tags\n"
496 " CTRL-x - quit with changes\n"
497 " ESCAPE - quit without changes"));
500 static void
501 view_tag_help (struct menu_input_s *m)
503 message (_("Tag Viewing Keys"), ANY_KEY_STR,
504 _(" UP/DOWN - previous/next menu item\n"
505 " HOME/END - first/last menu item\n"
506 " PGDN/PGUP - next/previous page\n"
507 " a-zA-Z0-9 - jump to item\n"
508 " ENTER - view selected item\n" " ESCAPE - cancel"));
511 static void
512 view_tag_quit (struct menu_input_s *m)
514 pushkey = -1;
517 static void
518 view_tag_value (struct menu_input_s *m)
520 struct menu_item_s *item = m->items[m->selected];
521 char buf[COLS - 4];
522 wchar_t *wc = str_to_wchar (item->name);
524 snprintf (buf, sizeof (buf), "%s \"%ls\"", _("Viewing Tag"), wc);
525 free (wc);
526 construct_message (buf, ANY_KEY_STR, 0, 1, NULL,
527 NULL, NULL, NULL, 0, 0, NULL, "%s", item->value);
530 static void
531 tag_print (WIN * win)
533 int i, len = 0, n;
534 struct menu_input_s *m = win->data;
535 wchar_t *wc;
537 for (i = 0; m->items[i]; i++)
539 wc = translate_tag_name (m->items[i]->name);
540 n = wcslen (wc);
541 free (wc);
542 if (len < n)
543 len = n;
546 wc = translate_tag_name (m->item->name);
547 mvwprintw (win->w, m->print_line, 1, "%ls", wc);
549 for (n = wcslen (wc) + 1; n <= len; n++)
550 mvwprintw (win->w, m->print_line, n, "%c", '.');
552 free (wc);
553 mvwprintw (win->w, m->print_line, n, ": ");
554 i = win->cols - n - 3;
555 wc = str_etc (m->item->value, i, 0);
556 mvwprintw (win->w, m->print_line, n + 2, "%-*ls", i, wc);
557 free (wc);
559 if (m->update)
561 wmove (stdscr, 0, 0);
562 wclrtobot (stdscr);
563 update_all (gp);
564 m->update = 0;
568 void
569 edit_tags (GAME g, BOARD b, int edit)
571 struct menu_key_s **keys = NULL;
572 TAG **data = NULL;
573 int i;
575 if (edit)
577 for (i = 0; gp->tag[i]; i++)
578 pgn_tag_add (&data, gp->tag[i]->name, gp->tag[i]->value);
580 add_menu_key (&keys, '\n', edit_tag_value);
581 add_menu_key (&keys, CTRL_KEY ('f'), edit_tag_add_fen);
582 add_menu_key (&keys, CTRL_KEY ('a'), edit_tag_add);
583 add_menu_key (&keys, CTRL_KEY ('r'), edit_tag_remove);
584 add_menu_key (&keys, CTRL_KEY ('t'), edit_tag_add_custom);
585 add_menu_key (&keys, CTRL_KEY ('x'), edit_tag_save);
586 add_menu_key (&keys, KEY_ESCAPE, edit_tag_abort);
587 add_menu_key (&keys, keycode_lookup (global_keys, do_global_help),
588 edit_tag_help);
590 else
592 add_menu_key (&keys, '\n', view_tag_value);
593 data = gp->tag;
594 add_menu_key (&keys, KEY_ESCAPE, view_tag_quit);
595 add_menu_key (&keys, keycode_lookup (global_keys, do_global_help),
596 view_tag_help);
599 construct_menu (0, 0, -1, -1,
600 (edit) ? _("Editing Roster Tags") :
601 _("Viewing Roster Tags"), 0, get_tag_items, keys, data,
602 tag_print, NULL, NULL);