chiark / gitweb /
New infrastructure feature. Games are now permitted to be
authorSimon Tatham <anakin@pobox.com>
Sat, 6 Sep 2008 09:27:56 +0000 (09:27 +0000)
committerSimon Tatham <anakin@pobox.com>
Sat, 6 Sep 2008 09:27:56 +0000 (09:27 +0000)
_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]

38 files changed:
blackbox.c
bridges.c
cube.c
devel.but
dominosa.c
fifteen.c
filling.c
flip.c
galaxies.c
gtk.c
guess.c
inertia.c
lightup.c
loopy.c
map.c
midend.c
mines.c
net.c
netslide.c
nullgame.c
osx.m
pattern.c
pegs.c
puzzles.h
rect.c
samegame.c
sixteen.c
slant.c
solo.c
tents.c
twiddle.c
unequal.c
unfinished/pearl.c
unfinished/separate.c
unfinished/slide.c
unfinished/sokoban.c
untangle.c
windows.c

index 458c0b805000184d45e97a7f5b4c44ebcc505259..a8c6d5a2cb5c0c303452585a77aaec855e09f782 100644 (file)
@@ -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,
index d56f287eeefee80f4ee6a30927e293c248eb5191..18296817e8177daf854bff7a65994d031f4d8311 100644 (file)
--- 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 bc919730b89941f164d6e9d551c4c6e08014051c..5df5fcb372a42684d021b463239d7369e29d5430 100644 (file)
--- 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,
index ea23d520d5a8b81a7e72505f900b38755e186103..11d198d05901613ad3e1567e2f576806f983ea63 100644 (file)
--- 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)
 
index 0264675bc96e398d031ed90b58351553c30c8342..42a7a024e81db2d2106185c266bf526ffec7fd1e 100644 (file)
@@ -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,
index ccd190380198bc8a76fff1a0ba325991a80b9bab..e74d10a92a5a78873b3cc91c02f559a15a370667 100644 (file)
--- 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,
index 48874b7195e5ef06eaadf7f7f36681e5242898c3..a42ffd30098807ddcedf33f909854a21cc113e50 100644 (file)
--- 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 0a537c3dc52c3c60483fe4dc78af3f14292ace6e..10c48250a06e240bfdc74d32296cbca062cafc72 100644 (file)
--- 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,
index 45d183599135799f88923f0637a95c911225f312..5858806487f98a50f743f3a2794ed3d3eec8ca86 100644 (file)
@@ -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 1f991d6a20c9f1f72570eb454b8d794a0786f19d..94e697524576893f5993dae219bbc586c69069c4 100644 (file)
--- 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 6fb3630383bf04863ae1f2584d678c61b95f5333..9117e8ef905b1b40785483769be273d02b666b96 100644 (file)
--- 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,
index dd80ebef1c04961fa25746941332fe22632255a5..965f5338a9d35f9164373de15ec1a80f3df0a6cd 100644 (file)
--- 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,
index 4f4f569447cb99a59616c721ac71f8a76acb8d7d..a60113089f80941c2557679b05a11e614703ebf7 100644 (file)
--- 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 10619afdb614d3d81260ae91072ea64627611a1b..a53e4525761a5e996a369598d78af2fb7a3c51e4 100644 (file)
--- 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 4e9bdd616491933d677090d780282c634e961a3e..bbd9a8ecae4f933dbba4df386a1dc861de07a5ff 100644 (file)
--- 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,
index 87df28a4e84f0192b6e795e215ad455479104c71..215482deba45f02c03e1c4403c93da00352d8a8b 100644 (file)
--- 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 3c4dc13f326984df2d84d286a375b8b7d74ec1d8..431cc08f049133675765ce1633d0dcd9b09e193c 100644 (file)
--- 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 0d514431dec9f003363cd2989fe52e33a7c33e00..ab16b1abebf79889c2d0805d6d7c084170b24a19 100644 (file)
--- 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,
index 8f2da255773a293f6525ec0464ea68576759931a..f96086550844ca18dda9953de51fe507cf5b56b4 100644 (file)
@@ -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,
index 4888a78a5edb74f0ee2d31d2576546d4b56d6373..118cefe43bc30d64637d36c882c681e4be4fe6a6 100644 (file)
@@ -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 ec9b6f94cbd96fc07162e4433e24d32748611966..33031e352e66e500ccbfaa8784d3420228ea9583 100644 (file)
--- 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
index 740f434bbdb11d6e69002b9e48a30e209a120fc3..a1d2424b518d80a3a3de0b675318516ad479d274 100644 (file)
--- 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 d1addd1482da5607b8e2f596c4749e16dfc1b2b4..7ac0ac8c8053bdfbd74929140e33d5600785df47 100644 (file)
--- 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,
index 1c5342d9b5081909b685682095eeee9a326a851f..1b1f013c6a9909ea17d8a772f1c06c7842249537 100644 (file)
--- 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 1fe873b957c41c1604f6e2302902d7fdadd9c675..c77e5b77fabdd61ad1fde7d6ddaf499b960d7859 100644 (file)
--- 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,
index f66e8898191224d7aac14527582c38bdb96865dd..8976ad44b50b8d84b0672849c81bf84a7f5a9b5b 100644 (file)
@@ -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,
index f16bc96ef05c534551aec737838250d5b2d7d3e4..ac3191c899d7baff1eaa9f8ead707d283bba4273 100644 (file)
--- 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 fc472091eddcefb44a9d87196974b253a9dbd523..000d880dc3ae587d2bde20b6d14e66ec7fd50468 100644 (file)
--- 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 8ec0296fb34a773cbeb1feeb5ce1734ef38d47d2..cbf00c516552b8af551bb30744377fc73c77134a 100644 (file)
--- 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 de203004e5c583ad92c3f68e99e1a23b6b37c4ef..bc07b4bfebfcc3e3f9408d70c177502171cb375f 100644 (file)
--- 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,
index 6b89d3af3eaf5ab9fa7d7260c1ef5f3838408cc2..c12872fa94ba83f297e76bc4c2ca6810a99c0b91 100644 (file)
--- 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,
index acc33d76364befd4a6c7c4eb4f364a8730ffb879..320dedf36954382d829bcee584fea649cac12b4c 100644 (file)
--- 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,
index af2541559842fca4930577deb436281abb8d6edc..41ab998f9d60b207cd1b77eaafcfc8ecb07982cd 100644 (file)
@@ -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,
index d6542a1ff8d1159f70e1062a9d260643d4262f76..2df4a5b86407b2709305628f74b95a4ba93f5f4f 100644 (file)
@@ -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,
index d1496573490db388d05e46e88a308031a70422cb..af8f21055005e4821f306544e8e4d1b55bcba7db 100644 (file)
@@ -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,
index 2d76954e2958d8b92e67f3815e693e435ee49251..91f02a6503ed13a1a3f33dcd40fe623cb9abc396 100644 (file)
@@ -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,
index 18b4acaac9679613d71306e8150634398e36d10c..12e5c75f53cf90c7dfacc0241240fa85f93544ca 100644 (file)
@@ -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,
index f7278690639b6a83d11d465d4affd60b9b32fe3c..2364bddd6439ca037464e1c5362ab45f3b28da57 100644 (file)
--- 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)