1 /* Atomix -- a little puzzle game about atoms and molecules.
2 * Copyright (C) 1999 Jens Finke
3 * Copyright (C) 2005 Guilherme de S. Pastore
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20 #include "board-gtk.h"
21 #include "main.h"
22
23 #define ANIM_TIMEOUT 8 /* time in milliseconds between
24 two atom movements */
25
26 typedef struct
27 {
28 gint timeout_id;
29 gint counter;
30 gint dest_row;
31 gint dest_col;
32 double x_step;
33 double y_step;
34 } AnimData;
35
36 typedef struct
37 {
38 guint row;
39 guint col;
40 gboolean selected;
41 gint arrow_show_timeout;
42 gint mouse_steering;
43 GtkWidget *sel_item;
44 GtkWidget *selector;
45 GSList *arrows;
46 GtkWidget *arrow_left;
47 GtkWidget *arrow_right;
48 GtkWidget *arrow_top;
49 GtkWidget *arrow_bottom;
50 } SelectorData;
51
52 typedef struct
53 {
54 GSList *moveables;
55 GtkWidget *logo;
56 } LevelItems;
57
58 typedef enum
59 {
60 UP,
61 DOWN,
62 LEFT,
63 RIGHT
64 } ItemDirection;
65
66
67 // FIXME get rid of static variables
68 /* Static declarations, to be removed */
69 extern AtomixApp *app;
70 static GtkFixed *board_canvas = NULL;
71 static Theme *board_theme = NULL;
72 static PlayField *board_env = NULL; /* the actual playfield */
73 static PlayField *board_sce = NULL; /* the actual playfield */
74 static PlayField *board_shadow = NULL; /* the shadow positions */
75 static AnimData *anim_data; /* holds the date for the atom
76 animation */
77 static LevelItems *level_items;
78 static GSList *board_canvas_items = NULL; /* a list of all used */
79 static Goal *board_goal = NULL; /* the goal of this level */
80 static SelectorData *selector_data; /* data about the selector */
81
82
83 /* Forward declarations of internal functions */
84 static gboolean on_key_press_event (GObject *widget, GdkEventKey *event,
85 gpointer user_data);
86 void board_gtk_render (void);
87 static void render_tile (Tile *tile, gint row, gint col);
88 GtkWidget* create_tile (double x, double y, Tile *tile);
89 void move_item (GtkWidget *item, ItemDirection direc);
90 int move_item_anim (void *data);
91 static GtkWidget *get_item_by_row_col (guint row, guint col);
92
93 static void selector_move_to (SelectorData *data, guint row, guint col);
94 static void selector_unselect (SelectorData *data);
95 static void selector_select (SelectorData *data, GtkWidget *item);
96 static SelectorData *selector_create (void);
97 static void selector_hide (SelectorData *data);
98 static void selector_show (SelectorData *data);
99 static void selector_arrows_hide (SelectorData *data);
100
101
102 /* Function implementations */
103
get_item_by_row_col(guint row,guint col)104 static GtkWidget *get_item_by_row_col (guint row, guint col)
105 {
106 gint width, height;
107 gint item_point_x, item_point_y;
108 guint item_row, item_col;
109 GSList *list_item = level_items->moveables;
110
111 theme_get_tile_size (board_theme, &width, &height);
112
113 while (list_item != NULL) {
114 gtk_container_child_get (GTK_CONTAINER (board_canvas), list_item->data, "x", &item_point_x, "y", &item_point_y, NULL);
115
116 convert_to_playfield (board_theme, board_env, item_point_x, item_point_y, &item_row, &item_col);
117
118 if (item_col == col && item_row == row)
119 return GTK_WIDGET (list_item->data);
120
121 list_item = g_slist_next (list_item);
122 }
123
124 return NULL;
125 }
126
get_row_col_by_item(GtkWidget * item,guint * row,guint * col)127 static void get_row_col_by_item (GtkWidget *item, guint *row, guint *col)
128 {
129 gint x, y;
130
131 g_return_if_fail (GTK_IS_WIDGET (item));
132
133 gtk_container_child_get (GTK_CONTAINER (board_canvas), item, "x", &x, "y", &y, NULL);
134 convert_to_playfield (board_theme, board_env, x, y, row, col);
135
136 }
137
move_item(GtkWidget * item,ItemDirection direc)138 void move_item (GtkWidget *item, ItemDirection direc)
139 {
140 gint x1, y1;
141 gint new_x1, new_y1;
142 guint src_row, src_col, dest_row, dest_col, tmp_row, tmp_col;
143 gint animstep;
144 Tile *tile;
145 gint tw, th;
146
147 gtk_container_child_get (GTK_CONTAINER (board_canvas), item, "x", &x1, "y", &y1, NULL);
148 theme_get_tile_size (board_theme, &tw, &th);
149 convert_to_playfield (board_theme, board_env, x1, y1, &src_row, &src_col);
150
151 /* find destination row/col */
152 tmp_row = dest_row = src_row;
153 tmp_col = dest_col = src_col;
154
155 while (TRUE) {
156 switch (direc) {
157 case UP:
158 tmp_row = tmp_row - 1;
159 break;
160
161 case DOWN:
162 tmp_row = tmp_row + 1;
163 break;
164
165 case LEFT:
166 tmp_col = tmp_col - 1;
167 break;
168
169 case RIGHT:
170 tmp_col = tmp_col + 1;
171 break;
172 default:
173 break;
174 }
175
176 if (tmp_row >= playfield_get_n_rows (board_sce) ||
177 tmp_col >= playfield_get_n_cols (board_sce))
178 break;
179
180 tile = playfield_get_tile (board_sce, tmp_row, tmp_col);
181 if (tile && (tile_get_tile_type (tile) == TILE_TYPE_ATOM ||
182 tile_get_tile_type (tile) == TILE_TYPE_WALL)) {
183 g_object_unref (tile);
184 break;
185 }
186
187 dest_row = tmp_row;
188 dest_col = tmp_col;
189 if (tile)
190 g_object_unref (tile);
191 }
192
193 /* move the item, if the new position is different */
194 if (src_row != dest_row || src_col != dest_col) {
195 if (!undo_exists()) {
196 app->state = GAME_STATE_RUNNING;
197 update_menu_item_state ();
198 }
199
200 undo_push_move (item, src_row, src_col, dest_row, dest_col);
201
202 convert_to_canvas (board_theme, board_env, dest_row, dest_col, &new_x1, &new_y1);
203 playfield_swap_tiles (board_sce, src_row, src_col, dest_row, dest_col);
204
205 selector_hide (selector_data);
206
207 animstep = theme_get_animstep (board_theme);
208 if (direc == UP || direc == DOWN) {
209 anim_data->counter = (gint) (fabs (new_y1 - y1) / animstep);
210 anim_data->x_step = 0;
211 anim_data->y_step = (direc == DOWN) ? animstep : -animstep;
212 } else {
213 anim_data->counter = (gint) (fabs (new_x1 - x1) / animstep);
214 anim_data->x_step = (direc == RIGHT) ? animstep : -animstep;
215 anim_data->y_step = 0;
216 }
217
218 anim_data->dest_row = dest_row;
219 anim_data->dest_col = dest_col;
220
221 anim_data->timeout_id = g_timeout_add (ANIM_TIMEOUT, move_item_anim,
222 anim_data);
223 }
224 }
225
move_item_anim(void * data)226 int move_item_anim (void *data) {
227 //AnimData *anim_data = (AnimData *) data;
228 gint x, y;
229
230 if (anim_data->counter > 0) {
231 gtk_container_child_get (GTK_CONTAINER (board_canvas),
232 selector_data->sel_item, "x", &x, "y", &y, NULL);
233 gtk_fixed_move (GTK_FIXED (board_canvas), selector_data->sel_item,
234 x + anim_data->x_step, y + anim_data->y_step);
235 anim_data->counter--;
236
237 return TRUE;
238 }
239 // else
240 anim_data->timeout_id = -1;
241 selector_move_to (selector_data, anim_data->dest_row, anim_data->dest_col);
242
243 if (goal_reached (board_goal, board_sce, anim_data->dest_row,
244 anim_data->dest_col)){
245 game_level_finished ();
246 } else if (selector_data->selected)
247 selector_select (selector_data, selector_data->sel_item);
248 return FALSE;
249 }
250
board_handle_arrow_event(GtkWidget * item,GdkEventButton * event,gpointer direction)251 static gboolean board_handle_arrow_event (GtkWidget *item,
252 GdkEventButton *event,
253 gpointer direction)
254 {
255 gtk_widget_grab_focus (GTK_WIDGET (board_canvas));
256 /* is currently an object moved? */
257 if (anim_data->timeout_id != -1)
258 return FALSE;
259 if (event->type == GDK_BUTTON_PRESS && selector_data->selected) {
260 selector_data->mouse_steering = TRUE;
261 move_item (selector_data->sel_item, GPOINTER_TO_INT (direction));
262 return TRUE;
263 }
264
265 return FALSE;
266 }
267
create_arrow(SelectorData * data,GdkPixbuf * pixbuf,ItemDirection direction)268 static GtkWidget* create_arrow (SelectorData *data, GdkPixbuf *pixbuf, ItemDirection direction) {
269 GtkWidget *image;
270 GtkWidget *arrow = gtk_event_box_new ();
271
272 image = gtk_image_new_from_pixbuf (pixbuf);
273 gtk_widget_show (image);
274 gtk_container_add (GTK_CONTAINER (arrow), image);
275 gtk_widget_set_events (arrow, GDK_BUTTON_PRESS_MASK);
276
277 g_signal_connect (G_OBJECT (arrow), "button-press-event",
278 G_CALLBACK (board_handle_arrow_event),
279 GINT_TO_POINTER (direction));
280
281 data->arrows = g_slist_prepend (data->arrows, arrow);
282 gtk_fixed_put (GTK_FIXED (board_canvas), arrow, 0, 0);
283 g_object_unref (pixbuf);
284 return arrow;
285 }
286
selector_create(void)287 static SelectorData *selector_create (void)
288 {
289 SelectorData *data;
290 GdkPixbuf *pixbuf;
291 GdkPixbuf *sel_arrows[4];
292 gint tile_width, tile_height;
293
294 data = g_new0 (SelectorData, 1);
295
296 pixbuf = theme_get_selector_image (board_theme);
297 theme_get_selector_arrow_images (board_theme, &sel_arrows[0]);
298 theme_get_tile_size (board_theme, &tile_width, &tile_height);
299
300 g_return_val_if_fail (pixbuf != NULL, NULL);
301
302 data->row = 0;
303 data->col = 0;
304 data->sel_item = NULL;
305 data->selected = FALSE;
306 data->arrow_show_timeout = -1;
307 data->mouse_steering = FALSE;
308
309 data->selector = gtk_image_new_from_pixbuf (pixbuf);
310 gtk_fixed_put (GTK_FIXED (board_canvas), data->selector, 0, 0);
311 g_object_unref (pixbuf);
312
313 data->arrow_top = create_arrow (data, sel_arrows[0], UP);
314 data->arrow_right = create_arrow (data, sel_arrows[1], RIGHT);
315 data->arrow_bottom = create_arrow (data, sel_arrows[2], DOWN);
316 data->arrow_left = create_arrow (data, sel_arrows[3], LEFT);
317
318 return data;
319 }
320
create_logo(void)321 static void create_logo (void)
322 {
323 GdkPixbuf *pixbuf;
324 int tile_width, tile_height;
325 GtkWidget *logo_image;
326 GtkWidget *tips_label;
327 GtkCssProvider *provider;
328 GtkStyleContext *context;
329
330 theme_get_tile_size (board_theme, &tile_width, &tile_height);
331 pixbuf = gdk_pixbuf_new_from_file (DATADIR "/atomix/atomix-logo.png", NULL);
332
333 level_items->logo = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
334
335 context = gtk_widget_get_style_context (level_items->logo);
336 provider = gtk_css_provider_new ();
337 gtk_css_provider_load_from_data (provider, "* { background-color: rgba(230, 230, 230, 0.6); }", -1, NULL);
338 gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
339 logo_image = gtk_image_new_from_pixbuf (pixbuf);
340 gtk_widget_set_valign (logo_image, GTK_ALIGN_END);
341 gtk_box_pack_start (GTK_BOX (level_items->logo), logo_image, TRUE, TRUE, 12);
342
343 tips_label = gtk_label_new (_("Guide the atoms through the maze to form molecules. "
344 "Click, or use the arrow keys and Enter, to select an atom and move it. "
345 "Be careful, though: an atom keeps moving until it hits a wall."));
346 gtk_box_pack_start (GTK_BOX (level_items->logo), tips_label, TRUE, TRUE, 12);
347 gtk_widget_set_valign (tips_label, GTK_ALIGN_START);
348 gtk_widget_show_all (level_items->logo);
349
350 gtk_fixed_put (GTK_FIXED (board_canvas), level_items->logo,
351 0, 0);
352 gtk_widget_set_size_request (level_items->logo, BGR_FLOOR_COLS * tile_width, BGR_FLOOR_ROWS * tile_height);
353 gtk_widget_set_size_request (GTK_WIDGET (tips_label), BGR_FLOOR_COLS * tile_width, -1);
354 gtk_label_set_justify (GTK_LABEL (tips_label), GTK_JUSTIFY_CENTER);
355 gtk_label_set_line_wrap (GTK_LABEL (tips_label), TRUE);
356
357 g_object_unref (pixbuf);
358 }
359
create_background_floor(void)360 static void create_background_floor (void)
361 {
362 int row, col;
363 Tile *tile;
364 GQuark quark;
365 int tile_width, tile_height;
366 double x, y;
367 GdkPixbuf *pixbuf = NULL;
368 GtkWidget *item;
369 int ca_width, ca_height;
370 int width, height;
371 GtkAllocation allocation;
372
373 quark = g_quark_from_static_string ("floor");
374 theme_get_tile_size (board_theme, &tile_width, &tile_height);
375
376 tile = tile_new (TILE_TYPE_FLOOR);
377 tile_set_base_id (tile, quark);
378 pixbuf = theme_get_tile_image (board_theme, tile);
379 g_object_unref (tile);
380
381 for (row = 0; row < BGR_FLOOR_ROWS; row++)
382 for (col = 0; col < BGR_FLOOR_COLS; col++) {
383 x = col * tile_width;
384 y = row * tile_height;
385
386 item = gtk_image_new_from_pixbuf (pixbuf);
387 gtk_fixed_put (GTK_FIXED (board_canvas), item, x, y);
388 }
389
390 g_object_unref (pixbuf);
391
392 /* center the whole thing */
393 gtk_widget_get_allocation (GTK_WIDGET (board_canvas), &allocation);
394 ca_width = allocation.width;
395 ca_height = allocation.height;
396
397 width = tile_width * BGR_FLOOR_COLS;
398 height = tile_height * BGR_FLOOR_ROWS;
399
400 if (width > ca_width) {
401 x = (width / 2) - (ca_width / 2);
402 width = ca_width;
403 } else
404 x = 0;
405
406 if (height > ca_height) {
407 y = (height / 2) - (ca_height / 2);
408 height = ca_height;
409 } else
410 y = 0;
411 }
412
board_gtk_init(Theme * theme,gpointer canvas)413 void board_gtk_init (Theme * theme, gpointer canvas)
414 {
415 board_theme = theme;
416 board_canvas = canvas;
417 g_object_ref (theme);
418 board_theme = theme;
419 board_env = NULL;
420 board_sce = NULL;
421 board_goal = NULL;
422
423 /* Animation Data Setup */
424 anim_data = g_new0 (AnimData, 1);
425 anim_data->timeout_id = -1;
426 anim_data->counter = 0;
427 anim_data->dest_row = 0;
428 anim_data->dest_col = 0;
429 anim_data->x_step = 0.0;
430 anim_data->y_step = 0.0;
431
432 undo_clear ();
433 level_items = g_new0 (LevelItems, 1);
434
435 create_background_floor ();
436 create_logo ();
437 gtk_widget_show_all (GTK_WIDGET(board_canvas));
438
439 /* add playfield canvas to left side */
440 g_signal_connect (GTK_WIDGET(board_canvas), "key-press-event",
441 G_CALLBACK (on_key_press_event), app);
442
443 selector_data = selector_create ();
444 }
445
board_gtk_render(void)446 void board_gtk_render (void) {
447 guint row, col;
448 Tile *tile;
449
450 g_return_if_fail (board_theme != NULL);
451
452 /* render one row more than the actual environment because
453 of the shadow, which is one row/col larger */
454
455 for (row = 0; row <= playfield_get_n_rows (board_env); row++) {
456 for (col = 0; col <= playfield_get_n_cols (board_env); col++) {
457 if (row < playfield_get_n_rows (board_env) &&
458 col < playfield_get_n_cols (board_env)) {
459 tile = playfield_get_tile (board_sce, row, col);
460 if (tile != NULL) {
461 render_tile (tile, row, col);
462 g_object_unref (tile);
463 }
464
465 tile = playfield_get_tile (board_env, row, col);
466
467 if (tile != NULL) {
468 render_tile (tile, row, col);
469 if (tile_get_tile_type (tile) == TILE_TYPE_WALL)
470 playfield_set_tile (board_sce, row, col, tile);
471
472 g_object_unref (tile);
473 }
474 }
475
476 tile = playfield_get_tile (board_shadow, row, col);
477 if (tile != NULL) {
478 render_tile (tile, row, col);
479 g_object_unref (tile);
480 }
481 }
482 }
483 }
484
render_tile(Tile * tile,gint row,gint col)485 static void render_tile (Tile *tile, gint row, gint col) {
486 TileType type;
487 gint x, y;
488
489 type = tile_get_tile_type (tile);
490 switch (type) {
491 case TILE_TYPE_ATOM:
492 case TILE_TYPE_WALL:
493 case TILE_TYPE_SHADOW:
494 convert_to_canvas (board_theme, board_env, row, col, &x, &y);
495 create_tile (x, y, tile);
496 break;
497
498 case TILE_TYPE_UNKNOWN:
499 case TILE_TYPE_FLOOR:
500 case TILE_TYPE_NONE:
501 case TILE_TYPE_LAST:
502 default:
503 break;
504 }
505 }
506
show_sensitive(GtkWidget * widget)507 static void show_sensitive (GtkWidget *widget)
508 {
509 gtk_widget_set_visible (widget, gtk_widget_is_sensitive (widget));
510 }
511
show_arrow_group(SelectorData * data)512 static gboolean show_arrow_group (SelectorData *data)
513 {
514 g_slist_foreach (data->arrows, (GFunc)show_sensitive, NULL);
515 data->arrow_show_timeout = -1;
516
517 return FALSE;
518 }
519
520
board_handle_item_event(GtkWidget * item,GdkEventButton * event,gpointer data)521 static gboolean board_handle_item_event (GtkWidget *item,
522 GdkEventButton *event, gpointer data) {
523 gboolean just_unselect;
524 guint new_row, new_col;
525
526 gtk_widget_grab_focus (GTK_WIDGET (board_canvas));
527 /* is currently an object moved? */
528 if (anim_data->timeout_id != -1)
529 return FALSE;
530
531 if (event->type == GDK_BUTTON_PRESS) {
532 selector_data->mouse_steering = TRUE;
533 just_unselect = (item == selector_data->sel_item);
534
535 if (selector_data->selected)
536 /* unselect item, show selector image */
537 selector_unselect (selector_data);
538
539 if (!just_unselect) {
540 get_row_col_by_item (item, &new_row, &new_col);
541 selector_move_to (selector_data, new_row, new_col);
542 selector_select (selector_data, item);
543 }
544
545 return FALSE;
546 }
547
548 return FALSE;
549 }
550
create_tile(double x,double y,Tile * tile)551 GtkWidget* create_tile (double x, double y,
552 Tile *tile)
553 {
554 GdkPixbuf *pixbuf = NULL;
555 GtkWidget *item = NULL;
556 GtkWidget *event_box = NULL;
557 pixbuf = theme_get_tile_image (board_theme, tile);
558
559 item = gtk_image_new_from_pixbuf (pixbuf);
560
561 if (tile_get_tile_type (tile) == TILE_TYPE_ATOM) {
562 event_box = gtk_event_box_new ();
563 gtk_container_add (GTK_CONTAINER (event_box), item);
564 gtk_widget_show (item);
565 gtk_widget_set_events (event_box, GDK_BUTTON_PRESS_MASK);
566 item = event_box;
567 g_signal_connect (G_OBJECT (item), "button-press-event",
568 G_CALLBACK (board_handle_item_event), NULL);
569 level_items->moveables = g_slist_prepend (level_items->moveables, item);
570 }
571
572 gtk_widget_show (item);
573 gtk_fixed_put (GTK_FIXED (board_canvas), item, x, y);
574
575 g_object_set_data (G_OBJECT (item), "tile", tile);
576
577 board_canvas_items = g_slist_prepend (board_canvas_items, item);
578 return item;
579 }
580
board_gtk_init_level(PlayField * base_env,PlayField * sce,Goal * goal)581 void board_gtk_init_level (PlayField * base_env, PlayField * sce, Goal * goal)
582 {
583 gint row, col;
584
585 /* init item anim structure */
586 anim_data->timeout_id = -1;
587 anim_data->counter = 0;
588 anim_data->dest_row = 0;
589 anim_data->dest_col = 0;
590 anim_data->x_step = 0.0;
591 anim_data->y_step = 0.0;
592
593 /* reset undo of moves */
594 undo_clear ();
595
596 board_gtk_clear ();
597
598 /* init board */
599 board_env = playfield_generate_environment (base_env, board_theme);
600 board_sce = playfield_copy (sce);
601 board_shadow = playfield_generate_shadow (base_env);
602
603 /* init goal */
604 board_goal = g_object_ref (goal);
605
606 /* init selector */
607 row = playfield_get_n_rows (board_env) / 2;
608 col = playfield_get_n_cols (board_env) / 2;
609 selector_move_to (selector_data, row, col);
610 selector_unselect (selector_data);
611 selector_show (selector_data);
612 selector_arrows_hide (selector_data);
613 /* render level */
614 board_gtk_render ();
615 board_gtk_show ();
616 }
617
board_gtk_destroy(void)618 void board_gtk_destroy (void)
619 {
620 if (board_env)
621 g_object_unref (board_env);
622 if (board_sce)
623 g_object_unref (board_sce);
624 if (anim_data)
625 g_free (anim_data);
626 if (level_items)
627 g_free (level_items);
628 undo_clear ();
629
630 if (selector_data) {
631 g_slist_free_full (selector_data->arrows, (GDestroyNotify)gtk_widget_destroy);
632 g_free (selector_data);
633 }
634
635 if (board_theme)
636 g_object_unref (board_theme);
637
638 if (board_goal)
639 g_object_unref (board_goal);
640
641 }
642
board_gtk_clear(void)643 void board_gtk_clear (void)
644 {
645 g_slist_foreach (board_canvas_items, (GFunc) gtk_widget_destroy, NULL);
646 g_slist_free (board_canvas_items);
647 board_canvas_items = NULL;
648
649 g_slist_free (level_items->moveables);
650 level_items->moveables = NULL;
651
652 /* clear board */
653 if (board_env)
654 {
655 g_object_unref (board_env);
656 board_env = NULL;
657 }
658 if (board_sce)
659 {
660 g_object_unref (board_sce);
661 board_sce = NULL;
662 }
663 if (board_goal)
664 {
665 g_object_unref (board_goal);
666 board_goal = NULL;
667 }
668 if (board_shadow)
669 {
670 g_object_unref (board_shadow);
671 board_shadow = NULL;
672 }
673
674 selector_hide (selector_data);
675
676 }
677
board_gtk_print(void)678 void board_gtk_print (void)
679 {
680 g_print ("Board:\n");
681 playfield_print (board_env);
682 }
683
board_gtk_hide(void)684 void board_gtk_hide (void)
685 {
686 g_slist_foreach (level_items->moveables, (GFunc)gtk_widget_hide, NULL);
687 g_slist_foreach (selector_data->arrows, (GFunc)gtk_widget_hide, NULL);
688 }
689
board_gtk_show(void)690 void board_gtk_show (void)
691 {
692 g_slist_foreach (level_items->moveables, (GFunc)gtk_widget_show, NULL);
693 if (undo_exists ())
694 g_slist_foreach (selector_data->arrows, (GFunc)gtk_widget_show, NULL);
695 }
696
board_gtk_undo_move(void)697 gboolean board_gtk_undo_move (void)
698 {
699 UndoMove *move;
700 gint x_src, y_src, x_dest, y_dest;
701 gint animstep;
702
703 g_return_val_if_fail (board_theme != NULL, FALSE);
704
705 if (anim_data->timeout_id != -1)
706 return FALSE;
707
708 move = undo_pop_move ();
709 if (move == NULL)
710 return FALSE;
711
712 playfield_swap_tiles (board_sce,
713 move->src_row, move->src_col,
714 move->dest_row, move->dest_col);
715
716 if (selector_data->selected) {
717 selector_hide (selector_data);
718 selector_move_to (selector_data, move->src_row, move->src_col);
719 }
720
721 convert_to_canvas (board_theme, board_env, move->src_row,
722 move->src_col, &x_src, &y_src);
723 convert_to_canvas (board_theme, board_env, move->dest_row,
724 move->dest_col, &x_dest, &y_dest);
725
726 animstep = theme_get_animstep (board_theme);
727 if (move->src_col == move->dest_col) {
728 anim_data->counter = (gint) (fabs (y_dest - y_src) / animstep);
729 anim_data->x_step = 0;
730 anim_data->y_step = animstep;
731 if (move->src_row < move->dest_row)
732 anim_data->y_step = -(anim_data->y_step);
733 } else {
734 anim_data->counter = (gint) (fabs (x_dest - x_src) / animstep);
735 anim_data->x_step = animstep;
736 anim_data->y_step = 0;
737 if (move->src_col < move->dest_col)
738 anim_data->x_step = -(anim_data->x_step);
739 }
740
741 anim_data->dest_row = move->src_row;
742 anim_data->dest_col = move->src_col;
743 selector_data->sel_item = move->item;
744
745 anim_data->timeout_id = g_timeout_add (ANIM_TIMEOUT,
746 move_item_anim, anim_data);
747 g_free (move);
748
749 return TRUE;
750 }
751
board_gtk_show_logo(gboolean visible)752 void board_gtk_show_logo (gboolean visible)
753 {
754 if (visible)
755 gtk_widget_show (level_items->logo);
756 else
757 gtk_widget_hide (level_items->logo);
758 }
759
board_gtk_handle_key_event(GObject * canvas,GdkEventKey * event,gpointer data)760 gboolean board_gtk_handle_key_event (GObject * canvas, GdkEventKey * event,
761 gpointer data)
762 {
763 GtkWidget *item;
764 guint new_row, new_col;
765 Tile *tile;
766
767 g_return_val_if_fail (selector_data != NULL, FALSE);
768
769 new_row = selector_data->row;
770 new_col = selector_data->col;
771
772 /* is currently an object moved? */
773 if (anim_data->timeout_id != -1)
774 return FALSE;
775
776 switch (event->keyval) {
777 case GDK_KEY_space:
778 case GDK_KEY_Return:
779 selector_data->mouse_steering = FALSE;
780 if (selector_data->selected)
781 /* unselect item, show selector image */
782 selector_unselect (selector_data);
783 else {
784 item = get_item_by_row_col (selector_data->row, selector_data->col);
785 if (item == NULL)
786 break;
787 if (g_object_get_data (G_OBJECT (item), "tile") == NULL)
788 break;
789
790 tile = TILE (g_object_get_data (G_OBJECT (item), "tile"));
791
792 if (tile_get_tile_type (tile) == TILE_TYPE_ATOM)
793 selector_select (selector_data, item);
794 }
795 break;
796 case GDK_KEY_Escape:
797 if (selector_data->selected)
798 /* unselect item, show selector image */
799 selector_unselect (selector_data);
800 break;
801 case GDK_KEY_Left:
802 selector_data->mouse_steering = FALSE;
803 if (!selector_data->selected) {
804 new_col--;
805 if (new_col < playfield_get_n_cols (board_env)) {
806 selector_show (selector_data);
807 selector_move_to (selector_data, new_row, new_col);
808 }
809 } else
810 move_item (selector_data->sel_item, LEFT); /* selector will be
811 moved in this
812 function */
813 break;
814
815 case GDK_KEY_Right:
816 selector_data->mouse_steering = FALSE;
817 if (!selector_data->selected) {
818 new_col++;
819 if (new_col < playfield_get_n_cols (board_env)) {
820 selector_show (selector_data);
821 selector_move_to (selector_data, new_row, new_col);
822 }
823 } else
824 move_item (selector_data->sel_item, RIGHT); /* selector will be
825 moved in this
826 function */
827 break;
828
829 case GDK_KEY_Up:
830 selector_data->mouse_steering = FALSE;
831 if (!selector_data->selected) {
832 new_row--;
833 if (new_row < playfield_get_n_rows (board_env)) {
834 selector_show (selector_data);
835 selector_move_to (selector_data, new_row, new_col);
836 }
837 } else
838 move_item (selector_data->sel_item, UP); /* selector will be moved
839 in this function */
840 break;
841
842 case GDK_KEY_Down:
843 selector_data->mouse_steering = FALSE;
844 if (!selector_data->selected) {
845 new_row++;
846 if (new_row < playfield_get_n_rows (board_env)) {
847 selector_show (selector_data);
848 selector_move_to (selector_data, new_row, new_col);
849 }
850 } else
851 move_item (selector_data->sel_item, DOWN); /* selector will be
852 moved in this function */
853 break;
854
855 default:
856 break;
857 }
858
859 return FALSE;
860 }
861
on_key_press_event(GObject * widget,GdkEventKey * event,gpointer user_data)862 static gboolean on_key_press_event (GObject *widget, GdkEventKey *event,
863 gpointer user_data)
864 {
865 if ((app->state == GAME_STATE_RUNNING) || (app->state == GAME_STATE_RUNNING_UNMOVED))
866 return board_gtk_handle_key_event (NULL, event, NULL);
867
868 return FALSE;
869 }
870
selector_move_to(SelectorData * data,guint row,guint col)871 static void selector_move_to (SelectorData *data, guint row, guint col)
872 {
873 int tile_width, tile_height;
874 int x, y;
875
876 g_return_if_fail (data != NULL);
877
878 if (data->arrow_show_timeout > -1)
879 g_source_remove (data->arrow_show_timeout);
880
881 data->arrow_show_timeout = -1;
882
883 theme_get_tile_size (board_theme, &tile_width, &tile_height);
884
885 convert_to_canvas (board_theme, board_env, row, col, &x, &y);
886
887 g_object_ref (data->selector);
888 gtk_container_remove (GTK_CONTAINER (board_canvas), data->selector);
889 gtk_fixed_put (GTK_FIXED (board_canvas), data->selector, x, y);
890 g_object_unref (data->selector);
891
892 gtk_fixed_move (GTK_FIXED (board_canvas), data->arrow_left, x - tile_width, y);
893 gtk_fixed_move (GTK_FIXED (board_canvas), data->arrow_right, x + tile_width, y);
894 gtk_fixed_move (GTK_FIXED (board_canvas), data->arrow_top, x, y - tile_width);
895 gtk_fixed_move (GTK_FIXED (board_canvas), data->arrow_bottom, x, y + tile_width);
896
897 data->row = row;
898 data->col = col;
899 }
900
check_for_arrow(gint r,gint c,GtkWidget * arrow)901 static void check_for_arrow (gint r, gint c, GtkWidget *arrow) {
902 Tile *tile;
903
904 tile = playfield_get_tile (board_sce, r, c);
905
906 gtk_widget_set_sensitive (arrow, tile == NULL);
907
908 if (tile != NULL)
909 g_object_unref (tile);
910 }
911
selector_arrows_show(SelectorData * data)912 static void selector_arrows_show (SelectorData *data)
913 {
914 guint r, c;
915
916 if (board_sce == NULL)
917 {
918 selector_arrows_hide (data);
919 return;
920 }
921
922 r = data->row - 1;
923 c = data->col;
924 // if (r >= 0)
925 check_for_arrow (r, c, data->arrow_top);
926
927 r = data->row;
928 c = data->col + 1;
929
930 if (c < playfield_get_n_cols (board_sce))
931 check_for_arrow (r, c, data->arrow_right);
932
933 r = data->row + 1;
934 c = data->col;
935
936 if (r < playfield_get_n_rows (board_sce))
937 check_for_arrow (r, c, data->arrow_bottom);
938
939 r = data->row;
940 c = data->col - 1;
941
942 // if (c >= 0)
943 check_for_arrow (r, c, data->arrow_left);
944
945 if (data->mouse_steering)
946 show_arrow_group (data);
947 else {
948 if (data->arrow_show_timeout > -1)
949 g_source_remove (data->arrow_show_timeout);
950
951 data->arrow_show_timeout =
952 g_timeout_add (2000, (GSourceFunc) show_arrow_group, data);
953 }
954 }
955
selector_select(SelectorData * data,GtkWidget * item)956 static void selector_select (SelectorData *data, GtkWidget *item)
957 {
958 gint x, y;
959
960 g_return_if_fail (data != NULL);
961 gtk_container_child_get (GTK_CONTAINER(board_canvas), item, "x", &x, "y", &y, NULL);
962
963 data->selected = TRUE;
964 data->sel_item = item;
965
966 gtk_widget_hide (data->selector);
967 selector_arrows_show (data);
968 }
969
selector_unselect(SelectorData * data)970 static void selector_unselect (SelectorData *data)
971 {
972 g_return_if_fail (data != NULL);
973
974 data->selected = FALSE;
975 data->sel_item = NULL;
976
977 if (!data->mouse_steering)
978 selector_show (data);
979
980 selector_arrows_hide (data);
981 }
982
selector_arrows_hide(SelectorData * data)983 static void selector_arrows_hide (SelectorData *data)
984 {
985 if (data->arrow_show_timeout > -1)
986 g_source_remove (data->arrow_show_timeout);
987 data->arrow_show_timeout = -1;
988 g_slist_foreach (data->arrows, (GFunc)gtk_widget_hide, NULL);
989 }
990
selector_hide(SelectorData * data)991 static void selector_hide (SelectorData *data)
992 {
993 gtk_widget_hide (data->selector);
994 selector_arrows_hide (data);
995 }
996
selector_show(SelectorData * data)997 static void selector_show (SelectorData *data)
998 {
999 gtk_widget_show (data->selector);
1000 }
1001
1002