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.
17 #include <gdk/gdkkeysyms.h>
19 #include <glib/gi18n.h>
22 #include "caveengine.h"
23 #include "caveobject.h"
24 #include "cavesound.h"
26 #include "c64import.h"
35 #include "editorexport.h"
42 static gboolean paused
=FALSE
;
43 static gboolean fast_forward
=FALSE
;
44 static gboolean fullscreen
=FALSE
;
46 typedef struct _gd_main_window
{
48 GtkActionGroup
*actions_normal
, *actions_title
, *actions_title_replay
, *actions_game
, *actions_snapshot
;
49 GtkWidget
*scroll_window
;
50 GtkWidget
*drawing_area
, *title_image
, *story_label
; /* three things that could be drawn in the main window */
51 GdkPixmap
**title_pixmaps
;
52 GtkWidget
*labels
; /* parts of main window which have to be shown or hidden */
53 GtkWidget
*label_topleft
, *label_topright
;
54 GtkWidget
*label_bottomleft
, *label_bottomright
;
55 GtkWidget
*label_variables
;
56 GtkWidget
*info_bar
, *info_label
;
57 GtkWidget
*menubar
, *toolbar
;
58 GtkWidget
*replay_image_align
;
63 static GdMainWindow main_window
;
65 static gboolean key_fire_1
=FALSE
, key_fire_2
=FALSE
, key_right
=FALSE
, key_up
=FALSE
, key_down
=FALSE
, key_left
=FALSE
, key_suicide
=FALSE
;
66 static gboolean key_alt_status_bar
=FALSE
;
67 static gboolean restart
; /* keys which control the game, but are handled differently than the above */
68 static int mouse_cell_x
=-1, mouse_cell_y
=-1, mouse_cell_click
=0;
71 static GdCave
*snapshot
=NULL
;
77 static void main_stop_game_but_maybe_highscore();
78 static void main_free_game();
83 /************************************************
89 /* closing the window by the window manager.
90 * ask the user if he want to save the cave first, and
91 * only then do quit the main loop. */
93 main_window_delete_event (GtkWidget
* widget
, GdkEvent
* event
, gpointer data
)
95 if (gd_discard_changes(main_window
.window
))
100 /* keypress. key_* can be (event_type==gdk_key_press), as we
101 * connected this function to key press and key release.
104 main_window_keypress_event (GtkWidget
* widget
, GdkEventKey
* event
, gpointer data
)
106 gboolean press
=event
->type
==GDK_KEY_PRESS
; /* true for press, false for release */
108 if (event
->keyval
==gd_gtk_key_left
) {
112 if (event
->keyval
==gd_gtk_key_right
) {
116 if (event
->keyval
==gd_gtk_key_up
) {
120 if (event
->keyval
==gd_gtk_key_down
) {
124 if (event
->keyval
==gd_gtk_key_fire_1
) {
128 if (event
->keyval
==GDK_Shift_L
) {
129 key_alt_status_bar
=press
;
132 if (event
->keyval
==gd_gtk_key_fire_2
) {
136 if (event
->keyval
==gd_gtk_key_suicide
) {
141 return FALSE
; /* if any other key, we did not process it. go on, let gtk handle it. */
144 /* focus leaves game play window. remember that keys are not pressed!
145 as we don't receive key released events along with focus out. */
147 main_window_focus_out_event(GtkWidget
*widget
, GdkEvent
*event
, gpointer data
)
156 key_alt_status_bar
=FALSE
;
161 /* for mouse play. */
162 /* mouse leaves drawing area event */
163 /* if pointer not inside window, no control of player. */
165 drawing_area_leave_event(GtkWidget
* widget
, GdkEventCrossing
* event
, gpointer data
)
173 /* mouse button press event */
175 drawing_area_button_event(GtkWidget
* widget
, GdkEventButton
* event
, gpointer data
)
177 /* see if it is a click or a release. */
178 mouse_cell_click
=event
->type
== GDK_BUTTON_PRESS
;
182 /* mouse motion event */
184 drawing_area_motion_event(GtkWidget
* widget
, GdkEventMotion
* event
, gpointer data
)
187 GdkModifierType state
;
190 gdk_window_get_pointer (event
->window
, &x
, &y
, &state
);
197 mouse_cell_x
=x
/ gd_cell_size_game
;
198 mouse_cell_y
=y
/ gd_cell_size_game
;
205 * draws the cave during game play. also called by the timers,
206 * and the drawing area expose event.
209 drawing_area_draw_cave()
213 if (!main_window
.drawing_area
)
215 if (!main_window
.drawing_area
->window
)
217 if (!main_window
.game
)
219 if (!main_window
.game
->cave
) /* if already no cave, just return */
221 if (!main_window
.game
->gfx_buffer
) /* if already no cave, just return */
224 /* x and y are cave coordinates, which might not start from 0, as the top or the left hand side of the cave might not be visible. */
225 /* on the other hand, xd and yd are screen coordinates (of course, multiply by cell size) */
226 /* inside the loop, calculate cave coordinates, which might not be equal to screen coordinates. */
227 /* top left part of cave:
230 *y=0 wwww|wwwwwwwwwwwwww
231 *yd=0 ----+--------------
232 * w...|o p........ visible part
233 * w...|..d...........
235 /* before drawing, process all pending events for the drawing area, so no confusion occurs. */
236 for (y
=main_window
.game
->cave
->y1
, yd
=0; y
<=main_window
.game
->cave
->y2
; y
++, yd
++) {
237 for (x
=main_window
.game
->cave
->x1
, xd
=0; x
<=main_window
.game
->cave
->x2
; x
++, xd
++) {
238 if (main_window
.game
->gfx_buffer
[y
][x
] & GD_REDRAW
) {
239 main_window
.game
->gfx_buffer
[y
][x
] &= ~GD_REDRAW
;
240 gdk_draw_drawable(main_window
.drawing_area
->window
, main_window
.drawing_area
->style
->black_gc
, gd_game_pixmap(main_window
.game
->gfx_buffer
[y
][x
]),
241 0, 0, xd
*gd_cell_size_game
, yd
*gd_cell_size_game
, gd_cell_size_game
, gd_cell_size_game
);
249 /* CAVE DRAWING is done in an idle func. it requires much time, especially on windows. */
250 static gboolean draw_idle_func_installed
=FALSE
;
252 static gboolean
draw_idle_func(gpointer data
)
254 drawing_area_draw_cave();
256 draw_idle_func_installed
=FALSE
;
260 static void schedule_draw()
262 /* update in an idle func, so we do not slow down the application, when there is no time to draw. */
263 /* the priority must be very low, as gtk also does its things in idle funcs, ie. window resizing and */
264 /* expose events. otherwise we would mess up the scrolling */
265 if (!draw_idle_func_installed
) {
266 draw_idle_func_installed
=TRUE
;
267 g_idle_add_full(G_PRIORITY_LOW
, (GSourceFunc
) draw_idle_func
, NULL
, NULL
);
275 drawing_area_expose_event(GtkWidget
*widget
, GdkEventExpose
*event
, gpointer data
)
277 int x1
, y1
, x2
, y2
, xd
, yd
;
281 if (!main_window
.game
)
283 if (!main_window
.game
->cave
) /* if already no cave, just return */
285 if (!main_window
.game
->gfx_buffer
) /* if already no cave, just return */
288 /* redraw entire area, not specific rectangles.
289 * this function gets called only when the main window gets exposed
290 * by the user removing another window. */
291 /* these are screen coordinates. */
292 x1
=event
->area
.x
/gd_cell_size_game
;
293 y1
=event
->area
.y
/gd_cell_size_game
;
294 x2
=(event
->area
.x
+event
->area
.width
-1)/gd_cell_size_game
;
295 y2
=(event
->area
.y
+event
->area
.height
-1)/gd_cell_size_game
;
296 /* when resizing the cave, we may get events which store the old size, if the drawing area is not yet resized. */
299 if (x2
>=main_window
.game
->cave
->w
) x2
=main_window
.game
->cave
->w
-1;
300 if (y2
>=main_window
.game
->cave
->h
) y2
=main_window
.game
->cave
->h
-1;
302 /* run through screen coordinates to refresh. */
303 /* inside the loop, calculate cave coordinates, which might not be equal to screen coordinates. */
304 /* top left part of cave:
307 *y=0 wwww|wwwwwwwwwwwwww
308 *yd=0 ----+--------------
309 * w...|o p........ visible part
310 * w...|..d...........
312 /* mark all cells to be redrawn with GD_REDRAW, and then call the normal drawcave routine. */
313 for (yd
=y1
; yd
<=y2
; yd
++) {
314 int y
=yd
+main_window
.game
->cave
->y1
;
315 for (xd
=x1
; xd
<=x2
; xd
++) {
316 int x
=xd
+main_window
.game
->cave
->x1
;
317 if (main_window
.game
->gfx_buffer
[y
][x
]!=-1)
318 main_window
.game
->gfx_buffer
[y
][x
] |= GD_REDRAW
;
321 /* schedule drawing as an idle func. */
333 * functions for the main window
341 main_window_set_fullscreen_idle_func(gpointer data
)
343 gtk_window_fullscreen(GTK_WINDOW(data
));
347 /* set or unset fullscreen if necessary */
348 /* hack: gtk-win32 does not correctly handle fullscreen & removing widgets.
349 so we put fullscreening call into a low priority idle function, which will be called
350 after all window resizing & the like did take place. */
352 main_window_set_fullscreen(gboolean ingame
)
354 if (ingame
&& fullscreen
) {
355 gtk_widget_hide(main_window
.menubar
);
356 gtk_widget_hide(main_window
.toolbar
);
357 g_idle_add_full(G_PRIORITY_LOW
, (GSourceFunc
) main_window_set_fullscreen_idle_func
, main_window
.window
, NULL
);
360 g_idle_remove_by_data(main_window
.window
);
361 gtk_window_unfullscreen(GTK_WINDOW(main_window
.window
));
362 gtk_widget_show(main_window
.menubar
);
363 gtk_widget_show(main_window
.toolbar
);
370 * creates title screen or drawing area
374 static gboolean main_window_title_animation_idle_func_installed
=FALSE
;
377 main_window_title_animation_idle_func(gpointer data
)
379 gtk_image_set_from_pixmap(GTK_IMAGE(main_window
.title_image
), (GdkPixmap
*) data
, NULL
);
381 main_window_title_animation_idle_func_installed
=FALSE
;
386 main_window_title_animation_install_idle(GdkPixmap
*pixbuf
)
388 if (!main_window_title_animation_idle_func_installed
) {
389 g_idle_add_full(G_PRIORITY_LOW
, main_window_title_animation_idle_func
, pixbuf
, NULL
);
390 main_window_title_animation_idle_func_installed
=TRUE
;
395 main_window_title_animation_func(gpointer data
)
397 static int animcycle
=0;
400 /* if title image widget does not exist for some reason, we quit now */
401 if (main_window
.title_image
==NULL
)
404 /* count the number of frames. */
406 while (main_window
.title_pixmaps
[count
]!=NULL
)
409 if (gtk_window_has_toplevel_focus(GTK_WINDOW(main_window
.window
))) {
410 animcycle
=(animcycle
+1)%count
;
411 /* do the drawing when we have time. */
412 main_window_title_animation_install_idle(main_window
.title_pixmaps
[animcycle
]);
418 main_window_title_animation_remove()
422 g_source_remove_by_user_data(main_window_title_animation_func
);
424 /* free animation frames, if any */
425 if (main_window
.title_pixmaps
!=NULL
) {
426 while (main_window
.title_pixmaps
[i
]!=NULL
) {
427 g_object_unref(main_window
.title_pixmaps
[i
]);
430 g_free(main_window
.title_pixmaps
);
431 main_window
.title_pixmaps
=NULL
;
436 gd_main_window_set_title_animation()
440 /* if main window does not exist, ignore. */
441 if (main_window
.window
==NULL
)
443 /* if exists but not showing a title image, that is an error. */
444 g_return_if_fail(main_window
.title_image
!=NULL
);
446 main_window_title_animation_remove();
447 main_window
.title_pixmaps
=gd_create_title_animation();
448 /* set first frame immediately */
449 gtk_image_set_from_pixmap(GTK_IMAGE(main_window
.title_image
), main_window
.title_pixmaps
[0], NULL
);
450 /* and if more frames, add animation timeout */
451 if (main_window
.title_pixmaps
[1]!=NULL
)
452 g_timeout_add(40, main_window_title_animation_func
, main_window_title_animation_func
); /* 25fps animation */
454 /* resize the scrolling window so the image fits - a bit larger than the image so scroll bars do not appear*/
455 gdk_drawable_get_size(GDK_DRAWABLE(main_window
.title_pixmaps
[0]), &w
, &h
);
456 /* also some minimum size... 320x200 is some adhoc value only. */
457 gtk_widget_set_size_request(main_window
.scroll_window
, MAX(w
, 320)+24, MAX(h
, 200)+24);
459 /* with the title screen, it is usually smaller than in the game. shrink it. */
460 gtk_window_resize(GTK_WINDOW(main_window
.window
), 1, 1);
464 main_window_init_title()
466 if (!main_window
.title_image
) {
467 /* destroy current widget in main window */
468 if (gtk_bin_get_child(GTK_BIN(main_window
.scroll_window
)))
469 gtk_widget_destroy(gtk_bin_get_child(GTK_BIN(main_window
.scroll_window
)));
472 main_window
.title_image
=gtk_image_new();
473 g_signal_connect (G_OBJECT(main_window
.title_image
), "destroy", G_CALLBACK(gtk_widget_destroyed
), &main_window
.title_image
);
474 g_signal_connect (G_OBJECT(main_window
.title_image
), "destroy", G_CALLBACK(main_window_title_animation_remove
), NULL
);
475 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(main_window
.scroll_window
), main_window
.title_image
);
477 gd_main_window_set_title_animation();
479 /* show newly created widgets */
480 gtk_widget_show_all(main_window
.scroll_window
);
484 gtk_widget_hide(main_window
.labels
);
485 gtk_widget_hide(main_window
.label_variables
);
486 if (gd_has_new_error()) {
487 gtk_label_set(GTK_LABEL(main_window
.info_label
), ((GdErrorMessage
*)(g_list_last(gd_errors
)->data
))->message
);
488 gtk_widget_show(main_window
.info_bar
);
490 gtk_widget_hide(main_window
.info_bar
);
493 /* enable menus and buttons of game */
494 gtk_action_group_set_sensitive(main_window
.actions_title
, !gd_editor_window
);
495 gtk_action_group_set_sensitive(main_window
.actions_title_replay
, !gd_editor_window
&& gd_caveset_has_replays());
496 gtk_action_group_set_sensitive(main_window
.actions_game
, FALSE
);
497 gtk_action_group_set_sensitive(main_window
.actions_snapshot
, snapshot
!=NULL
);
498 /* if editor window exists, no music. */
499 if (gd_editor_window
)
502 gd_music_play_random();
503 if (gd_editor_window
)
504 gtk_widget_set_sensitive(gd_editor_window
, TRUE
);
505 gtk_widget_hide(main_window
.replay_image_align
);
507 /* set or unset fullscreen if necessary */
508 main_window_set_fullscreen(FALSE
);
514 main_window_init_cave(GdCave
*cave
)
518 g_assert(cave
!=NULL
);
521 if (!main_window
.drawing_area
) {
524 /* destroy current widget in main window */
525 if (gtk_bin_get_child(GTK_BIN(main_window
.scroll_window
)))
526 gtk_widget_destroy(gtk_bin_get_child(GTK_BIN(main_window
.scroll_window
)));
528 /* put drawing area in an alignment, so window can be any large w/o problems */
529 align
=gtk_alignment_new(0.5, 0.5, 0, 0);
530 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(main_window
.scroll_window
), align
);
532 main_window
.drawing_area
=gtk_drawing_area_new();
533 gtk_widget_set_events (main_window
.drawing_area
, GDK_EXPOSURE_MASK
| GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
| GDK_POINTER_MOTION_MASK
| GDK_POINTER_MOTION_HINT_MASK
| GDK_LEAVE_NOTIFY_MASK
);
534 g_signal_connect (G_OBJECT(main_window
.drawing_area
), "button_press_event", G_CALLBACK(drawing_area_button_event
), NULL
);
535 g_signal_connect (G_OBJECT(main_window
.drawing_area
), "button_release_event", G_CALLBACK(drawing_area_button_event
), NULL
);
536 g_signal_connect (G_OBJECT(main_window
.drawing_area
), "motion_notify_event", G_CALLBACK(drawing_area_motion_event
), NULL
);
537 g_signal_connect (G_OBJECT(main_window
.drawing_area
), "leave_notify_event", G_CALLBACK(drawing_area_leave_event
), NULL
);
538 g_signal_connect (G_OBJECT(main_window
.drawing_area
), "expose_event", G_CALLBACK(drawing_area_expose_event
), NULL
);
539 g_signal_connect (G_OBJECT(main_window
.drawing_area
), "destroy", G_CALLBACK(gtk_widget_destroyed
), &main_window
.drawing_area
);
540 gtk_container_add (GTK_CONTAINER (align
), main_window
.drawing_area
);
542 gdk_window_set_cursor (main_window
.drawing_area
->window
, gdk_cursor_new(GDK_CROSSHAIR
));
544 /* show newly created widgets */
545 gtk_widget_show_all(main_window
.scroll_window
);
547 /* set the minimum size of the scroll window: 20*12 cells */
548 /* XXX adding some pixels for the scrollbars-here we add 24 */
549 gtk_widget_set_size_request(main_window
.scroll_window
, 20*gd_cell_size_game
+24, 12*gd_cell_size_game
+24);
550 gtk_widget_set_size_request(main_window
.drawing_area
, (cave
->x2
-cave
->x1
+1)*gd_cell_size_game
, (cave
->y2
-cave
->y1
+1)*gd_cell_size_game
);
553 gtk_widget_show(main_window
.labels
);
554 if (main_window
.game
->type
==GD_GAMETYPE_TEST
&& gd_show_test_label
)
555 gtk_widget_show(main_window
.label_variables
);
557 gtk_widget_hide(main_window
.label_variables
);
558 gtk_widget_hide(main_window
.info_bar
);
560 name_escaped
=g_markup_escape_text(cave
->name
, -1);
561 /* TRANSLATORS: cave name, level x */
562 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_topleft
), _("<b>%s</b>, level %d"), name_escaped
, cave
->rendered
);
563 g_free(name_escaped
);
565 /* enable menus and buttons of game */
566 gtk_action_group_set_sensitive(main_window
.actions_title
, FALSE
);
567 gtk_action_group_set_sensitive(main_window
.actions_title_replay
, FALSE
);
568 gtk_action_group_set_sensitive(main_window
.actions_game
, TRUE
);
569 gtk_action_group_set_sensitive(main_window
.actions_snapshot
, snapshot
!=NULL
);
572 if (gd_editor_window
)
573 gtk_widget_set_sensitive(gd_editor_window
, FALSE
);
574 gtk_widget_hide(main_window
.replay_image_align
); /* it will be shown if needed. */
576 /* set or unset fullscreen if necessary */
577 main_window_set_fullscreen(TRUE
);
581 main_window_init_story(GdCave
*cave
)
583 char *escaped_name
, *escaped_story
;
585 if (!main_window
.story_label
) {
586 /* destroy current widget in main window */
587 if (gtk_bin_get_child(GTK_BIN(main_window
.scroll_window
)))
588 gtk_widget_destroy(gtk_bin_get_child(GTK_BIN(main_window
.scroll_window
)));
591 main_window
.story_label
=gtk_label_new(NULL
);
592 gtk_label_set_line_wrap(GTK_LABEL(main_window
.story_label
), TRUE
);
593 g_signal_connect(G_OBJECT(main_window
.story_label
), "destroy", G_CALLBACK(gtk_widget_destroyed
), &main_window
.story_label
);
594 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(main_window
.scroll_window
), main_window
.story_label
);
596 /* show newly created widgets */
597 gtk_widget_show_all(main_window
.scroll_window
);
599 escaped_name
=g_markup_escape_text(cave
->name
, -1);
600 escaped_story
=g_markup_escape_text(cave
->story
->str
, -1);
601 gd_label_set_markup_printf(GTK_LABEL(main_window
.story_label
), _("<big><b>%s</b></big>\n\n%s\n\n<i>Press fire to continue</i>"), escaped_name
, escaped_story
);
602 g_free(escaped_name
);
603 g_free(escaped_story
);
604 /* the width of the label is 320, as the windows is usually not smaller than 320x200 */
605 gtk_widget_set_size_request(main_window
.story_label
, 320, -1);
608 gtk_widget_hide(main_window
.labels
);
609 gtk_widget_hide(main_window
.label_variables
);
610 if (gd_has_new_error()) {
611 gtk_widget_show(main_window
.info_bar
);
612 gtk_label_set(GTK_LABEL(main_window
.info_label
), ((GdErrorMessage
*)(g_list_last(gd_errors
)->data
))->message
);
614 gtk_widget_hide(main_window
.info_bar
);
617 /* enable menus and buttons of game */
618 gtk_action_group_set_sensitive(main_window
.actions_title
, FALSE
);
619 gtk_action_group_set_sensitive(main_window
.actions_title_replay
, FALSE
);
620 gtk_action_group_set_sensitive(main_window
.actions_game
, TRUE
);
621 gtk_action_group_set_sensitive(main_window
.actions_snapshot
, snapshot
!=NULL
);
622 /* if editor window exists, no music. */
624 if (gd_editor_window
)
625 gtk_widget_set_sensitive(gd_editor_window
, FALSE
);
626 gtk_widget_hide(main_window
.replay_image_align
);
628 /* set or unset fullscreen if necessary */
629 main_window_set_fullscreen(TRUE
);
634 /* game over, and highscore is achieved.
635 * ask for name, and if given, record it! */
637 game_over_highscore()
642 text
=g_strdup_printf(_("You have %d points, and achieved a highscore."), main_window
.game
->player_score
);
643 gd_infomessage(_("Game over!"), text
);
646 /* enter to highscore table */
647 rank
=gd_add_highscore(gd_caveset_data
->highscore
, main_window
.game
->player_name
, main_window
.game
->player_score
);
648 gd_show_highscore(main_window
.window
, NULL
, FALSE
, NULL
, rank
);
651 /* game over, but no highscore.
652 * only pops up a simple dialog box. */
654 game_over_without_highscore()
658 text
=g_strdup_printf(_("You have %d points."), main_window
.game
->player_score
);
659 gd_infomessage(_("Game over!"), text
);
665 /* amoeba state to string */
667 amoeba_state_string(GdAmoebaState a
)
670 case GD_AM_SLEEPING
: return _("sleeping"); /* sleeping - not yet let out. */
671 case GD_AM_AWAKE
: return _("awake"); /* living, growing */
672 case GD_AM_TOO_BIG
: return _("too big"); /* grown too big, will convert to stones */
673 case GD_AM_ENCLOSED
: return _("enclosed"); /* enclosed, will convert to diamonds */
678 /* amoeba state to string */
680 magic_wall_state_string(GdMagicWallState m
)
683 case GD_MW_DORMANT
: return _("dormant");
684 case GD_MW_ACTIVE
: return _("active");
685 case GD_MW_EXPIRED
: return _("expired");
693 main_int_set_labels()
695 const GdCave
*cave
=main_window
.game
->cave
;
698 /* lives reamining in game. */
699 /* not trivial, but this sometimes DOES change during the running of a cave. */
700 /* as when playing a replay, it might change from playing replay to continuing replay. */
701 switch (main_window
.game
->type
) {
702 /* for a normal cave, show number of lives or "bonus life" if it is an intermission */
703 case GD_GAMETYPE_NORMAL
:
704 if (!cave
->intermission
)
705 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_topright
), _("Lives: <b>%d</b>"), main_window
.game
->player_lives
);
707 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_topright
), _("<b>Bonus life</b>"));
710 /* for other game types, the number of lives is zero. so show the game type */
711 case GD_GAMETYPE_SNAPSHOT
:
712 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_topright
), _("Continuing from <b>snapshot</b>"));
714 case GD_GAMETYPE_TEST
:
715 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_topright
), _("<b>Testing</b> cave"));
717 case GD_GAMETYPE_REPLAY
:
718 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_topright
), _("Playing <b>replay</b>"));
720 case GD_GAMETYPE_CONTINUE_REPLAY
:
721 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_topright
), _("Continuing <b>replay</b>"));
726 /* SECOND ROW OF STATUS BAR. */
727 /* if the user is not pressing the left shift, show the normal status bar. otherwise, show the alternative. */
728 if (!key_alt_status_bar
) {
729 /* NORMAL STATUS BAR */
731 if (cave
->diamonds_needed
>0)
732 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_bottomleft
), _("Diamonds: <b>%03d</b> Value: <b>%02d</b>"), cave
->diamonds_collected
>=cave
->diamonds_needed
?0:cave
->diamonds_needed
-cave
->diamonds_collected
, cave
->diamond_value
);
734 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_bottomleft
), _("Diamonds: <b>??""?</b> Value: <b>%02d</b>"), cave
->diamond_value
);/* "" to avoid C trigraph ??< */
736 /* show time & score */
737 time
=gd_cave_time_show(cave
, cave
->time
);
739 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_bottomright
), "Time: <b>%02d:%02d</b> Score: <b>%06d</b>", time
/60, time
%60, main_window
.game
->player_score
);
741 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_bottomright
), "Time: <b>%03d</b> Score: <b>%06d</b>", time
, main_window
.game
->player_score
);
743 /* ALTERNATIVE STATUS BAR */
744 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_bottomleft
), _("Keys: <b>%d</b>, <b>%d</b>, <b>%d</b>"), cave
->key1
, cave
->key2
, cave
->key3
);
745 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_bottomright
), _("Skeletons: <b>%d</b> Gravity change: <b>%d</b>"), cave
->skeletons_collected
, gd_cave_time_show(cave
, cave
->gravity_will_change
));
748 if (gd_editor_window
&& gd_show_test_label
) {
749 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_variables
),
750 _("Speed: %dms, Amoeba 1: %ds %s, 2: %ds %s, Magic wall: %ds %s\n"
751 "Expanding wall: %s, Creatures: %ds, %s, Gravity: %s\n"
752 "Kill player: %s, Sweet eaten: %s, Diamond key: %s, Diamonds: %d"),
754 gd_cave_time_show(cave
, cave
->amoeba_time
),
755 amoeba_state_string(cave
->amoeba_state
),
756 gd_cave_time_show(cave
, cave
->amoeba_2_time
),
757 amoeba_state_string(cave
->amoeba_2_state
),
758 gd_cave_time_show(cave
, cave
->magic_wall_time
),
759 magic_wall_state_string(cave
->magic_wall_state
),
760 cave
->expanding_wall_changed
?_("vertical"):_("horizontal"),
761 gd_cave_time_show(cave
, cave
->creatures_direction_will_change
),
762 cave
->creatures_backwards
?_("backwards"):_("forwards"),
763 gettext(gd_direction_get_visible_name(cave
->gravity_disabled
?MV_STILL
:cave
->gravity
)),
764 cave
->kill_player
?_("yes"):_("no"),
765 cave
->sweet_eaten
?_("yes"):_("no"),
766 cave
->diamond_key_collected
?_("yes"):_("no"),
767 cave
->diamonds_collected
773 /* THE MAIN GAME TIMER */
775 /* for the gtk version, it seems nicer if we first draw, then scroll. */
776 /* this is because there is an expose event; scrolling the "old" drawing would draw the old, and then the new. */
777 /* (this is not true for the sdl version) */
779 static GTimer
*timer
=NULL
;
787 * scrolls to the player during game play.
792 static int scroll_desired_x
=0, scroll_desired_y
=0;
793 GtkAdjustment
*adjustment
;
794 int scroll_center_x
, scroll_center_y
;
795 gboolean out_of_window
=FALSE
;
798 int player_x
, player_y
;
799 const GdCave
*cave
=main_window
.game
->cave
;
800 gboolean exact_scroll
=FALSE
; /* to avoid compiler warning */
801 /* hystheresis size is this, multiplied by two.
802 * so player can move half the window without scrolling. */
803 int scroll_start_x
=main_window
.scroll_window
->allocation
.width
/4;
804 int scroll_to_x
=main_window
.scroll_window
->allocation
.width
/8;
805 int scroll_start_y
=main_window
.scroll_window
->allocation
.height
/5; /* window usually smaller vertically, so let the region be also smaller than the horizontal one */
806 int scroll_to_y
=main_window
.scroll_window
->allocation
.height
/10;
809 /* if cave not yet rendered, return. (might be the case for 50hz scrolling */
810 if (main_window
.game
==NULL
|| main_window
.game
->cave
==NULL
)
812 if (paused
&& main_window
.game
->cave
->player_state
!=GD_PL_NOT_YET
)
813 return; /* no scrolling when pause button is pressed, BUT ALLOW SCROLLING when the player is not yet born */
815 /* max scrolling speed depends on the speed of the cave. */
816 /* game moves cell_size_game* 1s/cave time pixels in a second. */
817 /* scrolling moves scroll speed * 1s/scroll_time in a second. */
818 /* these should be almost equal; scrolling speed a little slower. */
819 /* that way, the player might reach the border with a small probability, */
820 /* but the scrolling will not "oscillate", ie. turn on for little intervals as it has */
821 /* caught up with the desired position. smaller is better. */
822 scroll_speed
=gd_cell_size_game
*(gd_fine_scroll
?20:40)/cave
->speed
;
824 /* check player state. */
825 switch (main_window
.game
->cave
->player_state
) {
837 /* do not scroll when the player is dead or cave time is over. */
838 return; /* return from function */
841 player_x
=cave
->player_x
-cave
->x1
;
842 player_y
=cave
->player_y
-cave
->y1
;
844 /* get the size of the window so we know where to place player.
845 * first guess is the middle of the screen.
846 * main_window.drawing_area->parent->parent is the viewport.
847 * +cellsize/2 gets the stomach of player :) so the very center */
848 scroll_center_x
=player_x
*gd_cell_size_game
+ gd_cell_size_game
/2-main_window
.drawing_area
->parent
->parent
->allocation
.width
/2;
849 scroll_center_y
=player_y
*gd_cell_size_game
+ gd_cell_size_game
/2-main_window
.drawing_area
->parent
->parent
->allocation
.height
/2;
852 /* hystheresis function.
853 * when scrolling left, always go a bit less left than player being at the middle.
854 * when scrolling right, always go a bit less to the right. */
855 adjustment
=gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW(main_window
.scroll_window
));
857 scroll_desired_x
=scroll_center_x
;
859 if (adjustment
->value
+scroll_start_x
<scroll_center_x
)
860 scroll_desired_x
=scroll_center_x
-scroll_to_x
;
861 if (adjustment
->value
-scroll_start_x
>scroll_center_x
)
862 scroll_desired_x
=scroll_center_x
+scroll_to_x
;
864 scroll_desired_x
=CLAMP(scroll_desired_x
, 0, adjustment
->upper
-adjustment
->step_increment
-adjustment
->page_increment
);
867 if (adjustment
->value
<scroll_desired_x
) {
868 for (i
=0; i
<scroll_speed
; i
++)
869 if ((int)adjustment
->value
<scroll_desired_x
)
873 if (adjustment
->value
>scroll_desired_x
) {
874 for (i
=0; i
<scroll_speed
; i
++)
875 if ((int)adjustment
->value
>scroll_desired_x
)
880 gtk_adjustment_value_changed (adjustment
);
882 /* check if active player is outside drawing area. if yes, we should wait for scrolling */
883 if ((player_x
*gd_cell_size_game
)<adjustment
->value
|| (player_x
*gd_cell_size_game
+gd_cell_size_game
-1)>adjustment
->value
+main_window
.drawing_area
->parent
->parent
->allocation
.width
)
884 /* but only do the wait, if the player SHOULD BE visible, ie. he is inside the defined visible area of the cave */
885 if (cave
->player_x
>=cave
->x1
&& cave
->player_x
<=cave
->x2
)
892 adjustment
=gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW(main_window
.scroll_window
));
894 scroll_desired_y
=scroll_center_y
;
896 if (adjustment
->value
+scroll_start_y
<scroll_center_y
)
897 scroll_desired_y
=scroll_center_y
-scroll_to_y
;
898 if (adjustment
->value
-scroll_start_y
>scroll_center_y
)
899 scroll_desired_y
=scroll_center_y
+scroll_to_y
;
901 scroll_desired_y
=CLAMP(scroll_desired_y
, 0, adjustment
->upper
-adjustment
->step_increment
-adjustment
->page_increment
);
904 if (adjustment
->value
<scroll_desired_y
) {
905 for (i
=0; i
<scroll_speed
; i
++)
906 if ((int)adjustment
->value
<scroll_desired_y
)
910 if (adjustment
->value
> scroll_desired_y
) {
911 for (i
=0; i
<scroll_speed
; i
++)
912 if ((int)adjustment
->value
>scroll_desired_y
)
917 gtk_adjustment_value_changed(adjustment
);
919 /* check if active player is outside drawing area. if yes, we should wait for scrolling */
920 if ((player_y
*gd_cell_size_game
)<adjustment
->value
|| (player_y
*gd_cell_size_game
+gd_cell_size_game
-1)>adjustment
->value
+main_window
.drawing_area
->parent
->parent
->allocation
.height
)
921 /* but only do the wait, if the player SHOULD BE visible, ie. he is inside the defined visible area of the cave */
922 if (cave
->player_y
>=cave
->y1
&& cave
->player_y
<=cave
->y2
)
925 /* remember if player is visible inside window */
926 main_window
.game
->out_of_window
=out_of_window
;
928 /* if not yet born, we treat as visible. so cave will run. the user is unable to control an unborn player, so this is the right behaviour. */
929 if (main_window
.game
->cave
->player_state
==GD_PL_NOT_YET
)
930 main_window
.game
->out_of_window
=FALSE
;
932 /* XXX gdk_window_process_updates(main_window.drawing_area->window, TRUE); */
935 /* the timing thread runs in a separate thread. this variable is set to true,
936 * then the function exits (and also the thread.) */
937 static gboolean main_int_quit_thread
=FALSE
;
940 main_int(gpointer data
)
942 static gboolean toggle
=FALSE
; /* value irrelevant */
943 int up
, down
, left
, right
;
944 GdDirection player_move
;
951 g_message("%8d. call, avg %gms", called
, g_timer_elapsed(timer
, NULL
)*1000/called
);
954 if (main_window
.game
->type
==GD_GAMETYPE_REPLAY
)
955 gtk_widget_show(main_window
.replay_image_align
);
957 gtk_widget_hide(main_window
.replay_image_align
);
963 fire
=key_fire_1
|| key_fire_2
;
965 /* compare mouse coordinates to player coordinates, and make up movements */
966 if (gd_mouse_play
&& mouse_cell_x
>=0) {
967 down
=down
|| (main_window
.game
->cave
->player_y
<mouse_cell_y
);
968 up
=up
|| (main_window
.game
->cave
->player_y
>mouse_cell_y
);
969 left
=left
|| (main_window
.game
->cave
->player_x
>mouse_cell_x
);
970 right
=right
|| (main_window
.game
->cave
->player_x
<mouse_cell_x
);
971 fire
=fire
|| mouse_cell_click
;
974 /* call the game "interrupt" to do all things. */
975 player_move
=gd_direction_from_keypress(up
, down
, left
, right
);
976 /* tell the interrupt that 20ms has passed. */
977 state
=gd_game_main_int(main_window
.game
, 20, player_move
, fire
, key_suicide
, restart
, !paused
&& !main_window
.game
->out_of_window
, paused
, fast_forward
);
979 /* the game "interrupt" gives signals to us, which we act upon: update status bar, resize the drawing area... */
981 case GD_GAME_INVALID_STATE
:
982 g_assert_not_reached();
985 case GD_GAME_SHOW_STORY
:
986 main_window_init_story(main_window
.game
->cave
);
987 main_int_set_labels();
990 case GD_GAME_CAVE_LOADED
:
991 gd_select_pixbuf_colors(main_window
.game
->cave
->color0
, main_window
.game
->cave
->color1
, main_window
.game
->cave
->color2
, main_window
.game
->cave
->color3
, main_window
.game
->cave
->color4
, main_window
.game
->cave
->color5
);
992 main_window_init_cave(main_window
.game
->cave
);
993 main_int_set_labels();
994 restart
=FALSE
; /* so we do not remember the restart key from a previous cave run */
997 case GD_GAME_NO_MORE_LIVES
: /* <- only used by sdl version */
998 case GD_GAME_SHOW_STORY_WAIT
: /* <- only used by sdl version */
999 case GD_GAME_NOTHING
:
1000 /* normally continue. */
1003 case GD_GAME_LABELS_CHANGED
:
1004 case GD_GAME_TIMEOUT_NOW
: /* <- maybe we should do something else for this */
1005 /* normal, but we are told that the labels (score, ...) might have changed. */
1006 main_int_set_labels();
1010 gd_main_stop_game();
1011 return FALSE
; /* do not call again - it will be created later */
1013 case GD_GAME_GAME_OVER
:
1014 main_stop_game_but_maybe_highscore();
1015 if (gd_is_highscore(gd_caveset_data
->highscore
, main_window
.game
->player_score
))
1016 game_over_highscore(); /* achieved a high score! */
1018 game_over_without_highscore(); /* no high score */
1020 return FALSE
; /* do not call again - it will be created later */
1023 /* if drawing area already exists, draw cave. */
1024 /* remember that the drawings are cached, so if we did no change, this will barely do anything - so will not slow down. */
1025 if (main_window
.drawing_area
)
1027 /* do the scrolling at the given interval. */
1028 /* but only if the drawing area already exists. */
1029 /* if fine scrolling, drawing is called at a 50hz rate. */
1030 /* if not, only at a 25hz rate */
1032 if (main_window
.drawing_area
&& (gd_fine_scroll
|| toggle
))
1035 return TRUE
; /* call again */
1038 /* this is a simple wrapper which makes the main_int to be callable as an idle func. */
1039 /* when used as an idle func by the thread routine, it should run only once for every g_idle_add */
1041 main_int_return_false_wrapper(gpointer data
)
1047 /* this function will run in its own thread, and add the main int as an idle func in every 20ms */
1049 main_int_timer_thread(gpointer data
)
1051 int interval_msec
=20;
1053 /* wait before we first call it */
1054 g_usleep(interval_msec
*1000);
1055 while (!main_int_quit_thread
) {
1056 /* add processing as an idle func */
1057 /* no need to lock the main loop context, as glib does that automatically */
1058 g_idle_add(main_int_return_false_wrapper
, data
);
1060 g_usleep(interval_msec
*1000);
1066 uninstall game timers, if any installed.
1069 main_int_uninstall_timer()
1071 main_int_quit_thread
=TRUE
;
1072 /* remove timeout associated to game play */
1073 while (g_source_remove_by_user_data(main_window
.window
)) {
1080 main_int_install_timer()
1085 /* remove timer, if it is installed for some reason */
1086 main_int_uninstall_timer();
1089 gtk_window_present(GTK_WINDOW(main_window
.window
));
1093 timer
=g_timer_new();
1096 /* this makes the main int load the first cave, and then we do the drawing. */
1097 main_int(main_window
.window
);
1098 gdk_window_process_all_updates(); /* so resizes will be done (?) */
1099 /* after that, install timer. create a thread with higher priority than normal: */
1100 /* so its priority will be higher than the main thread, which does the drawing etc. */
1101 /* if the scheduling thread wants to do something, it gets processed first. this makes */
1102 /* the intervals more even. */
1103 main_int_quit_thread
=FALSE
;
1104 #ifdef G_THREADS_ENABLED
1105 thread
=g_thread_create_full(main_int_timer_thread
, main_window
.window
, 0, FALSE
, FALSE
, G_THREAD_PRIORITY_HIGH
, &error
);
1107 thread
=NULL
; /* if glib without thread support, we will use timeout source. */
1110 /* if unable to create thread */
1112 g_critical("%s", error
->message
);
1113 g_error_free(error
);
1115 /* use the main int as a timeout routine. */
1116 g_timeout_add(20, main_int
, main_window
.window
);
1120 g_timer_start(timer
);
1126 main_stop_game_but_maybe_highscore()
1128 main_int_uninstall_timer();
1129 gd_sound_off(); /* hack for game over dialog */
1131 main_window_init_title();
1132 /* if editor is active, go back to its window. */
1133 if (gd_editor_window
)
1134 gtk_window_present(GTK_WINDOW(gd_editor_window
));
1140 if (main_window
.game
) {
1141 gd_game_free(main_window
.game
);
1142 main_window
.game
=NULL
;
1149 main_stop_game_but_maybe_highscore();
1155 /* this starts a new game */
1157 main_new_game(const char *player_name
, const int cave
, const int level
)
1159 if (main_window
.game
)
1160 gd_main_stop_game();
1162 main_window
.game
=gd_game_new(player_name
, cave
, level
);
1163 main_int_install_timer();
1168 main_new_game_snapshot(GdCave
*snapshot
)
1170 if (main_window
.game
)
1171 gd_main_stop_game();
1173 main_window
.game
=gd_game_new_snapshot(snapshot
);
1174 main_int_install_timer();
1177 /* the new game for testing a level is global, not static.
1178 * it is used by the editor. */
1180 gd_main_new_game_test(GdCave
*test
, int level
)
1182 if (main_window
.game
)
1183 gd_main_stop_game();
1185 main_window
.game
=gd_game_new_test(test
, level
);
1186 main_int_install_timer();
1191 main_new_game_replay(GdCave
*cave
, GdReplay
*replay
)
1193 if (main_window
.game
)
1194 gd_main_stop_game();
1196 main_window
.game
=gd_game_new_replay(cave
, replay
);
1197 main_int_install_timer();
1208 * set the main window title from the caveset name.
1209 * made global, as the editor might ocassionally call it.
1213 gd_main_window_set_title()
1215 if (!g_str_equal(gd_caveset_data
->name
, "")) {
1218 text
=g_strdup_printf("GDash - %s", gd_caveset_data
->name
);
1219 gtk_window_set_title (GTK_WINDOW(main_window
.window
), text
);
1223 gtk_window_set_title (GTK_WINDOW(main_window
.window
), "GDash");
1230 /**********************************-
1237 help_cb(GtkWidget
* widget
, gpointer data
)
1239 gd_show_game_help (((GdMainWindow
*)data
)->window
);
1243 preferences_cb(GtkWidget
*widget
, gpointer data
)
1245 gd_preferences(((GdMainWindow
*)data
)->window
);
1249 control_settings_cb(GtkWidget
*widget
, gpointer data
)
1251 gd_control_settings(((GdMainWindow
*)data
)->window
);
1255 quit_cb(GtkWidget
* widget
, const gpointer data
)
1257 if (gd_discard_changes(main_window
.window
))
1262 stop_game_cb(GtkWidget
*widget
, gpointer data
)
1264 gd_main_stop_game();
1268 save_snapshot_cb(GtkWidget
* widget
, gpointer data
)
1271 gd_cave_free(snapshot
);
1273 snapshot
=gd_create_snapshot(main_window
.game
);
1274 gtk_action_group_set_sensitive (main_window
.actions_snapshot
, snapshot
!=NULL
);
1278 load_snapshot_cb(GtkWidget
* widget
, gpointer data
)
1280 g_return_if_fail(snapshot
!=NULL
);
1281 main_new_game_snapshot(snapshot
);
1284 /* restart level button clicked */
1286 restart_level_cb(GtkWidget
* widget
, gpointer data
)
1288 g_return_if_fail(main_window
.game
!=NULL
);
1289 g_return_if_fail(main_window
.game
->cave
!=NULL
);
1290 /* sets the restart variable, which will be interpreted by the iterate routine */
1295 about_cb(GtkWidget
*widget
, gpointer data
)
1297 gtk_show_about_dialog(GTK_WINDOW(main_window
.window
), "program-name", "GDash", "license", gd_about_license
, "wrap-license", TRUE
, "authors", gd_about_authors
, "version", PACKAGE_VERSION
, "comments", _(gd_about_comments
), "translator-credits", _(gd_about_translator_credits
), "website", gd_about_website
, "artists", gd_about_artists
, "documenters", gd_about_documenters
, NULL
);
1301 open_caveset_cb(GtkWidget
* widget
, gpointer data
)
1303 gd_open_caveset(main_window
.window
, NULL
);
1304 main_window_init_title();
1308 open_caveset_dir_cb(GtkWidget
* widget
, gpointer data
)
1310 gd_open_caveset(main_window
.window
, gd_system_caves_dir
);
1311 main_window_init_title();
1315 save_caveset_as_cb(GtkWidget
* widget
, gpointer data
)
1317 gd_save_caveset_as(main_window
.window
);
1321 save_caveset_cb(GtkWidget
* widget
, gpointer data
)
1323 gd_save_caveset(main_window
.window
);
1326 /* load internal game from the executable. those are inlined in caveset.c. */
1328 load_internal_cb(GtkWidget
* widget
, gpointer data
)
1330 gd_load_internal(main_window
.window
, GPOINTER_TO_INT(data
));
1331 main_window_init_title();
1335 highscore_cb(GtkWidget
*widget
, gpointer data
)
1337 gd_show_highscore(main_window
.window
, NULL
, FALSE
, NULL
, -1);
1343 show_errors_cb(GtkWidget
*widget
, gpointer data
)
1345 gtk_widget_hide(main_window
.info_bar
); /* if the user is presented the error list, the label is to be hidden */
1346 gd_show_errors(main_window
.window
);
1352 gd_open_cave_editor();
1353 /* to be sure no cave is playing. */
1354 /* this will also stop music. to be called after opening editor window, so the music stops. */
1355 main_window_init_title();
1358 /* called from the menu when a recent file is activated. */
1360 recent_chooser_activated_cb(GtkRecentChooser
*chooser
, gpointer data
)
1362 GtkRecentInfo
*current
;
1363 char *filename_utf8
, *filename
;
1365 current
=gtk_recent_chooser_get_current_item(chooser
);
1366 /* we do not support non-local files */
1367 if (!gtk_recent_info_is_local(current
)) {
1370 display_name
=gtk_recent_info_get_uri_display(current
);
1371 gd_errormessage(_("GDash cannot load file from a network link."), display_name
);
1372 g_free(display_name
);
1376 /* if the edited caveset is to be saved, but user cancels */
1377 if (!gd_discard_changes(main_window
.window
))
1380 filename_utf8
=gtk_recent_info_get_uri_display(current
);
1381 filename
=g_filename_from_utf8(filename_utf8
, -1, NULL
, NULL
, NULL
);
1382 /* ask for save first? */
1383 gd_open_caveset_in_ui(filename
, gd_use_bdcff_highscore
);
1385 /* things to do after loading. */
1386 main_window_init_title();
1387 gd_infomessage(_("Loaded caveset from file:"), filename_utf8
);
1390 g_free(filename_utf8
);
1396 toggle_pause_cb(GtkWidget
* widget
, gpointer data
)
1398 paused
=gtk_toggle_action_get_active(GTK_TOGGLE_ACTION (widget
));
1405 toggle_fullscreen_cb (GtkWidget
* widget
, gpointer data
)
1407 fullscreen
=gtk_toggle_action_get_active(GTK_TOGGLE_ACTION (widget
));
1408 main_window_set_fullscreen(main_window
.game
!=NULL
); /* we do not exactly know if in game, but try to guess */
1412 toggle_fast_cb (GtkWidget
* widget
, gpointer data
)
1414 fast_forward
=gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (widget
));
1426 * START NEW GAME DIALOG
1428 * show a dialog to the user so he can select the cave to start game at.
1432 typedef struct _new_game_dialog
{
1434 GtkWidget
*combo_cave
;
1435 GtkWidget
*spin_level
;
1436 GtkWidget
*entry_name
;
1441 /* keypress. key_* can be event_type==gdk_key_press, as we
1442 connected this function to key press and key release.
1445 new_game_keypress_event(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
1447 NewGameDialog
*jump_dialog
=(NewGameDialog
*)data
;
1448 int level
=gtk_range_get_value (GTK_RANGE(jump_dialog
->spin_level
));
1450 switch (event
->keyval
) {
1455 gtk_range_set_value (GTK_RANGE(jump_dialog
->spin_level
), level
);
1461 gtk_range_set_value (GTK_RANGE(jump_dialog
->spin_level
), level
);
1464 gtk_dialog_response(GTK_DIALOG(jump_dialog
->dialog
), GTK_RESPONSE_ACCEPT
);
1467 return FALSE
; /* if any other key, we did not process it. go on, let gtk handle it. */
1473 new_game_update_preview(GtkWidget
*widget
, gpointer data
)
1475 NewGameDialog
*jump_dialog
=(NewGameDialog
*)data
;
1476 GdkPixbuf
*cave_image
;
1479 /* loading cave, draw cave and scale to specified size. seed=0 */
1480 cave
=gd_cave_new_from_caveset(gtk_combo_box_get_active(GTK_COMBO_BOX (jump_dialog
->combo_cave
)), gtk_range_get_value (GTK_RANGE(jump_dialog
->spin_level
))-1, 0);
1481 cave_image
=gd_drawcave_to_pixbuf(cave
, 320, 240, TRUE
, TRUE
);
1482 gtk_image_set_from_pixbuf(GTK_IMAGE (jump_dialog
->image
), cave_image
);
1483 g_object_unref(cave_image
);
1485 /* freeing temporary cave data */
1490 new_game_cb (const GtkWidget
* widget
, const gpointer data
)
1492 static GdString player_name
="";
1493 GtkWidget
*table
, *expander
, *eventbox
;
1494 NewGameDialog jump_dialog
;
1495 GtkCellRenderer
*renderer
;
1496 GtkListStore
*store
;
1499 /* check if caveset is empty! */
1500 if (gd_caveset_count()==0) {
1501 gd_warningmessage(_("There are no caves in this cave set!"), NULL
);
1505 jump_dialog
.dialog
=gtk_dialog_new_with_buttons(_("Select cave to play"), GTK_WINDOW(main_window
.window
), GTK_DIALOG_NO_SEPARATOR
| GTK_DIALOG_DESTROY_WITH_PARENT
, GTK_STOCK_CANCEL
, GTK_RESPONSE_REJECT
, GTK_STOCK_JUMP_TO
, GTK_RESPONSE_ACCEPT
, NULL
);
1506 gtk_dialog_set_default_response (GTK_DIALOG(jump_dialog
.dialog
), GTK_RESPONSE_ACCEPT
);
1507 gtk_window_set_resizable (GTK_WINDOW(jump_dialog
.dialog
), FALSE
);
1509 table
=gtk_table_new(0, 0, FALSE
);
1510 gtk_box_pack_start(GTK_BOX (GTK_DIALOG(jump_dialog
.dialog
)->vbox
), table
, FALSE
, FALSE
, 0);
1511 gtk_container_set_border_width(GTK_CONTAINER (table
), 6);
1512 gtk_table_set_row_spacings(GTK_TABLE(table
), 6);
1513 gtk_table_set_col_spacings(GTK_TABLE(table
), 6);
1515 /* name, which will be used for highscore & the like */
1516 gtk_table_attach_defaults(GTK_TABLE(table
), gd_label_new_printf(_("Name:")), 0, 1, 0, 1);
1517 if (g_str_equal(player_name
, ""))
1518 gd_strcpy(player_name
, g_get_real_name());
1519 jump_dialog
.entry_name
=gtk_entry_new();
1520 /* little inconsistency below: max length has unicode characters, while gdstring will have utf-8.
1521 however this does not make too much difference */
1522 gtk_entry_set_max_length(GTK_ENTRY(jump_dialog
.entry_name
), sizeof(GdString
));
1523 gtk_entry_set_activates_default(GTK_ENTRY(jump_dialog
.entry_name
), TRUE
);
1524 gtk_entry_set_text(GTK_ENTRY(jump_dialog
.entry_name
), player_name
);
1525 gtk_table_attach_defaults(GTK_TABLE(table
), jump_dialog
.entry_name
, 1, 2, 0, 1);
1527 gtk_table_attach_defaults(GTK_TABLE(table
), gd_label_new_printf(_("Cave:")), 0, 1, 1, 2);
1529 /* store of caves: cave pointer, cave name, selectable */
1530 store
=gtk_list_store_new(3, G_TYPE_POINTER
, G_TYPE_STRING
, G_TYPE_BOOLEAN
);
1531 for (iter
=gd_caveset
; iter
; iter
=g_list_next (iter
)) {
1532 GdCave
*cave
=iter
->data
;
1533 GtkTreeIter treeiter
;
1535 gtk_list_store_insert_with_values (store
, &treeiter
, -1, 0, iter
->data
, 1, cave
->name
, 2, cave
->selectable
|| gd_all_caves_selectable
, -1);
1537 jump_dialog
.combo_cave
=gtk_combo_box_new_with_model (GTK_TREE_MODEL (store
));
1538 g_object_unref(store
);
1540 renderer
=gtk_cell_renderer_text_new();
1541 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(jump_dialog
.combo_cave
), renderer
, TRUE
);
1542 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT(jump_dialog
.combo_cave
), renderer
, "text", 1, "sensitive", 2, NULL
);
1543 /* we put the combo in an event box, so we can receive keypresses on our own */
1544 eventbox
=gtk_event_box_new();
1545 gtk_container_add(GTK_CONTAINER(eventbox
), jump_dialog
.combo_cave
);
1546 gtk_table_attach_defaults(GTK_TABLE(table
), eventbox
, 1, 2, 1, 2);
1548 gtk_table_attach_defaults(GTK_TABLE(table
), gd_label_new_printf(_("Level:")), 0, 1, 2, 3);
1549 jump_dialog
.spin_level
=gtk_hscale_new_with_range(1.0, 5.0, 1.0);
1550 gtk_range_set_increments(GTK_RANGE(jump_dialog
.spin_level
), 1.0, 1.0);
1551 gtk_scale_set_value_pos(GTK_SCALE(jump_dialog
.spin_level
), GTK_POS_LEFT
);
1552 gtk_table_attach_defaults(GTK_TABLE(table
), jump_dialog
.spin_level
, 1, 2, 2, 3);
1554 g_signal_connect(G_OBJECT(jump_dialog
.combo_cave
), "changed", G_CALLBACK(new_game_update_preview
), &jump_dialog
);
1555 gtk_widget_add_events(eventbox
, GDK_KEY_PRESS_MASK
);
1556 g_signal_connect(G_OBJECT(eventbox
), "key_press_event", G_CALLBACK(new_game_keypress_event
), &jump_dialog
);
1557 g_signal_connect(G_OBJECT(jump_dialog
.spin_level
), "value-changed", G_CALLBACK(new_game_update_preview
), &jump_dialog
);
1559 /* this allows the user to select if he wants to see a preview of the cave */
1560 expander
=gtk_expander_new(_("Preview"));
1561 gtk_expander_set_expanded(GTK_EXPANDER (expander
), gd_show_preview
);
1562 gtk_table_attach_defaults(GTK_TABLE(table
), expander
, 0, 2, 3, 4);
1563 jump_dialog
.image
=gtk_image_new();
1564 gtk_container_add(GTK_CONTAINER (expander
), jump_dialog
.image
);
1566 gtk_widget_show_all(jump_dialog
.dialog
);
1567 gtk_widget_grab_focus(jump_dialog
.combo_cave
);
1568 gtk_editable_select_region(GTK_EDITABLE(jump_dialog
.entry_name
), 0, 0);
1569 /* set default and also trigger redrawing */
1570 gtk_combo_box_set_active(GTK_COMBO_BOX(jump_dialog
.combo_cave
), gd_caveset_last_selected
);
1571 gtk_range_set_value(GTK_RANGE(jump_dialog
.spin_level
), 1);
1573 if (gtk_dialog_run (GTK_DIALOG(jump_dialog
.dialog
))==GTK_RESPONSE_ACCEPT
) {
1574 gd_strcpy(player_name
, gtk_entry_get_text(GTK_ENTRY(jump_dialog
.entry_name
)));
1575 gd_caveset_last_selected
=gtk_combo_box_get_active(GTK_COMBO_BOX(jump_dialog
.combo_cave
));
1576 gd_caveset_last_selected_level
=gtk_range_get_value(GTK_RANGE(jump_dialog
.spin_level
))-1;
1577 main_new_game (player_name
, gd_caveset_last_selected
, gd_caveset_last_selected_level
);
1579 gd_show_preview
=gtk_expander_get_expanded(GTK_EXPANDER(expander
)); /* remember expander state-even if cancel pressed */
1580 gtk_widget_destroy(jump_dialog
.dialog
);
1594 enum _replay_fields
{
1595 COL_REPLAY_CAVE_POINTER
,
1596 COL_REPLAY_REPLAY_POINTER
,
1597 COL_REPLAY_NAME
, /* cave or player name */
1598 COL_REPLAY_LEVEL
, /* level the replay is played on */
1603 COL_REPLAY_COMMENT
, /* set to a gtk stock icon if it has a comment */
1604 COL_REPLAY_VISIBLE
, /* set to true for replay lines, false for cave lines. so "saved" toggle and comment are not visible. */
1609 show_replays_tree_view_row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
, GtkTreeViewColumn
*column
, gpointer user_data
)
1611 GtkTreeModel
*model
=gtk_tree_view_get_model(view
);
1616 gtk_tree_model_get_iter(model
, &iter
, path
);
1617 gtk_tree_model_get(model
, &iter
, COL_REPLAY_CAVE_POINTER
, &cave
, COL_REPLAY_REPLAY_POINTER
, &replay
, -1);
1618 if (cave
!=NULL
&& replay
!=NULL
)
1619 main_new_game_replay(cave
, replay
);
1623 show_replays_dialog_response(GtkDialog
*dialog
, int response_id
, gpointer data
)
1625 gtk_widget_destroy(GTK_WIDGET(dialog
));
1629 show_replays_saved_toggled(GtkCellRendererText
*cell
, const gchar
*path_string
, gpointer data
)
1631 GtkTreeModel
*model
=(GtkTreeModel
*)data
;
1632 GtkTreePath
*path
=gtk_tree_path_new_from_string(path_string
);
1637 gtk_tree_model_get_iter (model
, &iter
, path
);
1638 gtk_tree_model_get(model
, &iter
, COL_REPLAY_REPLAY_POINTER
, &replay
, -1);
1639 /* if not available, maybe the user edited a cave line? do nothing. */
1643 replay
->saved
=!replay
->saved
;
1644 gtk_tree_store_set(GTK_TREE_STORE(model
), &iter
, COL_REPLAY_SAVED
, replay
->saved
, -1);
1645 /* we selected or unselected a replay for saving - the caveset is now edited. */
1646 gd_caveset_edited
=TRUE
;
1650 show_replays_play_button_clicked(GtkWidget
*widget
, gpointer data
)
1652 GtkTreeView
*view
=GTK_TREE_VIEW(data
);
1653 GtkTreeSelection
*selection
=gtk_tree_view_get_selection(view
);
1654 GtkTreeModel
*model
;
1655 gboolean got_selected
;
1660 got_selected
=gtk_tree_selection_get_selected(selection
, &model
, &iter
);
1661 if (!got_selected
) /* if nothing selected, return */
1664 gtk_tree_model_get(model
, &iter
, COL_REPLAY_CAVE_POINTER
, &cave
, COL_REPLAY_REPLAY_POINTER
, &replay
, -1);
1665 if (cave
!=NULL
&& replay
!=NULL
)
1666 main_new_game_replay(cave
, replay
);
1669 /* edit a replay comment for a given cave and a given replay.
1670 return true if comment is non empty.
1673 show_replays_edit_comment(GtkWindow
*parent
, GdCave
*cave
, GdReplay
*replay
)
1676 GtkTextBuffer
*buffer
;
1677 GtkWidget
*text
, *sw
;
1680 dialog
=gtk_dialog_new_with_buttons(_("Edit Replay Comment"), parent
,
1681 GTK_DIALOG_DESTROY_WITH_PARENT
|GTK_DIALOG_NO_SEPARATOR
,
1682 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
1683 GTK_STOCK_OK
, GTK_RESPONSE_OK
,
1685 gtk_window_set_default_size(GTK_WINDOW(dialog
), 480, 360);
1686 sw
=gtk_scrolled_window_new(NULL
, NULL
);
1687 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw
), GTK_SHADOW_ETCHED_IN
);
1688 buffer
=gtk_text_buffer_new(NULL
);
1689 text
=gtk_text_view_new_with_buffer(buffer
);
1690 gtk_text_buffer_set_text(buffer
, replay
->comment
->str
, -1);
1691 gtk_container_add(GTK_CONTAINER(sw
), text
);
1692 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog
)->vbox
), sw
);
1694 gtk_widget_show_all(dialog
);
1695 response
=gtk_dialog_run(GTK_DIALOG(dialog
));
1696 if (response
==GTK_RESPONSE_OK
) {
1697 GtkTextIter iter_start
, iter_end
;
1700 gtk_text_buffer_get_iter_at_offset(buffer
, &iter_start
, 0);
1701 gtk_text_buffer_get_iter_at_offset(buffer
, &iter_end
, -1);
1702 text
=gtk_text_buffer_get_text(buffer
, &iter_start
, &iter_end
, TRUE
);
1703 g_string_assign(replay
->comment
, text
);
1704 gd_caveset_edited
=TRUE
;
1707 gtk_widget_destroy(dialog
);
1708 return replay
->comment
->len
!=0;
1712 show_replays_edit_button_clicked(GtkWidget
*widget
, gpointer data
)
1714 GtkTreeView
*view
=GTK_TREE_VIEW(data
);
1715 GtkTreeSelection
*selection
=gtk_tree_view_get_selection(view
);
1716 GtkTreeModel
*model
;
1717 gboolean got_selected
;
1722 got_selected
=gtk_tree_selection_get_selected(selection
, &model
, &iter
);
1723 if (!got_selected
) /* if nothing selected, return */
1726 gtk_tree_model_get(model
, &iter
, COL_REPLAY_CAVE_POINTER
, &cave
, COL_REPLAY_REPLAY_POINTER
, &replay
, -1);
1727 if (cave
!=NULL
&& replay
!=NULL
) {
1728 gboolean has_comment
;
1730 has_comment
=show_replays_edit_comment(GTK_WINDOW(gtk_widget_get_toplevel(widget
)), cave
, replay
);
1731 gtk_tree_store_set(GTK_TREE_STORE(model
), &iter
, COL_REPLAY_COMMENT
, has_comment
?GTK_STOCK_EDIT
:"", -1);
1737 /* enables or disables play button on selection change */
1739 show_replays_tree_view_selection_changed(GtkTreeSelection
*selection
, gpointer data
)
1741 GtkTreeModel
*model
;
1745 if (gtk_tree_selection_get_selected(selection
, &model
, &iter
)) {
1748 gtk_tree_model_get(model
, &iter
, COL_REPLAY_REPLAY_POINTER
, &replay
, -1);
1749 enable
=replay
!=NULL
;
1753 gtk_widget_set_sensitive(GTK_WIDGET(data
), enable
);
1757 show_replays_cb(GtkWidget
*widget
, gpointer data
)
1759 static GtkWidget
*dialog
=NULL
;
1760 GtkWidget
*scroll
, *view
, *button
;
1762 GtkTreeStore
*model
;
1763 GtkCellRenderer
*renderer
;
1765 /* if window already open, just show it and return */
1767 gtk_window_present(GTK_WINDOW(dialog
));
1771 dialog
=gtk_dialog_new_with_buttons(_("Replays"), GTK_WINDOW(main_window
.window
), 0, NULL
);
1772 g_signal_connect(G_OBJECT(dialog
), "destroy", G_CALLBACK(gtk_widget_destroyed
), &dialog
);
1773 g_signal_connect(G_OBJECT(dialog
), "response", G_CALLBACK(show_replays_dialog_response
), NULL
);
1774 gtk_window_set_default_size(GTK_WINDOW(dialog
), 480, 360);
1776 gd_dialog_add_hint(GTK_DIALOG(dialog
), _("Hint: When watching a replay, you can use the usual movement keys (left, right...) to "
1777 "stop the replay and immediately continue the playing of the cave yourself."));
1779 /* scrolled window to show replays tree view */
1780 scroll
=gtk_scrolled_window_new(NULL
, NULL
);
1781 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(scroll
), GTK_SHADOW_ETCHED_IN
);
1782 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog
)->vbox
), scroll
, TRUE
, TRUE
, 6);
1784 /* create store containing replays */
1785 model
=gtk_tree_store_new(COL_REPLAY_MAX
, G_TYPE_POINTER
, G_TYPE_POINTER
, G_TYPE_STRING
, G_TYPE_INT
, G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_BOOLEAN
, G_TYPE_STRING
, G_TYPE_BOOLEAN
);
1786 for (iter
=gd_caveset
; iter
!=NULL
; iter
=iter
->next
) {
1788 GdCave
*cave
=(GdCave
*)iter
->data
;
1790 /* if the cave has replays */
1791 if (cave
->replays
) {
1792 GtkTreeIter caveiter
;
1794 gtk_tree_store_append(model
, &caveiter
, NULL
);
1795 gtk_tree_store_set(model
, &caveiter
, COL_REPLAY_NAME
, cave
->name
, -1);
1797 /* add each replay */
1798 for (replayiter
=cave
->replays
; replayiter
!=NULL
; replayiter
=replayiter
->next
) {
1800 GdReplay
*replay
=(GdReplay
*)replayiter
->data
;
1802 const char *imagestock
;
1804 /* we have to store the score as string, as for the cave lines the unset score field would also show zero */
1805 g_snprintf(score
, sizeof(score
), "%d", replay
->score
);
1806 gtk_tree_store_append(model
, &riter
, &caveiter
);
1807 if (replay
->wrong_checksum
)
1808 imagestock
=GTK_STOCK_DIALOG_ERROR
;
1810 imagestock
=replay
->success
?GTK_STOCK_YES
:GTK_STOCK_NO
;
1811 gtk_tree_store_set(model
, &riter
, COL_REPLAY_CAVE_POINTER
, cave
, COL_REPLAY_REPLAY_POINTER
, replay
, COL_REPLAY_NAME
, replay
->player_name
,
1812 COL_REPLAY_LEVEL
, replay
->level
+1,
1813 COL_REPLAY_DATE
, replay
->date
, COL_REPLAY_SCORE
, score
, COL_REPLAY_SUCCESS
, imagestock
,
1814 COL_REPLAY_COMMENT
, replay
->comment
->len
!=0?GTK_STOCK_EDIT
:"", COL_REPLAY_SAVED
, replay
->saved
, COL_REPLAY_VISIBLE
, TRUE
, -1);
1819 view
=gtk_tree_view_new_with_model(GTK_TREE_MODEL(model
)); /* create tree view which will show data */
1820 gtk_tree_view_expand_all(GTK_TREE_VIEW(view
));
1821 gtk_container_add(GTK_CONTAINER(scroll
), view
);
1823 renderer
=gtk_cell_renderer_text_new();
1824 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
), 0, _("Name"), renderer
, "text", COL_REPLAY_NAME
, NULL
); /* 0 = column number */
1825 gtk_tree_view_column_set_expand(gtk_tree_view_get_column(GTK_TREE_VIEW(view
), 0), TRUE
); /* name column expands */
1827 renderer
=gtk_cell_renderer_text_new();
1828 /* TRANSLATORS: "Lvl" here stands for Level. Some shorthand should be used.*/
1829 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
), 1, _("Lvl"), renderer
, "text", COL_REPLAY_LEVEL
, "visible", COL_REPLAY_VISIBLE
, NULL
); /* 0 = column number */
1830 gtk_tree_view_column_set_expand(gtk_tree_view_get_column(GTK_TREE_VIEW(view
), 0), TRUE
); /* name column expands */
1832 renderer
=gtk_cell_renderer_text_new();
1833 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
), 2, _("Date"), renderer
, "text", COL_REPLAY_DATE
, NULL
); /* 1 = column number */
1835 renderer
=gtk_cell_renderer_pixbuf_new();
1836 g_object_set(G_OBJECT(renderer
), "stock-size", GTK_ICON_SIZE_MENU
, NULL
);
1837 /* TRANSLATORS: "S." here stands for Successful. A one-letter shorthand should be used.*/
1838 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
), 3, _("S."), renderer
, "stock-id", COL_REPLAY_SUCCESS
, NULL
);
1840 renderer
=gtk_cell_renderer_text_new();
1841 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
), 4, _("Score"), renderer
, "text", COL_REPLAY_SCORE
, NULL
);
1843 renderer
=gtk_cell_renderer_pixbuf_new();
1844 g_object_set(G_OBJECT(renderer
), "stock-size", GTK_ICON_SIZE_MENU
, NULL
);
1845 /* TRANSLATORS: "C." here stands for Comment. A one-letter shorthand should be used.*/
1846 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
), 5, _("C."), renderer
, "stock-id", COL_REPLAY_COMMENT
, NULL
);
1848 renderer
=gtk_cell_renderer_toggle_new();
1849 g_signal_connect(renderer
, "toggled", G_CALLBACK(show_replays_saved_toggled
), model
);
1850 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
), 6, _("Saved"), renderer
, "active", COL_REPLAY_SAVED
, "visible", COL_REPLAY_VISIBLE
, NULL
);
1852 /* doubleclick will play the replay */
1853 g_signal_connect(G_OBJECT(view
), "row-activated", G_CALLBACK(show_replays_tree_view_row_activated_cb
), NULL
);
1854 gtk_tree_view_column_set_expand(gtk_tree_view_get_column(GTK_TREE_VIEW(view
), 4), TRUE
); /* name column expands */
1857 button
=gtk_button_new_from_stock(GTK_STOCK_MEDIA_PLAY
);
1858 gtk_widget_set_sensitive(button
, FALSE
); /* not sensitive by default. when the user selects a line, it will be enabled */
1859 g_signal_connect(G_OBJECT(button
), "clicked", G_CALLBACK(show_replays_play_button_clicked
), view
);
1860 g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(view
))), "changed", G_CALLBACK(show_replays_tree_view_selection_changed
), button
);
1861 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog
)->action_area
), button
);
1863 /* comment edit button */
1864 button
=gtk_button_new_from_stock(GTK_STOCK_EDIT
);
1865 gtk_widget_set_sensitive(button
, FALSE
); /* not sensitive by default. when the user selects a line, it will be enabled */
1866 g_signal_connect(G_OBJECT(button
), "clicked", G_CALLBACK(show_replays_edit_button_clicked
), view
);
1867 g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(view
))), "changed", G_CALLBACK(show_replays_tree_view_selection_changed
), button
);
1868 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog
)->action_area
), button
);
1871 /* this must be added after the play button, so it is the rightmost one */
1872 gtk_dialog_add_button(GTK_DIALOG(dialog
), GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
);
1873 gtk_dialog_set_default_response(GTK_DIALOG(dialog
), GTK_RESPONSE_CLOSE
);
1875 gtk_widget_show_all(dialog
);
1889 /* a cave name is selected, update the text boxt with current cave's data */
1891 cave_info_combo_changed(GtkComboBox
*widget
, gpointer data
)
1893 GtkTextBuffer
*buffer
=GTK_TEXT_BUFFER(data
);
1897 /* clear text buffer */
1898 gtk_text_buffer_set_text(buffer
, "", -1);
1899 gtk_text_buffer_get_iter_at_offset (buffer
, &iter
, 0);
1901 i
=gtk_combo_box_get_active(widget
);
1904 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, gd_caveset_data
->name
, -1, "heading", NULL
);
1905 gtk_text_buffer_insert(buffer
, &iter
, "\n\n", -1);
1907 /* show properties with title only if they are not empty string */
1908 if (!g_str_equal(gd_caveset_data
->description
, "")) {
1909 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Description: "), -1, "name", NULL
);
1910 gtk_text_buffer_insert(buffer
, &iter
, gd_caveset_data
->description
, -1);
1911 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1913 if (!g_str_equal(gd_caveset_data
->author
, "")) {
1914 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Author: "), -1, "name", NULL
);
1915 gtk_text_buffer_insert(buffer
, &iter
, gd_caveset_data
->author
, -1);
1916 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1918 if (!g_str_equal(gd_caveset_data
->date
, "")) {
1919 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Date: "), -1, "name", NULL
);
1920 gtk_text_buffer_insert(buffer
, &iter
, gd_caveset_data
->date
, -1);
1921 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1923 if (!g_str_equal(gd_caveset_data
->difficulty
, "")) {
1924 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Diffuculty: "), -1, "name", NULL
);
1925 gtk_text_buffer_insert(buffer
, &iter
, gd_caveset_data
->difficulty
, -1);
1926 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1928 if (!g_str_equal(gd_caveset_data
->story
->str
, "")) {
1929 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Story:\n"), -1, "name", NULL
);
1930 gtk_text_buffer_insert(buffer
, &iter
, gd_caveset_data
->story
->str
, -1);
1931 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1933 if (!g_str_equal(gd_caveset_data
->remark
->str
, "")) {
1934 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Remark:\n"), -1, "name", NULL
);
1935 gtk_text_buffer_insert(buffer
, &iter
, gd_caveset_data
->remark
->str
, -1);
1936 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1941 GdCave
*cave
=gd_return_nth_cave(i
-1);
1943 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, cave
->name
, -1, "heading", NULL
);
1944 gtk_text_buffer_insert(buffer
, &iter
, "\n\n", -1);
1946 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Type: "), -1, "name", NULL
);
1947 gtk_text_buffer_insert(buffer
, &iter
, cave
->intermission
?_("Intermission"):_("Normal cave"), -1);
1948 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1950 /* show properties with title only if they are not empty string */
1951 if (!g_str_equal(cave
->description
, "")) {
1952 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Description: "), -1, "name", NULL
);
1953 gtk_text_buffer_insert(buffer
, &iter
, cave
->description
, -1);
1954 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1956 if (!g_str_equal(cave
->author
, "")) {
1957 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Author: "), -1, "name", NULL
);
1958 gtk_text_buffer_insert(buffer
, &iter
, cave
->author
, -1);
1959 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1961 if (!g_str_equal(cave
->date
, "")) {
1962 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Date: "), -1, "name", NULL
);
1963 gtk_text_buffer_insert(buffer
, &iter
, cave
->date
, -1);
1964 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1966 if (!g_str_equal(cave
->difficulty
, "")) {
1967 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Difficulty: "), -1, "name", NULL
);
1968 gtk_text_buffer_insert(buffer
, &iter
, cave
->difficulty
, -1);
1969 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1972 if (!g_str_equal(cave
->story
->str
, "")) {
1973 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Story:\n"), -1, "name", NULL
);
1974 gtk_text_buffer_insert(buffer
, &iter
, cave
->story
->str
, -1);
1975 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1977 if (!g_str_equal(cave
->remark
->str
, "")) {
1978 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Remark:\n"), -1, "name", NULL
);
1979 gtk_text_buffer_insert(buffer
, &iter
, cave
->remark
->str
, -1);
1980 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1986 /* show info about cave or caveset */
1988 cave_info_cb(GtkWidget
*widget
, gpointer data
)
1990 GtkWidget
*dialog
, *view
, *sw
, *combo
;
1993 GtkTextBuffer
*buffer
;
1995 gboolean paused_save
;
1998 dialog
=gtk_dialog_new_with_buttons(_("Caveset information"), GTK_WINDOW(main_window
.window
),
1999 GTK_DIALOG_DESTROY_WITH_PARENT
| GTK_DIALOG_MODAL
| GTK_DIALOG_NO_SEPARATOR
,
2000 GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
,
2002 gtk_dialog_set_default_response(GTK_DIALOG(dialog
), GTK_RESPONSE_CLOSE
);
2003 gtk_window_set_default_size(GTK_WINDOW(dialog
), 360, 480);
2005 /* create a combo box. 0=caveset, 1, 2, 3... = caves */
2006 combo
=gtk_combo_box_new_text();
2007 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog
)->vbox
), combo
, FALSE
, FALSE
, 6);
2008 text
=g_strdup_printf("[%s]", gd_caveset_data
->name
); /* caveset name = line 0 */
2009 gtk_combo_box_append_text(GTK_COMBO_BOX(combo
), text
);
2011 for (citer
=gd_caveset
; citer
!=NULL
; citer
=citer
->next
) {
2012 GdCave
*c
=citer
->data
;
2014 gtk_combo_box_append_text(GTK_COMBO_BOX(combo
), c
->name
);
2017 /* create text buffer */
2018 buffer
=gtk_text_buffer_new(NULL
);
2019 gtk_text_buffer_get_iter_at_offset (buffer
, &iter
, 0);
2020 gtk_text_buffer_create_tag (buffer
, "heading", "weight", PANGO_WEIGHT_BOLD
, "scale", PANGO_SCALE_X_LARGE
, NULL
);
2021 gtk_text_buffer_create_tag (buffer
, "name", "weight", PANGO_WEIGHT_BOLD
, NULL
);
2022 gtk_text_buffer_insert_with_tags_by_name (buffer
, &iter
, "GDash " PACKAGE_VERSION
"\n\n", -1, "heading", NULL
);
2024 sw
=gtk_scrolled_window_new(NULL
, NULL
);
2025 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw
), GTK_SHADOW_ETCHED_IN
);
2026 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
2027 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog
)->vbox
), sw
);
2028 view
=gtk_text_view_new_with_buffer(buffer
);
2029 gtk_container_add(GTK_CONTAINER (sw
), view
);
2030 g_object_unref(buffer
);
2031 gtk_text_view_set_editable (GTK_TEXT_VIEW (view
), FALSE
);
2032 gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view
), FALSE
);
2033 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view
), GTK_WRAP_WORD
);
2034 gtk_text_view_set_pixels_above_lines (GTK_TEXT_VIEW (view
), 3);
2035 gtk_text_view_set_left_margin (GTK_TEXT_VIEW (view
), 6);
2036 gtk_text_view_set_right_margin (GTK_TEXT_VIEW (view
), 6);
2038 g_signal_connect(G_OBJECT(combo
), "changed", G_CALLBACK(cave_info_combo_changed
), buffer
);
2039 if (main_window
.game
&& main_window
.game
->original_cave
)
2040 /* currently playing a cave - show info for that */
2041 gtk_combo_box_set_active(GTK_COMBO_BOX(combo
), g_list_index(gd_caveset
, main_window
.game
->original_cave
)+1);
2043 /* show info for caveset */
2044 gtk_combo_box_set_active(GTK_COMBO_BOX(combo
), 0);
2047 gtk_widget_show_all(dialog
);
2049 paused
=TRUE
; /* set paused game, so it stops while the users sees the message box */
2050 gtk_dialog_run(GTK_DIALOG(dialog
));
2051 gtk_widget_destroy(dialog
);
2058 * Creates main window
2063 create_main_window()
2066 static GtkActionEntry action_entries_normal
[]={
2067 {"PlayMenu", NULL
, N_("_Play")},
2068 {"FileMenu", NULL
, N_("_File")},
2069 {"SettingsMenu", NULL
, N_("_Settings")},
2070 {"HelpMenu", NULL
, N_("_Help")},
2071 {"Quit", GTK_STOCK_QUIT
, NULL
, NULL
, NULL
, G_CALLBACK(quit_cb
)},
2072 {"About", GTK_STOCK_ABOUT
, NULL
, NULL
, NULL
, G_CALLBACK(about_cb
)},
2073 {"Errors", GTK_STOCK_DIALOG_ERROR
, N_("_Error console"), NULL
, NULL
, G_CALLBACK(show_errors_cb
)},
2074 {"Help", GTK_STOCK_HELP
, NULL
, NULL
, NULL
, G_CALLBACK(help_cb
)},
2075 {"CaveInfo", GTK_STOCK_DIALOG_INFO
, N_("Caveset _information"), NULL
, N_("Show information about the game and its caves"), G_CALLBACK(cave_info_cb
)},
2078 static GtkActionEntry action_entries_title
[]={
2079 {"GamePreferences", GTK_STOCK_PREFERENCES
, NULL
, NULL
, NULL
, G_CALLBACK(preferences_cb
)},
2080 {"GameControlSettings", GD_ICON_KEYBOARD
, N_("_Control keys"), NULL
, NULL
, G_CALLBACK(control_settings_cb
)},
2081 {"NewGame", GTK_STOCK_MEDIA_PLAY
, N_("_New game"), "<control>N", N_("Start new game"), G_CALLBACK(new_game_cb
)},
2082 {"CaveEditor", GD_ICON_CAVE_EDITOR
, N_("Cave _editor"), NULL
, NULL
, G_CALLBACK(cave_editor_cb
)},
2083 {"OpenFile", GTK_STOCK_OPEN
, NULL
, NULL
, NULL
, G_CALLBACK(open_caveset_cb
)},
2084 {"LoadInternal", GTK_STOCK_INDEX
, N_("Load _internal game")},
2085 {"LoadRecent", GTK_STOCK_DIALOG_INFO
, N_("Open _recent")},
2086 {"OpenCavesDir", GTK_STOCK_CDROM
, N_("O_pen shipped"), NULL
, NULL
, G_CALLBACK(open_caveset_dir_cb
)},
2087 {"SaveFile", GTK_STOCK_SAVE
, NULL
, NULL
, NULL
, G_CALLBACK(save_caveset_cb
)},
2088 {"SaveAsFile", GTK_STOCK_SAVE_AS
, NULL
, NULL
, NULL
, G_CALLBACK(save_caveset_as_cb
)},
2089 {"HighScore", GD_ICON_AWARD
, N_("Hi_ghscores"), NULL
, NULL
, G_CALLBACK(highscore_cb
)},
2092 static GtkActionEntry action_entries_title_replay
[]={
2093 {"ShowReplays", GD_ICON_REPLAY
, N_("Show _replays"), NULL
, N_("List replays which are recorded for caves in this caveset"), G_CALLBACK(show_replays_cb
)},
2096 static GtkActionEntry action_entries_game
[]={
2097 {"TakeSnapshot", GD_ICON_SNAPSHOT
, N_("_Take snapshot"), "F5", NULL
, G_CALLBACK(save_snapshot_cb
)},
2098 {"Restart", GD_ICON_RESTART_LEVEL
, N_("Re_start level"), "Escape", N_("Restart current level"), G_CALLBACK(restart_level_cb
)},
2099 {"EndGame", GTK_STOCK_STOP
, N_("_End game"), "F1", N_("End current game"), G_CALLBACK(stop_game_cb
)},
2102 static GtkActionEntry action_entries_snapshot
[]={
2103 {"RevertToSnapshot", GTK_STOCK_UNDO
, N_("_Revert to snapshot"), "F6", NULL
, G_CALLBACK(load_snapshot_cb
)},
2106 static GtkToggleActionEntry action_entries_toggle
[]={
2107 {"PauseGame", GTK_STOCK_MEDIA_PAUSE
, NULL
, "space", N_("Pause game"), G_CALLBACK(toggle_pause_cb
), FALSE
},
2108 {"FullScreen", GTK_STOCK_FULLSCREEN
, NULL
, "F11", N_("Fullscreen mode during play"), G_CALLBACK(toggle_fullscreen_cb
), FALSE
},
2109 {"FastForward", GTK_STOCK_MEDIA_FORWARD
, N_("Fast for_ward"), "<control>F", N_("Fast forward"), G_CALLBACK(toggle_fast_cb
), FALSE
},
2112 static const char *ui_info
=
2114 "<menubar name='MenuBar'>"
2115 "<menu action='FileMenu'>"
2117 "<menuitem action='OpenFile'/>"
2118 "<menuitem action='LoadRecent'/>"
2119 "<menuitem action='OpenCavesDir'/>"
2120 "<menuitem action='LoadInternal'/>"
2122 "<menuitem action='CaveEditor'/>"
2124 "<menuitem action='SaveFile'/>"
2125 "<menuitem action='SaveAsFile'/>"
2127 "<menuitem action='Quit'/>"
2129 "<menu action='PlayMenu'>"
2130 "<menuitem action='NewGame'/>"
2131 "<menuitem action='PauseGame'/>"
2132 "<menuitem action='FastForward'/>"
2133 "<menuitem action='TakeSnapshot'/>"
2134 "<menuitem action='RevertToSnapshot'/>"
2135 "<menuitem action='Restart'/>"
2136 "<menuitem action='EndGame'/>"
2138 "<menuitem action='CaveInfo'/>"
2139 "<menuitem action='HighScore'/>"
2140 "<menuitem action='ShowReplays'/>"
2142 "<menuitem action='FullScreen'/>"
2143 "<menuitem action='GameControlSettings'/>"
2144 "<menuitem action='GamePreferences'/>"
2146 "<menu action='HelpMenu'>"
2147 "<menuitem action='Help'/>"
2149 "<menuitem action='Errors'/>"
2150 "<menuitem action='About'/>"
2154 "<toolbar name='ToolBar'>"
2155 "<toolitem action='NewGame'/>"
2156 "<toolitem action='EndGame'/>"
2157 "<toolitem action='FullScreen'/>"
2159 "<toolitem action='PauseGame'/>"
2160 "<toolitem action='FastForward'/>"
2161 "<toolitem action='Restart'/>"
2163 "<toolitem action='CaveInfo'/>"
2164 "<toolitem action='ShowReplays'/>"
2168 GtkWidget
*vbox
, *hbox
, *recent_chooser
, *bar
, *button
;
2169 GtkRecentFilter
*recent_filter
;
2173 const gchar
**names
;
2177 gtk_window_set_default_icon (logo
);
2178 g_object_unref(logo
);
2180 main_window
.window
=gtk_window_new(GTK_WINDOW_TOPLEVEL
);
2181 gtk_window_set_default_size(GTK_WINDOW(main_window
.window
), 360, 300);
2182 g_signal_connect(G_OBJECT(main_window
.window
), "focus_out_event", G_CALLBACK(main_window_focus_out_event
), NULL
);
2183 g_signal_connect(G_OBJECT(main_window
.window
), "delete_event", G_CALLBACK(main_window_delete_event
), NULL
);
2184 g_signal_connect(G_OBJECT(main_window
.window
), "key_press_event", G_CALLBACK(main_window_keypress_event
), NULL
);
2185 g_signal_connect(G_OBJECT(main_window
.window
), "key_release_event", G_CALLBACK(main_window_keypress_event
), NULL
);
2188 vbox
=gtk_vbox_new(FALSE
, 0);
2189 gtk_container_add (GTK_CONTAINER (main_window
.window
), vbox
);
2192 main_window
.actions_normal
=gtk_action_group_new("main_window.actions_normal");
2193 gtk_action_group_set_translation_domain(main_window
.actions_normal
, PACKAGE
);
2194 gtk_action_group_add_actions(main_window
.actions_normal
, action_entries_normal
, G_N_ELEMENTS(action_entries_normal
), &main_window
);
2195 gtk_action_group_add_toggle_actions(main_window
.actions_normal
, action_entries_toggle
, G_N_ELEMENTS(action_entries_toggle
), NULL
);
2196 main_window
.actions_title
=gtk_action_group_new("main_window.actions_title");
2197 gtk_action_group_set_translation_domain(main_window
.actions_title
, PACKAGE
);
2198 gtk_action_group_add_actions(main_window
.actions_title
, action_entries_title
, G_N_ELEMENTS(action_entries_title
), &main_window
);
2199 main_window
.actions_title_replay
=gtk_action_group_new("main_window.actions_title_replay");
2200 gtk_action_group_set_translation_domain(main_window
.actions_title_replay
, PACKAGE
);
2201 gtk_action_group_add_actions(main_window
.actions_title_replay
, action_entries_title_replay
, G_N_ELEMENTS(action_entries_title_replay
), &main_window
);
2202 /* make this toolbar button always have a title */
2203 g_object_set (gtk_action_group_get_action (main_window
.actions_title
, "NewGame"), "is_important", TRUE
, NULL
);
2204 main_window
.actions_game
=gtk_action_group_new("main_window.actions_game");
2205 gtk_action_group_set_translation_domain(main_window
.actions_game
, PACKAGE
);
2206 gtk_action_group_add_actions(main_window
.actions_game
, action_entries_game
, G_N_ELEMENTS(action_entries_game
), &main_window
);
2207 main_window
.actions_snapshot
=gtk_action_group_new("main_window.actions_snapshot");
2208 gtk_action_group_set_translation_domain(main_window
.actions_snapshot
, PACKAGE
);
2209 gtk_action_group_add_actions(main_window
.actions_snapshot
, action_entries_snapshot
, G_N_ELEMENTS(action_entries_snapshot
), &main_window
);
2212 ui
=gtk_ui_manager_new();
2213 gtk_ui_manager_insert_action_group (ui
, main_window
.actions_normal
, 0);
2214 gtk_ui_manager_insert_action_group (ui
, main_window
.actions_title
, 0);
2215 gtk_ui_manager_insert_action_group (ui
, main_window
.actions_title_replay
, 0);
2216 gtk_ui_manager_insert_action_group (ui
, main_window
.actions_game
, 0);
2217 gtk_ui_manager_insert_action_group (ui
, main_window
.actions_snapshot
, 0);
2218 gtk_window_add_accel_group (GTK_WINDOW(main_window
.window
), gtk_ui_manager_get_accel_group (ui
));
2219 gtk_ui_manager_add_ui_from_string (ui
, ui_info
, -1, NULL
);
2220 main_window
.menubar
=gtk_ui_manager_get_widget (ui
, "/MenuBar");
2221 gtk_box_pack_start(GTK_BOX (vbox
), main_window
.menubar
, FALSE
, FALSE
, 0);
2222 main_window
.toolbar
=gtk_ui_manager_get_widget (ui
, "/ToolBar");
2223 gtk_toolbar_set_style(GTK_TOOLBAR(main_window
.toolbar
), GTK_TOOLBAR_BOTH_HORIZ
);
2224 gtk_box_pack_start(GTK_BOX (vbox
), main_window
.toolbar
, FALSE
, FALSE
, 0);
2225 gtk_toolbar_set_tooltips (GTK_TOOLBAR (main_window
.toolbar
), TRUE
);
2227 /* make a submenu, which contains the games compiled in. */
2229 menu
=gtk_menu_new();
2230 gtk_menu_item_set_submenu (GTK_MENU_ITEM (gtk_ui_manager_get_widget (ui
, "/MenuBar/FileMenu/LoadInternal")), menu
);
2231 names
=gd_caveset_get_internal_game_names ();
2233 GtkWidget
*menuitem
=gtk_menu_item_new_with_label(names
[i
]);
2235 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menuitem
);
2236 gtk_widget_show(menuitem
);
2237 g_signal_connect (G_OBJECT(menuitem
), "activate", G_CALLBACK(load_internal_cb
), GINT_TO_POINTER (i
));
2241 /* recent file chooser */
2242 recent_chooser
=gtk_recent_chooser_menu_new();
2243 recent_filter
=gtk_recent_filter_new();
2244 /* gdash file extensions */
2245 for (i
=0; gd_caveset_extensions
[i
]!=NULL
; i
++)
2246 gtk_recent_filter_add_pattern(recent_filter
, gd_caveset_extensions
[i
]);
2247 gtk_recent_chooser_add_filter(GTK_RECENT_CHOOSER(recent_chooser
), recent_filter
);
2248 gtk_recent_chooser_set_local_only(GTK_RECENT_CHOOSER(recent_chooser
), TRUE
);
2249 gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(recent_chooser
), 10);
2250 gtk_recent_chooser_set_sort_type(GTK_RECENT_CHOOSER(recent_chooser
), GTK_RECENT_SORT_MRU
);
2251 gtk_menu_item_set_submenu (GTK_MENU_ITEM (gtk_ui_manager_get_widget (ui
, "/MenuBar/FileMenu/LoadRecent")), recent_chooser
);
2252 g_signal_connect(G_OBJECT(recent_chooser
), "item-activated", G_CALLBACK(recent_chooser_activated_cb
), NULL
);
2256 /* this hbox will contain the replay logo and the labels */
2257 hbox
=gtk_hbox_new(FALSE
, 6);
2258 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
2260 /* replay logo... */
2261 main_window
.replay_image_align
=gtk_alignment_new(0.5, 0.5, 0, 0);
2262 gtk_box_pack_start(GTK_BOX(hbox
), main_window
.replay_image_align
, FALSE
, FALSE
, 0);
2263 gtk_container_add(GTK_CONTAINER(main_window
.replay_image_align
), gtk_image_new_from_stock(GD_ICON_REPLAY
, GTK_ICON_SIZE_LARGE_TOOLBAR
));
2266 main_window
.labels
=gtk_vbox_new(FALSE
, 0);
2267 gtk_box_pack_start_defaults(GTK_BOX(hbox
), main_window
.labels
);
2269 /* first hbox for labels ABOVE drawing */
2270 hbox
=gtk_hbox_new(FALSE
, 12);
2271 gtk_box_pack_start(GTK_BOX (main_window
.labels
), hbox
, FALSE
, FALSE
, 0);
2272 main_window
.label_topleft
=gtk_label_new(NULL
); /* NAME label */
2273 gtk_label_set_ellipsize(GTK_LABEL(main_window
.label_topleft
), PANGO_ELLIPSIZE_END
); /* enable ellipsize, as the cave name might be a long string */
2274 gtk_misc_set_alignment(GTK_MISC(main_window
.label_topleft
), 0, 0.5); /* as it will be expanded, we must set left justify (0) */
2275 gtk_box_pack_start(GTK_BOX(hbox
), main_window
.label_topleft
, TRUE
, TRUE
, 0); /* should expand, as it is ellipsized!! */
2277 gtk_box_pack_end(GTK_BOX(hbox
), main_window
.label_topright
=gtk_label_new(NULL
), FALSE
, FALSE
, 0);
2279 /* second row of labels */
2280 hbox
=gtk_hbox_new(FALSE
, 12);
2281 gtk_box_pack_start(GTK_BOX(main_window
.labels
), hbox
, FALSE
, FALSE
, 0);
2282 gtk_box_pack_start(GTK_BOX(hbox
), main_window
.label_bottomleft
=gtk_label_new(NULL
), FALSE
, FALSE
, 0); /* diamonds label */
2283 gtk_box_pack_end(GTK_BOX(hbox
), main_window
.label_bottomright
=gtk_label_new(NULL
), FALSE
, FALSE
, 0); /* time, score label */
2286 /* scroll window which contains the cave or the title image, so this is the main content of the window */
2287 main_window
.scroll_window
=gtk_scrolled_window_new(NULL
, NULL
);
2288 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(main_window
.scroll_window
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
2289 gtk_box_pack_start_defaults(GTK_BOX (vbox
), main_window
.scroll_window
);
2291 main_window
.label_variables
=gtk_label_new(NULL
);
2292 gtk_box_pack_start(GTK_BOX (vbox
), main_window
.label_variables
, FALSE
, FALSE
, 0);
2295 bar
=gtk_info_bar_new();
2296 gtk_box_pack_start(GTK_BOX(vbox
), bar
, FALSE
, FALSE
, 0);
2297 gtk_info_bar_set_message_type(GTK_INFO_BAR(bar
), GTK_MESSAGE_ERROR
);
2298 main_window
.info_label
=gtk_label_new(NULL
);
2299 gtk_box_pack_start(GTK_BOX(gtk_info_bar_get_content_area(GTK_INFO_BAR(bar
))), main_window
.info_label
, FALSE
, FALSE
, 0); /* error label */
2300 main_window
.info_bar
=bar
;
2301 button
=gtk_button_new_with_mnemonic(_("_Show"));
2302 gtk_box_pack_start(GTK_BOX(gtk_info_bar_get_action_area(GTK_INFO_BAR(bar
))), button
, FALSE
, FALSE
, 0); /* error label */
2303 g_signal_connect(button
, "clicked", G_CALLBACK(show_errors_cb
), NULL
);
2305 gtk_widget_show_all(main_window
.window
);
2306 gtk_window_present(GTK_WINDOW(main_window
.window
));
2315 main(int argc
, char *argv
[])
2318 gboolean editor
=FALSE
;
2319 char *gallery_filename
=NULL
;
2320 char *png_filename
=NULL
, *png_size
=NULL
;
2321 char *save_cave_name
=NULL
;
2322 gboolean force_quit_no_gtk
;
2325 GOptionContext
*context
;
2326 GOptionEntry entries
[]={
2327 {"editor", 'e', 0, G_OPTION_ARG_NONE
, &editor
, N_("Start editor")},
2328 {"gallery", 'g', 0, G_OPTION_ARG_FILENAME
, &gallery_filename
, N_("Save caveset in a HTML gallery")},
2329 {"stylesheet", 's', 0, G_OPTION_ARG_STRING
/* not filename! */, &gd_html_stylesheet_filename
, N_("Link stylesheet from file to a HTML gallery, eg. \"../style.css\"")},
2330 {"favicon", 's', 0, G_OPTION_ARG_STRING
/* not filename! */, &gd_html_favicon_filename
, N_("Link shortcut icon to a HTML gallery, eg. \"../favicon.ico\"")},
2331 {"png", 'p', 0, G_OPTION_ARG_FILENAME
, &png_filename
, N_("Save cave C, level L in a PNG image. If no cave selected, uses a random one")},
2332 {"png_size", 'P', 0, G_OPTION_ARG_STRING
, &png_size
, N_("Set PNG image size. Default is 128x96, set to 0x0 for unscaled")},
2333 {"save", 'S', 0, G_OPTION_ARG_FILENAME
, &save_cave_name
, N_("Save caveset in a BDCFF file")},
2334 {"quit", 'q', 0, G_OPTION_ARG_NONE
, &quit
, N_("Batch mode: quit after specified tasks")},
2338 context
=gd_option_context_new();
2339 g_option_context_add_main_entries (context
, entries
, PACKAGE
); /* gdash (gtk version) parameters */
2340 g_option_context_add_group (context
, gtk_get_option_group(FALSE
)); /* add gtk parameters */
2341 g_option_context_parse (context
, &argc
, &argv
, &error
);
2342 g_option_context_free (context
);
2344 g_warning("%s", error
->message
);
2345 g_error_free(error
);
2349 if (gd_param_license
) {
2350 char *wrapped
=gd_wrap_text(gd_about_license
, 72);
2352 g_print("%s", wrapped
);
2357 gd_install_log_handler();
2359 gd_settings_init_dirs();
2364 gd_settings_set_locale();
2366 force_quit_no_gtk
=FALSE
;
2367 if (!gtk_init_check(&argc
, &argv
)) {
2368 force_quit_no_gtk
=TRUE
;
2371 gd_settings_init_translation();
2375 gd_cave_sound_db_init();
2376 gd_c64_import_init_tables();
2378 gd_caveset_clear(); /* this also creates the default cave */
2380 gd_clear_error_flag();
2382 gd_wait_before_game_over
=FALSE
;
2384 /* LOAD A CAVESET FROM A FILE, OR AN INTERNAL ONE */
2385 /* if remaining arguments, they are filenames */
2386 if (gd_param_cavenames
&& gd_param_cavenames
[0]) {
2387 /* load caveset, "ignore" errors. */
2388 if (!gd_open_caveset_in_ui(gd_param_cavenames
[0], gd_use_bdcff_highscore
))
2389 g_critical (_("Errors during loading caveset from file '%s'"), gd_param_cavenames
[0]);
2391 else if (gd_param_internal
) {
2392 /* if specified an internal caveset; if error, halt now */
2393 if (!gd_caveset_load_from_internal (gd_param_internal
-1, gd_user_config_dir
))
2394 g_critical (_("%d: no such internal caveset"), gd_param_internal
);
2397 /* if nothing requested, load default */
2398 gd_caveset_load_from_internal(0, gd_user_config_dir
);
2400 /* always load c64 graphics, as it is the builtin, and we need an icon for the theme selector. */
2401 gd_loadcells_default();
2402 gd_create_pixbuf_for_builtin_theme();
2404 /* load other theme, if specified in config. */
2405 if (gd_theme
!=NULL
&& !g_str_equal(gd_theme
, "")) {
2406 if (!gd_loadcells_file(gd_theme
)) {
2407 /* forget the theme if we are unable to load */
2408 g_warning("Cannot load theme %s, switching to built-in theme", gd_theme
);
2411 gd_loadcells_default(); /* load default gfx */
2415 /* after loading cells... see if generating a gallery. */
2416 /* but only if there are any caves at all. */
2417 if (gd_caveset
&& gallery_filename
)
2418 gd_save_html (gallery_filename
, NULL
);
2420 /* if cave or level values given, check range */
2422 if (gd_param_cave
<1 || gd_param_cave
>gd_caveset_count() || gd_param_level
<1 || gd_param_level
>5)
2423 g_critical (_("Invalid cave or level number!"));
2429 GdCave
*renderedcave
;
2430 unsigned int size_x
=128, size_y
=96; /* default size */
2432 if (gd_param_cave
== 0)
2433 gd_param_cave
=g_random_int_range(0, gd_caveset_count ())+1;
2435 if (png_size
&& (sscanf (png_size
, "%ux%u", &size_x
, &size_y
) != 2))
2436 g_critical (_("Invalid image size: %s"), png_size
);
2437 if (size_x
<1 || size_y
<1) {
2442 /* rendering cave for png: seed=0 */
2443 renderedcave
=gd_cave_new_from_caveset (gd_param_cave
-1, gd_param_level
-1, 0);
2444 pixbuf
=gd_drawcave_to_pixbuf(renderedcave
, size_x
, size_y
, TRUE
, FALSE
);
2445 if (!gdk_pixbuf_save (pixbuf
, png_filename
, "png", &error
, "compression", "9", NULL
))
2446 g_critical ("Error saving PNG image %s: %s", png_filename
, error
->message
);
2447 g_object_unref(pixbuf
);
2448 gd_cave_free (renderedcave
);
2450 /* avoid starting game */
2455 gd_caveset_save(save_cave_name
);
2457 /* if batch mode, quit now */
2460 if (force_quit_no_gtk
) {
2461 g_critical("Cannot initialize GUI");
2466 gd_register_stock_icons();
2468 create_main_window();
2469 gd_main_window_set_title();
2474 gd_sound_set_music_volume(gd_sound_music_volume_percent
);
2475 gd_sound_set_chunk_volumes(gd_sound_chunks_volume_percent
);
2478 main_window_init_title();
2480 #ifdef G_THREADS_ENABLED
2481 if (!g_thread_supported())
2482 g_thread_init(NULL
);
2485 if (gd_param_cave
) {
2486 /* if cave number given, start game */
2487 main_new_game(g_get_real_name(), gd_param_cave
-1, gd_param_level
-1);
2490 cave_editor_cb(NULL
, &main_window
);
2494 gd_save_highscore(gd_user_config_dir
);