]> git.cworth.org Git - wordgame/blob - rack-fancy.c
Add key press handling (letter guessing and backspace)
[wordgame] / rack-fancy.c
1 /*
2  * Copyright © 2006 Carl Worth
3  *
4  * This program is free software; you can redistribute it and\/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2, or (at your option)
7  * any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA."
17  */
18 #include <stdlib.h>
19 #include <string.h>
20 #include <goocanvas.h>
21 #include <sys/time.h>
22 #include <time.h>
23 #include <ctype.h>
24 #include <math.h>
25 #include <gdk/gdkkeysyms.h>
26
27 #include "word-game.h"
28 #include "demo-item.h"
29
30 #define MAX_TILES 7
31
32 typedef struct _tile
33 {
34     char letter;
35     int rack_index;
36     int x, y;
37     GooCanvasItem *item;
38     gboolean guessed;
39 } tile_t;
40
41 typedef struct _rack
42 {
43     tile_t *tiles[MAX_TILES];
44     int num_tiles;
45 } rack_t;
46
47 static rack_t the_rack;
48
49 static char the_guess[MAX_TILES];
50 static int the_guess_index = 0;
51
52 #define LETTER_SIZE 60
53 #define LETTER_PAD 5
54
55 static void
56 rack_tile_position (int i, int *x, int *y)
57 {
58     *x = 20 + i * (LETTER_SIZE + LETTER_PAD);
59     *y = 85;
60 }
61
62 static void
63 guess_tile_position (int i, int *x, int *y)
64 {
65     rack_tile_position (i, x, y);
66     *y -= (LETTER_SIZE + LETTER_PAD);
67 }
68
69 static tile_t *
70 tile_create (GooCanvasItem *parent,
71              char letter, int rack_index)
72 {
73     tile_t *tile;
74
75     tile = g_malloc (sizeof (tile_t));
76     tile->letter = tolower (letter);
77     tile->rack_index = rack_index;
78     rack_tile_position (rack_index, &tile->x, &tile->y);
79     tile->item = goo_demo_item_new (parent,
80                                     tile->x, tile->y,
81                                     LETTER_SIZE,
82                                     toupper (letter),
83                                     NULL);
84     tile->guessed = FALSE;
85
86     return tile;
87 }
88
89 static void
90 tile_glide_to (tile_t *tile, int x, int y)
91 {
92     goo_demo_item_glide_to (tile->item, x, y);
93     tile->x = x;
94     tile->y = y;
95 }
96
97 static gboolean
98 on_delete_event (GtkWidget *window,
99                  GdkEvent  *event,
100                  gpointer   unused_data)
101 {
102     exit (0);
103 }
104
105 static int
106 rand_within (int num_values)
107 {
108     return (int) ((double) num_values * (rand() / (RAND_MAX + 1.0)));
109 }
110
111 static void
112 shuffle (int *array, int length)
113 {
114     int i, r, tmp;
115
116     for (i = 0; i < length; i++)
117     {
118         r = i + rand_within (length - i);
119         tmp = array[i];
120         array[i] = array[r];
121         array[r] = tmp;
122     }
123 }
124
125 static void
126 rack_init (rack_t *rack, GooCanvasItem *parent, char *word)
127 {
128     int i;
129
130     for (i = 0; i < MIN (MAX_TILES, strlen (word)); i++)
131         rack->tiles[i] = tile_create (parent, word[i], i);
132     rack->num_tiles = i;
133     while (i < MAX_TILES)
134         rack->tiles[i] = NULL;
135 }
136
137 static gboolean
138 rack_shuffle (rack_t *rack)
139 {
140     int indices[MAX_TILES];
141     int i, x, y;
142
143     for (i = 0; i < rack->num_tiles; i++)
144         indices[i] = i;
145
146     shuffle (indices, rack->num_tiles);
147
148     for (i = 0; i < rack->num_tiles; i++) {
149         rack->tiles[i]->rack_index = indices[i];
150         rack_tile_position (indices[i], &x, &y);
151         tile_glide_to (rack->tiles[i], x, y);
152     }
153
154     return TRUE;
155 }
156
157 static gboolean
158 on_key_press_event (GtkWidget   *widget,
159                     GdkEventKey *event,
160                     gpointer     user_data)
161 {
162     int i, x, y;
163     char guess_letter;
164     rack_t *rack = user_data;
165
166     if (event->keyval == GDK_space) {
167         for (i = 0; i < rack->num_tiles; i++) {
168             if (rack->tiles[i]->guessed) {
169                 rack_tile_position (rack->tiles[i]->rack_index, &x, &y);
170                 tile_glide_to (rack->tiles[i], x, y);
171                 rack->tiles[i]->guessed = FALSE;
172             }
173         }
174         the_guess_index = 0;
175         rack_shuffle (rack);
176         return TRUE;
177     }
178
179     if (event->keyval == GDK_BackSpace) {
180         gboolean found = FALSE;
181         int found_index;
182         x = 0;
183         for (i = 0; i < rack->num_tiles; i++) {
184             /* XXX: evil stuff here... please refactor a lot */
185             if (the_guess[the_guess_index-1] == rack->tiles[i]->letter &&
186                 rack->tiles[i]->guessed &&
187                 rack->tiles[i]->x > x)
188             {
189                 found = TRUE;
190                 found_index = i;
191             }
192         }
193         if (found) {
194             rack_tile_position (rack->tiles[found_index]->rack_index, &x, &y);
195             tile_glide_to (rack->tiles[found_index], x, y);
196             rack->tiles[found_index]->guessed = FALSE;
197             the_guess_index--;
198             return TRUE;
199         }
200         return FALSE;
201     }
202
203     /* XXX: event->string is deprecated, but the non-deprecated
204      * input-method stuff (GtkIMContext) is extremely non-obvious to
205      * use. */
206     guess_letter = tolower (event->string[0]);
207     for (i = 0; i < rack->num_tiles; i++) {
208         if (guess_letter == rack->tiles[i]->letter && 
209             ! rack->tiles[i]->guessed)
210         {
211             guess_tile_position (the_guess_index, &x, &y);
212             tile_glide_to (rack->tiles[i], x, y);
213             rack->tiles[i]->guessed = TRUE;
214             the_guess[the_guess_index++] = guess_letter;
215             return TRUE;
216         }
217     }
218
219     return FALSE;
220 }
221
222 static GtkWidget *
223 create_window (void)
224 {
225     GtkWidget *window, *scrolled_window;
226
227     window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
228     gtk_window_set_default_size (GTK_WINDOW (window), 500, 500);
229     gtk_widget_show (window);
230     g_signal_connect (window, "delete_event",
231                       (GtkSignalFunc) on_delete_event, NULL);
232
233     gtk_widget_add_events (window, GDK_KEY_PRESS_MASK);
234     g_signal_connect (window, "key_press_event",
235                       (GtkSignalFunc) on_key_press_event, &the_rack);
236
237     scrolled_window = gtk_scrolled_window_new (NULL, NULL);
238     gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
239                                          GTK_SHADOW_IN);
240     gtk_scrolled_window_set_policy  (GTK_SCROLLED_WINDOW (scrolled_window),
241                                      GTK_POLICY_AUTOMATIC,
242                                      GTK_POLICY_AUTOMATIC);
243     gtk_widget_show (scrolled_window);
244     gtk_container_add (GTK_CONTAINER (window), scrolled_window);
245
246     return scrolled_window;
247 }
248
249 static void
250 create_canvas (GtkWidget *parent, char *word)
251 {
252     GtkWidget *canvas;
253     GooCanvasItem *root;
254
255     canvas = goo_canvas_new ();
256     gtk_widget_set_size_request (canvas, 400, 400);
257     goo_canvas_set_bounds (GOO_CANVAS (canvas), 0, 0, 400, 400);
258     gtk_widget_show (canvas);
259     gtk_container_add (GTK_CONTAINER (parent), canvas);
260
261     root = goo_canvas_get_root_item (GOO_CANVAS (canvas));
262
263     rack_init (&the_rack, root, word);
264 }
265
266 int
267 main (int argc, char *argv[])
268 {
269     struct timeval tv;
270     bag_t bag;
271     char rack[8];
272     int i;
273     GtkWidget *window;
274
275     gettimeofday (&tv, NULL);
276     srand (tv.tv_sec ^ tv.tv_usec);
277
278     bag_init (&bag);
279     bag_shuffle (&bag);
280
281     memcpy (rack, bag.tiles, 7);
282     rack[7] = '\0';
283
284     for (i = 0; i < 7; i++)
285         rack[i] = toupper (rack[i]);
286
287     gtk_init (&argc, &argv);
288     window = create_window ();
289
290     create_canvas (window, rack);
291
292     gtk_main ();
293
294     return 0;
295 }