cb94b3ae2e
rb core allows you to load custom keyboard layouts this patch adds the ability to load a keyboard layout in a buffer the custom layout is temporary and does not overwrite the current layout use like so: unsigned short kbd[64]; unsigned short *kbd_p = kbd; if (!kbd_create_layout("ABCD1234\n", kbd, sizeof(kbd))) kbd_p = NULL; rb->kbd_input(buf,sizeof(buf), kbd_p); Change-Id: I7be2bd4a1b4797a147fa70228a9749dc56ac052a
1242 lines
31 KiB
C
1242 lines
31 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2007-2009 Joshua Simmons <mud at majidejima dot com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include "plugin.h"
|
|
#include "lib/playback_control.h"
|
|
#include "lib/configfile.h"
|
|
|
|
|
|
|
|
#include "goban.h"
|
|
#include "game.h"
|
|
#include "board.h"
|
|
#include "display.h"
|
|
#include "sgf.h"
|
|
#include "sgf_storage.h"
|
|
#include "types.h"
|
|
#include "util.h"
|
|
|
|
enum play_mode_t play_mode = MODE_PLAY;
|
|
|
|
#if defined(GBN_BUTTON_NAV_MODE)
|
|
|
|
#define NAV_MODE_BOARD 0
|
|
#define NAV_MODE_TREE 1
|
|
|
|
int nav_mode = NAV_MODE_BOARD;
|
|
|
|
#endif
|
|
|
|
/* the stack that uses this buffer is used for both storing intersections
|
|
* in board algorithms (could store one short for each board location), and
|
|
* for parsing (could store an indefinite number of ints, related to how
|
|
* many levels deep branches go (in other words, how many branches off of
|
|
* branches there are). 50 should take care of any reasonable file.
|
|
*/
|
|
#define PARSE_STACK_BUFFER_SIZE (max(MAX_BOARD_SIZE * MAX_BOARD_SIZE * sizeof(unsigned short), 50 * sizeof(int)))
|
|
|
|
/* used in SGF file parsing and outputting as well as in liberty counting
|
|
and capturing/uncapturing */
|
|
struct stack_t parse_stack;
|
|
char parse_stack_buffer[PARSE_STACK_BUFFER_SIZE];
|
|
|
|
static void global_setup (void);
|
|
static void global_cleanup (void);
|
|
|
|
static bool do_main_menu (void);
|
|
static void do_gameinfo_menu (void);
|
|
static enum prop_type_t menu_selection_to_prop (int selection);
|
|
static void do_context_menu (void);
|
|
static void do_options_menu (void);
|
|
|
|
static bool do_comment_edit (void);
|
|
static bool do_zoom (void);
|
|
static void set_defaults (void);
|
|
|
|
bool auto_show_comments = true;
|
|
bool disable_shutdown = false;
|
|
unsigned int autosave_time = 0;
|
|
unsigned int autosave_counter = 0;
|
|
|
|
#define SETTINGS_VERSION 2
|
|
#define SETTINGS_MIN_VERSION 1
|
|
#define SETTINGS_FILENAME "goban.cfg"
|
|
|
|
static struct configdata config[] =
|
|
{ /* INT in MAX_INT_SIZE is intersection, not integer */
|
|
{TYPE_INT, 0, MAX_INT_SIZE,
|
|
{ .int_p = &saved_circle_size },
|
|
"stone size", NULL},
|
|
{TYPE_BOOL, 0, 1,
|
|
{ .bool_p = &draw_variations },
|
|
"draw variations",
|
|
NULL},
|
|
{TYPE_BOOL, 0, 1,
|
|
{ .bool_p = &auto_show_comments },
|
|
"auto show comments",
|
|
NULL},
|
|
{TYPE_BOOL, 0, 1,
|
|
{ .bool_p = &disable_shutdown },
|
|
"disable shutdown",
|
|
NULL},
|
|
{TYPE_INT, 0, MAX_AUTOSAVE,
|
|
{ .int_p = &autosave_time },
|
|
"autosave time",
|
|
NULL}
|
|
};
|
|
|
|
static void
|
|
set_defaults (void)
|
|
{
|
|
saved_circle_size = 0;
|
|
draw_variations = true;
|
|
auto_show_comments = true;
|
|
|
|
disable_shutdown = false;
|
|
autosave_time = 7;
|
|
}
|
|
|
|
static const char*
|
|
komi_formatter (char *dest, size_t size, int menu_item, const char *unknown)
|
|
{
|
|
(void) unknown;
|
|
snprint_fixed (dest, size, menu_item);
|
|
return dest;
|
|
}
|
|
|
|
static const char*
|
|
ruleset_formatter (char *dest, size_t size, int menu_item, const char *unknown)
|
|
{
|
|
(void)dest, (void)size, (void)unknown;
|
|
return ruleset_names[menu_item];
|
|
}
|
|
|
|
static const char*
|
|
autosave_formatter (char *dest, size_t size, int menu_item, const char *
|
|
unknown)
|
|
{
|
|
(void) unknown;
|
|
if (menu_item == 0)
|
|
{
|
|
return "Off";
|
|
}
|
|
else
|
|
{
|
|
rb->snprintf (dest, size, "%d minute%s", menu_item,
|
|
menu_item == 1 ? "" : "s");
|
|
return dest;
|
|
}
|
|
}
|
|
|
|
static const char*
|
|
time_formatter (char *dest, size_t size, int menu_item, const char *unknown)
|
|
{
|
|
int time_values[4]; /* days hours minutes seconds */
|
|
int min_set, max_set;
|
|
int temp;
|
|
|
|
(void) unknown;
|
|
|
|
time_values[0] = menu_item / (24 * 60 * 60);
|
|
menu_item %= (24 * 60 * 60);
|
|
time_values[1] = menu_item / (60 * 60);
|
|
menu_item %= (60 * 60);
|
|
time_values[2] = menu_item / 60;
|
|
time_values[3] = menu_item % 60;
|
|
|
|
min_set = 500;
|
|
max_set = -1;
|
|
int i;
|
|
for (i = 0;
|
|
(unsigned int) i < (sizeof (time_values) / sizeof (time_values[0]));
|
|
++i)
|
|
{
|
|
if (time_values[i])
|
|
{
|
|
if (i < min_set)
|
|
{
|
|
min_set = i;
|
|
}
|
|
|
|
if (i > max_set)
|
|
{
|
|
max_set = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (max_set == -1)
|
|
{
|
|
return "0";
|
|
}
|
|
|
|
for (i = min_set; i <= 3; ++i)
|
|
{
|
|
if (i <= max_set)
|
|
{
|
|
if (i == 0 || i == 1 || i == min_set)
|
|
{
|
|
rb->snprintf (dest, size, "%d", time_values[i]);
|
|
}
|
|
else
|
|
{
|
|
rb->snprintf (dest, size, "%02d", time_values[i]);
|
|
}
|
|
temp = rb->strlen (dest);
|
|
dest += temp;
|
|
size -= temp;
|
|
}
|
|
else if (i != 3)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (i == 0) /* days */
|
|
{
|
|
rb->snprintf (dest, size, " d ");
|
|
}
|
|
else if (i == 3) /* seconds, print the final units */
|
|
{
|
|
if (min_set == 0 || min_set == 1)
|
|
{
|
|
rb->snprintf (dest, size, " h");
|
|
}
|
|
else if (min_set == 2)
|
|
{
|
|
rb->snprintf (dest, size, " m");
|
|
}
|
|
else
|
|
{
|
|
rb->snprintf (dest, size, " s");
|
|
}
|
|
}
|
|
else if (i != max_set)
|
|
{
|
|
rb->snprintf (dest, size, ":");
|
|
}
|
|
|
|
temp = rb->strlen (dest);
|
|
dest += temp;
|
|
size -= temp;
|
|
}
|
|
return dest;
|
|
}
|
|
|
|
enum plugin_status
|
|
plugin_start (const void *parameter)
|
|
{
|
|
int btn;
|
|
|
|
rb->mkdir (DEFAULT_SAVE_DIR);
|
|
|
|
global_setup ();
|
|
|
|
#ifdef GBN_TEST
|
|
run_tests ();
|
|
return PLUGIN_OK;
|
|
#endif
|
|
|
|
if (!(parameter && load_game (parameter)))
|
|
{
|
|
if (parameter)
|
|
{
|
|
rb->splashf (2 * HZ, "Loading %s failed.", (char *) parameter);
|
|
}
|
|
|
|
if (!load_game (DEFAULT_SAVE))
|
|
{
|
|
rb->strcpy (save_file, DEFAULT_SAVE);
|
|
|
|
if (!setup_game (MAX_BOARD_SIZE, MAX_BOARD_SIZE, 0, 0))
|
|
{
|
|
return PLUGIN_ERROR;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* game loaded */
|
|
if (rb->strcmp (save_file, DEFAULT_SAVE))
|
|
{
|
|
/* delete the scratch file if we loaded a game and it wasn't
|
|
* from the scratch file
|
|
*/
|
|
rb->remove (DEFAULT_SAVE);
|
|
}
|
|
}
|
|
|
|
draw_screen_display ();
|
|
|
|
autosave_counter = 0;
|
|
for (;;)
|
|
{
|
|
btn = rb->button_get_w_tmo (HZ * 30);
|
|
|
|
if (disable_shutdown)
|
|
{
|
|
/* tell rockbox we're not idle */
|
|
rb->reset_poweroff_timer ();
|
|
}
|
|
|
|
bool is_idle = false;
|
|
|
|
switch (btn)
|
|
{
|
|
|
|
#if defined(GBN_BUTTON_NAV_MODE)
|
|
case GBN_BUTTON_NAV_MODE:
|
|
case GBN_BUTTON_NAV_MODE | BUTTON_REPEAT:
|
|
if (nav_mode == NAV_MODE_TREE)
|
|
{
|
|
nav_mode = NAV_MODE_BOARD;
|
|
rb->splash (2 * HZ / 3, "board navigation mode");
|
|
draw_screen_display ();
|
|
}
|
|
else
|
|
{
|
|
nav_mode = NAV_MODE_TREE;
|
|
rb->splash (2 * HZ / 3, "tree navigation mode");
|
|
draw_screen_display ();
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#if defined(GBN_BUTTON_ADVANCE)
|
|
case GBN_BUTTON_ADVANCE:
|
|
case GBN_BUTTON_ADVANCE | BUTTON_REPEAT:
|
|
if (has_more_nodes_sgf ())
|
|
{
|
|
if (!redo_node_sgf ())
|
|
{
|
|
rb->splash (2 * HZ, "redo failed");
|
|
}
|
|
draw_screen_display ();
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#if defined(GBN_BUTTON_RETREAT)
|
|
case GBN_BUTTON_RETREAT:
|
|
case GBN_BUTTON_RETREAT | BUTTON_REPEAT:
|
|
if (has_prev_nodes_sgf ())
|
|
{
|
|
if (!undo_node_sgf ())
|
|
{
|
|
rb->splash (3 * HZ / 2, "Undo Failed");
|
|
}
|
|
draw_screen_display ();
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
case GBN_BUTTON_PLAY:
|
|
if (play_mode == MODE_PLAY || play_mode == MODE_FORCE_PLAY)
|
|
{
|
|
if (!play_move_sgf (cursor_pos, current_player))
|
|
{
|
|
rb->splash (HZ / 3, "Illegal Move");
|
|
}
|
|
}
|
|
else if (play_mode == MODE_ADD_BLACK)
|
|
{
|
|
if (!add_stone_sgf (cursor_pos, BLACK))
|
|
{
|
|
rb->splash (HZ / 3, "Illegal");
|
|
}
|
|
}
|
|
else if (play_mode == MODE_ADD_WHITE)
|
|
{
|
|
if (!add_stone_sgf (cursor_pos, WHITE))
|
|
{
|
|
rb->splash (HZ / 3, "Illegal");
|
|
}
|
|
}
|
|
else if (play_mode == MODE_REMOVE)
|
|
{
|
|
if (!add_stone_sgf (cursor_pos, EMPTY))
|
|
{
|
|
rb->splash (HZ / 3, "Illegal");
|
|
}
|
|
}
|
|
else if (play_mode == MODE_MARK)
|
|
{
|
|
if (!add_mark_sgf (cursor_pos, PROP_MARK))
|
|
{
|
|
rb->splash (HZ / 3, "Couldn't Mark");
|
|
}
|
|
}
|
|
else if (play_mode == MODE_CIRCLE)
|
|
{
|
|
if (!add_mark_sgf (cursor_pos, PROP_CIRCLE))
|
|
{
|
|
rb->splash (HZ / 3, "Couldn't Mark");
|
|
}
|
|
}
|
|
else if (play_mode == MODE_SQUARE)
|
|
{
|
|
if (!add_mark_sgf (cursor_pos, PROP_SQUARE))
|
|
{
|
|
rb->splash (HZ / 3, "Couldn't Mark");
|
|
}
|
|
}
|
|
else if (play_mode == MODE_TRIANGLE)
|
|
{
|
|
if (!add_mark_sgf (cursor_pos, PROP_TRIANGLE))
|
|
{
|
|
rb->splash (HZ / 3, "Couldn't Mark");
|
|
}
|
|
}
|
|
else if (play_mode == MODE_LABEL)
|
|
{
|
|
if (!add_mark_sgf (cursor_pos, PROP_LABEL))
|
|
{
|
|
rb->splash (HZ / 3, "Couldn't Label");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rb->splash (HZ, "mode not implemented");
|
|
}
|
|
|
|
draw_screen_display ();
|
|
break;
|
|
|
|
case GBN_BUTTON_RIGHT:
|
|
case GBN_BUTTON_RIGHT | BUTTON_REPEAT:
|
|
#if defined(GBN_BUTTON_NAV_MODE)
|
|
if (nav_mode == NAV_MODE_TREE)
|
|
{
|
|
if (has_more_nodes_sgf ())
|
|
{
|
|
if (!redo_node_sgf ())
|
|
{
|
|
rb->splash (2 * HZ, "Redo Failed");
|
|
}
|
|
draw_screen_display ();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
cursor_pos = WRAP (EAST (cursor_pos));
|
|
draw_screen_display ();
|
|
#if defined(GBN_BUTTON_NAV_MODE)
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case GBN_BUTTON_LEFT:
|
|
case GBN_BUTTON_LEFT | BUTTON_REPEAT:
|
|
#if defined(GBN_BUTTON_NAV_MODE)
|
|
if (nav_mode == NAV_MODE_TREE)
|
|
{
|
|
if (has_prev_nodes_sgf ())
|
|
{
|
|
if (!undo_node_sgf ())
|
|
{
|
|
rb->splash (2 * HZ, "Undo Failed");
|
|
}
|
|
draw_screen_display ();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
cursor_pos = WRAP (WEST (cursor_pos));
|
|
draw_screen_display ();
|
|
#if defined(GBN_BUTTON_NAV_MODE)
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case GBN_BUTTON_DOWN:
|
|
case GBN_BUTTON_DOWN | BUTTON_REPEAT:
|
|
cursor_pos = WRAP (SOUTH (cursor_pos));
|
|
draw_screen_display ();
|
|
break;
|
|
|
|
case GBN_BUTTON_UP:
|
|
case GBN_BUTTON_UP | BUTTON_REPEAT:
|
|
cursor_pos = WRAP (NORTH (cursor_pos));
|
|
draw_screen_display ();
|
|
break;
|
|
|
|
case GBN_BUTTON_MENU:
|
|
if (do_main_menu ())
|
|
{
|
|
save_game (DEFAULT_SAVE);
|
|
|
|
global_cleanup ();
|
|
return PLUGIN_OK;
|
|
}
|
|
|
|
draw_screen_display ();
|
|
break;
|
|
|
|
#if defined(GBN_BUTTON_CONTEXT)
|
|
case GBN_BUTTON_CONTEXT:
|
|
do_context_menu ();
|
|
draw_screen_display ();
|
|
break;
|
|
#endif
|
|
|
|
#if defined(GBN_BUTTON_NEXT_VAR)
|
|
case GBN_BUTTON_NEXT_VAR:
|
|
case GBN_BUTTON_NEXT_VAR | BUTTON_REPEAT:
|
|
{
|
|
int temp;
|
|
if ((temp = next_variation_sgf ()) >= 0)
|
|
{
|
|
draw_screen_display ();
|
|
rb->splashf (2 * HZ / 3, "%d of %d", temp,
|
|
num_variations_sgf ());
|
|
draw_screen_display ();
|
|
}
|
|
else
|
|
{
|
|
if (num_variations_sgf () > 1)
|
|
{
|
|
rb->splashf (HZ, "Error %d in next_variation_sgf", temp);
|
|
}
|
|
draw_screen_display ();
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
case BUTTON_NONE:
|
|
is_idle = true;
|
|
default:
|
|
if (rb->default_event_handler (btn) == SYS_USB_CONNECTED)
|
|
{
|
|
return PLUGIN_USB_CONNECTED;
|
|
}
|
|
break;
|
|
};
|
|
|
|
if (is_idle && autosave_dirty)
|
|
{
|
|
++autosave_counter;
|
|
|
|
if (autosave_time != 0 &&
|
|
autosave_counter / 2 >= autosave_time)
|
|
/* counter is in 30 second increments, autosave_time is in
|
|
* minutes
|
|
*/
|
|
{
|
|
DEBUGF("autosaving\n");
|
|
rb->splash(HZ / 4, "Autosaving...");
|
|
save_game(DEFAULT_SAVE);
|
|
draw_screen_display();
|
|
autosave_counter = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
autosave_counter = 0;
|
|
}
|
|
}
|
|
|
|
return PLUGIN_OK;
|
|
}
|
|
|
|
static void
|
|
global_cleanup (void)
|
|
{
|
|
cleanup_sgf ();
|
|
configfile_save(SETTINGS_FILENAME, config,
|
|
sizeof(config)/sizeof(*config),
|
|
SETTINGS_VERSION);
|
|
}
|
|
|
|
static void
|
|
global_setup (void)
|
|
{
|
|
setup_stack (&parse_stack, parse_stack_buffer,
|
|
sizeof (parse_stack_buffer));
|
|
setup_display ();
|
|
setup_sgf ();
|
|
|
|
set_defaults();
|
|
if (configfile_load(SETTINGS_FILENAME, config,
|
|
sizeof(config)/sizeof(*config),
|
|
SETTINGS_MIN_VERSION ) < 0)
|
|
{
|
|
/* If the loading failed, save a new config file (as the disk is
|
|
already spinning) */
|
|
|
|
/* set defaults again just in case (don't know if they can ever
|
|
* be messed up by configfile_load, and it's basically free anyway)
|
|
*/
|
|
set_defaults();
|
|
|
|
configfile_save(SETTINGS_FILENAME, config,
|
|
sizeof(config)/sizeof(*config),
|
|
SETTINGS_VERSION);
|
|
}
|
|
}
|
|
|
|
enum main_menu_selections
|
|
{
|
|
MAIN_NEW = 0,
|
|
MAIN_SAVE,
|
|
MAIN_SAVE_AS,
|
|
MAIN_GAME_INFO,
|
|
MAIN_PLAYBACK,
|
|
MAIN_ZOOM,
|
|
MAIN_OPTIONS,
|
|
MAIN_CONTEXT,
|
|
MAIN_QUIT
|
|
};
|
|
|
|
static bool
|
|
do_main_menu (void)
|
|
{
|
|
int selection = 0;
|
|
MENUITEM_STRINGLIST (menu, "Rockbox Goban", NULL,
|
|
"New",
|
|
"Save",
|
|
"Save As",
|
|
"Game Info",
|
|
"Playback Control",
|
|
"Zoom Level",
|
|
"Options",
|
|
"Context Menu",
|
|
"Quit");
|
|
|
|
/* for "New" in menu */
|
|
int new_handi = 0, new_bs = MAX_BOARD_SIZE, new_komi = 15;
|
|
|
|
char new_save_file[SAVE_FILE_LENGTH];
|
|
|
|
|
|
bool done = false;
|
|
|
|
while (!done)
|
|
{
|
|
selection = rb->do_menu (&menu, &selection, NULL, false);
|
|
|
|
switch (selection)
|
|
{
|
|
case MAIN_NEW:
|
|
rb->set_int ("board size", "lines", UNIT_INT,
|
|
&new_bs, NULL, 1, MIN_BOARD_SIZE, MAX_BOARD_SIZE,
|
|
NULL);
|
|
|
|
rb->set_int ("handicap", "stones", UNIT_INT,
|
|
&new_handi, NULL, 1, 0, 9, NULL);
|
|
|
|
if (new_handi > 0)
|
|
{
|
|
new_komi = 1;
|
|
}
|
|
else
|
|
{
|
|
new_komi = 13;
|
|
}
|
|
|
|
rb->set_int ("komi", "moku", UNIT_INT, &new_komi, NULL,
|
|
1, -300, 300, &komi_formatter);
|
|
|
|
setup_game (new_bs, new_bs, new_handi, new_komi);
|
|
draw_screen_display ();
|
|
done = true;
|
|
break;
|
|
|
|
case MAIN_SAVE:
|
|
if (!save_game (save_file))
|
|
{
|
|
rb->splash (2 * HZ, "Save Failed!");
|
|
}
|
|
else
|
|
{
|
|
rb->splash (2 * HZ / 3, "Saved");
|
|
}
|
|
done = true;
|
|
draw_screen_display ();
|
|
break;
|
|
|
|
case MAIN_SAVE_AS:
|
|
rb->strcpy (new_save_file, save_file);
|
|
|
|
if (!rb->kbd_input(new_save_file, SAVE_FILE_LENGTH, NULL))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!save_game (new_save_file))
|
|
{
|
|
rb->splash (2 * HZ, "Save Failed!");
|
|
}
|
|
else
|
|
{
|
|
rb->strcpy (save_file, new_save_file);
|
|
rb->splash (2 * HZ / 3, "Saved");
|
|
}
|
|
|
|
done = true;
|
|
draw_screen_display ();
|
|
break;
|
|
|
|
case MAIN_GAME_INFO:
|
|
do_gameinfo_menu ();
|
|
break;
|
|
|
|
case MAIN_PLAYBACK:
|
|
if (!audio_stolen_sgf ())
|
|
{
|
|
playback_control (NULL);
|
|
}
|
|
else
|
|
{
|
|
rb->splash (1 * HZ, "Audio has been disabled!");
|
|
}
|
|
break;
|
|
|
|
case MAIN_ZOOM:
|
|
if (do_zoom ())
|
|
{
|
|
return true;
|
|
}
|
|
done = true;
|
|
draw_screen_display ();
|
|
break;
|
|
|
|
case MAIN_OPTIONS:
|
|
do_options_menu();
|
|
break;
|
|
|
|
case MAIN_CONTEXT:
|
|
do_context_menu ();
|
|
done = true;
|
|
break;
|
|
|
|
case MAIN_QUIT:
|
|
case MENU_ATTACHED_USB:
|
|
return true;
|
|
|
|
case GO_TO_ROOT:
|
|
case GO_TO_PREVIOUS:
|
|
default:
|
|
|
|
done = true;
|
|
break;
|
|
};
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
zoom_preview (int current)
|
|
{
|
|
set_zoom_display (current);
|
|
draw_screen_display ();
|
|
rb->splash (0, "Preview");
|
|
}
|
|
|
|
static bool
|
|
do_zoom (void)
|
|
{
|
|
unsigned int zoom_level;
|
|
unsigned int old_val;
|
|
bool done = false;
|
|
|
|
unsigned int min_val = min_zoom_display ();
|
|
unsigned int max_val = max_zoom_display ();
|
|
|
|
zoom_level = old_val = current_zoom_display ();
|
|
|
|
int action;
|
|
|
|
zoom_preview (zoom_level);
|
|
while (!done)
|
|
{
|
|
switch (action = rb->get_action (CONTEXT_LIST, TIMEOUT_BLOCK))
|
|
{
|
|
case ACTION_STD_OK:
|
|
set_zoom_display (zoom_level);
|
|
done = true;
|
|
rb->splash (HZ / 2, "Zoom Set");
|
|
saved_circle_size = intersection_size;
|
|
break;
|
|
|
|
case ACTION_STD_CANCEL:
|
|
set_zoom_display (old_val);
|
|
done = true;
|
|
rb->splash (HZ / 2, "Cancelled");
|
|
break;
|
|
|
|
case ACTION_STD_CONTEXT:
|
|
zoom_level = old_val;
|
|
zoom_preview (zoom_level);
|
|
break;
|
|
|
|
case ACTION_STD_NEXT:
|
|
case ACTION_STD_NEXTREPEAT:
|
|
zoom_level = zoom_level * 3 / 2;
|
|
|
|
/* 1 * 3 / 2 is 1 again... */
|
|
if (zoom_level == 1)
|
|
{
|
|
++zoom_level;
|
|
}
|
|
|
|
if (zoom_level > max_val)
|
|
{
|
|
zoom_level = min_val;
|
|
}
|
|
|
|
zoom_preview (zoom_level);
|
|
break;
|
|
|
|
case ACTION_STD_PREV:
|
|
case ACTION_STD_PREVREPEAT:
|
|
zoom_level = zoom_level * 2 / 3;
|
|
|
|
if (zoom_level < min_val)
|
|
{
|
|
zoom_level = max_val;
|
|
}
|
|
zoom_preview (zoom_level);
|
|
break;
|
|
|
|
case ACTION_NONE:
|
|
break;
|
|
|
|
default:
|
|
if (rb->default_event_handler (action) == SYS_USB_CONNECTED)
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
enum gameinfo_menu_selections
|
|
{
|
|
GINFO_BASIC_INFO = 0,
|
|
GINFO_TIME_LIMIT,
|
|
GINFO_OVERTIME,
|
|
GINFO_RESULT,
|
|
GINFO_HANDICAP,
|
|
GINFO_KOMI,
|
|
GINFO_RULESET,
|
|
GINFO_BLACK_PLAYER,
|
|
GINFO_BLACK_RANK,
|
|
GINFO_BLACK_TEAM,
|
|
GINFO_WHITE_PLAYER,
|
|
GINFO_WHITE_RANK,
|
|
GINFO_WHITE_TEAM,
|
|
GINFO_DATE,
|
|
GINFO_EVENT,
|
|
GINFO_PLACE,
|
|
GINFO_ROUND,
|
|
GINFO_DONE
|
|
};
|
|
|
|
|
|
static void
|
|
do_gameinfo_menu (void)
|
|
{
|
|
MENUITEM_STRINGLIST (gameinfo_menu, "Game Info", NULL,
|
|
"Basic Info",
|
|
"Time Limit",
|
|
"Overtime",
|
|
"Result",
|
|
"Handicap",
|
|
"Komi",
|
|
"Ruleset",
|
|
"Black Player",
|
|
"Black Rank",
|
|
"Black Team",
|
|
"White Player",
|
|
"White Rank",
|
|
"White Team",
|
|
"Date",
|
|
"Event",
|
|
"Place",
|
|
"Round",
|
|
"Done");
|
|
/* IMPORTANT:
|
|
*
|
|
* if you edit this string list, make sure you keep
|
|
* menu_selection_to_prop function in line with it!! (see the bottom
|
|
* of this file).
|
|
*/
|
|
|
|
int new_ruleset = 0;
|
|
|
|
char *gameinfo_string;
|
|
int gameinfo_string_size;
|
|
|
|
bool done = false;
|
|
int selection = 0;
|
|
|
|
while (!done)
|
|
{
|
|
selection = rb->do_menu (&gameinfo_menu, &selection, NULL, false);
|
|
|
|
switch (selection)
|
|
{
|
|
case GINFO_OVERTIME:
|
|
case GINFO_RESULT:
|
|
case GINFO_BLACK_PLAYER:
|
|
case GINFO_BLACK_RANK:
|
|
case GINFO_BLACK_TEAM:
|
|
case GINFO_WHITE_PLAYER:
|
|
case GINFO_WHITE_RANK:
|
|
case GINFO_WHITE_TEAM:
|
|
case GINFO_DATE:
|
|
case GINFO_EVENT:
|
|
case GINFO_PLACE:
|
|
case GINFO_ROUND:
|
|
if (!get_header_string_and_size (&header,
|
|
menu_selection_to_prop
|
|
(selection), &gameinfo_string,
|
|
&gameinfo_string_size))
|
|
{
|
|
rb->splash (3 * HZ, "Couldn't get header string");
|
|
break;
|
|
}
|
|
|
|
rb->kbd_input(gameinfo_string, gameinfo_string_size, NULL);
|
|
sanitize_string (gameinfo_string);
|
|
set_game_modified();
|
|
break;
|
|
|
|
/* these need special handling in some way, so they are
|
|
separate */
|
|
|
|
case GINFO_BASIC_INFO:
|
|
metadata_summary();
|
|
break;
|
|
|
|
case GINFO_TIME_LIMIT:
|
|
rb->set_int ("Time Limit", "", UNIT_INT, &header.time_limit,
|
|
NULL, 60, 0, 24 * 60 * 60, &time_formatter);
|
|
set_game_modified();
|
|
break;
|
|
|
|
case GINFO_HANDICAP:
|
|
rb->splashf (0, "%d stones. Start a new game to set handicap",
|
|
header.handicap);
|
|
rb->action_userabort(TIMEOUT_BLOCK);
|
|
break;
|
|
|
|
case GINFO_KOMI:
|
|
rb->set_int ("Komi", "moku", UNIT_INT, &header.komi, NULL,
|
|
1, -300, 300, &komi_formatter);
|
|
set_game_modified();
|
|
break;
|
|
|
|
case GINFO_RULESET:
|
|
new_ruleset = 0;
|
|
rb->set_int ("Ruleset", "", UNIT_INT, &new_ruleset, NULL,
|
|
1, 0, NUM_RULESETS - 1, &ruleset_formatter);
|
|
|
|
rb->strcpy (header.ruleset, ruleset_names[new_ruleset]);
|
|
set_game_modified();
|
|
break;
|
|
|
|
case GINFO_DONE:
|
|
case GO_TO_ROOT:
|
|
case GO_TO_PREVIOUS:
|
|
case MENU_ATTACHED_USB:
|
|
default:
|
|
done = true;
|
|
break;
|
|
};
|
|
}
|
|
}
|
|
|
|
enum context_menu_selections
|
|
{
|
|
CTX_PLAY = 0,
|
|
CTX_ADD_BLACK,
|
|
CTX_ADD_WHITE,
|
|
CTX_ERASE,
|
|
CTX_PASS,
|
|
CTX_NEXT_VAR,
|
|
CTX_FORCE,
|
|
CTX_MARK,
|
|
CTX_CIRCLE,
|
|
CTX_SQUARE,
|
|
CTX_TRIANGLE,
|
|
CTX_LABEL,
|
|
CTX_COMMENT,
|
|
CTX_DONE
|
|
};
|
|
|
|
static void
|
|
do_context_menu (void)
|
|
{
|
|
int selection;
|
|
bool done = false;
|
|
int temp;
|
|
|
|
MENUITEM_STRINGLIST (context_menu, "Context Menu", NULL,
|
|
"Play Mode (default)",
|
|
"Add Black Mode",
|
|
"Add White Mode",
|
|
"Erase Stone Mode",
|
|
"Pass",
|
|
"Next Variation",
|
|
"Force Play Mode",
|
|
"Mark Mode",
|
|
"Circle Mode",
|
|
"Square Mode",
|
|
"Triangle Mode",
|
|
"Label Mode",
|
|
"Add/Edit Comment",
|
|
"Done");
|
|
|
|
while (!done)
|
|
{
|
|
selection = rb->do_menu (&context_menu, &selection, NULL, false);
|
|
|
|
switch (selection)
|
|
{
|
|
case CTX_PLAY:
|
|
play_mode = MODE_PLAY;
|
|
done = true;
|
|
break;
|
|
|
|
case CTX_ADD_BLACK:
|
|
play_mode = MODE_ADD_BLACK;
|
|
done = true;
|
|
break;
|
|
|
|
case CTX_ADD_WHITE:
|
|
play_mode = MODE_ADD_WHITE;
|
|
done = true;
|
|
break;
|
|
|
|
case CTX_ERASE:
|
|
play_mode = MODE_REMOVE;
|
|
done = true;
|
|
break;
|
|
|
|
case CTX_PASS:
|
|
if (!play_move_sgf (PASS_POS, current_player))
|
|
{
|
|
rb->splash (HZ, "Error while passing!");
|
|
}
|
|
done = true;
|
|
break;
|
|
|
|
case CTX_NEXT_VAR:
|
|
if ((temp = next_variation_sgf ()) >= 0)
|
|
{
|
|
draw_screen_display ();
|
|
rb->splashf (2 * HZ / 3, "%d of %d", temp,
|
|
num_variations_sgf ());
|
|
draw_screen_display ();
|
|
}
|
|
else
|
|
{
|
|
if (num_variations_sgf () > 1)
|
|
{
|
|
rb->splashf (HZ, "Error %d in next_variation_sgf", temp);
|
|
}
|
|
else
|
|
{
|
|
rb->splash (HZ, "No next variation");
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CTX_FORCE:
|
|
play_mode = MODE_FORCE_PLAY;
|
|
done = true;
|
|
break;
|
|
|
|
case CTX_MARK:
|
|
play_mode = MODE_MARK;
|
|
done = true;
|
|
break;
|
|
|
|
case CTX_CIRCLE:
|
|
play_mode = MODE_CIRCLE;
|
|
done = true;
|
|
break;
|
|
|
|
case CTX_SQUARE:
|
|
play_mode = MODE_SQUARE;
|
|
done = true;
|
|
break;
|
|
|
|
case CTX_TRIANGLE:
|
|
play_mode = MODE_TRIANGLE;
|
|
done = true;
|
|
break;
|
|
|
|
case CTX_LABEL:
|
|
play_mode = MODE_LABEL;
|
|
done = true;
|
|
break;
|
|
|
|
case CTX_COMMENT:
|
|
if (!do_comment_edit ())
|
|
{
|
|
DEBUGF ("Editing comment failed\n");
|
|
rb->splash (HZ, "Read or write failed!\n");
|
|
}
|
|
done = true;
|
|
break;
|
|
|
|
case CTX_DONE:
|
|
case GO_TO_ROOT:
|
|
case GO_TO_PREVIOUS:
|
|
case MENU_ATTACHED_USB:
|
|
default:
|
|
done = true;
|
|
break;
|
|
};
|
|
}
|
|
}
|
|
|
|
enum options_menu_selections
|
|
{
|
|
OMENU_SHOW_VARIATIONS = 0,
|
|
OMENU_DISABLE_POWEROFF,
|
|
OMENU_AUTOSAVE_TIME,
|
|
OMENU_AUTO_COMMENT
|
|
};
|
|
|
|
static void
|
|
do_options_menu (void)
|
|
{
|
|
int selection;
|
|
bool done = false;
|
|
|
|
MENUITEM_STRINGLIST (options_menu, "Options Menu", NULL,
|
|
"Show Child Variations?",
|
|
"Disable Idle Poweroff?",
|
|
"Idle Autosave Time",
|
|
"Automatically Show Comments?");
|
|
|
|
while (!done)
|
|
{
|
|
selection = rb->do_menu (&options_menu, &selection, NULL, false);
|
|
|
|
switch (selection)
|
|
{
|
|
case OMENU_SHOW_VARIATIONS:
|
|
rb->set_bool("Draw Variations?", &draw_variations);
|
|
clear_marks_display ();
|
|
set_all_marks_sgf ();
|
|
if (draw_variations)
|
|
{
|
|
mark_child_variations_sgf ();
|
|
}
|
|
break;
|
|
|
|
case OMENU_DISABLE_POWEROFF:
|
|
rb->set_bool("Disable Idle Poweroff?", &disable_shutdown);
|
|
break;
|
|
|
|
case OMENU_AUTOSAVE_TIME:
|
|
rb->set_int("Idle Autosave Time", "minutes", UNIT_INT,
|
|
&autosave_time, NULL, 1, 0, MAX_AUTOSAVE,
|
|
&autosave_formatter);
|
|
autosave_counter = 0;
|
|
break;
|
|
|
|
case OMENU_AUTO_COMMENT:
|
|
rb->set_bool("Auto Show Comments?", &auto_show_comments);
|
|
break;
|
|
|
|
case GO_TO_ROOT:
|
|
case GO_TO_PREVIOUS:
|
|
case MENU_ATTACHED_USB:
|
|
default:
|
|
done = true;
|
|
break;
|
|
};
|
|
}
|
|
|
|
}
|
|
|
|
static bool
|
|
do_comment_edit (void)
|
|
{
|
|
char cbuffer[512];
|
|
|
|
rb->memset (cbuffer, 0, sizeof (cbuffer));
|
|
|
|
if (read_comment_sgf (cbuffer, sizeof (cbuffer)) < 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!rb->kbd_input (cbuffer, sizeof (cbuffer), NULL))
|
|
{
|
|
/* user didn't edit, no reason to write it back */
|
|
return true;
|
|
}
|
|
|
|
if (write_comment_sgf (cbuffer) < 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static enum prop_type_t
|
|
menu_selection_to_prop (int selection)
|
|
{
|
|
switch (selection)
|
|
{
|
|
case GINFO_OVERTIME:
|
|
return PROP_OVERTIME;
|
|
case GINFO_RESULT:
|
|
return PROP_RESULT;
|
|
case GINFO_BLACK_PLAYER:
|
|
return PROP_BLACK_NAME;
|
|
case GINFO_BLACK_RANK:
|
|
return PROP_BLACK_RANK;
|
|
case GINFO_BLACK_TEAM:
|
|
return PROP_BLACK_TEAM;
|
|
case GINFO_WHITE_PLAYER:
|
|
return PROP_WHITE_NAME;
|
|
case GINFO_WHITE_RANK:
|
|
return PROP_WHITE_RANK;
|
|
case GINFO_WHITE_TEAM:
|
|
return PROP_WHITE_TEAM;
|
|
case GINFO_DATE:
|
|
return PROP_DATE;
|
|
case GINFO_EVENT:
|
|
return PROP_EVENT;
|
|
case GINFO_PLACE:
|
|
return PROP_PLACE;
|
|
case GINFO_ROUND:
|
|
return PROP_ROUND;
|
|
default:
|
|
DEBUGF ("Tried to get prop from invalid menu selection!!!\n");
|
|
return PROP_PLACE; /* just pick one, there's a major bug if
|
|
we got here */
|
|
};
|
|
}
|