20100212
[gdash.git] / src / editorwidgets.c
blobf9a2a169ad80a639feea7fe00c899f299ca173b0
1 /*
2 * Copyright (c) 2007, 2008, 2009, Czirkos Zoltan <cirix@fw.hu>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 #include "config.h"
17 #include <gtk/gtk.h>
18 #include <glib/gi18n.h>
19 #include "cave.h"
20 #include "cavedb.h"
21 #include "gtkgfx.h"
22 #include "editorwidgets.h"
25 * A COMBO BOX with c64 colors.
26 * use color_combo_new for creating and color_combo_get_color for getting color in gdash format.
29 /* this data field always stores the previously selected color. */
30 /* it is needed when a select atari or select rgb dialog is escaped, and we must set the original color. */
31 /* the combo box itself cannot store it, as is is already set to the select atari... or select rgb... line. */
32 #define GDASH_COLOR "gdash-color"
34 static GtkTreePath *selected_color_path=NULL;
36 enum {
37 COL_COLOR_ACTION,
38 COL_COLOR_CODE,
39 COL_COLOR_NAME,
40 COL_COLOR_PIXBUF,
43 typedef enum {
44 COLOR_ACTION_NONE,
45 COLOR_ACTION_SELECT_ATARI,
46 COLOR_ACTION_SELECT_DTV,
47 COLOR_ACTION_SELECT_RGB,
48 } ColorAction;
51 * creates a small pixbuf with the specified color
53 static GdkPixbuf *
54 color_combo_pixbuf_for_gd_color(GdColor col)
56 int x, y;
57 guint32 pixel;
58 GdkPixbuf *pixbuf;
60 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &x, &y);
62 pixbuf=gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, x, y);
63 pixel=(gd_color_get_r(col)<<24) + (gd_color_get_g(col)<<16) + (gd_color_get_b(col)<<8);
64 gdk_pixbuf_fill(pixbuf, pixel);
66 return pixbuf;
69 /* set to a color - first check if that is a c64 color. */
70 void
71 gd_color_combo_set(GtkComboBox *combo, GdColor color)
73 g_object_set_data(G_OBJECT(combo), GDASH_COLOR, GUINT_TO_POINTER(color));
75 if (gd_color_is_c64(color)) {
76 char *path;
77 GtkTreeIter iter;
79 path=g_strdup_printf("0:%d", gd_color_get_c64_index(color));
80 gtk_tree_model_get_iter_from_string(gtk_combo_box_get_model(combo), &iter, path);
81 g_free(path);
82 gtk_combo_box_set_active_iter(combo, &iter);
84 else {
85 GtkTreeModel *model=gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
86 GtkTreeIter iter;
87 GdkPixbuf *pixbuf;
89 gtk_tree_model_get_iter(model, &iter, selected_color_path);
91 pixbuf=color_combo_pixbuf_for_gd_color(color);
92 gtk_tree_store_set(GTK_TREE_STORE(model), &iter, COL_COLOR_CODE, color, COL_COLOR_PIXBUF, pixbuf, COL_COLOR_NAME, gd_color_get_visible_name(color), -1);
93 g_object_unref(pixbuf); /* now the tree store owns its own reference */
95 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter);
99 static gboolean
100 color_combo_drawing_area_button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer data)
102 GtkDialog *dialog=GTK_DIALOG(data);
104 gtk_dialog_response(dialog, GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(widget), GDASH_COLOR)));
106 return TRUE;
111 static void
112 color_combo_changed(GtkWidget *combo, gpointer data)
114 GtkTreeModel *model=gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
115 GtkTreeIter iter;
116 ColorAction action;
118 gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter);
119 gtk_tree_model_get(model, &iter, COL_COLOR_ACTION, &action, -1);
120 switch (action) {
121 case COLOR_ACTION_SELECT_RGB:
123 GtkWidget *dialog;
124 GtkColorSelection *colorsel;
125 gint response;
126 GdkColor gc;
127 GdColor prevcol;
129 dialog=gtk_color_selection_dialog_new (_("Select Color"));
130 gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (gtk_widget_get_toplevel(combo)));
132 colorsel=GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG (dialog)->colorsel);
134 prevcol=GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(combo), GDASH_COLOR));
135 gc.red=gd_color_get_r(prevcol)<<8;
136 gc.green=gd_color_get_g(prevcol)<<8;
137 gc.blue=gd_color_get_b(prevcol)<<8;
138 gtk_color_selection_set_previous_color (colorsel, &gc);
139 gtk_color_selection_set_current_color (colorsel, &gc);
140 gtk_color_selection_set_has_palette (colorsel, TRUE);
142 gtk_window_present_with_time(GTK_WINDOW(dialog), gtk_get_current_event_time());
143 response=gtk_dialog_run(GTK_DIALOG (dialog));
145 if (response==GTK_RESPONSE_OK) {
146 GdkColor gc;
147 GdColor color;
149 gtk_color_selection_get_current_color(colorsel, &gc);
150 color=gd_color_get_from_rgb(gc.red>>8, gc.green>>8, gc.blue>>8);
151 gd_color_combo_set(GTK_COMBO_BOX(combo), color);
152 } else {
153 gd_color_combo_set(GTK_COMBO_BOX(combo), prevcol);
156 gtk_widget_destroy (dialog);
158 break;
160 case COLOR_ACTION_SELECT_ATARI:
161 case COLOR_ACTION_SELECT_DTV:
163 GtkWidget *dialog, *table, *frame;
164 GdColor prevcol;
165 int i;
166 int result;
167 GdColor (*colorfunc) (int i);
168 const char *title;
170 switch (action) {
171 case COLOR_ACTION_SELECT_ATARI:
172 title=_("Select Atari Color");
173 colorfunc=gd_atari_color;
174 break;
175 case COLOR_ACTION_SELECT_DTV:
176 title=_("Select C64 DTV Color");
177 colorfunc=gd_c64dtv_color;
178 break;
179 default:
180 g_assert_not_reached();
183 dialog=gtk_dialog_new_with_buttons(title, GTK_WINDOW (gtk_widget_get_toplevel(combo)), GTK_DIALOG_NO_SEPARATOR, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
184 table=gtk_table_new(16, 16, TRUE);
185 for (i=0; i<256; i++) {
186 GtkWidget *da;
187 GdkColor color;
188 GdColor c;
190 da=gtk_drawing_area_new();
191 c=colorfunc(i);
192 g_object_set_data(G_OBJECT(da), GDASH_COLOR, GUINT_TO_POINTER(i)); /* attach the color index as data */
193 gtk_widget_add_events(da, GDK_BUTTON_PRESS_MASK);
194 gtk_widget_set_tooltip_text(da, gd_color_get_visible_name(c));
195 g_signal_connect(G_OBJECT(da), "button_press_event", G_CALLBACK(color_combo_drawing_area_button_press_event), dialog); /* mouse click */
196 color.red=gd_color_get_r(c)*256; /* 256 as gdk expect 16-bit/component */
197 color.green=gd_color_get_g(c)*256;
198 color.blue=gd_color_get_b(c)*256;
199 gtk_widget_modify_bg(da, GTK_STATE_NORMAL, &color);
200 gtk_widget_set_size_request(da, 16, 16);
201 gtk_table_attach_defaults(GTK_TABLE(table), da, i%16, i%16+1, i/16, i/16+1);
203 frame=gtk_frame_new(NULL);
204 gtk_container_set_border_width(GTK_CONTAINER(frame), 6);
205 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
206 gtk_container_add(GTK_CONTAINER(frame), table);
207 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame);
208 gtk_widget_show_all(GTK_DIALOG(dialog)->vbox);
210 prevcol=GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(combo), GDASH_COLOR));
211 result=gtk_dialog_run(GTK_DIALOG(dialog));
212 if (result>=0)
213 gd_color_combo_set(GTK_COMBO_BOX(combo), colorfunc(result));
214 else
215 gd_color_combo_set(GTK_COMBO_BOX(combo), prevcol);
217 gtk_widget_destroy(dialog);
219 break;
221 case COLOR_ACTION_NONE:
223 GdColor c;
225 gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter);
226 gtk_tree_model_get(model, &iter, COL_COLOR_CODE, &c, -1);
227 g_object_set_data(G_OBJECT(combo), GDASH_COLOR, GUINT_TO_POINTER(c));
229 break;
232 /* A GtkTreeViewRowSeparatorFunc that demonstrates how rows can be
233 * rendered as separators.
235 static gboolean
236 color_combo_is_separator (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
238 GtkTreePath *path;
239 gboolean result;
241 path=gtk_tree_model_get_path(model, iter);
242 result=!gtk_tree_path_compare(path, selected_color_path);
243 gtk_tree_path_free (path);
245 return result;
248 static void
249 color_combo_set_sensitive(GtkCellLayout *cell_layout, GtkCellRenderer *cell, GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
251 g_object_set(cell, "sensitive", !gtk_tree_model_iter_has_child(tree_model, iter), NULL);
254 /* combo box creator. */
255 GtkWidget *
256 gd_color_combo_new(const GdColor color)
258 /* this is the base of the cave editor element combo box.
259 categories are autodetected by their integer values being >O_MAX */
260 GtkTreeStore *store;
261 GtkWidget *combo;
262 GtkCellRenderer *renderer;
263 GtkTreeIter iter, parent;
264 int i;
266 /* tree store for colors. every combo has its own, as the custom color can be different. */
267 store=gtk_tree_store_new(4, G_TYPE_INT, G_TYPE_UINT, G_TYPE_STRING, GDK_TYPE_PIXBUF);
269 /* add 16 c64 colors */
270 gtk_tree_store_append(store, &parent, NULL);
271 gtk_tree_store_set(store, &parent, COL_COLOR_CODE, 0, COL_COLOR_NAME, _("C64 Colors"), COL_COLOR_PIXBUF, NULL, -1);
272 for (i = 0; i<16; i++) {
273 GdkPixbuf *pixbuf;
275 pixbuf=color_combo_pixbuf_for_gd_color(gd_c64_color(i));
276 gtk_tree_store_append(store, &iter, &parent);
277 gtk_tree_store_set(store, &iter, COL_COLOR_ACTION, COLOR_ACTION_NONE, COL_COLOR_CODE, gd_c64_color(i), COL_COLOR_NAME, _(gd_color_get_visible_name(gd_c64_color(i))), COL_COLOR_PIXBUF, pixbuf, -1);
278 g_object_unref (pixbuf);
280 gtk_tree_store_append(store, &iter, NULL);
281 if (!selected_color_path)
282 selected_color_path=gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
283 gtk_tree_store_append(store, &iter, NULL);
284 gtk_tree_store_set(store, &iter, COL_COLOR_ACTION, COLOR_ACTION_SELECT_ATARI, COL_COLOR_CODE, 0, COL_COLOR_NAME, _("Atari color..."), COL_COLOR_PIXBUF, NULL, -1);
285 gtk_tree_store_append(store, &iter, NULL);
286 gtk_tree_store_set(store, &iter, COL_COLOR_ACTION, COLOR_ACTION_SELECT_DTV, COL_COLOR_CODE, 0, COL_COLOR_NAME, _("C64DTV color..."), COL_COLOR_PIXBUF, NULL, -1);
287 gtk_tree_store_append(store, &iter, NULL);
288 gtk_tree_store_set(store, &iter, COL_COLOR_ACTION, COLOR_ACTION_SELECT_RGB, COL_COLOR_CODE, 0, COL_COLOR_NAME, _("RGB color..."), COL_COLOR_PIXBUF, NULL, -1);
290 combo=gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
291 /* first column, object image */
292 renderer=gtk_cell_renderer_pixbuf_new ();
293 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combo), renderer, FALSE);
294 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer, "pixbuf", COL_COLOR_PIXBUF, NULL);
295 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo), renderer, color_combo_set_sensitive, NULL, NULL);
296 /* second column, object name */
297 renderer = gtk_cell_renderer_text_new ();
298 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combo), renderer, TRUE);
299 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo), renderer, color_combo_set_sensitive, NULL, NULL);
300 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer, "text", COL_COLOR_NAME, NULL);
301 gtk_combo_box_set_row_separator_func(GTK_COMBO_BOX(combo), color_combo_is_separator, NULL, NULL);
303 gd_color_combo_set(GTK_COMBO_BOX(combo), color);
304 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(color_combo_changed), NULL);
306 return combo;
309 GdColor
310 gd_color_combo_get_color(GtkWidget *widget)
312 GtkTreeModel *model=gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
313 GtkTreeIter iter;
314 GdColor color;
316 gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget), &iter);
317 gtk_tree_model_get(model, &iter, COL_COLOR_CODE, &color, -1);
318 return color;
321 #undef GDASH_COLOR
335 /****************************************************
337 * create and return a button, which contains
338 * cave elements available in the editor.
339 * an initial value is also selected.
341 #define GDASH_CELL "gdash-cell"
342 #define GDASH_ELEMENT "gdash-element"
343 #define GDASH_HOVER "gdash-hover"
344 #define GDASH_BUTTON "gdash-button"
346 #define GDASH_DIALOG "gdash-dialog"
347 #define GDASH_WINDOW_TITLE "gdash-window-title"
348 #define GDASH_DIALOG_VBOX "gdash-dialog-vbox"
349 #define GDASH_BUTTON_IMAGE "gdash-button-image"
350 #define GDASH_BUTTON_LABEL "gdash-button-label"
352 static int element_button_animcycle;
354 /* this draws one element in the element selector box. */
355 static gboolean
356 element_button_drawing_area_expose_event (const GtkWidget * widget, const GdkEventExpose * event, const gpointer data)
358 GdkDrawable *cell=g_object_get_data(G_OBJECT(widget), GDASH_CELL);
360 if (!widget->window)
361 return FALSE;
363 if (cell)
364 gdk_draw_drawable (widget->window, widget->style->black_gc, cell, 0, 0, 2, 2, gd_cell_size_editor, gd_cell_size_editor);
365 return TRUE;
368 /* this is called when entering and exiting a drawing area with the mouse. */
369 /* so they can be colored when the mouse is over. */
370 static gboolean
371 element_button_drawing_area_crossing_event (const GtkWidget *widget, const GdkEventCrossing *event, const gpointer data)
373 g_object_set_data(G_OBJECT(widget), GDASH_HOVER, GINT_TO_POINTER(event->type==GDK_ENTER_NOTIFY));
374 return FALSE;
377 static gboolean
378 redraw_timeout (gpointer data)
380 GList *areas=(GList *)data;
381 GList *iter;
383 element_button_animcycle=(element_button_animcycle+1)&7;
385 for (iter=areas; iter!=NULL; iter=iter->next) {
386 GtkWidget *da=(GtkWidget *)iter->data;
387 GdElement element;
388 int draw;
389 gboolean hover;
391 /* which element is drawn? */
392 element=(GdElement)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(da), GDASH_ELEMENT));
393 hover=(gboolean)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(da), GDASH_HOVER));
394 /* get pixbuf index */
395 draw=gd_elements[element].image;
396 if (draw<0)
397 draw=-draw + element_button_animcycle;
398 if (hover)
399 draw+=NUM_OF_CELLS;
400 /* set cell and queue draw if different from previous */
401 /* at first start, previous is null, so always different. */
402 if (g_object_get_data(G_OBJECT(da), GDASH_CELL)!=gd_editor_pixmap(draw)) {
403 g_object_set_data(G_OBJECT(da), GDASH_CELL, gd_editor_pixmap(draw));
404 gtk_widget_queue_draw(da);
407 return TRUE;
411 void
412 gd_element_button_set(GtkWidget *button, const GdElement element)
414 GtkWidget *image, *label;
415 label=GTK_WIDGET(g_object_get_data(G_OBJECT(button), GDASH_BUTTON_LABEL));
416 image=GTK_WIDGET(g_object_get_data(G_OBJECT(button), GDASH_BUTTON_IMAGE));
418 gtk_image_set_from_pixbuf(GTK_IMAGE(image), gd_get_element_pixbuf_with_border(element));
419 gtk_label_set_text(GTK_LABEL(label), _(gd_elements[element].name));
420 g_object_set_data(G_OBJECT(button), GDASH_ELEMENT, GINT_TO_POINTER(element));
423 static void
424 element_button_da_clicked(GtkWidget *da, GdkEventButton *event, gpointer data)
426 GtkDialog *dialog=GTK_DIALOG(data);
427 GtkWidget *button;
428 GdElement element;
430 /* set the corresponding button to the element selected */
431 element=(GdElement)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(da), GDASH_ELEMENT));
432 button=GTK_WIDGET(g_object_get_data(G_OBJECT(da), GDASH_BUTTON));
433 gd_element_button_set(button, element);
435 /* if this is a modal window, then it is a does-not-stay-open element box. */
436 /* so we issue a dialog response. */
437 if (gtk_window_get_modal(GTK_WINDOW(dialog)))
438 gtk_dialog_response(dialog, element);
439 else {
440 /* if not modal, it is a stay-open element box. */
441 /* close if left mouse button; stay open for others. */
442 if (event->button==1)
443 gtk_widget_destroy(GTK_WIDGET(dialog));
447 static void
448 element_button_dialog_destroyed_free_list(GtkWidget *dialog, gpointer data)
450 GList *areas=(GList *) data;
452 g_source_remove_by_user_data(areas);
453 g_list_free(areas);
456 static void
457 element_button_dialog_destroyed_null_pointer(GtkWidget *dialog, gpointer data)
459 g_object_set_data(G_OBJECT(data), GDASH_DIALOG, NULL);
460 g_object_set_data(G_OBJECT(data), GDASH_DIALOG_VBOX, NULL);
463 static void
464 element_button_dialog_close_button_clicked(GtkWidget *button, gpointer data)
466 /* data is the dialog */
467 gtk_widget_destroy(GTK_WIDGET(data));
470 static void
471 element_button_clicked_func(GtkWidget *button, gboolean stay_open)
473 static const GdElement elements[]= {
474 /* normal */
475 O_SPACE, O_DIRT, O_DIAMOND, O_STONE, O_MEGA_STONE, O_FLYING_DIAMOND, O_FLYING_STONE, O_NUT,
476 O_BRICK, O_FALLING_WALL, O_BRICK_EATABLE, O_BRICK_NON_SLOPED, O_SPACE, O_STEEL, O_STEEL_EATABLE, O_STEEL_EXPLODABLE,
478 O_INBOX, O_PRE_OUTBOX, O_PRE_INVIS_OUTBOX, O_PLAYER_GLUED, O_VOODOO, O_SPACE, O_SPACE, O_SKELETON,
479 O_WALLED_KEY_1, O_WALLED_KEY_2, O_WALLED_KEY_3, O_WALLED_DIAMOND, O_STEEL_SLOPED_UP_RIGHT, O_STEEL_SLOPED_UP_LEFT, O_STEEL_SLOPED_DOWN_LEFT, O_STEEL_SLOPED_DOWN_RIGHT,
481 O_AMOEBA, O_AMOEBA_2, O_SLIME, O_ACID, O_MAGIC_WALL, O_WATER, O_LAVA, O_REPLICATOR,
482 O_KEY_1, O_KEY_2, O_KEY_3, O_DIAMOND_KEY, O_BRICK_SLOPED_DOWN_RIGHT, O_BRICK_SLOPED_DOWN_LEFT, O_BRICK_SLOPED_UP_LEFT, O_BRICK_SLOPED_UP_RIGHT,
484 O_BOMB, O_CLOCK, O_POT, O_BOX, O_SWEET, O_PNEUMATIC_HAMMER, O_NITRO_PACK, O_TELEPORTER,
485 O_DOOR_1, O_DOOR_2, O_DOOR_3, O_TRAPPED_DIAMOND, O_DIRT_SLOPED_UP_RIGHT, O_DIRT_SLOPED_UP_LEFT, O_DIRT_SLOPED_DOWN_LEFT, O_DIRT_SLOPED_DOWN_RIGHT,
487 O_GRAVITY_SWITCH, O_CREATURE_SWITCH, O_BITER_SWITCH, O_EXPANDING_WALL_SWITCH, O_REPLICATOR_SWITCH, O_SPACE, O_CONVEYOR_SWITCH, O_CONVEYOR_DIR_SWITCH,
488 O_H_EXPANDING_WALL, O_V_EXPANDING_WALL, O_EXPANDING_WALL, O_SPACE, O_DIRT_BALL, O_DIRT_LOOSE, O_SPACE, O_NONE,
490 O_CONVEYOR_LEFT, O_CONVEYOR_RIGHT, O_SPACE, O_SPACE, O_SPACE, O_DIRT_GLUED, O_DIAMOND_GLUED, O_STONE_GLUED,
491 O_H_EXPANDING_STEEL_WALL, O_V_EXPANDING_STEEL_WALL, O_EXPANDING_STEEL_WALL, O_BLADDER_SPENDER, O_BLADDER, O_GHOST, O_WAITING_STONE, O_CHASING_STONE,
493 O_SPACE, O_FIREFLY_2, O_ALT_FIREFLY_2, O_SPACE, O_SPACE, O_BUTTER_2, O_ALT_BUTTER_2, O_SPACE, O_SPACE, O_STONEFLY_2, O_SPACE, O_COW_2, O_BITER_1, O_SPACE, O_SPACE, O_DRAGONFLY_2,
494 O_FIREFLY_1, O_FIREFLY_3, O_ALT_FIREFLY_1, O_ALT_FIREFLY_3, O_BUTTER_1, O_BUTTER_3, O_ALT_BUTTER_1, O_ALT_BUTTER_3, O_STONEFLY_1, O_STONEFLY_3, O_COW_1, O_COW_3, O_BITER_4, O_BITER_2, O_DRAGONFLY_1, O_DRAGONFLY_3,
495 O_SPACE, O_FIREFLY_4, O_ALT_FIREFLY_4, O_SPACE, O_SPACE, O_BUTTER_4, O_ALT_BUTTER_4, O_SPACE, O_SPACE, O_STONEFLY_4, O_SPACE, O_COW_4, O_BITER_3, O_SPACE, O_SPACE, O_DRAGONFLY_4,
497 /* for effects */
498 O_DIAMOND_F, O_STONE_F, O_MEGA_STONE_F, O_FLYING_DIAMOND_F, O_FLYING_STONE_F, O_FALLING_WALL_F, O_NITRO_PACK_F, O_NUT_F, O_PRE_PL_1, O_PRE_PL_2, O_PRE_PL_3, O_PLAYER, O_PLAYER_BOMB, O_PLAYER_STIRRING, O_OUTBOX, O_INVIS_OUTBOX,
500 O_BLADDER_1, O_BLADDER_2, O_BLADDER_3, O_BLADDER_4, O_BLADDER_5, O_BLADDER_6, O_BLADDER_7, O_BLADDER_8, O_DIRT2,
501 O_COW_ENCLOSED_1, O_COW_ENCLOSED_2, O_COW_ENCLOSED_3, O_COW_ENCLOSED_4, O_COW_ENCLOSED_5, O_COW_ENCLOSED_6, O_COW_ENCLOSED_7,
503 O_WATER_1, O_WATER_2, O_WATER_3, O_WATER_4, O_WATER_5, O_WATER_6, O_WATER_7, O_WATER_8,
504 O_WATER_9, O_WATER_10, O_WATER_11, O_WATER_12, O_WATER_13, O_WATER_14, O_WATER_15, O_WATER_16,
506 O_BOMB_TICK_1, O_BOMB_TICK_2, O_BOMB_TICK_3, O_BOMB_TICK_4, O_BOMB_TICK_5, O_BOMB_TICK_6, O_BOMB_TICK_7,
507 O_BOMB_EXPL_1, O_BOMB_EXPL_2, O_BOMB_EXPL_3, O_BOMB_EXPL_4, O_NUT_EXPL_1, O_NUT_EXPL_2, O_NUT_EXPL_3, O_NUT_EXPL_4, O_UNKNOWN,
509 O_EXPLODE_1, O_EXPLODE_2, O_EXPLODE_3, O_EXPLODE_4, O_EXPLODE_5, O_TIME_PENALTY,
510 O_PRE_DIA_1, O_PRE_DIA_2, O_PRE_DIA_3, O_PRE_DIA_4, O_PRE_DIA_5, O_NITRO_PACK_EXPLODE, O_NITRO_EXPL_1, O_NITRO_EXPL_2, O_NITRO_EXPL_3, O_NITRO_EXPL_4,
511 O_PRE_STONE_1, O_PRE_STONE_2, O_PRE_STONE_3, O_PRE_STONE_4, O_PRE_STEEL_1, O_PRE_STEEL_2, O_PRE_STEEL_3, O_PRE_STEEL_4,
512 O_PRE_CLOCK_1, O_PRE_CLOCK_2, O_PRE_CLOCK_3, O_PRE_CLOCK_4, O_GHOST_EXPL_1, O_GHOST_EXPL_2, O_GHOST_EXPL_3, O_GHOST_EXPL_4,
517 int cols=16;
518 int i;
519 GList *areas=NULL;
520 GtkWidget *dialog, *expander, *table, *table2, *align, *vbox;
521 int into_second=0;
522 GdkColor c;
524 /* if the dialog is already open, only show it. */
525 dialog=(GtkWidget *)(g_object_get_data(G_OBJECT(button), GDASH_DIALOG));
526 if (dialog) {
527 gtk_window_present(GTK_WINDOW(dialog));
529 return;
532 /* elements dialog with no buttons; clicking on an element will do the trick. */
533 dialog=gtk_dialog_new();
534 gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(gtk_widget_get_toplevel(button)));
535 gtk_window_set_title(GTK_WINDOW(dialog), g_object_get_data(G_OBJECT(button), GDASH_WINDOW_TITLE));
536 gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
537 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
538 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
539 /* associate the dialog with the button, so we know that it is open */
540 g_object_set_data(G_OBJECT(button), GDASH_DIALOG, dialog);
542 vbox=gtk_vbox_new(FALSE, 0);
543 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox);
544 g_object_set_data(G_OBJECT(button), GDASH_DIALOG_VBOX, vbox);
546 align=gtk_alignment_new(0, 0.5, 0, 0);
547 gtk_container_add(GTK_CONTAINER(align), gtk_label_new(_("Normal elements")));
548 gtk_box_pack_start_defaults(GTK_BOX(vbox), align);
551 table=gtk_table_new(0, 0, TRUE);
552 gtk_container_set_border_width(GTK_CONTAINER(table), 6);
553 gtk_box_pack_start_defaults(GTK_BOX(vbox), table);
555 expander=gtk_expander_new(_("For effects"));
556 table2=gtk_table_new(0, 0, TRUE);
557 gtk_container_set_border_width(GTK_CONTAINER(table2), 6);
558 gtk_container_add(GTK_CONTAINER(expander), table2);
559 gtk_box_pack_start_defaults(GTK_BOX(vbox), expander);
561 /* color for background around elements. */
562 /* gdkcolors are 16bit, we should *256, but instead *200 so a bit darker. */
563 c.red=gd_color_get_r(gd_current_background_color())*200;
564 c.green=gd_color_get_g(gd_current_background_color())*200;
565 c.blue=gd_color_get_b(gd_current_background_color())*200;
566 /* create drawing areas */
567 for (i=0; i<G_N_ELEMENTS(elements); i++) {
568 GtkWidget *da;
570 da=gtk_drawing_area_new();
571 gtk_widget_modify_bg(da, GTK_STATE_NORMAL, &c);
572 areas=g_list_prepend(areas, da); /* put in list for animation timeout, that one will request redraw on them */
573 gtk_widget_add_events(da, GDK_BUTTON_PRESS_MASK|GDK_LEAVE_NOTIFY_MASK|GDK_ENTER_NOTIFY_MASK);
574 g_object_set_data(G_OBJECT(da), GDASH_ELEMENT, GINT_TO_POINTER(elements[i]));
575 g_object_set_data(G_OBJECT(da), GDASH_BUTTON, button); /* button to update on click */
576 gtk_widget_set_size_request(da, gd_cell_size_editor+4, gd_cell_size_editor+4); /* 2px border around them */
577 gtk_widget_set_tooltip_text(da, _(gd_elements[elements[i]].name));
578 g_signal_connect(G_OBJECT(da), "expose-event", G_CALLBACK(element_button_drawing_area_expose_event), GINT_TO_POINTER(elements[i]));
579 g_signal_connect(G_OBJECT(da), "leave-notify-event", G_CALLBACK(element_button_drawing_area_crossing_event), NULL);
580 g_signal_connect(G_OBJECT(da), "enter-notify-event", G_CALLBACK(element_button_drawing_area_crossing_event), NULL);
581 g_signal_connect(G_OBJECT(da), "button-press-event", G_CALLBACK(element_button_da_clicked), dialog);
582 if (elements[i]==O_DIAMOND_F)
583 /* the dirt2 the first element to be put in the effect list; from that one, always use table2 */
584 into_second=i;
585 if (!into_second)
586 gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(da), i%cols, i%cols+1, i/cols, i/cols+1);
587 else {
588 int j=i-into_second;
590 gtk_table_attach_defaults(GTK_TABLE(table2), GTK_WIDGET(da), j%cols, j%cols+1, j/cols, j/cols+1);
595 /* add a timeout which animates the drawing areas */
596 g_timeout_add(40, redraw_timeout, areas);
597 /* if the dialog is destroyed, we must free the list which contains the drawing areas */
598 g_signal_connect(G_OBJECT(dialog), "destroy", G_CALLBACK(element_button_dialog_destroyed_free_list), areas);
599 /* also remember that the button no longer has its own dialog open */
600 g_signal_connect(G_OBJECT(dialog), "destroy", G_CALLBACK(element_button_dialog_destroyed_null_pointer), button);
603 if (!stay_open) {
604 gtk_widget_show_all(dialog);
605 gtk_dialog_run(GTK_DIALOG(dialog));
606 gtk_widget_destroy(dialog);
607 } else {
608 /* if it is a stay-open element box, add a button which (also) closes it */
609 GtkWidget *close_button;
611 close_button=gtk_button_new_from_stock(GTK_STOCK_CLOSE);
612 gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->action_area), close_button, FALSE, FALSE, 0);
613 g_signal_connect(G_OBJECT(close_button), "clicked", (GCallback) element_button_dialog_close_button_clicked, dialog);
615 gtk_widget_show_all(dialog);
616 gtk_window_present_with_time(GTK_WINDOW(dialog), gtk_get_current_event_time());
620 GdElement
621 gd_element_button_get (GtkWidget *button)
623 gpointer element=g_object_get_data (G_OBJECT(button), GDASH_ELEMENT);
624 return (GdElement)GPOINTER_TO_INT(element);
627 /* the pixbufs might be changed during the lifetime of an element button. *
628 * for example, when colors of a cave are redefined. so this is made
629 * global and can be called. */
630 void
631 gd_element_button_update_pixbuf(GtkWidget *button)
633 /* set the same as it already shows, but pixbuf update will be triggered by this */
634 gd_element_button_set(button, gd_element_button_get(button));
637 static void
638 element_button_clicked_stay_open(GtkWidget *button, gpointer data)
640 element_button_clicked_func(button, TRUE);
643 static void
644 element_button_clicked_modal(GtkWidget *button, gpointer data)
646 element_button_clicked_func(button, FALSE);
649 /* set the title of the window associated with this element button. */
650 /* if the window is already open, also set the title there. */
651 void
652 gd_element_button_set_dialog_title(GtkWidget *button, const char *title)
654 char *old_title;
655 GtkWidget *dialog;
657 /* get original title, and free if needed */
658 old_title=g_object_get_data(G_OBJECT(button), GDASH_WINDOW_TITLE);
659 g_free(old_title);
660 /* remember new title */
661 g_object_set_data(G_OBJECT(button), GDASH_WINDOW_TITLE, title?g_strdup(title):g_strdup(_("Elements")));
663 /* if it has its own window open at the moment, also set it */
664 dialog=g_object_get_data(G_OBJECT(button), GDASH_DIALOG);
665 if (dialog)
666 gtk_window_set_title(GTK_WINDOW(dialog), title);
670 void
671 gd_element_button_set_dialog_sensitive(GtkWidget *button, gboolean sens)
673 GtkWidget *vbox;
675 vbox=g_object_get_data(G_OBJECT(button), GDASH_DIALOG_VBOX);
676 if (vbox)
677 gtk_widget_set_sensitive(vbox, sens);
680 /* frees the special title text, and optionally destroys the dialog, if exists. */
681 static void
682 element_button_destroyed(GtkWidget *button, gpointer data)
684 char *title;
685 GtkWidget *dialog;
687 title=g_object_get_data(G_OBJECT(button), GDASH_WINDOW_TITLE);
688 g_free(title);
689 /* if it has a dialog open for any reason, close that also */
690 dialog=g_object_get_data(G_OBJECT(button), GDASH_DIALOG);
691 if (dialog)
692 gtk_widget_destroy(dialog);
695 /* creates a new element button. optionally the dialog created by
696 * the particular button can stay open, and accept many clicks.
697 * also, it can have some title line, like "draw element" */
698 GtkWidget *
699 gd_element_button_new(GdElement initial_element, gboolean stays_open, const char *special_title)
701 GtkWidget *button, *image, *label, *hbox;
703 button=gtk_button_new();
704 hbox=gtk_hbox_new(FALSE, 3);
705 gtk_container_add(GTK_CONTAINER(button), hbox);
706 label=gtk_label_new(NULL);
707 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); /* left-align label */
708 gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_MIDDLE);
709 image=gtk_image_new();
710 gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
711 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
712 g_object_set_data(G_OBJECT(button), GDASH_BUTTON_IMAGE, image);
713 g_object_set_data(G_OBJECT(button), GDASH_BUTTON_LABEL, label);
715 gtk_widget_set_size_request(button, 96, -1); /* minimum width 96px */
716 g_signal_connect(G_OBJECT(button), "destroy", (GCallback) element_button_destroyed, NULL);
717 if (stays_open)
718 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(element_button_clicked_stay_open), NULL);
719 else
720 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(element_button_clicked_modal), NULL);
722 /* set the associated string which will be the title of the element box window opened */
723 gd_element_button_set_dialog_title(button, special_title);
725 gd_element_button_set(button, initial_element);
726 return button;
729 #undef GDASH_BUTTON_IMAGE
730 #undef GDASH_BUTTON_LABEL
731 #undef GDASH_WINDOW_TITLE
732 #undef GDASH_DIALOG_VBOX
733 #undef GDASH_DIALOG
734 #undef GDASH_CELL
735 #undef GDASH_ELEMENT
736 #undef GDASH_HOVER
737 #undef GDASH_BUTTON
755 enum
757 DIR_PIXBUF_COL,
758 DIR_TEXT_COL,
759 NUM_DIR_COLS
762 /* directions to be shown, and corresponding icons. */
763 static const GdDirection direction_combo_shown_directions[]= { MV_UP, MV_RIGHT, MV_DOWN, MV_LEFT };
764 static const char *direction_combo_shown_icons[]= { GTK_STOCK_GO_UP, GTK_STOCK_GO_FORWARD, GTK_STOCK_GO_DOWN, GTK_STOCK_GO_BACK };
766 GtkWidget *
767 gd_direction_combo_new(const GdDirection initial)
769 GtkWidget *combo;
770 static GtkListStore *store=NULL;
771 GtkCellRenderer *renderer;
772 int i;
774 /* create list, if did not do so far. */
775 if (!store) {
776 GtkTreeIter iter;
777 GtkWidget *cellview;
778 GdkPixbuf *pixbuf;
780 cellview=gtk_cell_view_new();
781 store=gtk_list_store_new (NUM_DIR_COLS, GDK_TYPE_PIXBUF, G_TYPE_STRING);
783 for (i=0; i<G_N_ELEMENTS(direction_combo_shown_directions); i++) {
784 gtk_list_store_append(store, &iter);
785 pixbuf=gtk_widget_render_icon(cellview, direction_combo_shown_icons[i], GTK_ICON_SIZE_MENU, NULL);
786 gtk_list_store_set(store, &iter, DIR_PIXBUF_COL, pixbuf, DIR_TEXT_COL, _(gd_direction_get_visible_name(direction_combo_shown_directions[i])), -1);
789 gtk_widget_destroy(cellview);
792 /* create combo box and renderer for icon and text */
793 combo=gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
794 renderer=gtk_cell_renderer_pixbuf_new ();
795 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combo), renderer, FALSE);
796 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer, "pixbuf", DIR_PIXBUF_COL, NULL);
797 renderer=gtk_cell_renderer_text_new ();
798 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combo), renderer, FALSE);
799 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer, "text", DIR_TEXT_COL, NULL);
801 /* set to initial value */
802 for (i=0; i<G_N_ELEMENTS(direction_combo_shown_directions); i++)
803 if (direction_combo_shown_directions[i]==initial)
804 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), i);
806 return combo;
809 GdDirection
810 gd_direction_combo_get_direction(GtkWidget *combo)
812 return (GdDirection) direction_combo_shown_directions[gtk_combo_box_get_active(GTK_COMBO_BOX(combo))];
816 /*****************************************************/
828 GtkWidget *
829 gd_scheduling_combo_new(const GdScheduling initial)
831 GtkWidget *combo;
832 int i;
834 combo=gtk_combo_box_new_text();
836 for (i=0; i<GD_SCHEDULING_MAX; i++)
837 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _(gd_scheduling_get_visible_name((GdScheduling) i)));
839 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), initial);
841 return combo;
844 GdScheduling
845 gd_scheduling_combo_get_scheduling(GtkWidget *combo)
847 return (GdScheduling)(gtk_combo_box_get_active(GTK_COMBO_BOX(combo)));