ref: ace2c7dafdc520b5e7ce34c6700e38c91fe219c9
parent: 9673a2a733972fc236c9a67b3ab1b7cf1d4d601e
author: Jacob Nevins <jacobn@chiark.greenend.org.uk>
date: Sun Feb 10 13:43:29 EST 2008
UI change to Filling: allow multiple squares to be set at once. (This change adds a new possibility to the save format, such that new save files won't necessarily be loadable by old binaries. I think that's acceptable -- it's certainly happened before -- but I couldn't find anything in the developer docs explicitly blessing it.) [originally from svn r7849]
--- a/filling.c
+++ b/filling.c
@@ -976,7 +976,7 @@
*****************************************************************************/
struct game_ui {
- int x, y; /* highlighted square, or (-1, -1) if none */
+ int *sel; /* w*h highlighted squares, or NULL */
};
static game_ui *new_ui(game_state *state)
@@ -983,7 +983,7 @@
{
game_ui *ui = snew(game_ui);
- ui->x = ui->y = -1;
+ ui->sel = NULL;
return ui;
}
@@ -990,6 +990,8 @@
static void free_ui(game_ui *ui)
{
+ if (ui->sel)
+ sfree(ui->sel);
sfree(ui);
}
@@ -1005,6 +1007,11 @@
static void game_changed_state(game_ui *ui, game_state *oldstate,
game_state *newstate)
{
+ /* Clear any selection */
+ if (ui->sel) {
+ sfree(ui->sel);
+ ui->sel = NULL;
+ }
}
#define PREFERRED_TILE_SIZE 32
@@ -1029,23 +1036,34 @@
const int tx = (x + TILE_SIZE - BORDER) / TILE_SIZE - 1;
const int ty = (y + TILE_SIZE - BORDER) / TILE_SIZE - 1;
+ char *move = NULL;
+ int i;
+
assert(ui);
assert(ds);
button &= ~MOD_MASK;
- if (tx >= 0 && tx < w && ty >= 0 && ty < h) {
+ if (button == LEFT_BUTTON || button == LEFT_DRAG) {
+ /* A left-click anywhere will clear the current selection. */
if (button == LEFT_BUTTON) {
- if ((tx == ui->x && ty == ui->y) || state->shared->clues[w*ty+tx])
- ui->x = ui->y = -1;
- else ui->x = tx, ui->y = ty;
- return ""; /* redraw */
+ if (ui->sel) {
+ sfree(ui->sel);
+ ui->sel = NULL;
+ }
}
+ if (tx >= 0 && tx < w && ty >= 0 && ty < h) {
+ if (!ui->sel) {
+ ui->sel = snewn(w*h, int);
+ memset(ui->sel, 0, w*h*sizeof(int));
+ }
+ if (!state->shared->clues[w*ty+tx])
+ ui->sel[w*ty+tx] = 1;
+ }
+ return ""; /* redraw */
}
- assert((ui->x == -1) == (ui->y == -1));
- if (ui->x == -1) return NULL;
- assert(state->shared->clues[w*ui->y + ui->x] == 0);
+ if (!ui->sel) return NULL;
switch (button) {
case ' ':
@@ -1061,17 +1079,32 @@
if (button > (w == 2 && h == 2? 3: max(w, h))) return NULL;
}
- {
- const int i = w*ui->y + ui->x;
- char buf[64];
- ui->x = ui->y = -1;
- if (state->board[i] == button) {
- return ""; /* no change - just update ui */
- } else {
- sprintf(buf, "%d_%d", i, button);
- return dupstr(buf);
- }
+ for (i = 0; i < w*h; i++) {
+ char buf[32];
+ if (ui->sel[i]) {
+ assert(state->shared->clues[i] == 0);
+ if (state->board[i] != button) {
+ sprintf(buf, "%s%d", move ? "," : "", i);
+ if (move) {
+ move = srealloc(move, strlen(move)+strlen(buf)+1);
+ strcat(move, buf);
+ } else {
+ move = smalloc(strlen(buf)+1);
+ strcpy(move, buf);
+ }
+ }
+ }
}
+ if (move) {
+ char buf[32];
+ sprintf(buf, "_%d", button);
+ move = srealloc(move, strlen(move)+strlen(buf)+1);
+ strcat(move, buf);
+ }
+ sfree(ui->sel);
+ ui->sel = NULL;
+ /* Need to update UI at least, as we cleared the selection */
+ return move ? move : "";
}
static game_state *execute_move(game_state *state, char *move)
@@ -1085,18 +1118,22 @@
for (++move; i < sz; ++i) new_state->board[i] = move[i] - '0';
new_state->cheated = TRUE;
} else {
- char *endptr;
- const int i = strtol(move, &endptr, 0);
int value;
- if (endptr == move) return NULL;
- if (*endptr != '_') return NULL;
- move = endptr + 1;
- value = strtol(move, &endptr, 0);
- if (endptr == move) return NULL;
- if (*endptr != '\0') return NULL;
- if (i < 0 || i >= sz || value < 0 || value > 9) return NULL;
+ char *endptr, *delim = strchr(move, '_');
+ if (!delim) return NULL;
+ value = strtol(delim+1, &endptr, 0);
+ if (*endptr || endptr == delim+1) return NULL;
+ if (value < 0 || value > 9) return NULL;
new_state = dup_game(state);
- new_state->board[i] = value;
+ while (*move) {
+ const int i = strtol(move, &endptr, 0);
+ if (endptr == move) return NULL;
+ if (i < 0 || i >= sz) return NULL;
+ new_state->board[i] = value;
+ if (*endptr == '_') break;
+ if (*endptr != ',') return NULL;
+ move = endptr + 1;
+ }
}
/*
@@ -1414,7 +1451,7 @@
if (flashy || !shading) {
/* clear all background flags */
- } else if (x == ui->x && y == ui->y) {
+ } else if (ui->sel && ui->sel[y*w+x]) {
flags |= CURSOR_BG;
} else if (v) {
int size = dsf_size(ds->dsf_scratch, y*w+x);
--- a/puzzles.but
+++ b/puzzles.but
@@ -2233,9 +2233,11 @@
\H{filling-controls} \I{controls, for Filling}Filling controls
To play Filling, simply click the mouse in any empty square and then
-type a digit on the keyboard to fill that square. If you make a
-mistake, click the mouse in the incorrect square and press 0, Space,
-Backspace or Enter to clear it again (or use the Undo feature).
+type a digit on the keyboard to fill that square. By dragging the
+mouse, you can select multiple squares to fill with a single keypress.
+If you make a mistake, click the mouse in the incorrect square and
+press 0, Space, Backspace or Enter to clear it again (or use the Undo
+feature).
(All the actions described in \k{common-actions} are also available.)