Functionality for keyboard and mouse in puzzle area
[wordblah.git] / wordblox_player.c
1 #include <gtk/gtk.h>
2
3 #include "constantstrings.h"
4 #include "wordblox_resource.c"
5 #include "wordblox.h"
6
7 GtkWidget *window;
8 GtkListStore *across_store;
9 GtkListStore *down_store;
10
11 MainPlayerData app_data;
12
13 /* update the clue items store */
14 void update_clue_items ()
15 {
16 gtk_list_store_clear (across_store);
17 gtk_list_store_clear (down_store);
18
19 /* if the puzzle is loaded */
20 if (app_data.is_loaded == true)
21 {
22 /* iterate through the puzzle grid and gte the clues */
23 for (int i = 0; i < app_data.puzzle.grid_size; i ++)
24 {
25 for (int j = 0; j < app_data.puzzle.grid_size; j ++)
26 {
27 /* if it is the start of an across word */
28 if (app_data.puzzle.start_across_word[i][j] != -1)
29 {
30 GtkTreeIter iter;
31
32 gtk_list_store_append (across_store, &iter);
33 gtk_list_store_set (across_store, &iter, 0,
34 app_data.puzzle.start_across_word[i][j],
35 1, app_data.puzzle.clue_across[i][j],
36 -1);
37 }
38 /* if it is the start of a down word */
39 if (app_data.puzzle.start_down_word[i][j] != -1)
40 {
41 GtkTreeIter iter;
42 gtk_list_store_append (down_store, &iter);
43 gtk_list_store_set (down_store, &iter, 0,
44 app_data.puzzle.start_down_word[i][j],
45 1, app_data.puzzle.clue_down[i][j],
46 -1);
47 }
48 }
49 }
50 }
51 }
52
53 /* slot for handling mouse button event for puzzle drawing area */
54 gboolean on_puzzle_area_button_press_event (GtkWidget *widget,
55 GdkEventButton *event, gpointer data)
56 {
57 if (event->type == GDK_BUTTON_PRESS && event->button == 1)
58 {
59 int col = (event->x - 5) / GRID_PIXELS;
60 int row = (event->y - 5) / GRID_PIXELS;
61
62 if (app_data.puzzle.chars[row][col] != '#')
63 {
64 if (row < app_data.puzzle.grid_size &&
65 col < app_data.puzzle.grid_size)
66 {
67 app_data.cur_row = row;
68 app_data.cur_col = col;
69 }
70 }
71 }
72
73 gtk_widget_queue_draw_area (widget, 0, 0,
74 app_data.puzzle.grid_size*GRID_PIXELS+10,
75 app_data.puzzle.grid_size*GRID_PIXELS+10);
76
77
78
79 return FALSE;
80 }
81
82 /* slot for handling key press event for puzzle drawing area */
83 gboolean on_puzzle_area_key_press_event (GtkWidget *widget,
84 GdkEventKey *event, gpointer data)
85 {
86 /* respond to key events only if the puzzle is loaded */
87 if (app_data.is_loaded == true)
88 {
89 switch (event->keyval)
90 {
91 case GDK_KEY_Down : move_current_row (&app_data, DIR_FORWARD);
92 break;
93 case GDK_KEY_Up : move_current_row (&app_data, DIR_BACK);
94 break;
95 case GDK_KEY_Right: move_current_col (&app_data, DIR_FORWARD);
96 break;
97 case GDK_KEY_Left : move_current_col (&app_data, DIR_BACK);
98 break;
99 case GDK_KEY_Delete:
100 case GDK_KEY_space:
101 app_data.char_ans[app_data.cur_row][app_data.cur_col] = ' ';
102 break;
103 case GDK_KEY_a :
104 case GDK_KEY_A :
105 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'A';
106 break;
107 case GDK_KEY_b :
108 case GDK_KEY_B :
109 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'B';
110 break;
111 case GDK_KEY_c :
112 case GDK_KEY_C :
113 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'C';
114 break;
115 case GDK_KEY_d :
116 case GDK_KEY_D :
117 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'D';
118 break;
119 case GDK_KEY_e :
120 case GDK_KEY_E :
121 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'E';
122 break;
123 case GDK_KEY_f :
124 case GDK_KEY_F :
125 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'F';
126 break;
127 case GDK_KEY_g :
128 case GDK_KEY_G :
129 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'G';
130 break;
131 case GDK_KEY_h :
132 case GDK_KEY_H :
133 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'H';
134 break;
135 case GDK_KEY_i :
136 case GDK_KEY_I :
137 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'I';
138 break;
139 case GDK_KEY_j :
140 case GDK_KEY_J :
141 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'J';
142 break;
143 case GDK_KEY_k :
144 case GDK_KEY_K :
145 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'K';
146 break;
147 case GDK_KEY_l :
148 case GDK_KEY_L :
149 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'L';
150 break;
151 case GDK_KEY_m :
152 case GDK_KEY_M :
153 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'M';
154 break;
155 case GDK_KEY_n :
156 case GDK_KEY_N :
157 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'N';
158 break;
159 case GDK_KEY_o :
160 case GDK_KEY_O :
161 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'O';
162 break;
163 case GDK_KEY_p :
164 case GDK_KEY_P :
165 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'P';
166 break;
167 case GDK_KEY_q :
168 case GDK_KEY_Q :
169 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'Q';
170 break;
171 case GDK_KEY_r :
172 case GDK_KEY_R :
173 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'R';
174 break;
175 case GDK_KEY_s :
176 case GDK_KEY_S :
177 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'S';
178 break;
179 case GDK_KEY_t :
180 case GDK_KEY_T :
181 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'T';
182 break;
183 case GDK_KEY_u :
184 case GDK_KEY_U :
185 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'U';
186 break;
187 case GDK_KEY_v :
188 case GDK_KEY_V :
189 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'V';
190 break;
191 case GDK_KEY_w :
192 case GDK_KEY_W :
193 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'W';
194 break;
195 case GDK_KEY_x :
196 case GDK_KEY_X :
197 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'X';
198 break;
199 case GDK_KEY_y :
200 case GDK_KEY_Y :
201 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'Y';
202 break;
203 case GDK_KEY_z :
204 case GDK_KEY_Z :
205 app_data.char_ans[app_data.cur_row][app_data.cur_col] = 'Z';
206 break;
207 default : return FALSE;
208
209 }
210 }
211
212 gtk_widget_queue_draw_area (widget, 0, 0,
213 app_data.puzzle.grid_size*GRID_PIXELS+10,
214 app_data.puzzle.grid_size*GRID_PIXELS+10);
215
216 return FALSE;
217 }
218
219 /* slot for drawing the puzzle */
220 gboolean on_puzzle_area_draw (GtkWidget *widget, cairo_t *cr, gpointer data)
221 {
222 /* if a puzzle is loaded */
223 if (app_data.is_loaded == true)
224 {
225 GdkRGBA colorfore, colorback, colorblue, colorbacksel;
226 gdk_rgba_parse (&colorfore, "#000000");
227 gdk_rgba_parse (&colorback, "#ffffff");
228 gdk_rgba_parse (&colorblue, "#0000dd");
229 gdk_rgba_parse (&colorbacksel, "#ffffaa");
230 cairo_set_line_width (cr, 3);
231
232 /* set the size of the drawing area */
233 gtk_widget_set_size_request (widget,
234 app_data.puzzle.grid_size*GRID_PIXELS+10,
235 app_data.puzzle.grid_size*GRID_PIXELS+10);
236
237 /* Draw the grid */
238 for (int i = 0; i < app_data.puzzle.grid_size; i ++)
239 {
240 for (int j = 0; j < app_data.puzzle.grid_size; j ++)
241 {
242 /* if it is the current selection or if -1 is the current
243 selection then let the current selection be the first word */
244 if (app_data.cur_col == -1 && app_data.cur_row == -1)
245 {
246 if (app_data.puzzle.start_across_word[i][j] == 1)
247 {
248 app_data.cur_row = i;
249 app_data.cur_col = j;
250 }
251 else if (app_data.puzzle.start_down_word[i][j] == 1)
252 {
253 app_data.cur_row = i;
254 app_data.cur_col = j;
255 }
256 }
257
258 gdk_cairo_set_source_rgba (cr, &colorfore);
259 cairo_rectangle (cr, j*GRID_PIXELS+5, i*GRID_PIXELS+5,
260 GRID_PIXELS, GRID_PIXELS);
261
262 cairo_stroke (cr);
263
264 /* if it is not a blank grid then set the background color
265 to black */
266 if (app_data.puzzle.chars[i][j] != '#')
267 gdk_cairo_set_source_rgba (cr, &colorback);
268
269 /* if it is a current selection then set the background
270 to yellow */
271 if (app_data.cur_row == i && app_data.cur_col == j)
272 gdk_cairo_set_source_rgba (cr, &colorbacksel);
273
274 cairo_rectangle (cr, j*GRID_PIXELS+5, i*GRID_PIXELS+5,
275 GRID_PIXELS, GRID_PIXELS);
276 cairo_fill (cr);
277
278 /* draw the word number if it is the start of a word */
279 if (app_data.puzzle.start_across_word[i][j] != -1 ||
280 app_data.puzzle.start_down_word[i][j] != -1)
281 {
282 int num;
283 if (app_data.puzzle.start_across_word[i][j] != -1)
284 num = app_data.puzzle.start_across_word[i][j];
285 else
286 num = app_data.puzzle.start_down_word[i][j];
287
288 gdk_cairo_set_source_rgba (cr, &colorblue);
289 cairo_select_font_face (cr, "sans serif",
290 CAIRO_FONT_SLANT_NORMAL,
291 CAIRO_FONT_WEIGHT_NORMAL);
292 cairo_set_font_size (cr, 11);
293 cairo_move_to (cr, j*GRID_PIXELS+7, i*GRID_PIXELS+15);
294 char cnum[3];
295 sprintf (cnum, "%d", num);
296 cairo_show_text (cr, (const char*)cnum);
297 }
298
299 /* draw the answer if it is there */
300 gdk_cairo_set_source_rgba (cr, &colorfore);
301 cairo_select_font_face (cr, "sans serif",
302 CAIRO_FONT_SLANT_NORMAL,
303 CAIRO_FONT_WEIGHT_BOLD);
304
305 cairo_set_font_size (cr, 16);
306 cairo_move_to (cr, j*GRID_PIXELS+GRID_PIXELS/2,
307 i*GRID_PIXELS+GRID_PIXELS-10);
308 char ctxt[3];
309 sprintf (ctxt, "%c", app_data.char_ans[i][j]);
310 cairo_show_text (cr, (const char*) ctxt);
311
312 }
313 }
314 }
315
316 return FALSE;
317
318 }
319
320 /* slot for reveal solution menu */
321 void on_menu_reveal_solution_activate (GtkMenuItem *item, gpointer *data)
322 {
323 /* TODO */
324 }
325
326 /* slot for save grid state menu */
327 void on_menu_save_grid_state_activate (GtkMenuItem *item, gpointer *data)
328 {
329 /* TODO */
330 }
331
332 /* slot for exit menu */
333 void on_menu_exit_activate (GtkMenuItem *item, gpointer data)
334 {
335 gtk_main_quit ();
336 }
337
338 /* slot for open menu */
339 void on_menu_open_activate (GtkMenuItem *item, GtkDrawingArea* area)
340 {
341 GtkWidget *dialog;
342 GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
343 gint res;
344 dialog = gtk_file_chooser_dialog_new (OPEN_FILE, GTK_WINDOW(window), action,
345 "_Cancel",
346 GTK_RESPONSE_CANCEL,
347 "_Open",
348 GTK_RESPONSE_ACCEPT,
349 NULL);
350 res = gtk_dialog_run (GTK_DIALOG (dialog));
351 if (res == GTK_RESPONSE_ACCEPT)
352 {
353 char *filename;
354 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog));
355 MainPlayerData temp;
356 reset_player_data (&temp, filename);
357
358 /* if the grid is not frozen then the game cannot be played */
359 if (temp.is_loaded == false)
360 {
361 GtkWidget *errordlg ;
362 errordlg = gtk_message_dialog_new (GTK_WINDOW(window),
363 GTK_DIALOG_DESTROY_WITH_PARENT,
364 GTK_MESSAGE_ERROR,
365 GTK_BUTTONS_CLOSE,
366 UNFROZEN_GRID_PLAYER);
367 gtk_dialog_run (GTK_DIALOG(errordlg));
368 gtk_widget_destroy (errordlg);
369 }
370 else
371 {
372 app_data = temp;
373 gtk_widget_queue_draw_area (GTK_WIDGET (area), 0, 0,
374 app_data.puzzle.grid_size*30+10,
375 app_data.puzzle.grid_size*30+10);
376
377 }
378 update_clue_items ();
379 g_free (filename);
380 }
381
382 gtk_widget_destroy (dialog);
383 }
384
385 /* slot for about menu */
386 void on_menu_about_activate (GtkMenuItem *item, gpointer data)
387 {
388 const char *AUTHOR[] = {"V.Harishankar", NULL};
389 gtk_show_about_dialog (GTK_WINDOW(window), "authors",AUTHOR,
390 "program-name", PROGRAM_NAME,
391 "copyright", COPYRIGHT,
392 "comments", COMMENTS,
393 "website", WEBSITE,
394 "website-label", WEBSITE_LABEL,
395 "license-type", GTK_LICENSE_GPL_2_0,
396 "version", VERSION,
397 (char*)NULL);
398 }
399
400 int main (int argc, char *argv [])
401 {
402 gtk_init (&argc, &argv);
403 GdkPixbuf *icon;
404 icon = gdk_pixbuf_new_from_resource
405 ("/org/harishankar/wordblox/wordblox.svg", NULL);
406 if (icon == NULL)
407 fprintf (stderr, ERROR_ICON);
408
409 GtkBuilder *builder;
410 builder = gtk_builder_new ();
411 guint ret = gtk_builder_add_from_resource (builder,
412 "/org/harishankar/wordblox/wordblox_player.glade", NULL);
413
414 app_data.is_loaded = false;
415
416 if (ret == 0)
417 {
418 fprintf (stderr, ERROR_WINDOW);
419 g_object_unref (builder);
420 return 1;
421 }
422 else
423 {
424 window = GTK_WIDGET (gtk_builder_get_object (builder, "main_window") );
425 across_store = GTK_LIST_STORE (gtk_builder_get_object
426 (builder, "store_across_clues"));
427 down_store = GTK_LIST_STORE (gtk_builder_get_object
428 (builder, "store_down_clues"));
429
430 if (window != NULL)
431 {
432 gtk_window_set_default_icon (icon);
433
434 GtkWidget *draw_area = GTK_WIDGET (
435 gtk_builder_get_object(builder, "puzzle_area"));
436
437 /* make drawing area respond to mouse event */
438 gtk_widget_set_events(draw_area,
439 gtk_widget_get_events(draw_area)
440 | GDK_BUTTON_PRESS_MASK);
441
442 gtk_builder_connect_signals (builder, NULL);
443 g_object_unref (builder);
444 gtk_widget_show (window);
445 gtk_main ();
446 return 0;
447 }
448 else
449 {
450 g_object_unref (builder);
451 fprintf (stderr, ERROR_WINDOW);
452 return 1;
453 }
454 }
455 }