From fa3abef5abe95dd9668a87b1cc57a724dcbf6354 Mon Sep 17 00:00:00 2001 From: simon Date: Sat, 6 Sep 2008 09:27:56 +0000 Subject: [PATCH] New infrastructure feature. Games are now permitted to be _conditionally_ able to format the current puzzle as text to be sent to the clipboard. For instance, if a game were to support playing on a square grid and on other kinds of grid such as hexagonal, then it might reasonably feel that only the former could be sensibly rendered in ASCII art; so it can now arrange for the "Copy" menu item to be greyed out depending on the game_params. To do this I've introduced a new backend function (can_format_as_text_now()), and renamed the existing static backend field "can_format_as_text" to "can_format_as_text_ever". The latter will cause compile errors for anyone maintaining a third-party front end; if any such person is reading this, I apologise to them for the inconvenience, but I did do it deliberately so that they'd know to update their front end. As yet, no checked-in game actually uses this feature; all current games can still either copy always or copy never. git-svn-id: svn://svn.tartarus.org/sgt/puzzles@8161 cda61777-01e9-0310-a592-d414129be87e --- blackbox.c | 7 +++++- bridges.c | 7 +++++- cube.c | 7 +++++- devel.but | 68 ++++++++++++++++++++++++++++++++++++++++++--------- dominosa.c | 7 +++++- fifteen.c | 7 +++++- filling.c | 7 +++++- flip.c | 7 +++++- galaxies.c | 7 +++++- gtk.c | 33 +++++++++++++++++++------ guess.c | 7 +++++- inertia.c | 7 +++++- lightup.c | 7 +++++- loopy.c | 7 +++++- map.c | 7 +++++- midend.c | 11 ++++++++- mines.c | 7 +++++- net.c | 7 +++++- netslide.c | 7 +++++- nullgame.c | 7 +++++- osx.m | 3 ++- pattern.c | 7 +++++- pegs.c | 7 +++++- puzzles.h | 4 ++- rect.c | 7 +++++- samegame.c | 7 +++++- sixteen.c | 7 +++++- slant.c | 7 +++++- solo.c | 7 +++++- tents.c | 7 +++++- twiddle.c | 7 +++++- unequal.c | 7 +++++- unfinished/pearl.c | 7 +++++- unfinished/separate.c | 7 +++++- unfinished/slide.c | 7 +++++- unfinished/sokoban.c | 7 +++++- untangle.c | 7 +++++- windows.c | 15 ++++++++++-- 38 files changed, 303 insertions(+), 55 deletions(-) diff --git a/blackbox.c b/blackbox.c index 458c0b8..a8c6d5a 100644 --- a/blackbox.c +++ b/blackbox.c @@ -463,6 +463,11 @@ static char *solve_game(game_state *state, game_state *currstate, return dupstr("S"); } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { return NULL; @@ -1413,7 +1418,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - FALSE, game_text_format, + FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/bridges.c b/bridges.c index d56f287..1829681 100644 --- a/bridges.c +++ b/bridges.c @@ -147,6 +147,11 @@ static void fixup_islands_for_realloc(game_state *state) } } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { int x, y, len, nl; @@ -2644,7 +2649,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - TRUE, game_text_format, + TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/cube.c b/cube.c index bc91973..5df5fcb 100644 --- a/cube.c +++ b/cube.c @@ -985,6 +985,11 @@ static char *solve_game(game_state *state, game_state *currstate, return NULL; } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { return NULL; @@ -1715,7 +1720,7 @@ const struct game thegame = { dup_game, free_game, FALSE, solve_game, - FALSE, game_text_format, + FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/devel.but b/devel.but index ea23d52..11d198d 100644 --- a/devel.but +++ b/devel.but @@ -1365,17 +1365,50 @@ called. \H{backend-misc} Miscellaneous -\S{backend-can-format-as-text} \c{can_format_as_text} +\S{backend-can-format-as-text-ever} \c{can_format_as_text_ever} -\c int can_format_as_text; +\c int can_format_as_text_ever; This boolean field is \cw{TRUE} if the game supports formatting a game state as ASCII text (typically ASCII art) for copying to the clipboard and pasting into other applications. If it is \cw{FALSE}, front ends will not offer the \q{Copy} command at all. -If this field is \cw{FALSE}, the function \cw{text_format()} -(\k{backend-text-format}) is not expected to do anything at all. +If this field is \cw{TRUE}, the game does not necessarily have to +support text formatting for \e{all} games: e.g. a game which can be +played on a square grid or a triangular one might only support copy +and paste for the former, because triangular grids in ASCII art are +just too difficult. + +If this field is \cw{FALSE}, the functions +\cw{can_format_as_text_now()} (\k{backend-can-format-as-text-now}) +and \cw{text_format()} (\k{backend-text-format}) are never called. + +\S{backend-can-format-as-text-now} \c{can_format_as_text_now()} + +\c int (*can_format_as_text_now)(game_params *params); + +This function is passed a \c{game_params} and returns a boolean, +which is \cw{TRUE} if the game can support ASCII text output for +this particular game type. If it returns \cw{FALSE}, front ends will +grey out or otherwise disable the \q{Copy} command. + +Games may enable and disable the copy-and-paste function for +different game \e{parameters}, but are currently constrained to +return the same answer from this function for all game \e{states} +sharing the same parameters. In other words, the \q{Copy} function +may enable or disable itself when the player changes game preset, +but will never change during play of a single game or when another +game of exactly the same type is generated. + +This function should not take into account aspects of the game +parameters which are not encoded by \cw{encode_params()} +(\k{backend-encode-params}) when the \c{full} parameter is set to +\cw{FALSE}. Such parameters will not necessarily match up between a +call to this function and a subsequent call to \cw{text_format()} +itself. (For instance, game \e{difficulty} should not affect whether +the game can be copied to the clipboard. Only the actual visible +\e{shape} of the game can affect that.) \S{backend-text-format} \cw{text_format()} @@ -1386,9 +1419,11 @@ allocated C string containing an ASCII representation of that game state. It is used to implement the \q{Copy} operation in many front ends. -This function should only be called if the back end field -\c{can_format_as_text} (\k{backend-can-format-as-text}) is -\cw{TRUE}. +This function will only ever be called if the back end field +\c{can_format_as_text_ever} (\k{backend-can-format-as-text-ever}) is +\cw{TRUE} \e{and} the function \cw{can_format_as_text_now()} +(\k{backend-can-format-as-text-now}) has returned \cw{TRUE} for the +currently selected game parameters. The returned string may contain line endings (and will probably want to), using the normal C internal \cq{\\n} convention. For @@ -2852,6 +2887,16 @@ Returns a descriptive game ID (i.e. one in the form \cq{params:description}) describing the game currently active in the mid-end. The returned string is dynamically allocated. +\H{midend-can-format-as-text-now} \cw{midend_can_format_as_text_now()} + +\c int midend_can_format_as_text_now(midend *me); + +Returns \cw{TRUE} if the game code is capable of formatting puzzles +of the currently selected game type as ASCII. + +If this returns \cw{FALSE}, then \cw{midend_text_format()} +(\k{midend-text-format}) will return \cw{NULL}. + \H{midend-text-format} \cw{midend_text_format()} \c char *midend_text_format(midend *me); @@ -2860,8 +2905,9 @@ Formats the current game's current state as ASCII text suitable for copying to the clipboard. The returned string is dynamically allocated. -You should not call this function if the game's -\c{can_format_as_text} flag is \cw{FALSE}. +If the game's \c{can_format_as_text_ever} flag is \cw{FALSE}, or if +its \cw{can_format_as_text_now()} function returns \cw{FALSE}, then +this function will return \cw{NULL}. If the returned string contains multiple lines (which is likely), it will use the normal C line ending convention (\cw{\\n} only). On @@ -2964,8 +3010,8 @@ mid-end because there didn't seem much point in doing so: \b fetching the \c{name} field to use in window titles and similar \b reading the \c{can_configure}, \c{can_solve} and -\c{can_format_as_text} fields to decide whether to add those items -to the menu bar or equivalent +\c{can_format_as_text_ever} fields to decide whether to add those +items to the menu bar or equivalent \b reading the \c{winhelp_topic} field (Windows only) diff --git a/dominosa.c b/dominosa.c index 0264675..42a7a02 100644 --- a/dominosa.c +++ b/dominosa.c @@ -1171,6 +1171,11 @@ static char *solve_game(game_state *state, game_state *currstate, return ret; } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { return NULL; @@ -1755,7 +1760,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - FALSE, game_text_format, + FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/fifteen.c b/fifteen.c index ccd1903..e74d10a 100644 --- a/fifteen.c +++ b/fifteen.c @@ -383,6 +383,11 @@ static char *solve_game(game_state *state, game_state *currstate, return dupstr("S"); } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { char *ret, *p, buf[80]; @@ -858,7 +863,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - TRUE, game_text_format, + TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/filling.c b/filling.c index 48874b7..a42ffd3 100644 --- a/filling.c +++ b/filling.c @@ -275,6 +275,11 @@ static char *board_to_string(int *board, int w, int h) { return repr; } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { const int w = state->shared->params.w; @@ -1650,7 +1655,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - TRUE, game_text_format, + TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/flip.c b/flip.c index 0a537c3..10c4825 100644 --- a/flip.c +++ b/flip.c @@ -853,6 +853,11 @@ static char *solve_game(game_state *state, game_state *currstate, return ret; } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { return NULL; @@ -1281,7 +1286,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - FALSE, game_text_format, + FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/galaxies.c b/galaxies.c index 45d1835..5858806 100644 --- a/galaxies.c +++ b/galaxies.c @@ -335,6 +335,11 @@ static struct space *sp2dot(game_state *state, int x, int y) #define IS_VERTICAL_EDGE(x) ((x % 2) == 0) +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { int maxlen = (state->sx+1)*state->sy, x, y; @@ -3425,7 +3430,7 @@ const struct game thegame = { #else TRUE, solve_game, #endif - TRUE, game_text_format, + TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/gtk.c b/gtk.c index 1f991d6..94e6975 100644 --- a/gtk.c +++ b/gtk.c @@ -79,7 +79,7 @@ void fatal(char *fmt, ...) * GTK front end to puzzles. */ -static void update_preset_tick(frontend *fe); +static void changed_preset(frontend *fe); struct font { #ifdef USE_PANGO @@ -127,6 +127,7 @@ struct frontend { int npresets; GtkWidget **preset_bullets; GtkWidget *preset_custom_bullet; + GtkWidget *copy_menu_item; }; void get_random_seed(void **randseed, int *randseedsize) @@ -849,7 +850,7 @@ static void config_ok_button_clicked(GtkButton *button, gpointer data) else { fe->cfgret = TRUE; gtk_widget_destroy(fe->cfgbox); - update_preset_tick(fe); + changed_preset(fe); } } @@ -1115,11 +1116,18 @@ static void update_menuitem_bullet(GtkWidget *label, int visible) } } -static void update_preset_tick(frontend *fe) +/* + * Called when any other code in this file has changed the + * selected game parameters. + */ +static void changed_preset(frontend *fe) { int n = midend_which_preset(fe->me); int i; + /* + * Update the tick mark in the Type menu. + */ if (fe->preset_bullets) { for (i = 0; i < fe->npresets; i++) update_menuitem_bullet(fe->preset_bullets[i], n == i); @@ -1127,6 +1135,14 @@ static void update_preset_tick(frontend *fe) if (fe->preset_custom_bullet) { update_menuitem_bullet(fe->preset_custom_bullet, n < 0); } + + /* + * Update the greying on the Copy menu option. + */ + if (fe->copy_menu_item) { + int enabled = midend_can_format_as_text_now(fe->me); + gtk_widget_set_sensitive(fe->copy_menu_item, enabled); + } } static void resize_fe(frontend *fe) @@ -1158,7 +1174,7 @@ static void menu_preset_event(GtkMenuItem *menuitem, gpointer data) midend_set_params(fe->me, params); midend_new_game(fe->me); - update_preset_tick(fe); + changed_preset(fe); resize_fe(fe); } @@ -1388,7 +1404,7 @@ static void menu_load_event(GtkMenuItem *menuitem, gpointer data) return; } - update_preset_tick(fe); + changed_preset(fe); resize_fe(fe); } } @@ -1673,7 +1689,7 @@ static frontend *new_window(char *arg, int argtype, char **error) } else fe->preset_custom_bullet = NULL; - update_preset_tick(fe); + changed_preset(fe); } else { fe->npresets = 0; fe->preset_bullets = NULL; @@ -1694,13 +1710,16 @@ static frontend *new_window(char *arg, int argtype, char **error) add_menu_separator(GTK_CONTAINER(menu)); add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Undo", 'u'); add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Redo", 'r'); - if (thegame.can_format_as_text) { + if (thegame.can_format_as_text_ever) { add_menu_separator(GTK_CONTAINER(menu)); menuitem = gtk_menu_item_new_with_label("Copy"); gtk_container_add(GTK_CONTAINER(menu), menuitem); gtk_signal_connect(GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(menu_copy_event), fe); gtk_widget_show(menuitem); + fe->copy_menu_item = menuitem; + } else { + fe->copy_menu_item = NULL; } if (thegame.can_solve) { add_menu_separator(GTK_CONTAINER(menu)); diff --git a/guess.c b/guess.c index 6fb3630..9117e8e 100644 --- a/guess.c +++ b/guess.c @@ -371,6 +371,11 @@ static char *solve_game(game_state *state, game_state *currstate, return dupstr("S"); } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { return NULL; @@ -1339,7 +1344,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - FALSE, game_text_format, + FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/inertia.c b/inertia.c index dd80ebe..965f533 100644 --- a/inertia.c +++ b/inertia.c @@ -1445,6 +1445,11 @@ static char *solve_game(game_state *state, game_state *currstate, return soln; } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { return NULL; @@ -2184,7 +2189,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - FALSE, game_text_format, + FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/lightup.c b/lightup.c index 4f4f569..a601130 100644 --- a/lightup.c +++ b/lightup.c @@ -1705,6 +1705,11 @@ done: return move; } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + /* 'borrowed' from slant.c, mainly. I could have printed it one * character per cell (like debug_state) but that comes out tiny. * 'L' is used for 'light here' because 'O' looks too much like '0' @@ -2240,7 +2245,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - TRUE, game_text_format, + TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/loopy.c b/loopy.c index 10619af..a53e452 100644 --- a/loopy.c +++ b/loopy.c @@ -151,6 +151,11 @@ struct game_drawstate { char *clue_error; }; +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state); static char *state_to_text(const game_state *state); static char *validate_desc(game_params *params, char *desc); @@ -3821,7 +3826,7 @@ const struct game thegame = { dup_game, free_game, 1, solve_game, - TRUE, game_text_format, + TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/map.c b/map.c index 4e9bdd6..bbd9a8e 100644 --- a/map.c +++ b/map.c @@ -2247,6 +2247,11 @@ static char *solve_game(game_state *state, game_state *currstate, return dupstr(aux); } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { return NULL; @@ -3122,7 +3127,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - FALSE, game_text_format, + FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/midend.c b/midend.c index 87df28a..215482d 100644 --- a/midend.c +++ b/midend.c @@ -1198,9 +1198,18 @@ char *midend_set_config(midend *me, int which, config_item *cfg) return NULL; } +int midend_can_format_as_text_now(midend *me) +{ + if (me->ourgame->can_format_as_text_ever) + return me->ourgame->can_format_as_text_now(me->params); + else + return FALSE; +} + char *midend_text_format(midend *me) { - if (me->ourgame->can_format_as_text && me->statepos > 0) + if (me->ourgame->can_format_as_text_ever && me->statepos > 0 && + me->ourgame->can_format_as_text_now(me->params)) return me->ourgame->text_format(me->states[me->statepos-1].state); else return NULL; diff --git a/mines.c b/mines.c index 3c4dc13..431cc08 100644 --- a/mines.c +++ b/mines.c @@ -2312,6 +2312,11 @@ static char *solve_game(game_state *state, game_state *currstate, return dupstr("S"); } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { char *ret; @@ -3091,7 +3096,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - TRUE, game_text_format, + TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/net.c b/net.c index 0d51443..ab16b1a 100644 --- a/net.c +++ b/net.c @@ -1782,6 +1782,11 @@ static char *solve_game(game_state *state, game_state *currstate, return ret; } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { return NULL; @@ -3005,7 +3010,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - FALSE, game_text_format, + FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/netslide.c b/netslide.c index 8f2da25..f960865 100644 --- a/netslide.c +++ b/netslide.c @@ -895,6 +895,11 @@ static char *solve_game(game_state *state, game_state *currstate, return dupstr(aux); } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { return NULL; @@ -1795,7 +1800,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - FALSE, game_text_format, + FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/nullgame.c b/nullgame.c index 4888a78..118cefe 100644 --- a/nullgame.c +++ b/nullgame.c @@ -123,6 +123,11 @@ static char *solve_game(game_state *state, game_state *currstate, return NULL; } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { return NULL; @@ -266,7 +271,7 @@ const struct game thegame = { dup_game, free_game, FALSE, solve_game, - FALSE, game_text_format, + FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/osx.m b/osx.m index ec9b6f9..33031e3 100644 --- a/osx.m +++ b/osx.m @@ -806,7 +806,8 @@ struct frontend { - (BOOL)validateMenuItem:(NSMenuItem *)item { if ([item action] == @selector(copy:)) - return (ourgame->can_format_as_text ? YES : NO); + return (ourgame->can_format_as_text_ever && + midend_can_format_as_text_now(me) ? YES : NO); else if ([item action] == @selector(solveGame:)) return (ourgame->can_solve ? YES : NO); else diff --git a/pattern.c b/pattern.c index 740f434..a1d2424 100644 --- a/pattern.c +++ b/pattern.c @@ -728,6 +728,11 @@ static char *solve_game(game_state *state, game_state *currstate, return ret; } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { return NULL; @@ -1269,7 +1274,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - FALSE, game_text_format, + FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/pegs.c b/pegs.c index d1addd1..7ac0ac8 100644 --- a/pegs.c +++ b/pegs.c @@ -711,6 +711,11 @@ static char *solve_game(game_state *state, game_state *currstate, return NULL; } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { int w = state->w, h = state->h; @@ -1201,7 +1206,7 @@ const struct game thegame = { dup_game, free_game, FALSE, solve_game, - TRUE, game_text_format, + TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/puzzles.h b/puzzles.h index 1c5342d..1b1f013 100644 --- a/puzzles.h +++ b/puzzles.h @@ -242,6 +242,7 @@ config_item *midend_get_config(midend *me, int which, char **wintitle); char *midend_set_config(midend *me, int which, config_item *cfg); char *midend_game_id(midend *me, char *id); char *midend_get_game_id(midend *me); +int midend_can_format_as_text_now(midend *me); char *midend_text_format(midend *me); char *midend_solve(midend *me); void midend_supersede_game_desc(midend *me, char *desc, char *privdesc); @@ -419,7 +420,8 @@ struct game { int can_solve; char *(*solve)(game_state *orig, game_state *curr, char *aux, char **error); - int can_format_as_text; + int can_format_as_text_ever; + int (*can_format_as_text_now)(game_params *params); char *(*text_format)(game_state *state); game_ui *(*new_ui)(game_state *state); void (*free_ui)(game_ui *ui); diff --git a/rect.c b/rect.c index 1fe873b..c77e5b7 100644 --- a/rect.c +++ b/rect.c @@ -2043,6 +2043,11 @@ static char *solve_game(game_state *state, game_state *currstate, return ret; } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { char *ret, *p, buf[80]; @@ -2878,7 +2883,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - TRUE, game_text_format, + TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/samegame.c b/samegame.c index f66e889..8976ad4 100644 --- a/samegame.c +++ b/samegame.c @@ -1022,6 +1022,11 @@ static char *solve_game(game_state *state, game_state *currstate, return NULL; } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { char *ret, *p; @@ -1641,7 +1646,7 @@ const struct game thegame = { dup_game, free_game, FALSE, solve_game, - TRUE, game_text_format, + TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/sixteen.c b/sixteen.c index f16bc96..ac3191c 100644 --- a/sixteen.c +++ b/sixteen.c @@ -509,6 +509,11 @@ static char *solve_game(game_state *state, game_state *currstate, return dupstr("S"); } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { char *ret, *p, buf[80]; @@ -1029,7 +1034,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - TRUE, game_text_format, + TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/slant.c b/slant.c index fc47209..000d880 100644 --- a/slant.c +++ b/slant.c @@ -1615,6 +1615,11 @@ static char *solve_game(game_state *state, game_state *currstate, return move; } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { int w = state->p.w, h = state->p.h, W = w+1, H = h+1; @@ -2201,7 +2206,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - TRUE, game_text_format, + TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/solo.c b/solo.c index 8ec0296..cbf00c5 100644 --- a/solo.c +++ b/solo.c @@ -3162,6 +3162,11 @@ static char *grid_text_format(int cr, struct block_structure *blocks, return ret; } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { return grid_text_format(state->cr, state->blocks, state->xtype, @@ -3935,7 +3940,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - TRUE, game_text_format, + TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/tents.c b/tents.c index de20300..bc07b4b 100644 --- a/tents.c +++ b/tents.c @@ -1369,6 +1369,11 @@ static char *solve_game(game_state *state, game_state *currstate, } } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { int w = state->p.w, h = state->p.h; @@ -2068,7 +2073,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - FALSE, game_text_format, + FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/twiddle.c b/twiddle.c index 6b89d3a..c12872f 100644 --- a/twiddle.c +++ b/twiddle.c @@ -545,6 +545,11 @@ static char *solve_game(game_state *state, game_state *currstate, return dupstr("S"); } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { char *ret, *p, buf[80]; @@ -1196,7 +1201,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - TRUE, game_text_format, + TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/unequal.c b/unequal.c index acc33d7..320dedf 100644 --- a/unequal.c +++ b/unequal.c @@ -408,6 +408,11 @@ static int c2n(int c, int order) { return -1; } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { int x, y, len, n; @@ -1736,7 +1741,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - TRUE, game_text_format, + TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/unfinished/pearl.c b/unfinished/pearl.c index af25415..41ab998 100644 --- a/unfinished/pearl.c +++ b/unfinished/pearl.c @@ -1236,6 +1236,11 @@ static char *solve_game(game_state *state, game_state *currstate, return NULL; } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { return NULL; @@ -1379,7 +1384,7 @@ const struct game thegame = { dup_game, free_game, FALSE, solve_game, - FALSE, game_text_format, + FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/unfinished/separate.c b/unfinished/separate.c index d6542a1..2df4a5b 100644 --- a/unfinished/separate.c +++ b/unfinished/separate.c @@ -680,6 +680,11 @@ static char *solve_game(game_state *state, game_state *currstate, return NULL; } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { return NULL; @@ -823,7 +828,7 @@ const struct game thegame = { dup_game, free_game, FALSE, solve_game, - FALSE, game_text_format, + FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/unfinished/slide.c b/unfinished/slide.c index d149657..af8f210 100644 --- a/unfinished/slide.c +++ b/unfinished/slide.c @@ -1166,6 +1166,11 @@ static char *solve_game(game_state *state, game_state *currstate, return ret; } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { return board_text_format(state->w, state->h, state->board, @@ -2317,7 +2322,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - TRUE, game_text_format, + TRUE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/unfinished/sokoban.c b/unfinished/sokoban.c index 2d76954..91f02a6 100644 --- a/unfinished/sokoban.c +++ b/unfinished/sokoban.c @@ -907,6 +907,11 @@ static char *solve_game(game_state *state, game_state *currstate, return NULL; } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { return NULL; @@ -1429,7 +1434,7 @@ const struct game thegame = { dup_game, free_game, FALSE, solve_game, - FALSE, game_text_format, + FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/untangle.c b/untangle.c index 18b4aca..12e5c75 100644 --- a/untangle.c +++ b/untangle.c @@ -1018,6 +1018,11 @@ static char *solve_game(game_state *state, game_state *currstate, return ret; } +static int game_can_format_as_text_now(game_params *params) +{ + return TRUE; +} + static char *game_text_format(game_state *state) { return NULL; @@ -1440,7 +1445,7 @@ const struct game thegame = { dup_game, free_game, TRUE, solve_game, - FALSE, game_text_format, + FALSE, game_can_format_as_text_now, game_text_format, new_ui, free_ui, encode_ui, diff --git a/windows.c b/windows.c index f727869..2364bdd 100644 --- a/windows.c +++ b/windows.c @@ -195,7 +195,7 @@ struct frontend { HBRUSH *brushes; HPEN *pens; HRGN clip; - HMENU typemenu; + HMENU gamemenu, typemenu; UINT timer; DWORD timer_last_tickcount; int npresets; @@ -222,6 +222,7 @@ struct frontend { }; static void update_type_menu_tick(frontend *fe); +static void update_copy_menu_greying(frontend *fe); void fatal(char *fmt, ...) { @@ -1573,6 +1574,7 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error) HMENU menu = SHGetSubMenu(SHFindMenuBar(fe->hwnd), ID_GAME); DeleteMenu(menu, 0, MF_BYPOSITION); #endif + fe->gamemenu = menu; AppendMenu(menu, MF_ENABLED, IDM_NEW, TEXT("&New")); AppendMenu(menu, MF_ENABLED, IDM_RESTART, TEXT("&Restart")); #ifndef _WIN32_WCE @@ -1635,7 +1637,7 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error) AppendMenu(menu, MF_ENABLED, IDM_UNDO, TEXT("Undo")); AppendMenu(menu, MF_ENABLED, IDM_REDO, TEXT("Redo")); #ifndef _WIN32_WCE - if (thegame.can_format_as_text) { + if (thegame.can_format_as_text_ever) { AppendMenu(menu, MF_SEPARATOR, 0, 0); AppendMenu(menu, MF_ENABLED, IDM_COPY, TEXT("&Copy")); } @@ -1680,6 +1682,7 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error) SetForegroundWindow(fe->hwnd); update_type_menu_tick(fe); + update_copy_menu_greying(fe); midend_redraw(fe->me); @@ -2657,11 +2660,19 @@ static void update_type_menu_tick(frontend *fe) DrawMenuBar(fe->hwnd); } +static void update_copy_menu_greying(frontend *fe) +{ + UINT enable = (midend_can_format_as_text_now(fe->me) ? + MF_ENABLED : MF_GRAYED); + EnableMenuItem(fe->gamemenu, IDM_COPY, MF_BYCOMMAND | enable); +} + static void new_game_type(frontend *fe) { midend_new_game(fe->me); new_game_size(fe); update_type_menu_tick(fe); + update_copy_menu_greying(fe); } static int is_alt_pressed(void) -- 2.11.0