summaryrefslogtreecommitdiff
path: root/apps/plugins/goban/goban.c
diff options
context:
space:
mode:
authorMustapha Senhaji <moos@rockbox.org>2009-02-11 16:03:17 +0000
committerMustapha Senhaji <moos@rockbox.org>2009-02-11 16:03:17 +0000
commitf2d5c3532fd21c04e77aa86732742b578283b697 (patch)
tree1c55cb7e69ed11391f77187c86ff8c435ed85877 /apps/plugins/goban/goban.c
parent21f0c9a2829415f52b64cbdf965b01525e78f17a (diff)
downloadrockbox-f2d5c3532fd21c04e77aa86732742b578283b697.tar.gz
rockbox-f2d5c3532fd21c04e77aa86732742b578283b697.zip
New game plugin by Joshua Simmons FS#7369: Goban plugin, Go/Igo/Weiqi/Baduk game recorder and viewer.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19972 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/goban/goban.c')
-rw-r--r--apps/plugins/goban/goban.c1232
1 files changed, 1232 insertions, 0 deletions
diff --git a/apps/plugins/goban/goban.c b/apps/plugins/goban/goban.c
new file mode 100644
index 0000000000..e5943c0ba6
--- /dev/null
+++ b/apps/plugins/goban/goban.c
@@ -0,0 +1,1232 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007-2009 Joshua Simmons <mud at majidejima dot com>
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "plugin.h"
23#include "lib/playback_control.h"
24#include "lib/configfile.h"
25
26PLUGIN_HEADER
27
28#include "goban.h"
29#include "game.h"
30#include "board.h"
31#include "display.h"
32#include "sgf.h"
33#include "sgf_storage.h"
34#include "types.h"
35#include "util.h"
36
37enum play_mode_t play_mode = MODE_PLAY;
38
39#if defined(GBN_BUTTON_NAV_MODE)
40
41#define NAV_MODE_BOARD 0
42#define NAV_MODE_TREE 1
43
44int nav_mode = NAV_MODE_BOARD;
45
46#endif
47
48#define PARSE_STACK_BUFFER_SIZE (max(MAX_BOARD_SIZE * MAX_BOARD_SIZE * sizeof(unsigned short), 50 * sizeof(int)))
49
50/* used in SGF file parsing and outputting as well as in liberty counting
51 and capturing/uncapturing */
52struct stack_t parse_stack;
53char parse_stack_buffer[PARSE_STACK_BUFFER_SIZE];
54
55static void global_setup (void);
56static void global_cleanup (void);
57
58static bool do_main_menu (void);
59static void do_gameinfo_menu (void);
60static enum prop_type_t menu_selection_to_prop (int selection);
61static void do_context_menu (void);
62static void do_options_menu (void);
63
64static bool do_comment_edit (void);
65static bool do_zoom (void);
66static void set_defaults (void);
67
68bool auto_show_comments = true;
69bool disable_shutdown = false;
70unsigned int autosave_time = 0;
71unsigned int autosave_counter = 0;
72
73#define SETTINGS_VERSION 2
74#define SETTINGS_MIN_VERSION 1
75#define SETTINGS_FILENAME "goban.cfg"
76
77static struct configdata config[] =
78{ /* INT in MAX_INT_SIZE is intersection, not integer */
79 {TYPE_INT, 0, MAX_INT_SIZE,
80 { .int_p = &saved_circle_size },
81 "stone size", NULL},
82 {TYPE_BOOL, 0, 1,
83 { .bool_p = &draw_variations },
84 "draw variations",
85 NULL},
86 {TYPE_BOOL, 0, 1,
87 { .bool_p = &auto_show_comments },
88 "auto show comments",
89 NULL},
90 {TYPE_BOOL, 0, 1,
91 { .bool_p = &disable_shutdown },
92 "disable shutdown",
93 NULL},
94 {TYPE_INT, 0, MAX_AUTOSAVE,
95 { .int_p = &autosave_time },
96 "autosave time",
97 NULL}
98};
99
100static void
101set_defaults (void)
102{
103 saved_circle_size = 0;
104 draw_variations = true;
105 auto_show_comments = true;
106
107 disable_shutdown = false;
108 autosave_time = 7;
109}
110
111static void
112komi_formatter (char *dest, size_t size, int menu_item, const char *unknown)
113{
114 (void) unknown;
115 snprint_fixed (dest, size, menu_item);
116}
117
118static void
119ruleset_formatter (char *dest, size_t size, int menu_item, const char *unknown)
120{
121 (void) unknown;
122 rb->snprintf (dest, size, "%s", ruleset_names[menu_item]);
123}
124
125static void
126autosave_formatter (char *dest, size_t size, int menu_item, const char *
127unknown)
128{
129 (void) unknown;
130 if (menu_item == 0)
131 {
132 rb->snprintf (dest, size, "Off");
133 }
134 else
135 {
136 rb->snprintf (dest, size, "%d minute%s", menu_item,
137 menu_item == 1 ? "" : "s");
138 }
139}
140
141static void
142time_formatter (char *dest, size_t size, int menu_item, const char *unknown)
143{
144 int time_values[4]; /* days hours minutes seconds */
145 int min_set, max_set;
146 int temp;
147
148 (void) unknown;
149
150 time_values[0] = menu_item / (24 * 60 * 60);
151 menu_item %= (24 * 60 * 60);
152 time_values[1] = menu_item / (60 * 60);
153 menu_item %= (60 * 60);
154 time_values[2] = menu_item / 60;
155 time_values[3] = menu_item % 60;
156
157 min_set = 500;
158 max_set = -1;
159 int i;
160 for (i = 0;
161 (unsigned int) i < (sizeof (time_values) / sizeof (time_values[0]));
162 ++i)
163 {
164 if (time_values[i])
165 {
166 if (i < min_set)
167 {
168 min_set = i;
169 }
170
171 if (i > max_set)
172 {
173 max_set = i;
174 }
175 }
176 }
177
178 if (max_set == -1)
179 {
180 rb->snprintf (dest, size, "0");
181 return;
182 }
183
184 for (i = min_set; i <= 3; ++i)
185 {
186 if (i <= max_set)
187 {
188 if (i == 0 || i == 1 || i == min_set)
189 {
190 rb->snprintf (dest, size, "%d", time_values[i]);
191 }
192 else
193 {
194 rb->snprintf (dest, size, "%02d", time_values[i]);
195 }
196 temp = rb->strlen (dest);
197 dest += temp;
198 size -= temp;
199 }
200 else if (i != 3)
201 {
202 continue;
203 }
204
205 if (i == 0) /* days */
206 {
207 rb->snprintf (dest, size, " d ");
208 }
209 else if (i == 3) /* seconds, print the final units */
210 {
211 if (min_set == 0 || min_set == 1)
212 {
213 rb->snprintf (dest, size, " h");
214 }
215 else if (min_set == 2)
216 {
217 rb->snprintf (dest, size, " m");
218 }
219 else
220 {
221 rb->snprintf (dest, size, " s");
222 }
223 }
224 else if (i != max_set)
225 {
226 rb->snprintf (dest, size, ":");
227 }
228
229 temp = rb->strlen (dest);
230 dest += temp;
231 size -= temp;
232 }
233}
234
235enum plugin_status
236plugin_start (const void *parameter)
237{
238 int btn;
239 int temp;
240
241 rb->mkdir ("/sgf");
242
243 global_setup ();
244
245#ifdef GBN_TEST
246 run_tests ();
247 return 0;
248#endif
249
250 if (!(parameter && load_game (parameter)))
251 {
252 if (parameter)
253 {
254 rb->splashf (2 * HZ, "Loading %s failed.", (char *) parameter);
255 }
256
257 if (!load_game (DEFAULT_SAVE))
258 {
259 rb->strcpy (save_file, DEFAULT_SAVE);
260
261 if (!setup_game (MAX_BOARD_SIZE, MAX_BOARD_SIZE, 0, 0))
262 {
263 return PLUGIN_ERROR;
264 }
265 }
266 }
267 else
268 {
269 /* game loaded */
270 if (rb->strcmp (save_file, DEFAULT_SAVE))
271 {
272 /* delete the scratch file if we loaded a game and it wasn't
273 * from the scratch file
274 */
275 rb->remove (DEFAULT_SAVE);
276 }
277 }
278
279 draw_screen_display ();
280
281 autosave_counter = 0;
282 for (;;)
283 {
284 btn = rb->button_get_w_tmo (HZ * 30);
285
286 if (disable_shutdown)
287 {
288 /* tell rockbox we're not idle */
289 rb->reset_poweroff_timer ();
290 }
291
292 bool is_idle = false;
293
294 switch (btn)
295 {
296
297#if defined(GBN_BUTTON_NAV_MODE)
298 case GBN_BUTTON_NAV_MODE:
299 case GBN_BUTTON_NAV_MODE | BUTTON_REPEAT:
300 if (nav_mode == NAV_MODE_TREE)
301 {
302 nav_mode = NAV_MODE_BOARD;
303 rb->splash (2 * HZ / 3, "board navigation mode");
304 draw_screen_display ();
305 }
306 else
307 {
308 nav_mode = NAV_MODE_TREE;
309 rb->splash (2 * HZ / 3, "tree navigation mode");
310 draw_screen_display ();
311 }
312 break;
313#endif
314
315#if defined(GBN_BUTTON_ADVANCE)
316 case GBN_BUTTON_ADVANCE:
317 case GBN_BUTTON_ADVANCE | BUTTON_REPEAT:
318 if (has_more_nodes_sgf ())
319 {
320 if (!redo_node_sgf ())
321 {
322 rb->splash (2 * HZ, "redo failed");
323 }
324 draw_screen_display ();
325 }
326 break;
327#endif
328
329#if defined(GBN_BUTTON_RETREAT)
330 case GBN_BUTTON_RETREAT:
331 case GBN_BUTTON_RETREAT | BUTTON_REPEAT:
332 if (has_prev_nodes_sgf ())
333 {
334 if (!undo_node_sgf ())
335 {
336 rb->splash (3 * HZ / 2, "Undo Failed");
337 }
338 draw_screen_display ();
339 }
340 break;
341#endif
342
343 case GBN_BUTTON_PLAY:
344 if (play_mode == MODE_PLAY || play_mode == MODE_FORCE_PLAY)
345 {
346 if (!play_move_sgf (cursor_pos, current_player))
347 {
348 rb->splash (HZ / 3, "Illegal Move");
349 }
350 }
351 else if (play_mode == MODE_ADD_BLACK)
352 {
353 if (!add_stone_sgf (cursor_pos, BLACK))
354 {
355 rb->splash (HZ / 3, "Illegal");
356 }
357 }
358 else if (play_mode == MODE_ADD_WHITE)
359 {
360 if (!add_stone_sgf (cursor_pos, WHITE))
361 {
362 rb->splash (HZ / 3, "Illegal");
363 }
364 }
365 else if (play_mode == MODE_REMOVE)
366 {
367 if (!add_stone_sgf (cursor_pos, EMPTY))
368 {
369 rb->splash (HZ / 3, "Illegal");
370 }
371 }
372 else if (play_mode == MODE_MARK)
373 {
374 if (!add_mark_sgf (cursor_pos, PROP_MARK))
375 {
376 rb->splash (HZ / 3, "Couldn't Mark");
377 }
378 }
379 else if (play_mode == MODE_CIRCLE)
380 {
381 if (!add_mark_sgf (cursor_pos, PROP_CIRCLE))
382 {
383 rb->splash (HZ / 3, "Couldn't Mark");
384 }
385 }
386 else if (play_mode == MODE_SQUARE)
387 {
388 if (!add_mark_sgf (cursor_pos, PROP_SQUARE))
389 {
390 rb->splash (HZ / 3, "Couldn't Mark");
391 }
392 }
393 else if (play_mode == MODE_TRIANGLE)
394 {
395 if (!add_mark_sgf (cursor_pos, PROP_TRIANGLE))
396 {
397 rb->splash (HZ / 3, "Couldn't Mark");
398 }
399 }
400 else if (play_mode == MODE_LABEL)
401 {
402 if (!add_mark_sgf (cursor_pos, PROP_LABEL))
403 {
404 rb->splash (HZ / 3, "Couldn't Label");
405 }
406 }
407 else
408 {
409 rb->splash (HZ, "mode not implemented");
410 }
411
412 draw_screen_display ();
413 break;
414
415 case GBN_BUTTON_RIGHT:
416 case GBN_BUTTON_RIGHT | BUTTON_REPEAT:
417#if defined(GBN_BUTTON_NAV_MODE)
418 if (nav_mode == NAV_MODE_TREE)
419 {
420 if (has_more_nodes_sgf ())
421 {
422 if (!redo_node_sgf ())
423 {
424 rb->splash (2 * HZ, "Redo Failed");
425 }
426 draw_screen_display ();
427 }
428 }
429 else
430 {
431#endif
432 cursor_pos = WRAP (EAST (cursor_pos));
433 draw_screen_display ();
434#if defined(GBN_BUTTON_NAV_MODE)
435 }
436#endif
437 break;
438
439 case GBN_BUTTON_LEFT:
440 case GBN_BUTTON_LEFT | BUTTON_REPEAT:
441#if defined(GBN_BUTTON_NAV_MODE)
442 if (nav_mode == NAV_MODE_TREE)
443 {
444 if (has_prev_nodes_sgf ())
445 {
446 if (!undo_node_sgf ())
447 {
448 rb->splash (2 * HZ, "Undo Failed");
449 }
450 draw_screen_display ();
451 }
452 }
453 else
454 {
455#endif
456 cursor_pos = WRAP (WEST (cursor_pos));
457 draw_screen_display ();
458#if defined(GBN_BUTTON_NAV_MODE)
459 }
460#endif
461 break;
462
463 case GBN_BUTTON_DOWN:
464 case GBN_BUTTON_DOWN | BUTTON_REPEAT:
465 cursor_pos = WRAP (SOUTH (cursor_pos));
466 draw_screen_display ();
467 break;
468
469 case GBN_BUTTON_UP:
470 case GBN_BUTTON_UP | BUTTON_REPEAT:
471 cursor_pos = WRAP (NORTH (cursor_pos));
472 draw_screen_display ();
473 break;
474
475 case GBN_BUTTON_MENU:
476 if (do_main_menu ())
477 {
478 save_game (DEFAULT_SAVE);
479
480 global_cleanup ();
481 return PLUGIN_OK;
482 }
483
484 draw_screen_display ();
485 break;
486
487#if defined(GBN_BUTTON_CONTEXT)
488 case GBN_BUTTON_CONTEXT:
489 do_context_menu ();
490 draw_screen_display ();
491 break;
492#endif
493
494#if defined(GBN_BUTTON_NEXT_VAR)
495 case GBN_BUTTON_NEXT_VAR:
496 case GBN_BUTTON_NEXT_VAR | BUTTON_REPEAT:
497 if ((temp = next_variation_sgf ()) >= 0)
498 {
499 draw_screen_display ();
500 rb->splashf (2 * HZ / 3, "%d of %d", temp,
501 num_variations_sgf ());
502 draw_screen_display ();
503 }
504 else
505 {
506 if (num_variations_sgf () > 1)
507 {
508 rb->splashf (HZ, "Error %d in next_variation_sgf", temp);
509 }
510 draw_screen_display ();
511 }
512 break;
513#endif
514
515 case BUTTON_NONE:
516 is_idle = true;
517 default:
518 if (rb->default_event_handler (btn) == SYS_USB_CONNECTED)
519 {
520 return PLUGIN_USB_CONNECTED;
521 }
522 break;
523 };
524
525 if (is_idle && autosave_dirty)
526 {
527 ++autosave_counter;
528
529 if (autosave_time != 0 &&
530 autosave_counter / 2 >= autosave_time)
531 /* counter is in 30 second increments, autosave_time is in
532 * minutes
533 */
534 {
535 DEBUGF("autosaving\n");
536 rb->splash(HZ / 4, "Autosaving...");
537 save_game(DEFAULT_SAVE);
538 draw_screen_display();
539 autosave_counter = 0;
540 }
541 }
542 else
543 {
544 autosave_counter = 0;
545 }
546 }
547
548 return PLUGIN_OK;
549}
550
551static void
552global_cleanup (void)
553{
554 cleanup_sgf ();
555 configfile_save(SETTINGS_FILENAME, config,
556 sizeof(config)/sizeof(*config),
557 SETTINGS_VERSION);
558}
559
560static void
561global_setup (void)
562{
563 setup_stack (&parse_stack, parse_stack_buffer,
564 sizeof (parse_stack_buffer));
565 setup_display ();
566 setup_sgf ();
567
568 set_defaults();
569 if (configfile_load(SETTINGS_FILENAME, config,
570 sizeof(config)/sizeof(*config),
571 SETTINGS_MIN_VERSION ) < 0)
572 {
573 /* If the loading failed, save a new config file (as the disk is
574 already spinning) */
575
576 /* set defaults again just in case (don't know if they can ever
577 * be messed up by configfile_load, and it's basically free anyway)
578 */
579 set_defaults();
580
581 configfile_save(SETTINGS_FILENAME, config,
582 sizeof(config)/sizeof(*config),
583 SETTINGS_VERSION);
584 }
585}
586
587enum main_menu_selections
588{
589 MAIN_NEW = 0,
590 MAIN_SAVE,
591 MAIN_SAVE_AS,
592 MAIN_GAME_INFO,
593 MAIN_PLAYBACK,
594 MAIN_ZOOM,
595 MAIN_OPTIONS,
596 MAIN_CONTEXT,
597 MAIN_QUIT
598};
599
600static bool
601do_main_menu (void)
602{
603 int selection = 0;
604 MENUITEM_STRINGLIST (menu, "Rockbox Goban", NULL,
605 "New",
606 "Save",
607 "Save As",
608 "Game Info",
609 "Playback Control",
610 "Zoom Level",
611 "Options",
612 "Context Menu",
613 "Quit");
614
615 /* for "New" in menu */
616 int new_handi = 0, new_bs = MAX_BOARD_SIZE, new_komi = 15;
617
618 char new_save_file[SAVE_FILE_LENGTH];
619
620
621 bool done = false;
622
623 while (!done)
624 {
625 selection = rb->do_menu (&menu, &selection, NULL, false);
626
627 switch (selection)
628 {
629 case MAIN_NEW:
630 rb->set_int ("board size", "lines", UNIT_INT,
631 &new_bs, NULL, 1, MIN_BOARD_SIZE, MAX_BOARD_SIZE,
632 NULL);
633
634 rb->set_int ("handicap", "stones", UNIT_INT,
635 &new_handi, NULL, 1, 0, 9, NULL);
636
637 if (new_handi > 0)
638 {
639 new_komi = 1;
640 }
641 else
642 {
643 new_komi = 13;
644 }
645
646 rb->set_int ("komi", "moku", UNIT_INT, &new_komi, NULL,
647 1, -300, 300, &komi_formatter);
648
649 setup_game (new_bs, new_bs, new_handi, new_komi);
650 draw_screen_display ();
651 done = true;
652 break;
653
654 case MAIN_SAVE:
655 if (!save_game (save_file))
656 {
657 rb->splash (2 * HZ, "Save Failed!");
658 }
659 else
660 {
661 rb->splash (2 * HZ / 3, "Saved");
662 }
663 done = true;
664 draw_screen_display ();
665 break;
666
667 case MAIN_SAVE_AS:
668 rb->strcpy (new_save_file, save_file);
669
670 if (!rb->kbd_input (new_save_file, SAVE_FILE_LENGTH))
671 {
672 break;
673 }
674
675 if (!save_game (new_save_file))
676 {
677 rb->splash (2 * HZ, "Save Failed!");
678 }
679 else
680 {
681 rb->strcpy (save_file, new_save_file);
682 rb->splash (2 * HZ / 3, "Saved");
683 }
684
685 done = true;
686 draw_screen_display ();
687 break;
688
689 case MAIN_GAME_INFO:
690 do_gameinfo_menu ();
691 break;
692
693 case MAIN_PLAYBACK:
694 if (!audio_stolen_sgf ())
695 {
696 playback_control (NULL);
697 }
698 else
699 {
700 rb->splash (1 * HZ, "Audio has been disabled!");
701 }
702 break;
703
704 case MAIN_ZOOM:
705 if (do_zoom ())
706 {
707 return true;
708 }
709 done = true;
710 draw_screen_display ();
711 break;
712
713 case MAIN_OPTIONS:
714 do_options_menu();
715 break;
716
717 case MAIN_CONTEXT:
718 do_context_menu ();
719 done = true;
720 break;
721
722 case MAIN_QUIT:
723 case MENU_ATTACHED_USB:
724 return true;
725
726 case GO_TO_ROOT:
727 case GO_TO_PREVIOUS:
728 default:
729
730 done = true;
731 break;
732 };
733 }
734
735 return false;
736}
737
738void
739zoom_preview (int current)
740{
741 set_zoom_display (current);
742 draw_screen_display ();
743 rb->splash (0, "Preview");
744}
745
746static bool
747do_zoom (void)
748{
749 unsigned int zoom_level;
750 unsigned int old_val;
751 bool done = false;
752
753 unsigned int min_val = min_zoom_display ();
754 unsigned int max_val = max_zoom_display ();
755
756 zoom_level = old_val = current_zoom_display ();
757
758 int action;
759
760 zoom_preview (zoom_level);
761 while (!done)
762 {
763 switch (action = rb->get_action (CONTEXT_LIST, TIMEOUT_BLOCK))
764 {
765 case ACTION_STD_OK:
766 set_zoom_display (zoom_level);
767 done = true;
768 rb->splash (HZ / 2, "Zoom Set");
769 saved_circle_size = intersection_size;
770 break;
771
772 case ACTION_STD_CANCEL:
773 set_zoom_display (old_val);
774 done = true;
775 rb->splash (HZ / 2, "Cancelled");
776 break;
777
778 case ACTION_STD_CONTEXT:
779 zoom_level = old_val;
780 zoom_preview (zoom_level);
781 break;
782
783 case ACTION_STD_NEXT:
784 case ACTION_STD_NEXTREPEAT:
785 zoom_level = zoom_level * 3 / 2;
786
787 /* 1 * 3 / 2 is 1 again... */
788 if (zoom_level == 1)
789 {
790 ++zoom_level;
791 }
792
793 if (zoom_level > max_val)
794 {
795 zoom_level = min_val;
796 }
797
798 zoom_preview (zoom_level);
799 break;
800
801 case ACTION_STD_PREV:
802 case ACTION_STD_PREVREPEAT:
803 zoom_level = zoom_level * 2 / 3;
804
805 if (zoom_level < min_val)
806 {
807 zoom_level = max_val;
808 }
809 zoom_preview (zoom_level);
810 break;
811
812 case ACTION_NONE:
813 break;
814
815 default:
816 if (rb->default_event_handler (action) == SYS_USB_CONNECTED)
817 {
818 return true;
819 }
820 break;
821 }
822 }
823
824 return false;
825}
826
827enum gameinfo_menu_selections
828{
829 GINFO_BASIC_INFO = 0,
830 GINFO_TIME_LIMIT,
831 GINFO_OVERTIME,
832 GINFO_RESULT,
833 GINFO_HANDICAP,
834 GINFO_KOMI,
835 GINFO_RULESET,
836 GINFO_BLACK_PLAYER,
837 GINFO_BLACK_RANK,
838 GINFO_BLACK_TEAM,
839 GINFO_WHITE_PLAYER,
840 GINFO_WHITE_RANK,
841 GINFO_WHITE_TEAM,
842 GINFO_DATE,
843 GINFO_EVENT,
844 GINFO_PLACE,
845 GINFO_ROUND,
846 GINFO_DONE
847};
848
849
850static void
851do_gameinfo_menu (void)
852{
853 MENUITEM_STRINGLIST (gameinfo_menu, "Game Info", NULL,
854 "Basic Info",
855 "Time Limit",
856 "Overtime",
857 "Result",
858 "Handicap",
859 "Komi",
860 "Ruleset",
861 "Black Player",
862 "Black Rank",
863 "Black Team",
864 "White Player",
865 "White Rank",
866 "White Team",
867 "Date",
868 "Event",
869 "Place",
870 "Round",
871 "Done");
872 /* IMPORTANT:
873 *
874 * if you edit this string list, make sure you keep
875 * menu_selection_to_prop function in line with it!! (see the bottom
876 * of this file).
877 */
878
879 int new_ruleset = 0;
880
881 char *gameinfo_string;
882 int gameinfo_string_size;
883
884 bool done = false;
885 int selection = 0;
886
887 while (!done)
888 {
889 selection = rb->do_menu (&gameinfo_menu, &selection, NULL, false);
890
891 switch (selection)
892 {
893 case GINFO_OVERTIME:
894 case GINFO_RESULT:
895 case GINFO_BLACK_PLAYER:
896 case GINFO_BLACK_RANK:
897 case GINFO_BLACK_TEAM:
898 case GINFO_WHITE_PLAYER:
899 case GINFO_WHITE_RANK:
900 case GINFO_WHITE_TEAM:
901 case GINFO_DATE:
902 case GINFO_EVENT:
903 case GINFO_PLACE:
904 case GINFO_ROUND:
905 if (!get_header_string_and_size (&header,
906 menu_selection_to_prop
907 (selection), &gameinfo_string,
908 &gameinfo_string_size))
909 {
910 rb->splash (3 * HZ, "Couldn't get header string");
911 break;
912 }
913
914 rb->kbd_input (gameinfo_string, gameinfo_string_size);
915 sanitize_string (gameinfo_string);
916 set_game_modified();
917 break;
918
919 /* these need special handling in some way, so they are
920 separate */
921
922 case GINFO_BASIC_INFO:
923 metadata_summary();
924 break;
925
926 case GINFO_TIME_LIMIT:
927 rb->set_int ("Time Limit", "", UNIT_INT, &header.time_limit,
928 NULL, 60, 0, 24 * 60 * 60, &time_formatter);
929 set_game_modified();
930 break;
931
932 case GINFO_HANDICAP:
933 rb->splashf (0, "%d stones. Start a new game to set handicap",
934 header.handicap);
935 rb->action_userabort(TIMEOUT_BLOCK);
936 break;
937
938 case GINFO_KOMI:
939 rb->set_int ("Komi", "moku", UNIT_INT, &header.komi, NULL,
940 1, -300, 300, &komi_formatter);
941 set_game_modified();
942 break;
943
944 case GINFO_RULESET:
945 new_ruleset = 0;
946 rb->set_int ("Ruleset", "", UNIT_INT, &new_ruleset, NULL,
947 1, 0, NUM_RULESETS - 1, &ruleset_formatter);
948
949 rb->strcpy (header.ruleset, ruleset_names[new_ruleset]);
950 set_game_modified();
951 break;
952
953 case GINFO_DONE:
954 case GO_TO_ROOT:
955 case GO_TO_PREVIOUS:
956 case MENU_ATTACHED_USB:
957 default:
958 done = true;
959 break;
960 };
961 }
962}
963
964enum context_menu_selections
965{
966 CTX_PLAY = 0,
967 CTX_ADD_BLACK,
968 CTX_ADD_WHITE,
969 CTX_ERASE,
970 CTX_PASS,
971 CTX_NEXT_VAR,
972 CTX_FORCE,
973 CTX_MARK,
974 CTX_CIRCLE,
975 CTX_SQUARE,
976 CTX_TRIANGLE,
977 CTX_LABEL,
978 CTX_COMMENT,
979 CTX_DONE
980};
981
982static void
983do_context_menu (void)
984{
985 int selection;
986 bool done = false;
987 int temp;
988
989 MENUITEM_STRINGLIST (context_menu, "Context Menu", NULL,
990 "Play Mode (default)",
991 "Add Black Mode",
992 "Add White Mode",
993 "Erase Stone Mode",
994 "Pass",
995 "Next Variation",
996 "Force Play Mode",
997 "Mark Mode",
998 "Circle Mode",
999 "Square Mode",
1000 "Triangle Mode",
1001 "Label Mode",
1002 "Add/Edit Comment",
1003 "Done");
1004
1005 while (!done)
1006 {
1007 selection = rb->do_menu (&context_menu, &selection, NULL, false);
1008
1009 switch (selection)
1010 {
1011 case CTX_PLAY:
1012 play_mode = MODE_PLAY;
1013 done = true;
1014 break;
1015
1016 case CTX_ADD_BLACK:
1017 play_mode = MODE_ADD_BLACK;
1018 done = true;
1019 break;
1020
1021 case CTX_ADD_WHITE:
1022 play_mode = MODE_ADD_WHITE;
1023 done = true;
1024 break;
1025
1026 case CTX_ERASE:
1027 play_mode = MODE_REMOVE;
1028 done = true;
1029 break;
1030
1031 case CTX_PASS:
1032 if (!play_move_sgf (PASS_POS, current_player))
1033 {
1034 rb->splash (HZ, "Error while passing!");
1035 }
1036 done = true;
1037 break;
1038
1039 case CTX_NEXT_VAR:
1040 if ((temp = next_variation_sgf ()) >= 0)
1041 {
1042 draw_screen_display ();
1043 rb->splashf (2 * HZ / 3, "%d of %d", temp,
1044 num_variations_sgf ());
1045 draw_screen_display ();
1046 }
1047 else
1048 {
1049 if (num_variations_sgf () > 1)
1050 {
1051 rb->splashf (HZ, "Error %d in next_variation_sgf", temp);
1052 }
1053 else
1054 {
1055 rb->splash (HZ, "No next variation");
1056 }
1057 }
1058 break;
1059
1060 case CTX_FORCE:
1061 play_mode = MODE_FORCE_PLAY;
1062 done = true;
1063 break;
1064
1065 case CTX_MARK:
1066 play_mode = MODE_MARK;
1067 done = true;
1068 break;
1069
1070 case CTX_CIRCLE:
1071 play_mode = MODE_CIRCLE;
1072 done = true;
1073 break;
1074
1075 case CTX_SQUARE:
1076 play_mode = MODE_SQUARE;
1077 done = true;
1078 break;
1079
1080 case CTX_TRIANGLE:
1081 play_mode = MODE_TRIANGLE;
1082 done = true;
1083 break;
1084
1085 case CTX_LABEL:
1086 play_mode = MODE_LABEL;
1087 done = true;
1088 break;
1089
1090 case CTX_COMMENT:
1091 if (!do_comment_edit ())
1092 {
1093 DEBUGF ("Editing comment failed\n");
1094 rb->splash (HZ, "Read or write failed!\n");
1095 }
1096 done = true;
1097 break;
1098
1099 case CTX_DONE:
1100 case GO_TO_ROOT:
1101 case GO_TO_PREVIOUS:
1102 case MENU_ATTACHED_USB:
1103 default:
1104 done = true;
1105 break;
1106 };
1107 }
1108}
1109
1110enum options_menu_selections
1111{
1112 OMENU_SHOW_VARIATIONS = 0,
1113 OMENU_DISABLE_POWEROFF,
1114 OMENU_AUTOSAVE_TIME,
1115 OMENU_AUTO_COMMENT
1116};
1117
1118static void
1119do_options_menu (void)
1120{
1121 int selection;
1122 bool done = false;
1123
1124 MENUITEM_STRINGLIST (options_menu, "Options Menu", NULL,
1125 "Show Child Variations?",
1126 "Disable Idle Poweroff?",
1127 "Idle Autosave Time",
1128 "Automatically Show Comments?");
1129
1130 while (!done)
1131 {
1132 selection = rb->do_menu (&options_menu, &selection, NULL, false);
1133
1134 switch (selection)
1135 {
1136 case OMENU_SHOW_VARIATIONS:
1137 rb->set_bool("Draw Variations?", &draw_variations);
1138 clear_marks_display ();
1139 set_all_marks_sgf ();
1140 if (draw_variations)
1141 {
1142 mark_child_variations_sgf ();
1143 }
1144 break;
1145
1146 case OMENU_DISABLE_POWEROFF:
1147 rb->set_bool("Disable Idle Poweroff?", &disable_shutdown);
1148 break;
1149
1150 case OMENU_AUTOSAVE_TIME:
1151 rb->set_int("Idle Autosave Time", "minutes", UNIT_INT,
1152 &autosave_time, NULL, 1, 0, MAX_AUTOSAVE,
1153 &autosave_formatter);
1154 autosave_counter = 0;
1155 break;
1156
1157 case OMENU_AUTO_COMMENT:
1158 rb->set_bool("Auto Show Comments?", &auto_show_comments);
1159 break;
1160
1161 case GO_TO_ROOT:
1162 case GO_TO_PREVIOUS:
1163 case MENU_ATTACHED_USB:
1164 default:
1165 done = true;
1166 break;
1167 };
1168 }
1169
1170}
1171
1172static bool
1173do_comment_edit (void)
1174{
1175 char cbuffer[512];
1176
1177 rb->memset (cbuffer, 0, sizeof (cbuffer));
1178
1179 if (read_comment_sgf (cbuffer, sizeof (cbuffer)) < 0)
1180 {
1181 return false;
1182 }
1183
1184 if (!rb->kbd_input (cbuffer, sizeof (cbuffer)))
1185 {
1186 /* user didn't edit, no reason to write it back */
1187 return true;
1188 }
1189
1190 if (write_comment_sgf (cbuffer) < 0)
1191 {
1192 return false;
1193 }
1194
1195 return true;
1196}
1197
1198static enum prop_type_t
1199menu_selection_to_prop (int selection)
1200{
1201 switch (selection)
1202 {
1203 case GINFO_OVERTIME:
1204 return PROP_OVERTIME;
1205 case GINFO_RESULT:
1206 return PROP_RESULT;
1207 case GINFO_BLACK_PLAYER:
1208 return PROP_BLACK_NAME;
1209 case GINFO_BLACK_RANK:
1210 return PROP_BLACK_RANK;
1211 case GINFO_BLACK_TEAM:
1212 return PROP_BLACK_TEAM;
1213 case GINFO_WHITE_PLAYER:
1214 return PROP_WHITE_NAME;
1215 case GINFO_WHITE_RANK:
1216 return PROP_WHITE_RANK;
1217 case GINFO_WHITE_TEAM:
1218 return PROP_WHITE_TEAM;
1219 case GINFO_DATE:
1220 return PROP_DATE;
1221 case GINFO_EVENT:
1222 return PROP_EVENT;
1223 case GINFO_PLACE:
1224 return PROP_PLACE;
1225 case GINFO_ROUND:
1226 return PROP_ROUND;
1227 default:
1228 DEBUGF ("Tried to get prop from invalid menu selection!!!\n");
1229 return PROP_PLACE; /* just pick one, there's a major bug if
1230 we got here */
1231 };
1232}