Introduce __attribute__((unused)) (#defined to UNUSED_ATTR) to mark possibly unused...
[kugel-rb.git] / apps / plugins / goban / goban.c
blobfa7dcdd66967e55c3bbf157fff3dc829296fc214
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007-2009 Joshua Simmons <mud at majidejima dot com>
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.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include "plugin.h"
23 #include "lib/playback_control.h"
24 #include "lib/configfile.h"
26 PLUGIN_HEADER
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"
37 enum play_mode_t play_mode = MODE_PLAY;
39 #if defined(GBN_BUTTON_NAV_MODE)
41 #define NAV_MODE_BOARD 0
42 #define NAV_MODE_TREE 1
44 int nav_mode = NAV_MODE_BOARD;
46 #endif
48 /* the stack that uses this buffer is used for both storing intersections
49 * in board algorithms (could store one short for each board location), and
50 * for parsing (could store an indefinite number of ints, related to how
51 * many levels deep branches go (in other words, how many branches off of
52 * branches there are). 50 should take care of any reasonable file.
54 #define PARSE_STACK_BUFFER_SIZE (max(MAX_BOARD_SIZE * MAX_BOARD_SIZE * sizeof(unsigned short), 50 * sizeof(int)))
56 /* used in SGF file parsing and outputting as well as in liberty counting
57 and capturing/uncapturing */
58 struct stack_t parse_stack;
59 char parse_stack_buffer[PARSE_STACK_BUFFER_SIZE];
61 static void global_setup (void);
62 static void global_cleanup (void);
64 static bool do_main_menu (void);
65 static void do_gameinfo_menu (void);
66 static enum prop_type_t menu_selection_to_prop (int selection);
67 static void do_context_menu (void);
68 static void do_options_menu (void);
70 static bool do_comment_edit (void);
71 static bool do_zoom (void);
72 static void set_defaults (void);
74 bool auto_show_comments = true;
75 bool disable_shutdown = false;
76 unsigned int autosave_time = 0;
77 unsigned int autosave_counter = 0;
79 #define SETTINGS_VERSION 2
80 #define SETTINGS_MIN_VERSION 1
81 #define SETTINGS_FILENAME "goban.cfg"
83 static struct configdata config[] =
84 { /* INT in MAX_INT_SIZE is intersection, not integer */
85 {TYPE_INT, 0, MAX_INT_SIZE,
86 { .int_p = &saved_circle_size },
87 "stone size", NULL},
88 {TYPE_BOOL, 0, 1,
89 { .bool_p = &draw_variations },
90 "draw variations",
91 NULL},
92 {TYPE_BOOL, 0, 1,
93 { .bool_p = &auto_show_comments },
94 "auto show comments",
95 NULL},
96 {TYPE_BOOL, 0, 1,
97 { .bool_p = &disable_shutdown },
98 "disable shutdown",
99 NULL},
100 {TYPE_INT, 0, MAX_AUTOSAVE,
101 { .int_p = &autosave_time },
102 "autosave time",
103 NULL}
106 static void
107 set_defaults (void)
109 saved_circle_size = 0;
110 draw_variations = true;
111 auto_show_comments = true;
113 disable_shutdown = false;
114 autosave_time = 7;
117 static void
118 komi_formatter (char *dest, size_t size, int menu_item, const char *unknown)
120 (void) unknown;
121 snprint_fixed (dest, size, menu_item);
124 static void
125 ruleset_formatter (char *dest, size_t size, int menu_item, const char *unknown)
127 (void) unknown;
128 rb->snprintf (dest, size, "%s", ruleset_names[menu_item]);
131 static void
132 autosave_formatter (char *dest, size_t size, int menu_item, const char *
133 unknown)
135 (void) unknown;
136 if (menu_item == 0)
138 rb->snprintf (dest, size, "Off");
140 else
142 rb->snprintf (dest, size, "%d minute%s", menu_item,
143 menu_item == 1 ? "" : "s");
147 static void
148 time_formatter (char *dest, size_t size, int menu_item, const char *unknown)
150 int time_values[4]; /* days hours minutes seconds */
151 int min_set, max_set;
152 int temp;
154 (void) unknown;
156 time_values[0] = menu_item / (24 * 60 * 60);
157 menu_item %= (24 * 60 * 60);
158 time_values[1] = menu_item / (60 * 60);
159 menu_item %= (60 * 60);
160 time_values[2] = menu_item / 60;
161 time_values[3] = menu_item % 60;
163 min_set = 500;
164 max_set = -1;
165 int i;
166 for (i = 0;
167 (unsigned int) i < (sizeof (time_values) / sizeof (time_values[0]));
168 ++i)
170 if (time_values[i])
172 if (i < min_set)
174 min_set = i;
177 if (i > max_set)
179 max_set = i;
184 if (max_set == -1)
186 rb->snprintf (dest, size, "0");
187 return;
190 for (i = min_set; i <= 3; ++i)
192 if (i <= max_set)
194 if (i == 0 || i == 1 || i == min_set)
196 rb->snprintf (dest, size, "%d", time_values[i]);
198 else
200 rb->snprintf (dest, size, "%02d", time_values[i]);
202 temp = rb->strlen (dest);
203 dest += temp;
204 size -= temp;
206 else if (i != 3)
208 continue;
211 if (i == 0) /* days */
213 rb->snprintf (dest, size, " d ");
215 else if (i == 3) /* seconds, print the final units */
217 if (min_set == 0 || min_set == 1)
219 rb->snprintf (dest, size, " h");
221 else if (min_set == 2)
223 rb->snprintf (dest, size, " m");
225 else
227 rb->snprintf (dest, size, " s");
230 else if (i != max_set)
232 rb->snprintf (dest, size, ":");
235 temp = rb->strlen (dest);
236 dest += temp;
237 size -= temp;
241 enum plugin_status
242 plugin_start (UNUSED_ATTR const void *parameter)
244 int btn;
246 rb->mkdir (DEFAULT_SAVE_DIR);
248 global_setup ();
250 #ifdef GBN_TEST
251 run_tests ();
252 return PLUGIN_OK;
253 #endif
255 if (!(parameter && load_game (parameter)))
257 if (parameter)
259 rb->splashf (2 * HZ, "Loading %s failed.", (char *) parameter);
262 if (!load_game (DEFAULT_SAVE))
264 rb->strcpy (save_file, DEFAULT_SAVE);
266 if (!setup_game (MAX_BOARD_SIZE, MAX_BOARD_SIZE, 0, 0))
268 return PLUGIN_ERROR;
272 else
274 /* game loaded */
275 if (rb->strcmp (save_file, DEFAULT_SAVE))
277 /* delete the scratch file if we loaded a game and it wasn't
278 * from the scratch file
280 rb->remove (DEFAULT_SAVE);
284 draw_screen_display ();
286 autosave_counter = 0;
287 for (;;)
289 btn = rb->button_get_w_tmo (HZ * 30);
291 if (disable_shutdown)
293 /* tell rockbox we're not idle */
294 rb->reset_poweroff_timer ();
297 bool is_idle = false;
299 switch (btn)
302 #if defined(GBN_BUTTON_NAV_MODE)
303 case GBN_BUTTON_NAV_MODE:
304 case GBN_BUTTON_NAV_MODE | BUTTON_REPEAT:
305 if (nav_mode == NAV_MODE_TREE)
307 nav_mode = NAV_MODE_BOARD;
308 rb->splash (2 * HZ / 3, "board navigation mode");
309 draw_screen_display ();
311 else
313 nav_mode = NAV_MODE_TREE;
314 rb->splash (2 * HZ / 3, "tree navigation mode");
315 draw_screen_display ();
317 break;
318 #endif
320 #if defined(GBN_BUTTON_ADVANCE)
321 case GBN_BUTTON_ADVANCE:
322 case GBN_BUTTON_ADVANCE | BUTTON_REPEAT:
323 if (has_more_nodes_sgf ())
325 if (!redo_node_sgf ())
327 rb->splash (2 * HZ, "redo failed");
329 draw_screen_display ();
331 break;
332 #endif
334 #if defined(GBN_BUTTON_RETREAT)
335 case GBN_BUTTON_RETREAT:
336 case GBN_BUTTON_RETREAT | BUTTON_REPEAT:
337 if (has_prev_nodes_sgf ())
339 if (!undo_node_sgf ())
341 rb->splash (3 * HZ / 2, "Undo Failed");
343 draw_screen_display ();
345 break;
346 #endif
348 case GBN_BUTTON_PLAY:
349 if (play_mode == MODE_PLAY || play_mode == MODE_FORCE_PLAY)
351 if (!play_move_sgf (cursor_pos, current_player))
353 rb->splash (HZ / 3, "Illegal Move");
356 else if (play_mode == MODE_ADD_BLACK)
358 if (!add_stone_sgf (cursor_pos, BLACK))
360 rb->splash (HZ / 3, "Illegal");
363 else if (play_mode == MODE_ADD_WHITE)
365 if (!add_stone_sgf (cursor_pos, WHITE))
367 rb->splash (HZ / 3, "Illegal");
370 else if (play_mode == MODE_REMOVE)
372 if (!add_stone_sgf (cursor_pos, EMPTY))
374 rb->splash (HZ / 3, "Illegal");
377 else if (play_mode == MODE_MARK)
379 if (!add_mark_sgf (cursor_pos, PROP_MARK))
381 rb->splash (HZ / 3, "Couldn't Mark");
384 else if (play_mode == MODE_CIRCLE)
386 if (!add_mark_sgf (cursor_pos, PROP_CIRCLE))
388 rb->splash (HZ / 3, "Couldn't Mark");
391 else if (play_mode == MODE_SQUARE)
393 if (!add_mark_sgf (cursor_pos, PROP_SQUARE))
395 rb->splash (HZ / 3, "Couldn't Mark");
398 else if (play_mode == MODE_TRIANGLE)
400 if (!add_mark_sgf (cursor_pos, PROP_TRIANGLE))
402 rb->splash (HZ / 3, "Couldn't Mark");
405 else if (play_mode == MODE_LABEL)
407 if (!add_mark_sgf (cursor_pos, PROP_LABEL))
409 rb->splash (HZ / 3, "Couldn't Label");
412 else
414 rb->splash (HZ, "mode not implemented");
417 draw_screen_display ();
418 break;
420 case GBN_BUTTON_RIGHT:
421 case GBN_BUTTON_RIGHT | BUTTON_REPEAT:
422 #if defined(GBN_BUTTON_NAV_MODE)
423 if (nav_mode == NAV_MODE_TREE)
425 if (has_more_nodes_sgf ())
427 if (!redo_node_sgf ())
429 rb->splash (2 * HZ, "Redo Failed");
431 draw_screen_display ();
434 else
436 #endif
437 cursor_pos = WRAP (EAST (cursor_pos));
438 draw_screen_display ();
439 #if defined(GBN_BUTTON_NAV_MODE)
441 #endif
442 break;
444 case GBN_BUTTON_LEFT:
445 case GBN_BUTTON_LEFT | BUTTON_REPEAT:
446 #if defined(GBN_BUTTON_NAV_MODE)
447 if (nav_mode == NAV_MODE_TREE)
449 if (has_prev_nodes_sgf ())
451 if (!undo_node_sgf ())
453 rb->splash (2 * HZ, "Undo Failed");
455 draw_screen_display ();
458 else
460 #endif
461 cursor_pos = WRAP (WEST (cursor_pos));
462 draw_screen_display ();
463 #if defined(GBN_BUTTON_NAV_MODE)
465 #endif
466 break;
468 case GBN_BUTTON_DOWN:
469 case GBN_BUTTON_DOWN | BUTTON_REPEAT:
470 cursor_pos = WRAP (SOUTH (cursor_pos));
471 draw_screen_display ();
472 break;
474 case GBN_BUTTON_UP:
475 case GBN_BUTTON_UP | BUTTON_REPEAT:
476 cursor_pos = WRAP (NORTH (cursor_pos));
477 draw_screen_display ();
478 break;
480 case GBN_BUTTON_MENU:
481 if (do_main_menu ())
483 save_game (DEFAULT_SAVE);
485 global_cleanup ();
486 return PLUGIN_OK;
489 draw_screen_display ();
490 break;
492 #if defined(GBN_BUTTON_CONTEXT)
493 case GBN_BUTTON_CONTEXT:
494 do_context_menu ();
495 draw_screen_display ();
496 break;
497 #endif
499 #if defined(GBN_BUTTON_NEXT_VAR)
500 case GBN_BUTTON_NEXT_VAR:
501 case GBN_BUTTON_NEXT_VAR | BUTTON_REPEAT:
503 int temp;
504 if ((temp = next_variation_sgf ()) >= 0)
506 draw_screen_display ();
507 rb->splashf (2 * HZ / 3, "%d of %d", temp,
508 num_variations_sgf ());
509 draw_screen_display ();
511 else
513 if (num_variations_sgf () > 1)
515 rb->splashf (HZ, "Error %d in next_variation_sgf", temp);
517 draw_screen_display ();
519 break;
521 #endif
523 case BUTTON_NONE:
524 is_idle = true;
525 default:
526 if (rb->default_event_handler (btn) == SYS_USB_CONNECTED)
528 return PLUGIN_USB_CONNECTED;
530 break;
533 if (is_idle && autosave_dirty)
535 ++autosave_counter;
537 if (autosave_time != 0 &&
538 autosave_counter / 2 >= autosave_time)
539 /* counter is in 30 second increments, autosave_time is in
540 * minutes
543 DEBUGF("autosaving\n");
544 rb->splash(HZ / 4, "Autosaving...");
545 save_game(DEFAULT_SAVE);
546 draw_screen_display();
547 autosave_counter = 0;
550 else
552 autosave_counter = 0;
556 return PLUGIN_OK;
559 static void
560 global_cleanup (void)
562 cleanup_sgf ();
563 configfile_save(SETTINGS_FILENAME, config,
564 sizeof(config)/sizeof(*config),
565 SETTINGS_VERSION);
568 static void
569 global_setup (void)
571 setup_stack (&parse_stack, parse_stack_buffer,
572 sizeof (parse_stack_buffer));
573 setup_display ();
574 setup_sgf ();
576 set_defaults();
577 if (configfile_load(SETTINGS_FILENAME, config,
578 sizeof(config)/sizeof(*config),
579 SETTINGS_MIN_VERSION ) < 0)
581 /* If the loading failed, save a new config file (as the disk is
582 already spinning) */
584 /* set defaults again just in case (don't know if they can ever
585 * be messed up by configfile_load, and it's basically free anyway)
587 set_defaults();
589 configfile_save(SETTINGS_FILENAME, config,
590 sizeof(config)/sizeof(*config),
591 SETTINGS_VERSION);
595 enum main_menu_selections
597 MAIN_NEW = 0,
598 MAIN_SAVE,
599 MAIN_SAVE_AS,
600 MAIN_GAME_INFO,
601 MAIN_PLAYBACK,
602 MAIN_ZOOM,
603 MAIN_OPTIONS,
604 MAIN_CONTEXT,
605 MAIN_QUIT
608 static bool
609 do_main_menu (void)
611 int selection = 0;
612 MENUITEM_STRINGLIST (menu, "Rockbox Goban", NULL,
613 "New",
614 "Save",
615 "Save As",
616 "Game Info",
617 "Playback Control",
618 "Zoom Level",
619 "Options",
620 "Context Menu",
621 "Quit");
623 /* for "New" in menu */
624 int new_handi = 0, new_bs = MAX_BOARD_SIZE, new_komi = 15;
626 char new_save_file[SAVE_FILE_LENGTH];
629 bool done = false;
631 while (!done)
633 selection = rb->do_menu (&menu, &selection, NULL, false);
635 switch (selection)
637 case MAIN_NEW:
638 rb->set_int ("board size", "lines", UNIT_INT,
639 &new_bs, NULL, 1, MIN_BOARD_SIZE, MAX_BOARD_SIZE,
640 NULL);
642 rb->set_int ("handicap", "stones", UNIT_INT,
643 &new_handi, NULL, 1, 0, 9, NULL);
645 if (new_handi > 0)
647 new_komi = 1;
649 else
651 new_komi = 13;
654 rb->set_int ("komi", "moku", UNIT_INT, &new_komi, NULL,
655 1, -300, 300, &komi_formatter);
657 setup_game (new_bs, new_bs, new_handi, new_komi);
658 draw_screen_display ();
659 done = true;
660 break;
662 case MAIN_SAVE:
663 if (!save_game (save_file))
665 rb->splash (2 * HZ, "Save Failed!");
667 else
669 rb->splash (2 * HZ / 3, "Saved");
671 done = true;
672 draw_screen_display ();
673 break;
675 case MAIN_SAVE_AS:
676 rb->strcpy (new_save_file, save_file);
678 if (!rb->kbd_input (new_save_file, SAVE_FILE_LENGTH))
680 break;
683 if (!save_game (new_save_file))
685 rb->splash (2 * HZ, "Save Failed!");
687 else
689 rb->strcpy (save_file, new_save_file);
690 rb->splash (2 * HZ / 3, "Saved");
693 done = true;
694 draw_screen_display ();
695 break;
697 case MAIN_GAME_INFO:
698 do_gameinfo_menu ();
699 break;
701 case MAIN_PLAYBACK:
702 if (!audio_stolen_sgf ())
704 playback_control (NULL);
706 else
708 rb->splash (1 * HZ, "Audio has been disabled!");
710 break;
712 case MAIN_ZOOM:
713 if (do_zoom ())
715 return true;
717 done = true;
718 draw_screen_display ();
719 break;
721 case MAIN_OPTIONS:
722 do_options_menu();
723 break;
725 case MAIN_CONTEXT:
726 do_context_menu ();
727 done = true;
728 break;
730 case MAIN_QUIT:
731 case MENU_ATTACHED_USB:
732 return true;
734 case GO_TO_ROOT:
735 case GO_TO_PREVIOUS:
736 default:
738 done = true;
739 break;
743 return false;
746 void
747 zoom_preview (int current)
749 set_zoom_display (current);
750 draw_screen_display ();
751 rb->splash (0, "Preview");
754 static bool
755 do_zoom (void)
757 unsigned int zoom_level;
758 unsigned int old_val;
759 bool done = false;
761 unsigned int min_val = min_zoom_display ();
762 unsigned int max_val = max_zoom_display ();
764 zoom_level = old_val = current_zoom_display ();
766 int action;
768 zoom_preview (zoom_level);
769 while (!done)
771 switch (action = rb->get_action (CONTEXT_LIST, TIMEOUT_BLOCK))
773 case ACTION_STD_OK:
774 set_zoom_display (zoom_level);
775 done = true;
776 rb->splash (HZ / 2, "Zoom Set");
777 saved_circle_size = intersection_size;
778 break;
780 case ACTION_STD_CANCEL:
781 set_zoom_display (old_val);
782 done = true;
783 rb->splash (HZ / 2, "Cancelled");
784 break;
786 case ACTION_STD_CONTEXT:
787 zoom_level = old_val;
788 zoom_preview (zoom_level);
789 break;
791 case ACTION_STD_NEXT:
792 case ACTION_STD_NEXTREPEAT:
793 zoom_level = zoom_level * 3 / 2;
795 /* 1 * 3 / 2 is 1 again... */
796 if (zoom_level == 1)
798 ++zoom_level;
801 if (zoom_level > max_val)
803 zoom_level = min_val;
806 zoom_preview (zoom_level);
807 break;
809 case ACTION_STD_PREV:
810 case ACTION_STD_PREVREPEAT:
811 zoom_level = zoom_level * 2 / 3;
813 if (zoom_level < min_val)
815 zoom_level = max_val;
817 zoom_preview (zoom_level);
818 break;
820 case ACTION_NONE:
821 break;
823 default:
824 if (rb->default_event_handler (action) == SYS_USB_CONNECTED)
826 return true;
828 break;
832 return false;
835 enum gameinfo_menu_selections
837 GINFO_BASIC_INFO = 0,
838 GINFO_TIME_LIMIT,
839 GINFO_OVERTIME,
840 GINFO_RESULT,
841 GINFO_HANDICAP,
842 GINFO_KOMI,
843 GINFO_RULESET,
844 GINFO_BLACK_PLAYER,
845 GINFO_BLACK_RANK,
846 GINFO_BLACK_TEAM,
847 GINFO_WHITE_PLAYER,
848 GINFO_WHITE_RANK,
849 GINFO_WHITE_TEAM,
850 GINFO_DATE,
851 GINFO_EVENT,
852 GINFO_PLACE,
853 GINFO_ROUND,
854 GINFO_DONE
858 static void
859 do_gameinfo_menu (void)
861 MENUITEM_STRINGLIST (gameinfo_menu, "Game Info", NULL,
862 "Basic Info",
863 "Time Limit",
864 "Overtime",
865 "Result",
866 "Handicap",
867 "Komi",
868 "Ruleset",
869 "Black Player",
870 "Black Rank",
871 "Black Team",
872 "White Player",
873 "White Rank",
874 "White Team",
875 "Date",
876 "Event",
877 "Place",
878 "Round",
879 "Done");
880 /* IMPORTANT:
882 * if you edit this string list, make sure you keep
883 * menu_selection_to_prop function in line with it!! (see the bottom
884 * of this file).
887 int new_ruleset = 0;
889 char *gameinfo_string;
890 int gameinfo_string_size;
892 bool done = false;
893 int selection = 0;
895 while (!done)
897 selection = rb->do_menu (&gameinfo_menu, &selection, NULL, false);
899 switch (selection)
901 case GINFO_OVERTIME:
902 case GINFO_RESULT:
903 case GINFO_BLACK_PLAYER:
904 case GINFO_BLACK_RANK:
905 case GINFO_BLACK_TEAM:
906 case GINFO_WHITE_PLAYER:
907 case GINFO_WHITE_RANK:
908 case GINFO_WHITE_TEAM:
909 case GINFO_DATE:
910 case GINFO_EVENT:
911 case GINFO_PLACE:
912 case GINFO_ROUND:
913 if (!get_header_string_and_size (&header,
914 menu_selection_to_prop
915 (selection), &gameinfo_string,
916 &gameinfo_string_size))
918 rb->splash (3 * HZ, "Couldn't get header string");
919 break;
922 rb->kbd_input (gameinfo_string, gameinfo_string_size);
923 sanitize_string (gameinfo_string);
924 set_game_modified();
925 break;
927 /* these need special handling in some way, so they are
928 separate */
930 case GINFO_BASIC_INFO:
931 metadata_summary();
932 break;
934 case GINFO_TIME_LIMIT:
935 rb->set_int ("Time Limit", "", UNIT_INT, &header.time_limit,
936 NULL, 60, 0, 24 * 60 * 60, &time_formatter);
937 set_game_modified();
938 break;
940 case GINFO_HANDICAP:
941 rb->splashf (0, "%d stones. Start a new game to set handicap",
942 header.handicap);
943 rb->action_userabort(TIMEOUT_BLOCK);
944 break;
946 case GINFO_KOMI:
947 rb->set_int ("Komi", "moku", UNIT_INT, &header.komi, NULL,
948 1, -300, 300, &komi_formatter);
949 set_game_modified();
950 break;
952 case GINFO_RULESET:
953 new_ruleset = 0;
954 rb->set_int ("Ruleset", "", UNIT_INT, &new_ruleset, NULL,
955 1, 0, NUM_RULESETS - 1, &ruleset_formatter);
957 rb->strcpy (header.ruleset, ruleset_names[new_ruleset]);
958 set_game_modified();
959 break;
961 case GINFO_DONE:
962 case GO_TO_ROOT:
963 case GO_TO_PREVIOUS:
964 case MENU_ATTACHED_USB:
965 default:
966 done = true;
967 break;
972 enum context_menu_selections
974 CTX_PLAY = 0,
975 CTX_ADD_BLACK,
976 CTX_ADD_WHITE,
977 CTX_ERASE,
978 CTX_PASS,
979 CTX_NEXT_VAR,
980 CTX_FORCE,
981 CTX_MARK,
982 CTX_CIRCLE,
983 CTX_SQUARE,
984 CTX_TRIANGLE,
985 CTX_LABEL,
986 CTX_COMMENT,
987 CTX_DONE
990 static void
991 do_context_menu (void)
993 int selection;
994 bool done = false;
995 int temp;
997 MENUITEM_STRINGLIST (context_menu, "Context Menu", NULL,
998 "Play Mode (default)",
999 "Add Black Mode",
1000 "Add White Mode",
1001 "Erase Stone Mode",
1002 "Pass",
1003 "Next Variation",
1004 "Force Play Mode",
1005 "Mark Mode",
1006 "Circle Mode",
1007 "Square Mode",
1008 "Triangle Mode",
1009 "Label Mode",
1010 "Add/Edit Comment",
1011 "Done");
1013 while (!done)
1015 selection = rb->do_menu (&context_menu, &selection, NULL, false);
1017 switch (selection)
1019 case CTX_PLAY:
1020 play_mode = MODE_PLAY;
1021 done = true;
1022 break;
1024 case CTX_ADD_BLACK:
1025 play_mode = MODE_ADD_BLACK;
1026 done = true;
1027 break;
1029 case CTX_ADD_WHITE:
1030 play_mode = MODE_ADD_WHITE;
1031 done = true;
1032 break;
1034 case CTX_ERASE:
1035 play_mode = MODE_REMOVE;
1036 done = true;
1037 break;
1039 case CTX_PASS:
1040 if (!play_move_sgf (PASS_POS, current_player))
1042 rb->splash (HZ, "Error while passing!");
1044 done = true;
1045 break;
1047 case CTX_NEXT_VAR:
1048 if ((temp = next_variation_sgf ()) >= 0)
1050 draw_screen_display ();
1051 rb->splashf (2 * HZ / 3, "%d of %d", temp,
1052 num_variations_sgf ());
1053 draw_screen_display ();
1055 else
1057 if (num_variations_sgf () > 1)
1059 rb->splashf (HZ, "Error %d in next_variation_sgf", temp);
1061 else
1063 rb->splash (HZ, "No next variation");
1066 break;
1068 case CTX_FORCE:
1069 play_mode = MODE_FORCE_PLAY;
1070 done = true;
1071 break;
1073 case CTX_MARK:
1074 play_mode = MODE_MARK;
1075 done = true;
1076 break;
1078 case CTX_CIRCLE:
1079 play_mode = MODE_CIRCLE;
1080 done = true;
1081 break;
1083 case CTX_SQUARE:
1084 play_mode = MODE_SQUARE;
1085 done = true;
1086 break;
1088 case CTX_TRIANGLE:
1089 play_mode = MODE_TRIANGLE;
1090 done = true;
1091 break;
1093 case CTX_LABEL:
1094 play_mode = MODE_LABEL;
1095 done = true;
1096 break;
1098 case CTX_COMMENT:
1099 if (!do_comment_edit ())
1101 DEBUGF ("Editing comment failed\n");
1102 rb->splash (HZ, "Read or write failed!\n");
1104 done = true;
1105 break;
1107 case CTX_DONE:
1108 case GO_TO_ROOT:
1109 case GO_TO_PREVIOUS:
1110 case MENU_ATTACHED_USB:
1111 default:
1112 done = true;
1113 break;
1118 enum options_menu_selections
1120 OMENU_SHOW_VARIATIONS = 0,
1121 OMENU_DISABLE_POWEROFF,
1122 OMENU_AUTOSAVE_TIME,
1123 OMENU_AUTO_COMMENT
1126 static void
1127 do_options_menu (void)
1129 int selection;
1130 bool done = false;
1132 MENUITEM_STRINGLIST (options_menu, "Options Menu", NULL,
1133 "Show Child Variations?",
1134 "Disable Idle Poweroff?",
1135 "Idle Autosave Time",
1136 "Automatically Show Comments?");
1138 while (!done)
1140 selection = rb->do_menu (&options_menu, &selection, NULL, false);
1142 switch (selection)
1144 case OMENU_SHOW_VARIATIONS:
1145 rb->set_bool("Draw Variations?", &draw_variations);
1146 clear_marks_display ();
1147 set_all_marks_sgf ();
1148 if (draw_variations)
1150 mark_child_variations_sgf ();
1152 break;
1154 case OMENU_DISABLE_POWEROFF:
1155 rb->set_bool("Disable Idle Poweroff?", &disable_shutdown);
1156 break;
1158 case OMENU_AUTOSAVE_TIME:
1159 rb->set_int("Idle Autosave Time", "minutes", UNIT_INT,
1160 &autosave_time, NULL, 1, 0, MAX_AUTOSAVE,
1161 &autosave_formatter);
1162 autosave_counter = 0;
1163 break;
1165 case OMENU_AUTO_COMMENT:
1166 rb->set_bool("Auto Show Comments?", &auto_show_comments);
1167 break;
1169 case GO_TO_ROOT:
1170 case GO_TO_PREVIOUS:
1171 case MENU_ATTACHED_USB:
1172 default:
1173 done = true;
1174 break;
1180 static bool
1181 do_comment_edit (void)
1183 char cbuffer[512];
1185 rb->memset (cbuffer, 0, sizeof (cbuffer));
1187 if (read_comment_sgf (cbuffer, sizeof (cbuffer)) < 0)
1189 return false;
1192 if (!rb->kbd_input (cbuffer, sizeof (cbuffer)))
1194 /* user didn't edit, no reason to write it back */
1195 return true;
1198 if (write_comment_sgf (cbuffer) < 0)
1200 return false;
1203 return true;
1206 static enum prop_type_t
1207 menu_selection_to_prop (int selection)
1209 switch (selection)
1211 case GINFO_OVERTIME:
1212 return PROP_OVERTIME;
1213 case GINFO_RESULT:
1214 return PROP_RESULT;
1215 case GINFO_BLACK_PLAYER:
1216 return PROP_BLACK_NAME;
1217 case GINFO_BLACK_RANK:
1218 return PROP_BLACK_RANK;
1219 case GINFO_BLACK_TEAM:
1220 return PROP_BLACK_TEAM;
1221 case GINFO_WHITE_PLAYER:
1222 return PROP_WHITE_NAME;
1223 case GINFO_WHITE_RANK:
1224 return PROP_WHITE_RANK;
1225 case GINFO_WHITE_TEAM:
1226 return PROP_WHITE_TEAM;
1227 case GINFO_DATE:
1228 return PROP_DATE;
1229 case GINFO_EVENT:
1230 return PROP_EVENT;
1231 case GINFO_PLACE:
1232 return PROP_PLACE;
1233 case GINFO_ROUND:
1234 return PROP_ROUND;
1235 default:
1236 DEBUGF ("Tried to get prop from invalid menu selection!!!\n");
1237 return PROP_PLACE; /* just pick one, there's a major bug if
1238 we got here */