b59da1b374aa7d4921bcd3ddf856e61854e6d972
[projects/chimara/chimara.git] / player / player.c
1 #include <glib-object.h>
2 #include <libchimara/chimara-glk.h>
3 #include <libchimara/chimara-if.h>
4 #include "player.h"
5 #include "error.h"
6 #include "app.h"
7
8 typedef struct _ChimaraPlayerPrivate {
9         int dummy;
10 } ChimaraPlayerPrivate;
11
12 #define CHIMARA_PLAYER_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), CHIMARA_TYPE_PLAYER, ChimaraPlayerPrivate))
13 #define CHIMARA_PLAYER_USE_PRIVATE ChimaraPlayerPrivate *priv = CHIMARA_PLAYER_PRIVATE(self)
14
15 G_DEFINE_TYPE(ChimaraPlayer, chimara_player, GTK_TYPE_WINDOW);
16
17 static GObject *
18 load_object(GtkBuilder *builder, const gchar *name)
19 {
20         GObject *retval;
21         if( (retval = gtk_builder_get_object(builder, name)) == NULL) {
22                 error_dialog(NULL, NULL, "Error while getting object '%s'", name);
23                 g_error("Error while getting object '%s'", name);
24         }
25         return retval;
26 }
27
28 static void
29 change_window_title(ChimaraGlk *glk, GParamSpec *pspec, GtkWindow *window)
30 {
31         gchar *program_name, *story_name, *title;
32         g_object_get(glk, "program-name", &program_name, "story-name", &story_name, NULL);
33         if(!program_name) {
34                 gtk_window_set_title(window, "Chimara");
35                 return;
36         }
37         else if(!story_name)
38                 title = g_strdup_printf("%s - Chimara", program_name);
39         else
40                 title = g_strdup_printf("%s - %s - Chimara", program_name, story_name);
41         
42         g_free(program_name);
43         g_free(story_name);
44         gtk_window_set_title(window, title);
45         g_free(title);
46 }
47
48 static void
49 chimara_player_dispose(GObject *object)
50 {
51         ChimaraPlayer *self = CHIMARA_PLAYER(object);
52         if(chimara_glk_get_running(CHIMARA_GLK(self->glk))) {
53                 chimara_glk_stop(CHIMARA_GLK(self->glk));
54                 g_printerr("Stopping...\n");
55                 chimara_glk_wait(CHIMARA_GLK(self->glk));
56                 g_printerr("Done Waiting\n");
57         }
58         
59         /* Chain up */
60         G_OBJECT_CLASS(chimara_player_parent_class)->dispose(object);
61 }
62
63 static void
64 chimara_player_finalize(GObject *object)
65 {
66         g_printerr("Unreffing\n");
67         g_object_unref(CHIMARA_PLAYER(object)->glk);
68         
69         /* Chain up */
70         G_OBJECT_CLASS(chimara_player_parent_class)->finalize(object);
71 }
72
73 static void
74 chimara_player_class_init(ChimaraPlayerClass *klass)
75 {
76         /* Override methods of parent classes */
77         GObjectClass *object_class = G_OBJECT_CLASS(klass);
78         //object_class->set_property = chimara_if_set_property;
79         //object_class->get_property = chimara_if_get_property;
80         object_class->dispose = chimara_player_dispose;
81         object_class->finalize = chimara_player_finalize;
82         
83         /* Signals */
84
85         /* Properties */
86
87         /* Private data */
88         g_type_class_add_private(klass, sizeof(ChimaraPlayerPrivate));
89 }
90
91 static void
92 chimara_player_init(ChimaraPlayer *self)
93 {       
94         GError *error = NULL;
95         
96         GtkBuilder *builder = gtk_builder_new();
97         char *object_ids[] = {
98                 "actiongroup",
99                 "vbox",
100                 NULL
101         };
102         
103         if( !gtk_builder_add_objects_from_file(builder, PACKAGE_DATA_DIR "/chimara.ui", object_ids, &error) ) {
104 #ifdef DEBUG
105                 g_error_free(error);
106                 error = NULL;
107                 if( !gtk_builder_add_objects_from_file(builder, PACKAGE_SRC_DIR "/chimara.ui", object_ids, &error) ) {
108 #endif /* DEBUG */
109                         error_dialog(NULL, error, "Error while building interface: ");  
110                         return;
111 #ifdef DEBUG
112                 }
113 #endif /* DEBUG */
114         }
115         
116         GtkActionGroup *actiongroup = GTK_ACTION_GROUP(load_object(builder, "actiongroup"));
117         
118         /* Set the default value of the "View/Toolbar" menu item upon creation of a
119          new window to the "show-toolbar-default" setting, but bind the setting
120          one-way only - we don't want toolbars to disappear suddenly */
121         GtkToggleAction *toolbar_action = GTK_TOGGLE_ACTION(load_object(builder, "toolbar"));
122         //gtk_toggle_action_set_active(toolbar_action, g_settings_get_boolean(state_settings, "show-toolbar-default"));
123         //g_settings_bind(state_settings, "show-toolbar-default", toolbar_action, "active", G_SETTINGS_BIND_SET);
124                 
125         GtkUIManager *uimanager = gtk_ui_manager_new();
126         if( !gtk_ui_manager_add_ui_from_file(uimanager, PACKAGE_DATA_DIR "/chimara.menus", &error) ) {
127 #ifdef DEBUG
128                 g_error_free(error);
129                 error = NULL;
130                 if( !gtk_ui_manager_add_ui_from_file(uimanager, PACKAGE_SRC_DIR "/chimara.menus", &error) ) {
131 #endif /* DEBUG */
132                         error_dialog(NULL, error, "Error while building interface: ");
133                         return;
134 #ifdef DEBUG
135                 }
136 #endif /* DEBUG */
137         }
138         
139         self->glk = chimara_if_new();
140         g_object_set(self->glk,
141                                  "ignore-errors", TRUE,
142                                  /*"interpreter-number", CHIMARA_IF_ZMACHINE_TANDY_COLOR,*/
143                                  NULL);
144         if( !chimara_glk_set_css_from_file(CHIMARA_GLK(self->glk), PACKAGE_DATA_DIR "/style.css", &error) ) {
145 #ifdef DEBUG
146                 g_error_free(error);
147                 error = NULL;
148                 if( !chimara_glk_set_css_from_file(CHIMARA_GLK(self->glk), PACKAGE_SRC_DIR "/style.css", &error) ) {
149 #endif /* DEBUG */
150                         error_dialog(NULL, error, "Couldn't open CSS file: ");
151                         return;
152 #ifdef DEBUG
153                 }
154 #endif /* DEBUG */
155         }
156         
157         /* DON'T UNCOMMENT THIS your eyes will burn
158          but it is a good test of programmatically altering just one style
159          chimara_glk_set_css_from_string(CHIMARA_GLK(glk),
160          "buffer.normal { font-family: 'Comic Sans MS'; }");*/
161         
162         GtkBox *vbox = GTK_BOX(load_object(builder, "vbox"));                   
163
164         ChimaraApp *theapp = chimara_app_get();
165
166         gtk_ui_manager_insert_action_group(uimanager, actiongroup, 0);
167         gtk_ui_manager_insert_action_group(uimanager, chimara_app_get_action_group(theapp), 1);
168         GtkWidget *menubar = gtk_ui_manager_get_widget(uimanager, "/player_menu");
169         self->toolbar = gtk_ui_manager_get_widget(uimanager, "/player_toolbar");
170         gtk_widget_set_no_show_all(self->toolbar, TRUE);
171         if(gtk_toggle_action_get_active(toolbar_action))
172                 gtk_widget_show(self->toolbar);
173         else
174                 gtk_widget_hide(self->toolbar);
175         
176         /* Connect the accelerators */
177         GtkAccelGroup *accels = gtk_ui_manager_get_accel_group(uimanager);
178         gtk_window_add_accel_group(GTK_WINDOW(self), accels);
179         
180         gtk_box_pack_end(vbox, self->glk, TRUE, TRUE, 0);
181         g_object_ref(self->glk); /* add an extra reference to keep it alive while
182                                                           the Glk program shuts down */
183         gtk_box_pack_start(vbox, menubar, FALSE, FALSE, 0);
184         gtk_box_pack_start(vbox, self->toolbar, FALSE, FALSE, 0);
185         gtk_container_add(GTK_CONTAINER(self), GTK_WIDGET(vbox));
186         
187         gtk_builder_connect_signals(builder, self);
188         g_signal_connect(self->glk, "notify::program-name", G_CALLBACK(change_window_title), self);
189         g_signal_connect(self->glk, "notify::story-name", G_CALLBACK(change_window_title), self);
190
191         g_object_unref( G_OBJECT(builder) );
192         g_object_unref( G_OBJECT(uimanager) );
193 }
194
195 /* PUBLIC FUNCTIONS */
196
197 GtkWidget *
198 chimara_player_new(void)
199 {
200     return GTK_WIDGET(g_object_new(CHIMARA_TYPE_PLAYER,
201                 "type", GTK_WINDOW_TOPLEVEL,
202                 NULL));
203 }
204
205 /* GLADE CALLBACKS */
206
207 #if 0
208 /* If a game is running in @glk, warn the user that they will quit the currently
209 running game if they open a new one. Returns TRUE if no game was running.
210 Returns FALSE if the user cancelled. Returns TRUE and shuts down the running
211 game if the user wishes to continue. */
212 static gboolean
213 confirm_open_new_game(ChimaraGlk *glk)
214 {
215         g_return_val_if_fail(glk && CHIMARA_IS_GLK(glk), FALSE);
216         
217         GtkWindow *window = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(glk)));
218         
219         if(chimara_glk_get_running(glk)) {
220                 GtkWidget *dialog = gtk_message_dialog_new(window,
221                     GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
222                     GTK_MESSAGE_WARNING,
223                     GTK_BUTTONS_CANCEL,
224                     _("Are you sure you want to open a new game?"));
225                 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
226                     _("If you open a new game, you will quit the one you are currently playing."));
227                 gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_OPEN, GTK_RESPONSE_OK);
228                 gint response = gtk_dialog_run(GTK_DIALOG(dialog));
229                 gtk_widget_destroy(dialog);
230                 
231                 if(response != GTK_RESPONSE_OK)
232                         return FALSE;
233
234                 chimara_glk_stop(glk);
235                 chimara_glk_wait(glk);
236         }
237         return TRUE;
238 }
239 #endif
240
241 void
242 on_stop_activate(GtkAction *action, ChimaraPlayer *player)
243 {
244         chimara_glk_stop(CHIMARA_GLK(player->glk));
245 }
246
247 void
248 on_copy_activate(GtkAction *action, ChimaraPlayer *player)
249 {
250         GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(player));
251         /* Call "copy clipboard" on any widget that defines it */
252         if(GTK_IS_LABEL(focus) || GTK_IS_ENTRY(focus) || GTK_IS_TEXT_VIEW(focus))
253                 g_signal_emit_by_name(focus, "copy-clipboard");
254 }
255
256 void
257 on_paste_activate(GtkAction *action, ChimaraPlayer *player)
258 {
259         GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(player));
260         /* Call "paste clipboard" on any widget that defines it */
261         if(GTK_IS_ENTRY(focus) || GTK_IS_TEXT_VIEW(focus))
262                 g_signal_emit_by_name(focus, "paste-clipboard");
263 }
264
265 void
266 on_toolbar_toggled(GtkToggleAction *action, ChimaraPlayer *player)
267 {
268         if(gtk_toggle_action_get_active(action))
269                 gtk_widget_show(player->toolbar);
270         else
271                 gtk_widget_hide(player->toolbar);
272 }
273
274 void
275 on_undo_activate(GtkAction *action, ChimaraPlayer *player)
276 {
277         chimara_glk_feed_line_input(CHIMARA_GLK(player->glk), "undo");
278 }
279
280 void 
281 on_save_activate(GtkAction *action, ChimaraPlayer *player)
282 {
283         chimara_glk_feed_line_input(CHIMARA_GLK(player->glk), "save");
284 }
285
286 void 
287 on_restore_activate(GtkAction *action, ChimaraPlayer *player)
288 {
289         chimara_glk_feed_line_input(CHIMARA_GLK(player->glk), "restore");
290 }
291
292 void 
293 on_restart_activate(GtkAction *action, ChimaraPlayer *player)
294 {
295         chimara_glk_feed_line_input(CHIMARA_GLK(player->glk), "restart");
296 }
297
298 void 
299 on_quit_activate(GtkAction *action, ChimaraPlayer *player)
300 {
301         chimara_glk_feed_line_input(CHIMARA_GLK(player->glk), "quit");
302 }
303
304 gboolean 
305 on_window_delete_event(GtkWidget *widget, GdkEvent *event, ChimaraPlayer *player) 
306 {
307         gtk_main_quit();
308         return TRUE;
309 }
310