ref: a7431c0b7ce232f296ebcd70172ca64e58300105
parent: c6b1d4472b2f339c54c9c9de06c6ebef2a92dba9
author: Simon Tatham <[email protected]>
date: Sat Sep 6 05:27:56 EDT 2008
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. [originally from svn r8161]
--- a/blackbox.c
+++ b/blackbox.c
@@ -463,6 +463,11 @@
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 @@
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,
--- a/bridges.c
+++ b/bridges.c
@@ -147,6 +147,11 @@
}
}
+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 @@
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,
--- a/cube.c
+++ b/cube.c
@@ -985,6 +985,11 @@
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 @@
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,
--- a/devel.but
+++ b/devel.but
@@ -1365,9 +1365,9 @@
\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
@@ -1374,9 +1374,42 @@
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()}
\c char *(*text_format)(game_state *state);
@@ -1386,9 +1419,11 @@
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 @@
\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 @@
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 @@
\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)
--- a/dominosa.c
+++ b/dominosa.c
@@ -1171,6 +1171,11 @@
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 @@
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,
--- a/fifteen.c
+++ b/fifteen.c
@@ -383,6 +383,11 @@
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 @@
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,
--- a/filling.c
+++ b/filling.c
@@ -275,6 +275,11 @@
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 @@
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,
--- a/flip.c
+++ b/flip.c
@@ -853,6 +853,11 @@
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 @@
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,
--- a/galaxies.c
+++ b/galaxies.c
@@ -335,6 +335,11 @@
#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 @@
#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,
--- a/gtk.c
+++ b/gtk.c
@@ -79,7 +79,7 @@
* 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 @@
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 @@
else {
fe->cfgret = TRUE;
gtk_widget_destroy(fe->cfgbox);
- update_preset_tick(fe);
+ changed_preset(fe);
}
}
@@ -1115,11 +1116,18 @@
}
}
-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 @@
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 @@
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 @@
return;
}
- update_preset_tick(fe);
+ changed_preset(fe);
resize_fe(fe);
}
}
@@ -1673,7 +1689,7 @@
} else
fe->preset_custom_bullet = NULL;
- update_preset_tick(fe);
+ changed_preset(fe);
} else {
fe->npresets = 0;
fe->preset_bullets = NULL;
@@ -1694,7 +1710,7 @@
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);
@@ -1701,6 +1717,9 @@
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));
--- a/guess.c
+++ b/guess.c
@@ -371,6 +371,11 @@
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 @@
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,
--- a/inertia.c
+++ b/inertia.c
@@ -1445,6 +1445,11 @@
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 @@
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,
--- a/lightup.c
+++ b/lightup.c
@@ -1705,6 +1705,11 @@
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 @@
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,
--- a/loopy.c
+++ b/loopy.c
@@ -151,6 +151,11 @@
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 @@
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,
--- a/map.c
+++ b/map.c
@@ -2247,6 +2247,11 @@
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 @@
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,
--- a/midend.c
+++ b/midend.c
@@ -1198,9 +1198,18 @@
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;
--- a/mines.c
+++ b/mines.c
@@ -2312,6 +2312,11 @@
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 @@
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,
--- a/net.c
+++ b/net.c
@@ -1782,6 +1782,11 @@
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 @@
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,
--- a/netslide.c
+++ b/netslide.c
@@ -895,6 +895,11 @@
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 @@
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,
--- a/nullgame.c
+++ b/nullgame.c
@@ -123,6 +123,11 @@
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 @@
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,
--- a/osx.m
+++ b/osx.m
@@ -806,7 +806,8 @@
- (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
--- a/pattern.c
+++ b/pattern.c
@@ -728,6 +728,11 @@
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 @@
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,
--- a/pegs.c
+++ b/pegs.c
@@ -711,6 +711,11 @@
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 @@
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,
--- a/puzzles.h
+++ b/puzzles.h
@@ -242,6 +242,7 @@
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 @@
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);
--- a/rect.c
+++ b/rect.c
@@ -2043,6 +2043,11 @@
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 @@
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,
--- a/samegame.c
+++ b/samegame.c
@@ -1022,6 +1022,11 @@
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 @@
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,
--- a/sixteen.c
+++ b/sixteen.c
@@ -509,6 +509,11 @@
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 @@
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,
--- a/slant.c
+++ b/slant.c
@@ -1615,6 +1615,11 @@
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 @@
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,
--- a/solo.c
+++ b/solo.c
@@ -3162,6 +3162,11 @@
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 @@
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,
--- a/tents.c
+++ b/tents.c
@@ -1369,6 +1369,11 @@
}
}
+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 @@
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,
--- a/twiddle.c
+++ b/twiddle.c
@@ -545,6 +545,11 @@
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 @@
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,
--- a/unequal.c
+++ b/unequal.c
@@ -408,6 +408,11 @@
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 @@
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,
--- a/unfinished/pearl.c
+++ b/unfinished/pearl.c
@@ -1236,6 +1236,11 @@
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 @@
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,
--- a/unfinished/separate.c
+++ b/unfinished/separate.c
@@ -680,6 +680,11 @@
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 @@
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,
--- a/unfinished/slide.c
+++ b/unfinished/slide.c
@@ -1166,6 +1166,11 @@
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 @@
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,
--- a/unfinished/sokoban.c
+++ b/unfinished/sokoban.c
@@ -907,6 +907,11 @@
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 @@
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,
--- a/untangle.c
+++ b/untangle.c
@@ -1018,6 +1018,11 @@
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 @@
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,
--- a/windows.c
+++ b/windows.c
@@ -195,7 +195,7 @@
HBRUSH *brushes;
HPEN *pens;
HRGN clip;
- HMENU typemenu;
+ HMENU gamemenu, typemenu;
UINT timer;
DWORD timer_last_tickcount;
int npresets;
@@ -222,6 +222,7 @@
};
static void update_type_menu_tick(frontend *fe);
+static void update_copy_menu_greying(frontend *fe);
void fatal(char *fmt, ...)
{
@@ -1573,6 +1574,7 @@
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 @@
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 @@
SetForegroundWindow(fe->hwnd);
update_type_menu_tick(fe);
+ update_copy_menu_greying(fe);
midend_redraw(fe->me);
@@ -2657,11 +2660,19 @@
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)