[player] Call Gdk.threads_init() before import Gtk
[projects/chimara/chimara.git] / player / callbacks.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * callbacks.c
4  * Copyright (C) Philip en Marijn 2008 <>
5  * 
6  * callbacks.c is free software copyrighted by Philip en Marijn.
7  * 
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name ``Philip en Marijn'' nor the name of any other
17  *    contributor may be used to endorse or promote products derived
18  *    from this software without specific prior written permission.
19  * 
20  * callbacks.c IS PROVIDED BY Philip en Marijn ``AS IS'' AND ANY EXPRESS
21  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL Philip en Marijn OR ANY OTHER CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
27  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
30  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include <glib.h>
34 #include <glib/gi18n.h>
35 #include <gtk/gtk.h>
36 #include <libchimara/chimara-glk.h>
37 #include <libchimara/chimara-if.h>
38 #include <config.h>
39 #include "error.h"
40
41 /* If a game is running in @glk, warn the user that they will quit the currently
42 running game if they open a new one. Returns TRUE if no game was running.
43 Returns FALSE if the user cancelled. Returns TRUE and shuts down the running
44 game if the user wishes to continue. */
45 static gboolean
46 confirm_open_new_game(ChimaraGlk *glk)
47 {
48         g_return_val_if_fail(glk && CHIMARA_IS_GLK(glk), FALSE);
49         
50         GtkWindow *window = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(glk)));
51         
52         if(chimara_glk_get_running(glk)) {
53                 GtkWidget *dialog = gtk_message_dialog_new(window,
54                     GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
55                     GTK_MESSAGE_WARNING,
56                     GTK_BUTTONS_CANCEL,
57                     _("Are you sure you want to open a new game?"));
58                 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
59                     _("If you open a new game, you will quit the one you are currently playing."));
60                 gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_OPEN, GTK_RESPONSE_OK);
61                 gint response = gtk_dialog_run(GTK_DIALOG(dialog));
62                 gtk_widget_destroy(dialog);
63                 
64                 if(response != GTK_RESPONSE_OK)
65                         return FALSE;
66
67                 chimara_glk_stop(glk);
68                 chimara_glk_wait(glk);
69         }
70         return TRUE;
71 }
72
73 /* Internal function: See if there is a corresponding graphics file */
74 static void
75 search_for_graphics_file(const char *filename, ChimaraIF *glk)
76 {
77
78         extern GSettings *prefs_settings;
79
80         /* First get the name of the story file */
81         char *scratch = g_path_get_basename(filename);
82         char *ext = strrchr(scratch, '.');
83         if(strcmp(ext, ".zlb") == 0 ||
84                 strcmp(ext, ".zblorb") == 0 ||
85                 strcmp(ext, ".glb") == 0 ||
86                 strcmp(ext, ".gblorb") == 0 ||
87                 strcmp(ext, ".blorb") == 0 ||
88                 strcmp(ext, ".blb") == 0)
89         {
90                 g_object_set(glk, "graphics-file", NULL, NULL);
91                 return;
92         }
93         *ext = '\0';
94
95         /* Check in the stored resource path, if set */
96         char *resource_path;
97         g_settings_get(prefs_settings, "resource-path", "ms", &resource_path);
98
99         /* Otherwise check in the current directory */
100         if(!resource_path)
101                 resource_path = g_path_get_dirname(filename);
102
103         char *blorbfile = g_strconcat(resource_path, "/", scratch, ".blb", NULL);
104         if(g_file_test(blorbfile, G_FILE_TEST_EXISTS))
105                 g_object_set(glk, "graphics-file", blorbfile, NULL);
106
107         g_free(blorbfile);
108         g_free(scratch);
109         g_free(resource_path);
110 }
111
112 void
113 on_open_activate(GtkAction *action, ChimaraGlk *glk) 
114 {
115         GtkWindow *window = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(glk)));
116         
117         if(!confirm_open_new_game(glk))
118                 return;
119
120         GtkWidget *dialog = gtk_file_chooser_dialog_new(_("Open Game"),
121             window,
122             GTK_FILE_CHOOSER_ACTION_OPEN,
123             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
124             GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
125             NULL);
126
127         /* Get last opened path */
128         extern GSettings *state_settings;
129         gchar *path;
130         g_settings_get(state_settings, "last-open-path", "ms", &path);
131         if(path) {
132                 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path);
133                 g_free(path);
134         }
135
136         if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
137                 GError *error = NULL;
138                 extern GSettings *prefs_settings;
139                 char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
140
141                 search_for_graphics_file(filename, CHIMARA_IF(glk));
142                 if(!chimara_if_run_game(CHIMARA_IF(glk), filename, &error)) {
143                         error_dialog(window, error, _("Could not open game file '%s': "), filename);
144                         g_free(filename);
145                         gtk_widget_destroy(dialog);
146                         return;
147                 }
148                 
149                 path = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog));
150                 if(path) {
151                         g_settings_set(state_settings, "last-open-path", "ms", path);
152                         g_free(path);
153                 }
154
155                 /* Add file to recent files list */
156                 GtkRecentManager *manager = gtk_recent_manager_get_default();
157                 gchar *uri;
158                 
159                 if(!(uri = g_filename_to_uri(filename, NULL, &error)))
160                         g_warning(_("Could not convert filename '%s' to URI: %s"), filename, error->message);
161                 else {
162                         if(!gtk_recent_manager_add_item(manager, uri))
163                                 g_warning(_("Could not add URI '%s' to recent files list."), uri);
164                         g_free(uri);
165                 }
166                 g_free(filename);
167         }
168         gtk_widget_destroy(dialog);
169 }
170
171 void
172 on_recent_item_activated(GtkRecentChooser *chooser, ChimaraGlk *glk)
173 {
174         GError *error = NULL;
175         GtkWindow *window = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(glk)));
176         gchar *uri = gtk_recent_chooser_get_current_uri(chooser);
177         gchar *filename;
178         if(!(filename = g_filename_from_uri(uri, NULL, &error))) {
179                 error_dialog(window, error, _("Could not open game file '%s': "), uri);
180                 goto finally;
181         }
182         
183         if(!confirm_open_new_game(glk))
184                 goto finally2;
185         
186         search_for_graphics_file(filename, CHIMARA_IF(glk));
187         if(!chimara_if_run_game(CHIMARA_IF(glk), filename, &error)) {
188                 error_dialog(window, error, _("Could not open game file '%s': "), filename);
189                 goto finally2;
190         }
191         
192         /* Add file to recent files list again, this updates it to most recently used */
193         GtkRecentManager *manager = gtk_recent_manager_get_default();
194         if(!gtk_recent_manager_add_item(manager, uri))
195                 g_warning(_("Could not add URI '%s' to recent files list."), uri);
196
197 finally2:
198         g_free(filename);
199 finally:
200         g_free(uri);
201 }
202
203 void
204 on_stop_activate(GtkAction *action, ChimaraGlk *glk)
205 {
206         chimara_glk_stop(glk);
207 }
208
209 void 
210 on_quit_chimara_activate(GtkAction *action, ChimaraGlk *glk) 
211 {
212         gtk_main_quit();
213 }
214
215 void
216 on_copy_activate(GtkAction *action, ChimaraGlk *glk)
217 {
218         GtkWindow *toplevel = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(glk)));
219         GtkWidget *focus = gtk_window_get_focus(toplevel);
220         /* Call "copy clipboard" on any widget that defines it */
221         if(GTK_IS_LABEL(focus) || GTK_IS_ENTRY(focus) || GTK_IS_TEXT_VIEW(focus))
222                 g_signal_emit_by_name(focus, "copy-clipboard");
223 }
224
225 void
226 on_paste_activate(GtkAction *action, ChimaraGlk *glk)
227 {
228         GtkWindow *toplevel = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(glk)));
229         GtkWidget *focus = gtk_window_get_focus(toplevel);
230         /* Call "paste clipboard" on any widget that defines it */
231         if(GTK_IS_ENTRY(focus) || GTK_IS_TEXT_VIEW(focus))
232                 g_signal_emit_by_name(focus, "paste-clipboard");
233 }
234
235 void
236 on_preferences_activate(GtkAction *action, ChimaraGlk *glk)
237 {
238         extern GtkWidget *prefswindow;
239         gtk_window_present(GTK_WINDOW(prefswindow));
240 }
241
242 void
243 on_toolbar_toggled(GtkToggleAction *action, ChimaraGlk *glk)
244 {
245         extern GtkWidget *toolbar;
246         
247         if(gtk_toggle_action_get_active(action))
248                 gtk_widget_show(toolbar);
249         else
250                 gtk_widget_hide(toolbar);
251 }
252
253 void
254 on_undo_activate(GtkAction *action, ChimaraGlk *glk)
255 {
256         chimara_glk_feed_line_input(glk, "undo");
257 }
258
259 void 
260 on_save_activate(GtkAction *action, ChimaraGlk *glk) 
261 {
262         chimara_glk_feed_line_input(glk, "save");
263 }
264
265 void 
266 on_restore_activate(GtkAction *action, ChimaraGlk *glk) 
267 {
268         chimara_glk_feed_line_input(glk, "restore");
269 }
270
271 void 
272 on_restart_activate(GtkAction *action, ChimaraGlk *glk) 
273 {
274         chimara_glk_feed_line_input(glk, "restart");
275 }
276
277 void 
278 on_quit_activate(GtkAction *action, ChimaraGlk *glk) 
279 {
280         chimara_glk_feed_line_input(glk, "quit");
281 }
282
283 void
284 on_about_activate(GtkAction *action, ChimaraGlk *glk)
285 {
286         extern GtkWidget *aboutwindow;
287         gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(aboutwindow), PACKAGE_VERSION);
288         gtk_window_present(GTK_WINDOW(aboutwindow));
289 }
290
291 gboolean 
292 on_window_delete_event(GtkWidget *widget, GdkEvent *event, ChimaraGlk *glk) 
293 {
294         gtk_main_quit();
295         return TRUE;
296 }