2 * Copyright (c) 2007, 2008 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 static gboolean quit_thread
;
48 typedef struct _gd_main_window
{
50 GtkActionGroup
*actions_normal
, *actions_title
, *actions_game
, *actions_snapshot
;
51 GtkWidget
*scroll_window
;
52 GtkWidget
*drawing_area
, *title_image
; /* two things that could be drawn in the main window */
53 GdkPixmap
**title_pixmaps
;
54 GtkWidget
*labels
; /* parts of main window which have to be shown or hidden */
55 GtkWidget
*label_score
, *label_value
, *label_time
, *label_cave_name
, *label_diamonds
, *label_skeletons
, *label_lives
; /* different text labels in main window */
56 GtkWidget
*label_key1
, *label_key2
, *label_key3
, *label_gravity_will_change
;
57 GtkWidget
*label_variables
;
58 GtkWidget
*error_hbox
, *error_label
;
59 GtkWidget
*menubar
, *toolbar
;
60 GtkWidget
*replay_image_align
;
63 static GDMainWindow main_window
;
65 static gboolean key_lctrl
=FALSE
, key_rctrl
=FALSE
, key_right
=FALSE
, key_up
=FALSE
, key_down
=FALSE
, key_left
=FALSE
, key_suicide
=FALSE
;
66 static gboolean restart
; /* keys which control the game, but are handled differently than the above */
67 static int mouse_cell_x
=-1, mouse_cell_y
=-1, mouse_cell_click
=0;
69 static void init_mainwindow(Cave
*);
70 static void install_game_timer();
80 fullscreen_idle_func(gpointer data
)
82 gtk_window_fullscreen(GTK_WINDOW(data
));
86 /* set or unset fullscreen if necessary */
87 /* hack: gtk-win32 does not correctly handle fullscreen & removing widgets.
88 so we put fullscreening call into a low priority idle function, which will be called
89 after all window resizing & the like did take place. */
93 if (gd_gameplay
.cave
&& fullscreen
) {
94 gtk_widget_hide(main_window
.menubar
);
95 gtk_widget_hide(main_window
.toolbar
);
96 g_idle_add_full(G_PRIORITY_LOW
, (GSourceFunc
) fullscreen_idle_func
, main_window
.window
, NULL
);
99 g_idle_remove_by_data (main_window
.window
);
100 gtk_window_unfullscreen (GTK_WINDOW(main_window
.window
));
101 gtk_widget_show(main_window
.menubar
);
102 gtk_widget_show(main_window
.toolbar
);
108 uninstall game timers, if any installed.
111 uninstall_game_timeout()
114 /* remove timeout associated to game play */
115 while (g_source_remove_by_user_data (main_window
.window
)) {
123 uninstall_game_timeout();
124 init_mainwindow(NULL
);
127 /* if editor is active, go back to its window. */
129 gtk_window_present(GTK_WINDOW(editor_window
));
133 gd_main_window_set_title()
135 if (!g_str_equal(gd_caveset_data
->name
, "")) {
138 text
=g_strdup_printf("GDash - %s", gd_caveset_data
->name
);
139 gtk_window_set_title (GTK_WINDOW(main_window
.window
), text
);
143 gtk_window_set_title (GTK_WINDOW(main_window
.window
), "GDash");
150 /* game over, and highscore is achieved. ask for name, and if given, record it! */
152 game_over_highscore()
157 text
=g_strdup_printf(_("You have %d points, and achieved a highscore."), gd_gameplay
.player_score
);
158 gd_infomessage(_("Game over!"), text
);
161 /* enter to highscore table */
162 rank
=gd_add_highscore(gd_caveset_data
->highscore
, gd_gameplay
.player_name
, gd_gameplay
.player_score
);
163 gd_show_highscore(main_window
.window
, NULL
, FALSE
, NULL
, rank
);
167 game_over_without_highscore()
171 text
=g_strdup_printf(_("You have %d points."), gd_gameplay
.player_score
);
172 gd_infomessage(_("Game over!"), text
);
179 /* this starts a new game */
181 new_game(const char *player_name
, const int cave
, const int level
)
183 gd_new_game(player_name
, cave
, level
);
184 install_game_timer();
189 gd_main_new_game_snapshot(Cave
*snapshot
)
191 gd_new_game_snapshot(snapshot
);
192 install_game_timer();
196 gd_main_new_game_test(Cave
*test
, int level
)
198 gd_new_game_test(test
, level
);
199 install_game_timer();
204 gd_main_new_game_replay(Cave
*cave
, GdReplay
*replay
)
206 gd_new_game_replay(cave
, replay
);
207 install_game_timer();
211 /************************************************
217 /* closing the window by the window manager */
219 delete_event (GtkWidget
* widget
, GdkEvent
* event
, gpointer data
)
221 if (gd_discard_changes(main_window
.window
))
226 /* keypress. key_* can be event_type==gdk_key_press, as we
227 connected this function to key press and key release.
230 keypress_event (GtkWidget
* widget
, GdkEventKey
* event
, gpointer data
)
232 gboolean press
=event
->type
==GDK_KEY_PRESS
; /* true for press, false for release */
234 if (event
->keyval
==gd_gtk_key_left
) {
238 if (event
->keyval
==gd_gtk_key_right
) {
242 if (event
->keyval
==gd_gtk_key_up
) {
246 if (event
->keyval
==gd_gtk_key_down
) {
250 if (event
->keyval
==gd_gtk_key_fire_1
) {
254 if (event
->keyval
==gd_gtk_key_fire_2
) {
258 if (event
->keyval
==gd_gtk_key_suicide
) {
263 return FALSE
; /* if any other key, we did not process it. go on, let gtk handle it. */
266 /* for mouse play. */
267 /* mouse leaves drawing area event */
268 /* if pointer not inside window, no control of player. */
270 drawing_area_leave_event(GtkWidget
* widget
, GdkEventCrossing
* event
, gpointer data
)
278 /* mouse button press event */
280 drawing_area_button_event(GtkWidget
* widget
, GdkEventButton
* event
, gpointer data
)
282 /* see if it is a click or a release. */
283 mouse_cell_click
=event
->type
== GDK_BUTTON_PRESS
;
287 /* mouse motion event */
289 drawing_area_motion_event(GtkWidget
* widget
, GdkEventMotion
* event
, gpointer data
)
292 GdkModifierType state
;
295 gdk_window_get_pointer (event
->window
, &x
, &y
, &state
);
302 mouse_cell_x
=x
/ gd_cell_size_game
;
303 mouse_cell_y
=y
/ gd_cell_size_game
;
310 * draws the cave during game play
317 if (!main_window
.drawing_area
->window
)
319 if (!gd_gameplay
.cave
) /* if already no cave, just return */
321 if (!gd_gameplay
.gfx_buffer
) /* if already no cave, just return */
324 /* 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. */
325 /* on the other hand, xd and yd are screen coordinates (of course, multiply by cell size) */
326 /* inside the loop, calculate cave coordinates, which might not be equal to screen coordinates. */
327 /* top left part of cave:
330 *y=0 wwww|wwwwwwwwwwwwww
331 *yd=0 ----+--------------
332 * w...|o p........ visible part
333 * w...|..d...........
335 for (y
=gd_gameplay
.cave
->y1
, yd
=0; y
<=gd_gameplay
.cave
->y2
; y
++, yd
++) {
336 for (x
=gd_gameplay
.cave
->x1
, xd
=0; x
<=gd_gameplay
.cave
->x2
; x
++, xd
++) {
337 if (gd_gameplay
.gfx_buffer
[y
][x
] & GD_REDRAW
) {
338 gd_gameplay
.gfx_buffer
[y
][x
] &= ~GD_REDRAW
;
339 gdk_draw_drawable(main_window
.drawing_area
->window
, main_window
.drawing_area
->style
->black_gc
, gd_game_pixmap(gd_gameplay
.gfx_buffer
[y
][x
]),
340 0, 0, xd
*gd_cell_size_game
, yd
*gd_cell_size_game
, gd_cell_size_game
, gd_cell_size_game
);
349 drawing_area_expose_event(GtkWidget
*widget
, GdkEventExpose
*event
, gpointer data
)
351 int x1
, y1
, x2
, y2
, xd
, yd
;
355 if (!gd_gameplay
.cave
) /* if already no cave, just return */
357 if (!gd_gameplay
.gfx_buffer
) /* if already no cave, just return */
360 /* redraw entire area, not specific rectangles.
361 * this function gets called only when the main window gets exposed
362 * by the user removing another window. */
363 /* these are screen coordinates. */
364 x1
=event
->area
.x
/ gd_cell_size_game
;
365 y1
=event
->area
.y
/ gd_cell_size_game
;
366 x2
=(event
->area
.x
+ event
->area
.width
-1) / gd_cell_size_game
;
367 y2
=(event
->area
.y
+ event
->area
.height
-1) / gd_cell_size_game
;
369 /* run through screen coordinates to refresh. */
370 /* inside the loop, calculate cave coordinates, which might not be equal to screen coordinates. */
371 /* top left part of cave:
374 *y=0 wwww|wwwwwwwwwwwwww
375 *yd=0 ----+--------------
376 * w...|o p........ visible part
377 * w...|..d...........
379 /* mark all cells to be redrawn with GD_REDRAW, and then call the normal drawcave routine. */
380 for (yd
=y1
; yd
<=y2
; yd
++) {
381 int y
=yd
+gd_gameplay
.cave
->y1
;
382 for (xd
=x1
; xd
<=x2
; xd
++) {
383 int x
=xd
+gd_gameplay
.cave
->x1
;
384 if (gd_gameplay
.gfx_buffer
[y
][x
]!=-1) {
385 if (!(gd_gameplay
.gfx_buffer
[y
][x
] & GD_REDRAW
))
386 gd_gameplay
.gfx_buffer
[y
][x
] |= GD_REDRAW
;
395 /* focus leaves game play window. remember that keys are not pressed!
396 as we don't receive key released events along with focus out. */
398 focus_out_event(GtkWidget
*widget
, GdkEvent
*event
, gpointer data
)
411 /**********************************-
418 help_cb(GtkWidget
* widget
, gpointer data
)
420 gd_show_game_help (((GDMainWindow
*)data
)->window
);
424 preferences_cb(GtkWidget
*widget
, gpointer data
)
426 gd_preferences(((GDMainWindow
*)data
)->window
);
430 control_settings_cb(GtkWidget
*widget
, gpointer data
)
432 gd_control_settings(((GDMainWindow
*)data
)->window
);
436 quit_cb(GtkWidget
* widget
, const gpointer data
)
442 stop_game_cb(GtkWidget
*widget
, gpointer data
)
448 save_snapshot_cb(GtkWidget
* widget
, gpointer data
)
451 gd_cave_free(snapshot
);
453 snapshot
=gd_create_snapshot();
454 gtk_action_group_set_sensitive (main_window
.actions_snapshot
, snapshot
!=NULL
);
458 load_snapshot_cb(GtkWidget
* widget
, gpointer data
)
460 g_return_if_fail(snapshot
!=NULL
);
461 gd_main_new_game_snapshot(snapshot
);
464 /* restart level button clicked */
466 restart_level_cb(GtkWidget
* widget
, gpointer data
)
468 g_return_if_fail(gd_gameplay
.cave
!=NULL
);
469 /* sets the restart variable, which will be interpreted by the iterate routine */
474 about_cb(GtkWidget
*widget
, gpointer data
)
476 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
);
480 open_caveset_cb(GtkWidget
* widget
, gpointer data
)
482 gd_open_caveset(main_window
.window
, NULL
);
483 gd_main_window_set_title();
487 open_caveset_dir_cb(GtkWidget
* widget
, gpointer data
)
489 gd_open_caveset(main_window
.window
, gd_system_caves_dir
);
490 gd_main_window_set_title();
494 save_caveset_as_cb(GtkWidget
* widget
, gpointer data
)
496 gd_save_caveset_as(main_window
.window
);
500 save_caveset_cb(GtkWidget
* widget
, gpointer data
)
502 gd_save_caveset(main_window
.window
);
505 /* load internal game from the executable. those are inlined in caveset.c. */
507 load_internal_cb(GtkWidget
* widget
, gpointer data
)
509 gd_load_internal(main_window
.window
, GPOINTER_TO_INT(data
));
510 gd_main_window_set_title();
514 toggle_pause_cb(GtkWidget
* widget
, gpointer data
)
516 paused
=gtk_toggle_action_get_active(GTK_TOGGLE_ACTION (widget
));
523 toggle_fullscreen_cb (GtkWidget
* widget
, gpointer data
)
525 fullscreen
=gtk_toggle_action_get_active(GTK_TOGGLE_ACTION (widget
));
530 toggle_fast_cb (GtkWidget
* widget
, gpointer data
)
532 fast_forward
=gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (widget
));
538 const Cave
*cave
=gd_gameplay
.cave
;
542 time
=gd_cave_time_show(cave
, cave
->time
);
544 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_time
), _("Time: <b>%02d:%02d</b>"), time
/60, time
%60);
546 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_time
), _("Time: <b>%03d</b>"), time
);
548 /* lives reamining in game */
549 switch (gd_gameplay
.type
) {
550 case GD_GAMETYPE_NORMAL
:
551 if (!cave
->intermission
)
552 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_lives
), _("Lives: <b>%d</b>"), gd_gameplay
.player_lives
);
554 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_lives
), _("<b>Bonus life</b>"));
556 case GD_GAMETYPE_SNAPSHOT
:
557 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_lives
), _("Continuing from <b>snapshot</b>"));
559 case GD_GAMETYPE_TEST
:
560 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_lives
), _("<b>Testing</b> cave"));
562 case GD_GAMETYPE_REPLAY
:
563 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_lives
), _("Playing <b>replay</b>"));
565 case GD_GAMETYPE_CONTINUE_REPLAY
:
566 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_lives
), _("Continuing <b>replay</b>"));
570 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_score
), _("Score: <b>%d</b>"), gd_gameplay
.player_score
);
573 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_value
), _("Value: <b>%d</b>"), cave
->diamond_value
);
575 /* diamonds needed */
576 if (cave
->diamonds_needed
>0)
577 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_diamonds
), _("Diamonds: <b>%d</b>"), cave
->diamonds_collected
>=cave
->diamonds_needed
?0:cave
->diamonds_needed
-cave
->diamonds_collected
);
579 /* did not already count diamonds */
580 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_diamonds
), _("Diamonds: <b>??""?</b>")); /* "" to avoid C trigraph ??< */
583 /* skeletons collected */
584 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_skeletons
), _("Skeletons: <b>%d</b>"), cave
->skeletons_collected
);
587 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_key1
), _("Key 1: <b>%d</b>"), cave
->key1
);
588 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_key2
), _("Key 2: <b>%d</b>"), cave
->key2
);
589 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_key3
), _("Key 3: <b>%d</b>"), cave
->key3
);
592 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_gravity_will_change
), _("Gravity change: <b>%d</b>"), gd_cave_time_show(cave
, cave
->gravity_will_change
));
594 if (editor_window
&& gd_show_test_label
) {
595 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_variables
),
596 "Speed: %dms, Amoeba timer: %ds %d, %ds %d, Magic wall timer: %ds\n"
597 "Expanding wall: %s, Creatures: %ds, %s, Gravity: %s\n"
598 "Kill player: %s, Sweet eaten: %s, Diamond key: %s",
600 gd_cave_time_show(cave
, cave
->amoeba_time
),
602 gd_cave_time_show(cave
, cave
->amoeba_2_time
),
603 cave
->amoeba_2_state
,
604 gd_cave_time_show(cave
, cave
->magic_wall_time
),
605 // XXX cave->magic_wall_state,
606 cave
->expanding_wall_changed
?"vertical":"horizontal",
607 gd_cave_time_show(cave
, cave
->creatures_direction_will_change
),
608 cave
->creatures_backwards
?"backwards":"forwards",
609 gd_direction_get_visible_name(cave
->gravity_disabled
?MV_STILL
:cave
->gravity
),
610 cave
->kill_player
?"yes":"no",
611 cave
->sweet_eaten
?"yes":"no",
612 cave
->diamond_key_collected
?"yes":"no"
620 * creates title screen or drawing area
623 gboolean
title_animation_func(gpointer data
)
625 static int animcycle
=0;
628 /* count the number of frames. */
630 while (main_window
.title_pixmaps
[count
]!=NULL
)
633 if (gtk_window_has_toplevel_focus (GTK_WINDOW(main_window
.window
))) {
634 animcycle
=(animcycle
+1)%count
;
635 gtk_image_set_from_pixmap(GTK_IMAGE(main_window
.title_image
), main_window
.title_pixmaps
[animcycle
], NULL
);
640 void title_animation_remove()
644 g_source_remove_by_user_data(title_animation_func
);
646 while (main_window
.title_pixmaps
[i
]!=NULL
) {
647 g_object_unref(main_window
.title_pixmaps
[i
]);
650 g_free(main_window
.title_pixmaps
);
651 main_window
.title_pixmaps
=NULL
;
655 init_mainwindow(Cave
*cave
)
661 if (main_window
.title_image
)
662 gtk_widget_destroy (main_window
.title_image
->parent
); /* bit tricky, destroy the viewport which was automatically added */
664 if (!main_window
.drawing_area
) {
667 /* put drawing area in an alignment, so window can be any large w/o problems */
668 align
=gtk_alignment_new(0.5, 0.5, 0, 0);
669 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(main_window
.scroll_window
), align
);
671 main_window
.drawing_area
=gtk_drawing_area_new();
672 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
);
673 g_signal_connect (G_OBJECT(main_window
.drawing_area
), "button_press_event", G_CALLBACK(drawing_area_button_event
), NULL
);
674 g_signal_connect (G_OBJECT(main_window
.drawing_area
), "button_release_event", G_CALLBACK(drawing_area_button_event
), NULL
);
675 g_signal_connect (G_OBJECT(main_window
.drawing_area
), "motion_notify_event", G_CALLBACK(drawing_area_motion_event
), NULL
);
676 g_signal_connect (G_OBJECT(main_window
.drawing_area
), "leave_notify_event", G_CALLBACK(drawing_area_leave_event
), NULL
);
677 g_signal_connect (G_OBJECT(main_window
.drawing_area
), "expose_event", G_CALLBACK(drawing_area_expose_event
), NULL
);
678 g_signal_connect (G_OBJECT(main_window
.drawing_area
), "destroy", G_CALLBACK(gtk_widget_destroyed
), &main_window
.drawing_area
);
679 gtk_container_add (GTK_CONTAINER (align
), main_window
.drawing_area
);
681 gdk_window_set_cursor (main_window
.drawing_area
->window
, gdk_cursor_new(GDK_CROSSHAIR
));
683 /* set the minimum size of the scroll window: 20*12 cells */
684 /* XXX adding some pixels for the scrollbars-here we add 24 */
685 gtk_widget_set_size_request(main_window
.scroll_window
, 20*gd_cell_size_game
+24, 12*gd_cell_size_game
+24);
686 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
);
689 gtk_widget_show(main_window
.labels
);
690 if (editor_window
&& gd_show_test_label
)
691 gtk_widget_show(main_window
.label_variables
);
693 gtk_widget_hide(main_window
.label_variables
);
694 gtk_widget_hide(main_window
.error_hbox
);
696 name_escaped
=g_markup_escape_text(cave
->name
, -1);
697 gd_label_set_markup_printf(GTK_LABEL(main_window
.label_cave_name
), _("<b>%s</b>, level %d"), name_escaped
, cave
->rendered
);
698 g_free(name_escaped
);
701 if (main_window
.drawing_area
)
702 /* parent is the align, parent of align is the viewport automatically added. */
703 gtk_widget_destroy (main_window
.drawing_area
->parent
->parent
);
705 if (!main_window
.title_image
) {
709 main_window
.title_pixmaps
=gd_create_title_animation();
710 main_window
.title_image
=gtk_image_new();
711 g_signal_connect (G_OBJECT(main_window
.title_image
), "destroy", G_CALLBACK(gtk_widget_destroyed
), &main_window
.title_image
);
712 g_signal_connect (G_OBJECT(main_window
.title_image
), "destroy", G_CALLBACK(title_animation_remove
), NULL
);
713 g_timeout_add(40, title_animation_func
, title_animation_func
);
714 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(main_window
.scroll_window
), main_window
.title_image
);
716 /* resize the scrolling window so the image fits - a bit larger than the image so scroll bars do not appear*/
717 gdk_drawable_get_size(GDK_DRAWABLE(main_window
.title_pixmaps
[0]), &w
, &h
);
718 gtk_widget_set_size_request(main_window
.scroll_window
, w
+24, h
+24);
722 gtk_widget_hide(main_window
.labels
);
723 gtk_widget_hide(main_window
.label_variables
);
724 if (gd_has_new_error()) {
725 gtk_widget_show(main_window
.error_hbox
);
726 gtk_label_set(GTK_LABEL(main_window
.error_label
), ((GdErrorMessage
*)(g_list_last(gd_errors
)->data
))->message
);
728 gtk_widget_hide(main_window
.error_hbox
);
732 /* show newly created widgets */
733 gtk_widget_show_all(main_window
.scroll_window
);
735 /* set or unset fullscreen if necessary */
738 /* enable menus and buttons of game */
739 gtk_action_group_set_sensitive(main_window
.actions_title
, cave
==NULL
&& !editor_window
);
740 gtk_action_group_set_sensitive(main_window
.actions_game
, cave
!=NULL
);
741 gtk_action_group_set_sensitive(main_window
.actions_snapshot
, snapshot
!=NULL
);
742 /* if playing a cave or editor window exists, no music. */
743 if (cave
|| editor_window
)
746 gd_music_play_random();
748 gtk_widget_set_sensitive(editor_window
, cave
==NULL
);
749 gtk_widget_hide(main_window
.replay_image_align
); /* it will be shown if needed. */
754 * scrolls to the player during game play.
759 static int scroll_desired_x
=0, scroll_desired_y
=0;
760 static int scroll_speed_x
=0, scroll_speed_y
=0;
761 GtkAdjustment
*adjustment
;
762 int scroll_center_x
, scroll_center_y
;
763 gboolean out_of_window
=FALSE
;
766 int player_x
, player_y
;
767 const Cave
*cave
=gd_gameplay
.cave
;
768 gboolean exact_scroll
;
771 scroll_divisor
=12; /* some sort of scrolling speed */
773 scroll_divisor
*=2; /* as fine scrolling is 50hz, whereas normal is 25hz only */
775 /* if cave not yet rendered, return. (might be the case for 50hz scrolling */
776 if (gd_gameplay
.cave
==NULL
)
778 /* no scrolling when pause button is pressed */
782 /* check player state. */
783 switch (gd_gameplay
.cave
->player_state
) {
795 /* do not scroll when the player is dead or cave time is over. */
796 return; /* return from function */
799 player_x
=cave
->player_x
-cave
->x1
;
800 player_y
=cave
->player_y
-cave
->y1
;
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
/4;
806 int scroll_to_y
=main_window
.scroll_window
->allocation
.height
/8;
808 /* get the size of the window so we know where to place player.
809 * first guess is the middle of the screen.
810 * main_window.drawing_area->parent->parent is the viewport.
811 * +cellsize/2 gets the stomach of player :) so the very center */
812 scroll_center_x
=player_x
*gd_cell_size_game
+ gd_cell_size_game
/2-main_window
.drawing_area
->parent
->parent
->allocation
.width
/2;
813 scroll_center_y
=player_y
*gd_cell_size_game
+ gd_cell_size_game
/2-main_window
.drawing_area
->parent
->parent
->allocation
.height
/2;
816 /* hystheresis function.
817 * when scrolling left, always go a bit less left than player being at the middle.
818 * when scrolling right, always go a bit less to the right. */
819 adjustment
=gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW(main_window
.scroll_window
));
821 scroll_desired_x
=scroll_center_x
;
823 if (adjustment
->value
+scroll_start_x
<scroll_center_x
)
824 scroll_desired_x
=scroll_center_x
-scroll_to_x
;
825 if (adjustment
->value
-scroll_start_x
>scroll_center_x
)
826 scroll_desired_x
=scroll_center_x
+scroll_to_x
;
828 scroll_desired_x
=CLAMP(scroll_desired_x
, 0, adjustment
->upper
-adjustment
->step_increment
-adjustment
->page_increment
);
829 /* check if active player is outside drawing area. if yes, we should wait for scrolling */
830 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
)
831 /* but only do the wait, if the player SHOULD BE visible, ie. he is inside the defined visible area of the cave */
832 if (cave
->player_x
>=cave
->x1
&& cave
->player_x
<=cave
->x2
)
835 /* adaptive scrolling speed.
836 * gets faster with distance.
837 * minimum speed is 1, to allow scrolling precisely to the desired positions (important at borders).
839 if (scroll_speed_x
<ABS(scroll_desired_x
-adjustment
->value
)/scroll_divisor
+1)
841 if (scroll_speed_x
>ABS(scroll_desired_x
-adjustment
->value
)/scroll_divisor
+1)
844 if (adjustment
->value
<scroll_desired_x
) {
845 for (i
=0; i
<scroll_speed_x
; i
++)
846 if ((int)adjustment
->value
<scroll_desired_x
)
850 if (adjustment
->value
> scroll_desired_x
) {
851 for (i
=0; i
< scroll_speed_x
; i
++)
852 if ((int)adjustment
->value
>scroll_desired_x
)
857 gtk_adjustment_value_changed (adjustment
);
860 adjustment
=gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW(main_window
.scroll_window
));
862 scroll_desired_y
=scroll_center_y
;
864 if (adjustment
->value
+ scroll_start_y
< scroll_center_y
)
865 scroll_desired_y
=scroll_center_y
-scroll_to_y
;
866 if (adjustment
->value
-scroll_start_y
> scroll_center_y
)
867 scroll_desired_y
=scroll_center_y
+ scroll_to_y
;
869 scroll_desired_y
=CLAMP(scroll_desired_y
, 0, adjustment
->upper
-adjustment
->step_increment
-adjustment
->page_increment
);
870 /* check if active player is outside drawing area. if yes, we should wait for scrolling */
871 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
)
872 /* but only do the wait, if the player SHOULD BE visible, ie. he is inside the defined visible area of the cave */
873 if (cave
->player_y
>=cave
->y1
&& cave
->player_y
<=cave
->y2
)
876 if (scroll_speed_y
<ABS(scroll_desired_y
-adjustment
->value
)/scroll_divisor
+1)
878 if (scroll_speed_y
>ABS(scroll_desired_y
-adjustment
->value
)/scroll_divisor
+1)
881 if (adjustment
->value
< scroll_desired_y
) {
882 for (i
=0; i
< scroll_speed_y
; i
++)
883 if ((int)adjustment
->value
< scroll_desired_y
)
887 if (adjustment
->value
> scroll_desired_y
) {
888 for (i
=0; i
< scroll_speed_y
; i
++)
889 if ((int)adjustment
->value
> scroll_desired_y
)
894 gtk_adjustment_value_changed (adjustment
);
896 /* remember if player is visible inside window */
897 gd_gameplay
.out_of_window
=out_of_window
;
899 /* 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. */
900 if (gd_gameplay
.cave
->player_state
==GD_PL_NOT_YET
)
901 gd_gameplay
.out_of_window
=FALSE
;
912 * START NEW GAME DIALOG
914 * show a dialog to the user so he can select the cave to start game at.
918 typedef struct _gd_jump_dialog
{
920 GtkWidget
*combo_cave
;
921 GtkWidget
*spin_level
;
922 GtkWidget
*entry_name
;
927 /* keypress. key_* can be event_type==gdk_key_press, as we
928 connected this function to key press and key release.
931 new_game_keypress_event(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
933 GDJumpDialog
*jump_dialog
=(GDJumpDialog
*)data
;
934 int level
=gtk_range_get_value (GTK_RANGE(jump_dialog
->spin_level
));
936 switch (event
->keyval
) {
941 gtk_range_set_value (GTK_RANGE(jump_dialog
->spin_level
), level
);
947 gtk_range_set_value (GTK_RANGE(jump_dialog
->spin_level
), level
);
950 gtk_dialog_response(GTK_DIALOG(jump_dialog
->dialog
), GTK_RESPONSE_ACCEPT
);
953 return FALSE
; /* if any other key, we did not process it. go on, let gtk handle it. */
959 jump_cave_changed_signal(GtkWidget
*widget
, gpointer data
)
961 GDJumpDialog
*jump_dialog
=(GDJumpDialog
*)data
;
962 GdkPixbuf
*cave_image
;
965 /* loading cave, draw cave and scale to specified size. seed=0 */
966 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);
967 cave_image
=gd_drawcave_to_pixbuf(cave
, 320, 240, TRUE
);
968 gtk_image_set_from_pixbuf(GTK_IMAGE (jump_dialog
->image
), cave_image
);
969 g_object_unref(cave_image
);
971 /* freeing temporary cave data */
976 new_game_cb (const GtkWidget
* widget
, const gpointer data
)
978 static GdString player_name
="";
979 GtkWidget
*table
, *expander
, *eventbox
;
980 GDJumpDialog jump_dialog
;
981 GtkCellRenderer
*renderer
;
985 /* check if caveset is empty! */
986 if (gd_caveset_count()==0) {
987 gd_warningmessage(_("There are no caves in this cave set!"), NULL
);
991 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
);
992 gtk_dialog_set_default_response (GTK_DIALOG(jump_dialog
.dialog
), GTK_RESPONSE_ACCEPT
);
993 gtk_window_set_resizable (GTK_WINDOW(jump_dialog
.dialog
), FALSE
);
995 table
=gtk_table_new(0, 0, FALSE
);
996 gtk_box_pack_start(GTK_BOX (GTK_DIALOG(jump_dialog
.dialog
)->vbox
), table
, FALSE
, FALSE
, 0);
997 gtk_container_set_border_width(GTK_CONTAINER (table
), 6);
998 gtk_table_set_row_spacings(GTK_TABLE(table
), 6);
999 gtk_table_set_col_spacings(GTK_TABLE(table
), 6);
1001 /* name, which will be used for highscore & the like */
1002 gtk_table_attach_defaults(GTK_TABLE(table
), gd_label_new_printf(_("Name:")), 0, 1, 0, 1);
1003 if (g_str_equal(player_name
, ""))
1004 gd_strcpy(player_name
, g_get_real_name());
1005 jump_dialog
.entry_name
=gtk_entry_new();
1006 /* little inconsistency below: max length has unicode characters, while gdstring will have utf-8.
1007 however this does not make too much difference */
1008 gtk_entry_set_max_length(GTK_ENTRY(jump_dialog
.entry_name
), sizeof(GdString
));
1009 gtk_entry_set_activates_default(GTK_ENTRY(jump_dialog
.entry_name
), TRUE
);
1010 gtk_entry_set_text(GTK_ENTRY(jump_dialog
.entry_name
), player_name
);
1011 gtk_table_attach_defaults(GTK_TABLE(table
), jump_dialog
.entry_name
, 1, 2, 0, 1);
1013 gtk_table_attach_defaults(GTK_TABLE(table
), gd_label_new_printf(_("Cave:")), 0, 1, 1, 2);
1015 /* store of caves: cave pointer, cave name, selectable */
1016 store
=gtk_list_store_new(3, G_TYPE_POINTER
, G_TYPE_STRING
, G_TYPE_BOOLEAN
);
1017 for (iter
=gd_caveset
; iter
; iter
=g_list_next (iter
)) {
1018 Cave
*cave
=iter
->data
;
1019 GtkTreeIter treeiter
;
1021 gtk_list_store_insert_with_values (store
, &treeiter
, -1, 0, iter
->data
, 1, cave
->name
, 2, cave
->selectable
|| gd_all_caves_selectable
, -1);
1023 jump_dialog
.combo_cave
=gtk_combo_box_new_with_model (GTK_TREE_MODEL (store
));
1024 g_object_unref(store
);
1026 renderer
=gtk_cell_renderer_text_new();
1027 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(jump_dialog
.combo_cave
), renderer
, TRUE
);
1028 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT(jump_dialog
.combo_cave
), renderer
, "text", 1, "sensitive", 2, NULL
);
1029 /* we put the combo in an event box, so we can receive keypresses on our own */
1030 eventbox
=gtk_event_box_new();
1031 gtk_container_add(GTK_CONTAINER(eventbox
), jump_dialog
.combo_cave
);
1032 gtk_table_attach_defaults(GTK_TABLE(table
), eventbox
, 1, 2, 1, 2);
1034 gtk_table_attach_defaults(GTK_TABLE(table
), gd_label_new_printf(_("Level:")), 0, 1, 2, 3);
1035 jump_dialog
.spin_level
=gtk_hscale_new_with_range(1.0, 5.0, 1.0);
1036 gtk_scale_set_value_pos(GTK_SCALE(jump_dialog
.spin_level
), GTK_POS_LEFT
);
1037 gtk_table_attach_defaults(GTK_TABLE(table
), jump_dialog
.spin_level
, 1, 2, 2, 3);
1039 g_signal_connect(G_OBJECT(jump_dialog
.combo_cave
), "changed", G_CALLBACK(jump_cave_changed_signal
), &jump_dialog
);
1040 gtk_widget_add_events(eventbox
, GDK_KEY_PRESS_MASK
);
1041 g_signal_connect(G_OBJECT(eventbox
), "key_press_event", G_CALLBACK(new_game_keypress_event
), &jump_dialog
);
1042 g_signal_connect(G_OBJECT(jump_dialog
.spin_level
), "value-changed", G_CALLBACK(jump_cave_changed_signal
), &jump_dialog
);
1044 /* this allows the user to select if he wants to see a preview of the cave */
1045 expander
=gtk_expander_new(_("Preview"));
1046 gtk_expander_set_expanded(GTK_EXPANDER (expander
), gd_show_preview
);
1047 gtk_table_attach_defaults(GTK_TABLE(table
), expander
, 0, 2, 3, 4);
1048 jump_dialog
.image
=gtk_image_new();
1049 gtk_container_add(GTK_CONTAINER (expander
), jump_dialog
.image
);
1051 gtk_widget_show_all(jump_dialog
.dialog
);
1052 gtk_widget_grab_focus(jump_dialog
.combo_cave
);
1053 gtk_editable_select_region(GTK_EDITABLE(jump_dialog
.entry_name
), 0, 0);
1054 /* set default and also trigger redrawing */
1055 gtk_combo_box_set_active(GTK_COMBO_BOX(jump_dialog
.combo_cave
), gd_caveset_last_selected
);
1056 gtk_range_set_value(GTK_RANGE(jump_dialog
.spin_level
), 1);
1058 if (gtk_dialog_run (GTK_DIALOG(jump_dialog
.dialog
)) == GTK_RESPONSE_ACCEPT
) {
1059 gd_strcpy(player_name
, gtk_entry_get_text(GTK_ENTRY(jump_dialog
.entry_name
)));
1060 gd_caveset_last_selected
=gtk_combo_box_get_active(GTK_COMBO_BOX(jump_dialog
.combo_cave
));
1061 gd_caveset_last_selected_level
=gtk_range_get_value(GTK_RANGE(jump_dialog
.spin_level
))-1;
1062 new_game (player_name
, gd_caveset_last_selected
, gd_caveset_last_selected_level
);
1064 gd_show_preview
=gtk_expander_get_expanded(GTK_EXPANDER (expander
)); /* remember expander state-even if cancel pressed */
1065 gtk_widget_destroy(jump_dialog
.dialog
);
1070 /* THE MAIN GAME TIMER */
1071 /* called at 50hz */
1072 /* for the gtk version, it seems nicer if we first draw, then scroll. */
1073 /* this is because there is an expose event; scrolling the "old" drawing would draw the old, and then the new. */
1074 /* (this is not true for the sdl version) */
1076 static GTimer
*timer
=NULL
;
1077 static int called
=0;
1081 main_int(gpointer data
)
1083 static gboolean toggle
=FALSE
; /* value irrelevant */
1084 int up
, down
, left
, right
;
1085 GdDirection player_move
;
1092 g_message("%8d. call, avg %gms", called
, g_timer_elapsed(timer
, NULL
)*1000/called
);
1095 if (gd_gameplay
.type
==GD_GAMETYPE_REPLAY
)
1096 gtk_widget_show(main_window
.replay_image_align
);
1098 gtk_widget_hide(main_window
.replay_image_align
);
1104 fire
=key_lctrl
|| key_rctrl
;
1106 /* compare mouse coordinates to player coordinates, and make up movements */
1107 if (gd_mouse_play
&& mouse_cell_x
>=0) {
1108 down
=down
|| (gd_gameplay
.cave
->player_y
<mouse_cell_y
);
1109 up
=up
|| (gd_gameplay
.cave
->player_y
>mouse_cell_y
);
1110 left
=left
|| (gd_gameplay
.cave
->player_x
>mouse_cell_x
);
1111 right
=right
|| (gd_gameplay
.cave
->player_x
<mouse_cell_x
);
1112 fire
=fire
|| mouse_cell_click
;
1115 /* call the game "interrupt" to do all things. */
1116 player_move
=gd_direction_from_keypress(up
, down
, left
, right
);
1117 /* tell the interrupt that 20ms has passed. */
1118 state
=gd_game_main_int(20, player_move
, fire
, key_suicide
, restart
, !paused
&& !gd_gameplay
.out_of_window
, paused
, fast_forward
);
1120 /* the game "interrupt" gives signals to us, which we act upon: update status bar, resize the drawing area... */
1122 case GD_GAME_INVALID_STATE
:
1123 g_assert_not_reached();
1126 case GD_GAME_CAVE_LOADED
:
1127 gd_select_pixbuf_colors(gd_gameplay
.cave
->color0
, gd_gameplay
.cave
->color1
, gd_gameplay
.cave
->color2
, gd_gameplay
.cave
->color3
, gd_gameplay
.cave
->color4
, gd_gameplay
.cave
->color5
);
1128 init_mainwindow(gd_gameplay
.cave
);
1130 restart
=FALSE
; /* so we do not remember the restart key from a previous cave run */
1133 case GD_GAME_NO_MORE_LIVES
: /* <- only used by sdl version */
1134 case GD_GAME_NOTHING
:
1135 /* normally continue. */
1138 case GD_GAME_LABELS_CHANGED
:
1139 case GD_GAME_TIMEOUT_NOW
: /* <- maybe we should do something else for this */
1140 /* normal, but we are told that the labels (score, ...) might have changed. */
1145 gd_main_stop_game();
1147 return FALSE
; /* do not call again - it will be created later */
1149 case GD_GAME_GAME_OVER
:
1150 gd_main_stop_game();
1151 if (gd_is_highscore(gd_caveset_data
->highscore
, gd_gameplay
.player_score
))
1152 game_over_highscore(); /* achieved a high score! */
1154 game_over_without_highscore(); /* no high score */
1156 return FALSE
; /* do not call again - it will be created later */
1159 /* if fine scrolling, drawing is called at a 50hz rate. */
1160 /* if not, only at a 25hz rate */
1163 /* do the scrolling at the given interval. */
1164 /* but only if the drawing area already exists. */
1165 if (main_window
.drawing_area
&& (gd_fine_scroll
|| toggle
)) {
1170 return TRUE
; /* call again */
1173 /* this is a simple wrapper which makes the main_int to be callable as an idle func. */
1174 /* when used as an idle func by the thread routine, it should run only once for every g_idle_add */
1176 main_int_return_false_wrapper(gpointer data
)
1182 /* this function will run in its own thread, and add the main int as an idle func in every 20ms */
1184 timer_thread(gpointer data
)
1186 int interval_msec
=20;
1188 /* wait before we first call it */
1189 g_usleep(interval_msec
*1000);
1190 while (!quit_thread
) {
1191 /* add processing as an idle func */
1192 /* no need to lock the main loop context, as glib does that automatically */
1193 g_idle_add(main_int_return_false_wrapper
, data
);
1195 g_usleep(interval_msec
*1000);
1201 install_game_timer()
1206 /* remove timer, if it is installed for some reason */
1207 uninstall_game_timeout();
1210 gtk_window_present(GTK_WINDOW(main_window
.window
));
1214 timer
=g_timer_new();
1217 /* this makes the main int load the first cave, and then we do the drawing. */
1218 main_int(main_window
.window
);
1219 gdk_window_process_all_updates();
1220 /* after that, install timer. create a thread with higher priority than normal: */
1221 /* so its priority will be higher than the main thread, which does the drawing etc. */
1222 /* if the scheduling thread wants to do something, it gets processed first. this makes */
1223 /* the intervals more even. */
1225 #ifdef G_THREADS_ENABLED
1226 thread
=g_thread_create_full(timer_thread
, main_window
.window
, 0, FALSE
, FALSE
, G_THREAD_PRIORITY_HIGH
, &error
);
1228 thread
=NULL
; /* if glib without thread support, we will use timeout source. */
1231 /* if unable to create thread */
1233 g_critical("%s", error
->message
);
1234 g_error_free(error
);
1236 /* use the main int as a timeout routine. */
1237 g_timeout_add(20, main_int
, main_window
.window
);
1241 g_timer_start(timer
);
1247 highscore_cb(GtkWidget
*widget
, gpointer data
)
1249 gd_show_highscore(main_window
.window
, NULL
, FALSE
, NULL
, -1);
1255 show_errors_cb(GtkWidget
*widget
, gpointer data
)
1257 gtk_widget_hide(main_window
.error_hbox
); /* if the user is presented the error list, the label is to be hidden */
1258 gd_show_errors(main_window
.window
);
1264 gd_open_cave_editor();
1265 /* to be sure no cave is playing. */
1266 /* this will also stop music. to be called after opening editor window, so the music stops. */
1267 init_mainwindow(NULL
);
1270 /* called from the menu when a recent file is activated. */
1272 recent_chooser_activated_cb(GtkRecentChooser
*chooser
, gpointer data
)
1274 GtkRecentInfo
*current
;
1275 char *filename_utf8
, *filename
;
1277 current
=gtk_recent_chooser_get_current_item(chooser
);
1278 /* we do not support non-local files */
1279 if (!gtk_recent_info_is_local(current
)) {
1282 display_name
=gtk_recent_info_get_uri_display(current
);
1283 gd_errormessage(_("GDash cannot load file from a network link."), display_name
);
1284 g_free(display_name
);
1288 /* if the edited caveset is to be saved, but user cancels */
1289 if (!gd_discard_changes(main_window
.window
))
1292 filename_utf8
=gtk_recent_info_get_uri_display(current
);
1293 filename
=g_filename_from_utf8(filename_utf8
, -1, NULL
, NULL
, NULL
);
1294 /* ask for save first? */
1295 gd_open_caveset_in_ui(filename
, gd_use_bdcff_highscore
);
1297 /* things to do after loading. */
1298 gd_main_window_set_title();
1299 if (gd_has_new_error())
1300 gd_show_last_error(main_window
.window
);
1302 gd_infomessage(_("Loaded caveset from file:"), filename_utf8
);
1305 g_free(filename_utf8
);
1309 enum _replay_fields
{
1310 COL_REPLAY_CAVE_POINTER
,
1311 COL_REPLAY_REPLAY_POINTER
,
1312 COL_REPLAY_NAME
, /* cave or player name */
1318 COL_REPLAY_VISIBLE
, /* set to true for replay lines, false for cave lines. so "saved" toggle and comment are not visible. */
1323 show_replays_tree_view_row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
, GtkTreeViewColumn
*column
, gpointer user_data
)
1325 GtkTreeModel
*model
=gtk_tree_view_get_model(view
);
1330 gtk_tree_model_get_iter(model
, &iter
, path
);
1331 gtk_tree_model_get(model
, &iter
, COL_REPLAY_CAVE_POINTER
, &cave
, COL_REPLAY_REPLAY_POINTER
, &replay
, -1);
1332 if (cave
!=NULL
&& replay
!=NULL
)
1333 gd_main_new_game_replay(cave
, replay
);
1337 show_replays_response_cb(GtkDialog
*dialog
, int response_id
, gpointer data
)
1339 gtk_widget_destroy(GTK_WIDGET(dialog
));
1343 replay_comment_edited(GtkCellRendererText
*cell
, const gchar
*path_string
, const gchar
*new_text
, gpointer data
)
1345 GtkTreeModel
*model
=(GtkTreeModel
*)data
;
1346 GtkTreePath
*path
=gtk_tree_path_new_from_string(path_string
);
1351 gtk_tree_model_get_iter (model
, &iter
, path
);
1352 gtk_tree_model_get(model
, &iter
, COL_REPLAY_REPLAY_POINTER
, &replay
, -1);
1353 /* if not available, maybe the user edited a cave line? do nothing. */
1357 gtk_tree_store_set(GTK_TREE_STORE(model
), &iter
, COL_REPLAY_COMMENT
, new_text
, -1);
1358 gd_strcpy(replay
->comment
, new_text
);
1359 /* if this is a saved replay, now the caveset is edited. */
1361 gd_caveset_edited
=TRUE
;
1365 replay_saved_toggled(GtkCellRendererText
*cell
, const gchar
*path_string
, gpointer data
)
1367 GtkTreeModel
*model
=(GtkTreeModel
*)data
;
1368 GtkTreePath
*path
=gtk_tree_path_new_from_string(path_string
);
1373 gtk_tree_model_get_iter (model
, &iter
, path
);
1374 gtk_tree_model_get(model
, &iter
, COL_REPLAY_REPLAY_POINTER
, &replay
, -1);
1375 /* if not available, maybe the user edited a cave line? do nothing. */
1379 replay
->saved
=!replay
->saved
;
1380 gtk_tree_store_set(GTK_TREE_STORE(model
), &iter
, COL_REPLAY_SAVED
, replay
->saved
, -1);
1381 /* we selected or unselected a replay for saving - the caveset is now edited. */
1382 gd_caveset_edited
=TRUE
;
1386 replay_play_button_clicked_cb(GtkWidget
*widget
, gpointer data
)
1388 GtkTreeView
*view
=GTK_TREE_VIEW(data
);
1389 GtkTreeSelection
*selection
=gtk_tree_view_get_selection(view
);
1390 GtkTreeModel
*model
;
1391 gboolean got_selected
;
1396 got_selected
=gtk_tree_selection_get_selected(selection
, &model
, &iter
);
1397 if (!got_selected
) /* if nothing selected, return */
1400 gtk_tree_model_get(model
, &iter
, COL_REPLAY_CAVE_POINTER
, &cave
, COL_REPLAY_REPLAY_POINTER
, &replay
, -1);
1401 if (cave
!=NULL
&& replay
!=NULL
)
1402 gd_main_new_game_replay(cave
, replay
);
1405 /* enables or disables play button on selection change */
1407 replay_tree_view_selection_changed(GtkTreeSelection
*selection
, gpointer data
)
1409 GtkWidget
*button
=GTK_WIDGET(data
);
1410 GtkTreeModel
*model
;
1414 if (gtk_tree_selection_get_selected(selection
, &model
, &iter
)) {
1417 gtk_tree_model_get(model
, &iter
, COL_REPLAY_REPLAY_POINTER
, &replay
, -1);
1418 enable
=replay
!=NULL
;
1422 gtk_widget_set_sensitive(button
, enable
);
1428 show_replays_cb(GtkWidget
*widget
, gpointer data
)
1430 static GtkWidget
*dialog
=NULL
;
1431 GtkWidget
*scroll
, *view
, *button
;
1433 GtkTreeStore
*model
;
1434 GtkCellRenderer
*renderer
;
1436 /* if window already open, just show it and return */
1438 gtk_window_present(GTK_WINDOW(dialog
));
1442 dialog
=gtk_dialog_new_with_buttons(_("Replays"), GTK_WINDOW(main_window
.window
), 0, NULL
);
1443 g_signal_connect(G_OBJECT(dialog
), "destroy", G_CALLBACK(gtk_widget_destroyed
), &dialog
);
1444 g_signal_connect(G_OBJECT(dialog
), "response", G_CALLBACK(show_replays_response_cb
), NULL
);
1445 gtk_window_set_default_size(GTK_WINDOW(dialog
), 480, 360);
1446 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog
)->vbox
), 6);
1448 gd_dialog_add_hint(GTK_DIALOG(dialog
), _("Hint: When watching a replay, you can use the usual movement keys (left, right...) to "
1449 "stop the replay and immediately continue the playing of the cave yourself."));
1451 /* scrolled window to show replays tree view */
1452 scroll
=gtk_scrolled_window_new(NULL
, NULL
);
1453 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(scroll
), GTK_SHADOW_ETCHED_IN
);
1454 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog
)->vbox
), scroll
);
1456 /* create store containing replays */
1457 model
=gtk_tree_store_new(COL_REPLAY_MAX
, G_TYPE_POINTER
, G_TYPE_POINTER
, G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_BOOLEAN
, G_TYPE_STRING
, G_TYPE_BOOLEAN
);
1458 for (iter
=gd_caveset
; iter
!=NULL
; iter
=iter
->next
) {
1460 Cave
*cave
=(Cave
*)iter
->data
;
1462 /* if the cave has replays */
1463 if (cave
->replays
) {
1464 GtkTreeIter caveiter
;
1466 gtk_tree_store_append(model
, &caveiter
, NULL
);
1467 gtk_tree_store_set(model
, &caveiter
, COL_REPLAY_NAME
, cave
->name
, -1);
1469 /* add each replay */
1470 for (replayiter
=cave
->replays
; replayiter
!=NULL
; replayiter
=replayiter
->next
) {
1472 GdReplay
*replay
=(GdReplay
*)replayiter
->data
;
1475 /* we have to store the score as string, as for the cave lines the unset score field would also show zero */
1476 g_snprintf(score
, sizeof(score
), "%d", replay
->score
);
1477 gtk_tree_store_append(model
, &riter
, &caveiter
);
1478 gtk_tree_store_set(model
, &riter
, COL_REPLAY_CAVE_POINTER
, cave
, COL_REPLAY_REPLAY_POINTER
, replay
, COL_REPLAY_NAME
, replay
->player_name
,
1479 COL_REPLAY_DATE
, replay
->date
, COL_REPLAY_SCORE
, score
, COL_REPLAY_SUCCESS
, replay
->success
?GTK_STOCK_YES
:GTK_STOCK_NO
,
1480 COL_REPLAY_COMMENT
, replay
->comment
, COL_REPLAY_SAVED
, replay
->saved
, COL_REPLAY_VISIBLE
, TRUE
, -1);
1485 view
=gtk_tree_view_new_with_model(GTK_TREE_MODEL(model
)); /* create tree view which will show data */
1486 gtk_tree_view_expand_all(GTK_TREE_VIEW(view
));
1487 gtk_container_add(GTK_CONTAINER(scroll
), view
);
1489 renderer
=gtk_cell_renderer_text_new();
1490 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
), 0, _("Name"), renderer
, "text", COL_REPLAY_NAME
, NULL
); /* 0 = column number */
1491 gtk_tree_view_column_set_expand(gtk_tree_view_get_column(GTK_TREE_VIEW(view
), 0), TRUE
); /* name column expands */
1493 renderer
=gtk_cell_renderer_text_new();
1494 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
), 1, _("Date"), renderer
, "text", COL_REPLAY_DATE
, NULL
); /* 1 = column number */
1496 renderer
=gtk_cell_renderer_pixbuf_new();
1497 g_object_set(G_OBJECT(renderer
), "stock-size", GTK_ICON_SIZE_MENU
, NULL
);
1498 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
), 2, NULL
, renderer
, "stock-id", COL_REPLAY_SUCCESS
, NULL
);
1500 renderer
=gtk_cell_renderer_text_new();
1501 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
), 3, _("Score"), renderer
, "text", COL_REPLAY_SCORE
, NULL
);
1503 renderer
=gtk_cell_renderer_text_new();
1504 g_object_set(G_OBJECT(renderer
), "editable", TRUE
, "ellipsize", PANGO_ELLIPSIZE_END
, NULL
);
1505 g_signal_connect(renderer
, "edited", G_CALLBACK(replay_comment_edited
), model
);
1506 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
), 4, _("Comment"), renderer
, "text", COL_REPLAY_COMMENT
, "visible", COL_REPLAY_VISIBLE
, NULL
);
1507 /* doubleclick will play the replay */
1508 g_signal_connect(G_OBJECT(view
), "row-activated", G_CALLBACK(show_replays_tree_view_row_activated_cb
), NULL
);
1509 gtk_tree_view_column_set_expand(gtk_tree_view_get_column(GTK_TREE_VIEW(view
), 4), TRUE
); /* name column expands */
1511 renderer
=gtk_cell_renderer_toggle_new();
1512 g_signal_connect(renderer
, "toggled", G_CALLBACK(replay_saved_toggled
), model
);
1513 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
), 5, _("Saved"), renderer
, "active", COL_REPLAY_SAVED
, "visible", COL_REPLAY_VISIBLE
, NULL
);
1516 button
=gtk_button_new_from_stock(GTK_STOCK_MEDIA_PLAY
);
1517 gtk_widget_set_sensitive(button
, FALSE
); /* not sensitive by default. when the user selects a line, it will be enabled */
1518 g_signal_connect(G_OBJECT(button
), "clicked", G_CALLBACK(replay_play_button_clicked_cb
), view
);
1519 g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(view
))), "changed", G_CALLBACK(replay_tree_view_selection_changed
), button
);
1520 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog
)->action_area
), button
);
1522 /* this must be added after the play button, so it is the rightmost one */
1523 gtk_dialog_add_button(GTK_DIALOG(dialog
), GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
);
1524 gtk_dialog_set_default_response(GTK_DIALOG(dialog
), GTK_RESPONSE_CLOSE
);
1526 gtk_widget_show_all(dialog
);
1534 /* a cave name is selected, update the text boxt with current cave's data */
1536 cave_info_combo_changed(GtkComboBox
*widget
, gpointer data
)
1538 GtkTextBuffer
*buffer
=GTK_TEXT_BUFFER(data
);
1542 /* clear text buffer */
1543 gtk_text_buffer_set_text(buffer
, "", -1);
1544 gtk_text_buffer_get_iter_at_offset (buffer
, &iter
, 0);
1546 i
=gtk_combo_box_get_active(widget
);
1549 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, gd_caveset_data
->name
, -1, "heading", NULL
);
1550 gtk_text_buffer_insert(buffer
, &iter
, "\n\n", -1);
1552 /* show properties with title only if they are not empty string */
1553 if (!g_str_equal(gd_caveset_data
->description
, "")) {
1554 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Description: "), -1, "name", NULL
);
1555 gtk_text_buffer_insert(buffer
, &iter
, gd_caveset_data
->description
, -1);
1556 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1558 if (!g_str_equal(gd_caveset_data
->author
, "")) {
1559 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Author: "), -1, "name", NULL
);
1560 gtk_text_buffer_insert(buffer
, &iter
, gd_caveset_data
->author
, -1);
1561 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1563 if (!g_str_equal(gd_caveset_data
->date
, "")) {
1564 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Date: "), -1, "name", NULL
);
1565 gtk_text_buffer_insert(buffer
, &iter
, gd_caveset_data
->date
, -1);
1566 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1568 if (!g_str_equal(gd_caveset_data
->difficulty
, "")) {
1569 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Diffuculty: "), -1, "name", NULL
);
1570 gtk_text_buffer_insert(buffer
, &iter
, gd_caveset_data
->difficulty
, -1);
1571 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1573 if (!g_str_equal(gd_caveset_data
->remark
, "")) {
1574 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Remark: "), -1, "name", NULL
);
1575 gtk_text_buffer_insert(buffer
, &iter
, gd_caveset_data
->remark
, -1);
1576 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1578 if (!g_str_equal(gd_caveset_data
->notes
->str
, "")) {
1579 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Notes:\n"), -1, "name", NULL
);
1580 gtk_text_buffer_insert(buffer
, &iter
, gd_caveset_data
->notes
->str
, -1);
1581 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1586 Cave
*cave
=gd_return_nth_cave(i
-1);
1588 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, cave
->name
, -1, "heading", NULL
);
1589 gtk_text_buffer_insert(buffer
, &iter
, "\n\n", -1);
1591 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Type: "), -1, "name", NULL
);
1592 gtk_text_buffer_insert(buffer
, &iter
, cave
->intermission
?_("Intermission"):_("Normal cave"), -1);
1593 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1595 /* show properties with title only if they are not empty string */
1596 if (!g_str_equal(cave
->description
, "")) {
1597 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Description: "), -1, "name", NULL
);
1598 gtk_text_buffer_insert(buffer
, &iter
, cave
->description
, -1);
1599 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1601 if (!g_str_equal(cave
->author
, "")) {
1602 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Author: "), -1, "name", NULL
);
1603 gtk_text_buffer_insert(buffer
, &iter
, cave
->author
, -1);
1604 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1606 if (!g_str_equal(cave
->date
, "")) {
1607 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Date: "), -1, "name", NULL
);
1608 gtk_text_buffer_insert(buffer
, &iter
, cave
->date
, -1);
1609 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1611 if (!g_str_equal(cave
->difficulty
, "")) {
1612 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Difficulty: "), -1, "name", NULL
);
1613 gtk_text_buffer_insert(buffer
, &iter
, cave
->difficulty
, -1);
1614 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1616 if (!g_str_equal(cave
->remark
, "")) {
1617 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Remark: "), -1, "name", NULL
);
1618 gtk_text_buffer_insert(buffer
, &iter
, cave
->remark
, -1);
1619 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1621 if (!g_str_equal(cave
->notes
->str
, "")) {
1622 gtk_text_buffer_insert_with_tags_by_name(buffer
, &iter
, _("Notes:\n"), -1, "name", NULL
);
1623 gtk_text_buffer_insert(buffer
, &iter
, cave
->notes
->str
, -1);
1624 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
1630 /* show info about cave or caveset */
1632 cave_info_cb(GtkWidget
*widget
, gpointer data
)
1634 GtkWidget
*dialog
, *view
, *sw
, *combo
;
1637 GtkTextBuffer
*buffer
;
1639 gboolean paused_save
;
1642 dialog
=gtk_dialog_new_with_buttons(_("Caveset information"), GTK_WINDOW(main_window
.window
),
1643 GTK_DIALOG_DESTROY_WITH_PARENT
| GTK_DIALOG_MODAL
| GTK_DIALOG_NO_SEPARATOR
,
1644 GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
,
1646 gtk_dialog_set_default_response(GTK_DIALOG(dialog
), GTK_RESPONSE_CLOSE
);
1647 gtk_window_set_default_size(GTK_WINDOW(dialog
), 360, 480);
1649 /* create a combo box. 0=caveset, 1, 2, 3... = caves */
1650 combo
=gtk_combo_box_new_text();
1651 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog
)->vbox
), combo
, FALSE
, FALSE
, 6);
1652 text
=g_strdup_printf("[%s]", gd_caveset_data
->name
); /* caveset name = line 0 */
1653 gtk_combo_box_append_text(GTK_COMBO_BOX(combo
), text
);
1655 for (citer
=gd_caveset
; citer
!=NULL
; citer
=citer
->next
) {
1656 Cave
*c
=citer
->data
;
1658 gtk_combo_box_append_text(GTK_COMBO_BOX(combo
), c
->name
);
1661 /* create text buffer */
1662 buffer
=gtk_text_buffer_new(NULL
);
1663 gtk_text_buffer_get_iter_at_offset (buffer
, &iter
, 0);
1664 gtk_text_buffer_create_tag (buffer
, "heading", "weight", PANGO_WEIGHT_BOLD
, "scale", PANGO_SCALE_X_LARGE
, NULL
);
1665 gtk_text_buffer_create_tag (buffer
, "name", "weight", PANGO_WEIGHT_BOLD
, NULL
);
1666 gtk_text_buffer_insert_with_tags_by_name (buffer
, &iter
, "GDash " PACKAGE_VERSION
"\n\n", -1, "heading", NULL
);
1668 sw
=gtk_scrolled_window_new(NULL
, NULL
);
1669 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw
), GTK_SHADOW_ETCHED_IN
);
1670 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
1671 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog
)->vbox
), sw
);
1672 view
=gtk_text_view_new_with_buffer(buffer
);
1673 gtk_container_add(GTK_CONTAINER (sw
), view
);
1674 g_object_unref(buffer
);
1675 gtk_text_view_set_editable (GTK_TEXT_VIEW (view
), FALSE
);
1676 gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view
), FALSE
);
1677 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view
), GTK_WRAP_WORD
);
1678 gtk_text_view_set_pixels_above_lines (GTK_TEXT_VIEW (view
), 3);
1679 gtk_text_view_set_left_margin (GTK_TEXT_VIEW (view
), 6);
1680 gtk_text_view_set_right_margin (GTK_TEXT_VIEW (view
), 6);
1682 g_signal_connect(G_OBJECT(combo
), "changed", G_CALLBACK(cave_info_combo_changed
), buffer
);
1683 /* select current cave. if no current cave, or not in the list, g_list_index will returns -1. */
1684 /* we must add +1 anyway, so in that cave it will be 0 -> caveset info. :) */
1685 gtk_combo_box_set_active(GTK_COMBO_BOX(combo
), g_list_index(gd_caveset
, gd_gameplay
.original_cave
)+1);
1687 gtk_widget_show_all(dialog
);
1689 paused
=TRUE
; /* set paused game, so it stops while the users sees the message box */
1690 gtk_dialog_run(GTK_DIALOG(dialog
));
1692 gtk_widget_destroy(dialog
);
1698 * Creates main window
1703 gd_create_main_window(void)
1706 static GtkActionEntry action_entries_normal
[]={
1707 {"PlayMenu", NULL
, N_("_Play")},
1708 {"FileMenu", NULL
, N_("_File")},
1709 {"SettingsMenu", NULL
, N_("_Settings")},
1710 {"HelpMenu", NULL
, N_("_Help")},
1711 {"Quit", GTK_STOCK_QUIT
, NULL
, NULL
, NULL
, G_CALLBACK(quit_cb
)},
1712 {"About", GTK_STOCK_ABOUT
, NULL
, NULL
, NULL
, G_CALLBACK(about_cb
)},
1713 {"Errors", GTK_STOCK_DIALOG_ERROR
, N_("_Error console"), NULL
, NULL
, G_CALLBACK(show_errors_cb
)},
1714 {"Help", GTK_STOCK_HELP
, NULL
, NULL
, NULL
, G_CALLBACK(help_cb
)},
1715 {"CaveInfo", GTK_STOCK_DIALOG_INFO
, N_("Caveset _information"), NULL
, N_("Show information about the game and its caves"), G_CALLBACK(cave_info_cb
)},
1718 static GtkActionEntry action_entries_title
[]={
1719 {"GamePreferences", GTK_STOCK_PREFERENCES
, NULL
, NULL
, NULL
, G_CALLBACK(preferences_cb
)},
1720 {"GameControlSettings", GD_ICON_KEYBOARD
, N_("_Control keys"), NULL
, NULL
, G_CALLBACK(control_settings_cb
)},
1721 {"NewGame", GTK_STOCK_MEDIA_PLAY
, N_("_New game"), "<control>N", N_("Start new game"), G_CALLBACK(new_game_cb
)},
1722 {"CaveEditor", GD_ICON_CAVE_EDITOR
, N_("Cave _editor"), NULL
, NULL
, G_CALLBACK(cave_editor_cb
)},
1723 {"OpenFile", GTK_STOCK_OPEN
, NULL
, NULL
, NULL
, G_CALLBACK(open_caveset_cb
)},
1724 {"LoadInternal", GTK_STOCK_INDEX
, N_("Load _internal game")},
1725 {"LoadRecent", GTK_STOCK_DIALOG_INFO
, N_("Open _recent")},
1726 {"OpenCavesDir", GTK_STOCK_CDROM
, N_("O_pen shipped"), NULL
, NULL
, G_CALLBACK(open_caveset_dir_cb
)},
1727 {"SaveFile", GTK_STOCK_SAVE
, NULL
, NULL
, NULL
, G_CALLBACK(save_caveset_cb
)},
1728 {"SaveAsFile", GTK_STOCK_SAVE_AS
, NULL
, NULL
, NULL
, G_CALLBACK(save_caveset_as_cb
)},
1729 {"HighScore", GD_ICON_AWARD
, N_("Hi_ghscores"), NULL
, NULL
, G_CALLBACK(highscore_cb
)},
1730 {"ShowReplays", GD_ICON_REPLAY
, N_("Show _replays"), NULL
, NULL
, G_CALLBACK(show_replays_cb
)},
1733 static GtkActionEntry action_entries_game
[]={
1734 {"TakeSnapshot", GD_ICON_SNAPSHOT
, N_("_Take snapshot"), "F5", NULL
, G_CALLBACK(save_snapshot_cb
)},
1735 {"Restart", GD_ICON_RESTART_LEVEL
, N_("Re_start level"), "Escape", N_("Restart current level"), G_CALLBACK(restart_level_cb
)},
1736 {"EndGame", GTK_STOCK_STOP
, N_("_End game"), "F1", N_("End current game"), G_CALLBACK(stop_game_cb
)},
1739 static GtkActionEntry action_entries_snapshot
[]={
1740 {"RevertToSnapshot", GTK_STOCK_UNDO
, N_("_Revert to snapshot"), "F6", NULL
, G_CALLBACK(load_snapshot_cb
)},
1743 static GtkToggleActionEntry action_entries_toggle
[]={
1744 {"PauseGame", GTK_STOCK_MEDIA_PAUSE
, NULL
, "space", N_("Pause game"), G_CALLBACK(toggle_pause_cb
), FALSE
},
1745 {"FullScreen", GTK_STOCK_FULLSCREEN
, NULL
, "F11", N_("Fullscreen mode during play"), G_CALLBACK(toggle_fullscreen_cb
), FALSE
},
1746 {"FastForward", GTK_STOCK_MEDIA_FORWARD
, N_("Fast for_ward"), "<control>F", N_("Fast forward"), G_CALLBACK(toggle_fast_cb
), FALSE
},
1749 static const char *ui_info
=
1751 "<menubar name='MenuBar'>"
1752 "<menu action='FileMenu'>"
1754 "<menuitem action='OpenFile'/>"
1755 "<menuitem action='LoadRecent'/>"
1756 "<menuitem action='OpenCavesDir'/>"
1757 "<menuitem action='LoadInternal'/>"
1759 "<menuitem action='CaveEditor'/>"
1761 "<menuitem action='SaveFile'/>"
1762 "<menuitem action='SaveAsFile'/>"
1764 "<menuitem action='Quit'/>"
1766 "<menu action='PlayMenu'>"
1767 "<menuitem action='NewGame'/>"
1768 "<menuitem action='PauseGame'/>"
1769 "<menuitem action='FastForward'/>"
1770 "<menuitem action='TakeSnapshot'/>"
1771 "<menuitem action='RevertToSnapshot'/>"
1772 "<menuitem action='Restart'/>"
1773 "<menuitem action='EndGame'/>"
1775 "<menuitem action='CaveInfo'/>"
1776 "<menuitem action='HighScore'/>"
1777 "<menuitem action='ShowReplays'/>"
1779 "<menuitem action='FullScreen'/>"
1780 "<menuitem action='GameControlSettings'/>"
1781 "<menuitem action='GamePreferences'/>"
1783 "<menu action='HelpMenu'>"
1784 "<menuitem action='Help'/>"
1786 "<menuitem action='Errors'/>"
1787 "<menuitem action='About'/>"
1791 "<toolbar name='ToolBar'>"
1792 "<toolitem action='NewGame'/>"
1793 "<toolitem action='EndGame'/>"
1794 "<toolitem action='FullScreen'/>"
1796 "<toolitem action='PauseGame'/>"
1797 "<toolitem action='FastForward'/>"
1798 "<toolitem action='Restart'/>"
1800 "<toolitem action='CaveInfo'/>"
1804 GtkWidget
*vbox
, *hbox
, *recent_chooser
;
1805 GtkRecentFilter
*recent_filter
;
1809 const gchar
**names
;
1813 gtk_window_set_default_icon (logo
);
1814 g_object_unref(logo
);
1816 main_window
.window
=gtk_window_new(GTK_WINDOW_TOPLEVEL
);
1817 gtk_window_set_default_size(GTK_WINDOW(main_window
.window
), 360, 300);
1818 g_signal_connect(G_OBJECT(main_window
.window
), "focus_out_event", G_CALLBACK(focus_out_event
), NULL
);
1819 g_signal_connect(G_OBJECT(main_window
.window
), "delete_event", G_CALLBACK(delete_event
), NULL
);
1820 g_signal_connect(G_OBJECT(main_window
.window
), "key_press_event", G_CALLBACK(keypress_event
), NULL
);
1821 g_signal_connect(G_OBJECT(main_window
.window
), "key_release_event", G_CALLBACK(keypress_event
), NULL
);
1824 vbox
=gtk_vbox_new(FALSE
, 0);
1825 gtk_container_add (GTK_CONTAINER (main_window
.window
), vbox
);
1828 main_window
.actions_normal
=gtk_action_group_new("main_window.actions_normal");
1829 gtk_action_group_set_translation_domain (main_window
.actions_normal
, PACKAGE
);
1830 gtk_action_group_add_actions (main_window
.actions_normal
, action_entries_normal
, G_N_ELEMENTS (action_entries_normal
), &main_window
);
1831 gtk_action_group_add_toggle_actions (main_window
.actions_normal
, action_entries_toggle
, G_N_ELEMENTS (action_entries_toggle
), NULL
);
1832 main_window
.actions_title
=gtk_action_group_new("main_window.actions_title");
1833 gtk_action_group_set_translation_domain (main_window
.actions_title
, PACKAGE
);
1834 gtk_action_group_add_actions (main_window
.actions_title
, action_entries_title
, G_N_ELEMENTS (action_entries_title
), &main_window
);
1835 /* make this toolbar button always have a title */
1836 g_object_set (gtk_action_group_get_action (main_window
.actions_title
, "NewGame"), "is_important", TRUE
, NULL
);
1837 main_window
.actions_game
=gtk_action_group_new("main_window.actions_game");
1838 gtk_action_group_set_translation_domain (main_window
.actions_game
, PACKAGE
);
1839 gtk_action_group_add_actions (main_window
.actions_game
, action_entries_game
, G_N_ELEMENTS (action_entries_game
), &main_window
);
1840 main_window
.actions_snapshot
=gtk_action_group_new("main_window.actions_snapshot");
1841 gtk_action_group_set_translation_domain (main_window
.actions_snapshot
, PACKAGE
);
1842 gtk_action_group_add_actions (main_window
.actions_snapshot
, action_entries_snapshot
, G_N_ELEMENTS (action_entries_snapshot
), &main_window
);
1845 ui
=gtk_ui_manager_new();
1846 gtk_ui_manager_insert_action_group (ui
, main_window
.actions_normal
, 0);
1847 gtk_ui_manager_insert_action_group (ui
, main_window
.actions_title
, 0);
1848 gtk_ui_manager_insert_action_group (ui
, main_window
.actions_game
, 0);
1849 gtk_ui_manager_insert_action_group (ui
, main_window
.actions_snapshot
, 0);
1850 gtk_window_add_accel_group (GTK_WINDOW(main_window
.window
), gtk_ui_manager_get_accel_group (ui
));
1851 gtk_ui_manager_add_ui_from_string (ui
, ui_info
, -1, NULL
);
1852 main_window
.menubar
=gtk_ui_manager_get_widget (ui
, "/MenuBar");
1853 gtk_box_pack_start(GTK_BOX (vbox
), main_window
.menubar
, FALSE
, FALSE
, 0);
1854 main_window
.toolbar
=gtk_ui_manager_get_widget (ui
, "/ToolBar");
1855 gtk_toolbar_set_style(GTK_TOOLBAR(main_window
.toolbar
), GTK_TOOLBAR_BOTH_HORIZ
);
1856 gtk_box_pack_start(GTK_BOX (vbox
), main_window
.toolbar
, FALSE
, FALSE
, 0);
1857 gtk_toolbar_set_tooltips (GTK_TOOLBAR (main_window
.toolbar
), TRUE
);
1859 /* make a submenu, which contains the games compiled in. */
1861 menu
=gtk_menu_new();
1862 gtk_menu_item_set_submenu (GTK_MENU_ITEM (gtk_ui_manager_get_widget (ui
, "/MenuBar/FileMenu/LoadInternal")), menu
);
1863 names
=gd_caveset_get_internal_game_names ();
1865 GtkWidget
*menuitem
=gtk_menu_item_new_with_label (names
[i
]);
1867 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menuitem
);
1868 gtk_widget_show(menuitem
);
1869 g_signal_connect (G_OBJECT(menuitem
), "activate", G_CALLBACK(load_internal_cb
), GINT_TO_POINTER (i
));
1873 /* recent file chooser */
1874 recent_chooser
=gtk_recent_chooser_menu_new();
1875 recent_filter
=gtk_recent_filter_new();
1876 /* gdash file extensions */
1877 for (i
=0; gd_caveset_extensions
[i
]!=NULL
; i
++)
1878 gtk_recent_filter_add_pattern(recent_filter
, gd_caveset_extensions
[i
]);
1879 gtk_recent_chooser_add_filter(GTK_RECENT_CHOOSER(recent_chooser
), recent_filter
);
1880 gtk_menu_item_set_submenu (GTK_MENU_ITEM (gtk_ui_manager_get_widget (ui
, "/MenuBar/FileMenu/LoadRecent")), recent_chooser
);
1881 g_signal_connect(G_OBJECT(recent_chooser
), "item-activated", G_CALLBACK(recent_chooser_activated_cb
), NULL
);
1885 /* this hbox will contain the replay logo and the labels */
1886 hbox
=gtk_hbox_new(FALSE
, 6);
1887 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
1889 /* replay logo... */
1890 main_window
.replay_image_align
=gtk_alignment_new(0.5, 0.5, 0, 0);
1891 gtk_box_pack_start(GTK_BOX(hbox
), main_window
.replay_image_align
, FALSE
, FALSE
, 0);
1892 gtk_container_add(GTK_CONTAINER(main_window
.replay_image_align
), gtk_image_new_from_stock(GD_ICON_REPLAY
, GTK_ICON_SIZE_LARGE_TOOLBAR
));
1895 main_window
.labels
=gtk_vbox_new(FALSE
, 0);
1896 gtk_box_pack_start_defaults(GTK_BOX(hbox
), main_window
.labels
);
1898 /* first hbox for labels ABOVE drawing */
1899 hbox
=gtk_hbox_new(FALSE
, 12);
1900 gtk_box_pack_start(GTK_BOX (main_window
.labels
), hbox
, FALSE
, FALSE
, 0);
1901 main_window
.label_cave_name
=gtk_label_new(NULL
); /* NAME label */
1902 gtk_label_set_ellipsize (GTK_LABEL(main_window
.label_cave_name
), PANGO_ELLIPSIZE_END
);
1903 gtk_box_pack_start_defaults(GTK_BOX (hbox
), main_window
.label_cave_name
); /* NAME label */
1904 gtk_misc_set_alignment(GTK_MISC(main_window
.label_cave_name
), 0, 0.5);
1905 gtk_box_pack_start(GTK_BOX(hbox
), main_window
.label_key3
=gtk_label_new(NULL
), FALSE
, FALSE
, 0); /* key3 label */
1906 gtk_box_pack_start(GTK_BOX(hbox
), main_window
.label_key2
=gtk_label_new(NULL
), FALSE
, FALSE
, 0); /* key2 label */
1907 gtk_box_pack_start(GTK_BOX(hbox
), main_window
.label_key1
=gtk_label_new(NULL
), FALSE
, FALSE
, 0); /* key1 label */
1909 /* second row of labels */
1910 hbox
=gtk_hbox_new(FALSE
, 12);
1911 gtk_box_pack_start(GTK_BOX(main_window
.labels
), hbox
, FALSE
, FALSE
, 0);
1912 gtk_box_pack_start(GTK_BOX(hbox
), main_window
.label_lives
=gtk_label_new(NULL
), FALSE
, FALSE
, 0); /* LIVES label */
1913 gtk_box_pack_end(GTK_BOX(hbox
), main_window
.label_diamonds
=gtk_label_new(NULL
), FALSE
, FALSE
, 0); /* DIAMONDS label */
1914 gtk_box_pack_end(GTK_BOX(hbox
), main_window
.label_skeletons
=gtk_label_new(NULL
), FALSE
, FALSE
, 0); /* SKELETONS label */
1917 hbox
=gtk_hbox_new(FALSE
, 12);
1918 gtk_box_pack_start(GTK_BOX(main_window
.labels
), hbox
, FALSE
, FALSE
, 0);
1919 gtk_box_pack_start(GTK_BOX(hbox
), main_window
.label_value
=gtk_label_new(NULL
), FALSE
, FALSE
, 0); /* VALUE label */
1920 gtk_box_pack_start(GTK_BOX(hbox
), main_window
.label_score
=gtk_label_new(NULL
), FALSE
, FALSE
, 0); /* SCORE label */
1921 gtk_box_pack_end(GTK_BOX(hbox
), main_window
.label_time
=gtk_label_new(NULL
), FALSE
, FALSE
, 0); /* TIME label */
1922 gtk_box_pack_end(GTK_BOX(hbox
), main_window
.label_gravity_will_change
=gtk_label_new(NULL
), FALSE
, FALSE
, 0); /* gravity label */
1924 main_window
.scroll_window
=gtk_scrolled_window_new(NULL
, NULL
);
1925 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(main_window
.scroll_window
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
1926 gtk_box_pack_start_defaults(GTK_BOX (vbox
), main_window
.scroll_window
);
1928 main_window
.label_variables
=gtk_label_new(NULL
);
1929 gtk_box_pack_start(GTK_BOX (vbox
), main_window
.label_variables
, FALSE
, FALSE
, 0);
1931 hbox
=gtk_hbox_new(FALSE
, 6);
1932 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
1933 gtk_box_pack_start(GTK_BOX(hbox
), gtk_image_new_from_stock(GTK_STOCK_DIALOG_ERROR
, GTK_ICON_SIZE_MENU
), FALSE
, FALSE
, 0);
1934 gtk_box_pack_start(GTK_BOX(hbox
), main_window
.error_label
=gtk_label_new(NULL
), FALSE
, FALSE
, 0); /* error label */
1935 main_window
.error_hbox
=hbox
;
1937 gtk_widget_show_all(main_window
.window
);
1938 gtk_window_present(GTK_WINDOW(main_window
.window
));
1947 main(int argc
, char *argv
[])
1950 gboolean editor
=FALSE
;
1951 char *gallery_filename
=NULL
;
1952 char *png_filename
=NULL
, *png_size
=NULL
;
1953 char *save_cave_name
=NULL
;
1956 GOptionContext
*context
;
1957 GOptionEntry entries
[]={
1958 {"editor", 'e', 0, G_OPTION_ARG_NONE
, &editor
, N_("Start editor")},
1959 {"gallery", 'g', 0, G_OPTION_ARG_FILENAME
, &gallery_filename
, N_("Save caveset in a HTML gallery")},
1960 {"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")},
1961 {"png_size", 's', 0, G_OPTION_ARG_STRING
, &png_size
, N_("Set PNG image size. Default is 128x96, set to 0x0 for unscaled")},
1962 {"save", 'S', 0, G_OPTION_ARG_FILENAME
, &save_cave_name
, N_("Save caveset in a BDCFF file")},
1963 {"quit", 'q', 0, G_OPTION_ARG_NONE
, &quit
, N_("Batch mode: quit after specified tasks")},
1967 context
=gd_option_context_new();
1968 g_option_context_add_main_entries (context
, entries
, PACKAGE
); /* gdash (gtk version) parameters */
1969 g_option_context_add_group (context
, gtk_get_option_group (TRUE
)); /* add gtk parameters */
1970 g_option_context_parse (context
, &argc
, &argv
, &error
);
1971 g_option_context_free (context
);
1973 g_warning("%s", error
->message
);
1974 g_error_free(error
);
1978 if (gd_param_license
) {
1979 char *wrapped
=gd_wrap_text(gd_about_license
, 72);
1981 g_print("%s", wrapped
);
1986 gd_install_log_handler();
1988 gd_settings_init_dirs();
1993 gd_settings_set_locale();
1995 gtk_init(&argc
, &argv
);
1997 gd_settings_init_translation();
2001 gd_cave_sound_db_init();
2002 gd_c64_import_init_tables();
2004 gd_caveset_clear(); /* this also creates the default cave */
2006 gd_clear_error_flag();
2008 gd_wait_before_game_over
=FALSE
;
2010 /* LOAD A CAVESET FROM A FILE, OR AN INTERNAL ONE */
2011 /* if remaining arguments, they are filenames */
2012 if (gd_param_cavenames
&& gd_param_cavenames
[0]) {
2013 /* load caveset, "ignore" errors. */
2014 if (!gd_open_caveset_in_ui(gd_param_cavenames
[0], gd_use_bdcff_highscore
))
2015 g_critical (_("Errors during loading caveset from file '%s'"), gd_param_cavenames
[0]);
2017 else if (gd_param_internal
) {
2018 /* if specified an internal caveset; if error, halt now */
2019 if (!gd_caveset_load_from_internal (gd_param_internal
-1, gd_user_config_dir
))
2020 g_critical (_("%d: no such internal caveset"), gd_param_internal
);
2023 /* if failed or nothing requested, load default */
2024 if (gd_caveset
==NULL
)
2025 gd_caveset_load_from_internal (0, gd_user_config_dir
);
2027 /* always load c64 graphics, as it is the builtin, and we need an icon for the theme selector. */
2028 gd_loadcells_default();
2029 gd_create_pixbuf_for_builtin_gfx();
2031 /* load other theme, if specified in config. */
2032 if (gd_theme
!=NULL
&& !g_str_equal(gd_theme
, "")) {
2033 if (!gd_loadcells_file(gd_theme
)) {
2034 /* forget the theme if we are unable to load */
2035 g_warning("Cannot load theme %s, switching to built-in theme", gd_theme
);
2038 gd_loadcells_default(); /* load default gfx */
2042 /* after loading cells... see if generating a gallery. */
2043 if (gallery_filename
)
2044 gd_save_html (gallery_filename
, NULL
);
2046 /* if cave or level values given, check range */
2048 if (gd_param_cave
<1 || gd_param_cave
>=gd_caveset_count () || gd_param_level
<1 || gd_param_level
>5)
2049 g_critical (_("Invalid cave or level number!\n"));
2056 unsigned int size_x
=128, size_y
=96; /* default size */
2058 if (gd_param_cave
== 0)
2059 gd_param_cave
=g_random_int_range(0, gd_caveset_count ())+1;
2061 if (png_size
&& (sscanf (png_size
, "%ux%u", &size_x
, &size_y
) != 2))
2062 g_critical (_("Invalid image size: %s"), png_size
);
2063 if (size_x
<1 || size_y
<1) {
2068 /* rendering cave for png: seed=0 */
2069 renderedcave
=gd_cave_new_from_caveset (gd_param_cave
-1, gd_param_level
-1, 0);
2070 pixbuf
=gd_drawcave_to_pixbuf(renderedcave
, size_x
, size_y
, TRUE
);
2071 if (!gdk_pixbuf_save (pixbuf
, png_filename
, "png", &error
, "compression", "9", NULL
))
2072 g_critical ("Error saving PNG image %s: %s", png_filename
, error
->message
);
2073 g_object_unref(pixbuf
);
2074 gd_cave_free (renderedcave
);
2076 /* avoid starting game */
2081 gd_caveset_save(save_cave_name
, FALSE
);
2083 /* if batch mode, quit now */
2088 gd_create_stock_icons();
2089 gd_create_main_window();
2090 gd_main_window_set_title();
2094 init_mainwindow(NULL
);
2096 #ifdef G_THREADS_ENABLED
2097 if (!g_thread_supported())
2098 g_thread_init(NULL
);
2101 if (gd_param_cave
) {
2102 /* if cave number given, start game */
2103 new_game(g_get_real_name(), gd_param_cave
-1, gd_param_level
-1);
2106 cave_editor_cb(NULL
, &main_window
);
2110 gd_save_highscore(gd_user_config_dir
);