Keyboard control patch for Pattern, from James H.
[sgt/puzzles] / pattern.c
index eacade4..787e591 100644 (file)
--- a/pattern.c
+++ b/pattern.c
@@ -18,6 +18,7 @@ enum {
     COL_TEXT,
     COL_UNKNOWN,
     COL_GRID,
+    COL_CURSOR,
     NCOLOURS
 };
 
@@ -728,6 +729,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;
@@ -740,6 +746,7 @@ struct game_ui {
     int drag_end_x;
     int drag_end_y;
     int drag, release, state;
+    int cur_x, cur_y, cur_visible;
 };
 
 static game_ui *new_ui(game_state *state)
@@ -748,6 +755,7 @@ static game_ui *new_ui(game_state *state)
 
     ret = snew(game_ui);
     ret->dragging = FALSE;
+    ret->cur_x = ret->cur_y = ret->cur_visible = 0;
 
     return ret;
 }
@@ -776,6 +784,7 @@ struct game_drawstate {
     int w, h;
     int tilesize;
     unsigned char *visible;
+    int cur_x, cur_y;
 };
 
 static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
@@ -789,17 +798,28 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
     if (x >= 0 && x < state->w && y >= 0 && y < state->h &&
         (button == LEFT_BUTTON || button == RIGHT_BUTTON ||
          button == MIDDLE_BUTTON)) {
+#ifdef STYLUS_BASED
+        int currstate = state->grid[y * state->w + x];
+#endif
 
         ui->dragging = TRUE;
 
         if (button == LEFT_BUTTON) {
             ui->drag = LEFT_DRAG;
             ui->release = LEFT_RELEASE;
+#ifdef STYLUS_BASED
+            ui->state = currstate == GRID_FULL ? GRID_UNKNOWN : GRID_FULL;
+#else
             ui->state = GRID_FULL;
+#endif
         } else if (button == RIGHT_BUTTON) {
             ui->drag = RIGHT_DRAG;
             ui->release = RIGHT_RELEASE;
+#ifdef STYLUS_BASED
+            ui->state = currstate == GRID_EMPTY ? GRID_UNKNOWN : GRID_EMPTY;
+#else
             ui->state = GRID_EMPTY;
+#endif
         } else /* if (button == MIDDLE_BUTTON) */ {
             ui->drag = MIDDLE_DRAG;
             ui->release = MIDDLE_RELEASE;
@@ -808,6 +828,7 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
 
         ui->drag_start_x = ui->drag_end_x = x;
         ui->drag_start_y = ui->drag_end_y = y;
+        ui->cur_visible = 0;
 
         return "";                    /* UI activity occurred */
     }
@@ -868,6 +889,35 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
             return "";                /* UI activity occurred */
     }
 
+    if (IS_CURSOR_MOVE(button)) {
+        move_cursor(button, &ui->cur_x, &ui->cur_y, state->w, state->h, 0);
+        ui->cur_visible = 1;
+        return "";
+    }
+    if (IS_CURSOR_SELECT(button)) {
+        int currstate = state->grid[ui->cur_y * state->w + ui->cur_x];
+        int newstate;
+        char buf[80];
+
+        if (!ui->cur_visible) {
+            ui->cur_visible = 1;
+            return "";
+        }
+
+        if (button == CURSOR_SELECT2)
+            newstate = currstate == GRID_UNKNOWN ? GRID_EMPTY :
+                currstate == GRID_EMPTY ? GRID_FULL : GRID_UNKNOWN;
+        else
+            newstate = currstate == GRID_UNKNOWN ? GRID_FULL :
+                currstate == GRID_FULL ? GRID_EMPTY : GRID_UNKNOWN;
+
+        sprintf(buf, "%c%d,%d,%d,%d",
+                (char)(newstate == GRID_FULL ? 'F' :
+                      newstate == GRID_EMPTY ? 'E' : 'U'),
+                ui->cur_x, ui->cur_y, 1, 1);
+        return dupstr(buf);
+    }
+
     return NULL;
 }
 
@@ -966,28 +1016,20 @@ static void game_set_size(drawing *dr, game_drawstate *ds,
 static float *game_colours(frontend *fe, int *ncolours)
 {
     float *ret = snewn(3 * NCOLOURS, float);
+    int i;
 
     frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]);
 
-    ret[COL_GRID * 3 + 0] = 0.3F;
-    ret[COL_GRID * 3 + 1] = 0.3F;
-    ret[COL_GRID * 3 + 2] = 0.3F;
-
-    ret[COL_UNKNOWN * 3 + 0] = 0.5F;
-    ret[COL_UNKNOWN * 3 + 1] = 0.5F;
-    ret[COL_UNKNOWN * 3 + 2] = 0.5F;
-
-    ret[COL_TEXT * 3 + 0] = 0.0F;
-    ret[COL_TEXT * 3 + 1] = 0.0F;
-    ret[COL_TEXT * 3 + 2] = 0.0F;
-
-    ret[COL_FULL * 3 + 0] = 0.0F;
-    ret[COL_FULL * 3 + 1] = 0.0F;
-    ret[COL_FULL * 3 + 2] = 0.0F;
-
-    ret[COL_EMPTY * 3 + 0] = 1.0F;
-    ret[COL_EMPTY * 3 + 1] = 1.0F;
-    ret[COL_EMPTY * 3 + 2] = 1.0F;
+    for (i = 0; i < 3; i++) {
+        ret[COL_GRID    * 3 + i] = 0.3F;
+        ret[COL_UNKNOWN * 3 + i] = 0.5F;
+        ret[COL_TEXT    * 3 + i] = 0.0F;
+        ret[COL_FULL    * 3 + i] = 0.0F;
+        ret[COL_EMPTY   * 3 + i] = 1.0F;
+    }
+    ret[COL_CURSOR * 3 + 0] = 1.0F;
+    ret[COL_CURSOR * 3 + 1] = 0.25F;
+    ret[COL_CURSOR * 3 + 2] = 0.25F;
 
     *ncolours = NCOLOURS;
     return ret;
@@ -1014,9 +1056,9 @@ static void game_free_drawstate(drawing *dr, game_drawstate *ds)
 }
 
 static void grid_square(drawing *dr, game_drawstate *ds,
-                        int y, int x, int state)
+                        int y, int x, int state, int cur)
 {
-    int xl, xr, yt, yb;
+    int xl, xr, yt, yb, dx, dy, dw, dh;
 
     draw_rect(dr, TOCOORD(ds->w, x), TOCOORD(ds->h, y),
               TILE_SIZE, TILE_SIZE, COL_GRID);
@@ -1026,10 +1068,18 @@ static void grid_square(drawing *dr, game_drawstate *ds,
     xr = (x % 5 == 4 || x == ds->w-1 ? 1 : 0);
     yb = (y % 5 == 4 || y == ds->h-1 ? 1 : 0);
 
-    draw_rect(dr, TOCOORD(ds->w, x) + 1 + xl, TOCOORD(ds->h, y) + 1 + yt,
-              TILE_SIZE - xl - xr - 1, TILE_SIZE - yt - yb - 1,
+    dx = TOCOORD(ds->w, x) + 1 + xl;
+    dy = TOCOORD(ds->h, y) + 1 + yt;
+    dw = TILE_SIZE - xl - xr - 1;
+    dh = TILE_SIZE - yt - yb - 1;
+
+    draw_rect(dr, dx, dy, dw, dh,
               (state == GRID_FULL ? COL_FULL :
                state == GRID_EMPTY ? COL_EMPTY : COL_UNKNOWN));
+    if (cur) {
+        draw_rect_outline(dr, dx, dy, dw, dh, COL_CURSOR);
+        draw_rect_outline(dr, dx+1, dy+1, dw-2, dh-2, COL_CURSOR);
+    }
 
     draw_update(dr, TOCOORD(ds->w, x), TOCOORD(ds->h, y),
                 TILE_SIZE, TILE_SIZE);
@@ -1084,6 +1134,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
 {
     int i, j;
     int x1, x2, y1, y2;
+    int cx, cy, cmoved;
 
     if (!ds->started) {
         /*
@@ -1120,13 +1171,20 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
         x1 = x2 = y1 = y2 = -1;        /* placate gcc warnings */
     }
 
+    if (ui->cur_visible) {
+        cx = ui->cur_x; cy = ui->cur_y;
+    } else {
+        cx = cy = -1;
+    }
+    cmoved = (cx != ds->cur_x || cy != ds->cur_y);
+
     /*
      * Now draw any grid squares which have changed since last
      * redraw.
      */
     for (i = 0; i < ds->h; i++) {
         for (j = 0; j < ds->w; j++) {
-            int val;
+            int val, cc = 0;
 
             /*
              * Work out what state this square should be drawn in,
@@ -1137,6 +1195,13 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
             else
                 val = state->grid[i * state->w + j];
 
+            if (cmoved) {
+                /* the cursor has moved; if we were the old or
+                 * the new cursor position we need to redraw. */
+                if (j == cx && i == cy) cc = 1;
+                if (j == ds->cur_x && i == ds->cur_y) cc = 1;
+            }
+
             /*
              * Briefly invert everything twice during a completion
              * flash.
@@ -1146,12 +1211,14 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
                 val != GRID_UNKNOWN)
                 val = (GRID_FULL ^ GRID_EMPTY) ^ val;
 
-            if (ds->visible[i * ds->w + j] != val) {
-                grid_square(dr, ds, i, j, val);
+            if (ds->visible[i * ds->w + j] != val || cc) {
+                grid_square(dr, ds, i, j, val,
+                            (j == cx && i == cy));
                 ds->visible[i * ds->w + j] = val;
             }
         }
     }
+    ds->cur_x = cx; ds->cur_y = cy;
 }
 
 static float game_anim_length(game_state *oldstate,
@@ -1182,8 +1249,8 @@ static void game_print_size(game_params *params, float *x, float *y)
      * I'll use 5mm squares by default.
      */
     game_compute_size(params, 500, &pw, &ph);
-    *x = pw / 100.0;
-    *y = ph / 100.0;
+    *x = pw / 100.0F;
+    *y = ph / 100.0F;
 }
 
 static void game_print(drawing *dr, game_state *state, int tilesize)
@@ -1243,7 +1310,7 @@ static void game_print(drawing *dr, game_state *state, int tilesize)
 #endif
 
 const struct game thegame = {
-    "Pattern", "games.pattern",
+    "Pattern", "games.pattern", "pattern",
     default_params,
     game_fetch_preset,
     decode_params,
@@ -1258,7 +1325,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,
@@ -1276,7 +1343,7 @@ const struct game thegame = {
     TRUE, FALSE, game_print_size, game_print,
     FALSE,                            /* wants_statusbar */
     FALSE, game_timing_state,
-    0,                                /* flags */
+    REQUIRE_RBUTTON,                  /* flags */
 };
 
 #ifdef STANDALONE_SOLVER
@@ -1363,3 +1430,5 @@ int main(int argc, char **argv)
 }
 
 #endif
+
+/* vim: set shiftwidth=4 tabstop=8: */