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