General code cleanup
[projects/chimara/chimara.git] / player / player.c
1 /*
2  * Copyright (C) 2008, 2009, 2010, 2011 Philip Chimento and Marijn van Vliet.
3  * All rights reserved.
4  *
5  * Chimara is free software copyrighted by Philip Chimento and Marijn van Vliet.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice,
11  *    this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright notice,
13  *    this list of conditions and the following disclaimer in the documentation
14  *    and/or other materials provided with the distribution.
15  * 3. Neither of the names Philip Chimento or Marijn van Vliet, nor the name of
16  *    any other contributor may be used to endorse or promote products derived
17  *    from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include <glib.h>
33 #include <glib-object.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 "player.h"
39 #include "app.h"
40 #include "error.h"
41 #include "util.h"
42
43 typedef struct _ChimaraPlayerPrivate {
44         int dummy;
45 } ChimaraPlayerPrivate;
46
47 #define CHIMARA_PLAYER_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), CHIMARA_TYPE_PLAYER, ChimaraPlayerPrivate))
48 #define CHIMARA_PLAYER_USE_PRIVATE ChimaraPlayerPrivate *priv = CHIMARA_PLAYER_PRIVATE(self)
49
50 G_DEFINE_TYPE(ChimaraPlayer, chimara_player, GTK_TYPE_WINDOW);
51
52 static void
53 change_window_title(ChimaraGlk *glk, GParamSpec *pspec, GtkWindow *window)
54 {
55         gchar *program_name, *story_name, *title;
56         g_object_get(glk, "program-name", &program_name, "story-name", &story_name, NULL);
57         if(!program_name) {
58                 gtk_window_set_title(window, "Chimara");
59                 return;
60         }
61         else if(!story_name)
62                 title = g_strdup_printf("%s - Chimara", program_name);
63         else
64                 title = g_strdup_printf("%s - %s - Chimara", program_name, story_name);
65         
66         g_free(program_name);
67         g_free(story_name);
68         gtk_window_set_title(window, title);
69         g_free(title);
70 }
71
72 static void
73 chimara_player_dispose(GObject *object)
74 {
75         ChimaraPlayer *self = CHIMARA_PLAYER(object);
76         if(chimara_glk_get_running(CHIMARA_GLK(self->glk))) {
77                 chimara_glk_stop(CHIMARA_GLK(self->glk));
78                 g_printerr("Stopping...\n");
79                 chimara_glk_wait(CHIMARA_GLK(self->glk));
80                 g_printerr("Done Waiting\n");
81         }
82         
83         /* Chain up */
84         G_OBJECT_CLASS(chimara_player_parent_class)->dispose(object);
85 }
86
87 static void
88 chimara_player_finalize(GObject *object)
89 {
90         g_printerr("Unreffing\n");
91         g_object_unref(CHIMARA_PLAYER(object)->glk);
92         
93         /* Chain up */
94         G_OBJECT_CLASS(chimara_player_parent_class)->finalize(object);
95 }
96
97 static void
98 chimara_player_class_init(ChimaraPlayerClass *klass)
99 {
100         /* Override methods of parent classes */
101         GObjectClass *object_class = G_OBJECT_CLASS(klass);
102         //object_class->set_property = chimara_if_set_property;
103         //object_class->get_property = chimara_if_get_property;
104         object_class->dispose = chimara_player_dispose;
105         object_class->finalize = chimara_player_finalize;
106         
107         /* Signals */
108
109         /* Properties */
110
111         /* Private data */
112         g_type_class_add_private(klass, sizeof(ChimaraPlayerPrivate));
113 }
114
115 static void
116 chimara_player_init(ChimaraPlayer *self)
117 {       
118         GError *error = NULL;
119         ChimaraApp *theapp = chimara_app_get();
120
121         /* Set parent properties */
122         g_object_set(self,
123                 "title", _("Chimara"),
124                 "default-width", 600,
125                 "default-height", 800,
126                 NULL);
127
128         /* Construct user interface */
129         char *object_ids[] = {
130                 "actiongroup",
131                 "player-vbox",
132                 NULL
133         };
134         GtkBuilder *builder = new_builder_with_objects(object_ids);
135
136         GtkActionGroup *actiongroup = GTK_ACTION_GROUP(load_object(builder, "actiongroup"));
137
138         /* Set the default value of the "View/Toolbar" menu item upon creation of a
139          new window to the "show-toolbar-default" setting, but bind the setting
140          one-way only - we don't want toolbars to disappear suddenly */
141         GtkToggleAction *toolbar_action = GTK_TOGGLE_ACTION(load_object(builder, "toolbar"));
142         gtk_toggle_action_set_active(toolbar_action, g_settings_get_boolean(theapp->state_settings, "show-toolbar-default"));
143         g_settings_bind(theapp->state_settings, "show-toolbar-default", toolbar_action, "active", G_SETTINGS_BIND_SET);
144
145         self->glk = chimara_if_new();
146         g_object_set(self->glk,
147                                  "ignore-errors", TRUE,
148                                  /*"interpreter-number", CHIMARA_IF_ZMACHINE_TANDY_COLOR,*/
149                                  NULL);
150         char *default_css = get_data_file_path("style.css");
151         if( !chimara_glk_set_css_from_file(CHIMARA_GLK(self->glk), default_css, &error) ) {
152                 error_dialog(GTK_WINDOW(self), error, "Couldn't open default CSS file: ");
153         }
154         
155         /* DON'T UNCOMMENT THIS your eyes will burn
156          but it is a good test of programmatically altering just one style
157          chimara_glk_set_css_from_string(CHIMARA_GLK(glk),
158          "buffer.normal { font-family: 'Comic Sans MS'; }");*/
159
160         GtkUIManager *uimanager = new_ui_manager("player.menus");
161         gtk_ui_manager_insert_action_group(uimanager, actiongroup, 0);
162         gtk_ui_manager_insert_action_group(uimanager, chimara_app_get_action_group(theapp), 1);
163         GtkWidget *menubar = gtk_ui_manager_get_widget(uimanager, "/player_menu");
164         self->toolbar = gtk_ui_manager_get_widget(uimanager, "/player_toolbar");
165         gtk_widget_set_no_show_all(self->toolbar, TRUE);
166         if(gtk_toggle_action_get_active(toolbar_action))
167                 gtk_widget_show(self->toolbar);
168         else
169                 gtk_widget_hide(self->toolbar);
170         
171         /* Connect the accelerators */
172         GtkAccelGroup *accels = gtk_ui_manager_get_accel_group(uimanager);
173         gtk_window_add_accel_group(GTK_WINDOW(self), accels);
174
175         GtkBox *vbox = GTK_BOX(load_object(builder, "player-vbox"));
176         gtk_box_pack_end(vbox, self->glk, TRUE, TRUE, 0);
177         g_object_ref(self->glk); /* add an extra reference to keep it alive while
178                                                           the Glk program shuts down */
179         gtk_box_pack_start(vbox, menubar, FALSE, FALSE, 0);
180         gtk_box_pack_start(vbox, self->toolbar, FALSE, FALSE, 0);
181         gtk_container_add(GTK_CONTAINER(self), GTK_WIDGET(vbox));
182         
183         gtk_builder_connect_signals(builder, self);
184         g_signal_connect(self->glk, "notify::program-name", G_CALLBACK(change_window_title), self);
185         g_signal_connect(self->glk, "notify::story-name", G_CALLBACK(change_window_title), self);
186
187         g_object_unref(builder);
188         g_object_unref(uimanager);
189 }
190
191 /* PUBLIC FUNCTIONS */
192
193 GtkWidget *
194 chimara_player_new(void)
195 {
196     return GTK_WIDGET(g_object_new(CHIMARA_TYPE_PLAYER,
197                 "type", GTK_WINDOW_TOPLEVEL,
198                 NULL));
199 }
200
201 /* GLADE CALLBACKS */
202
203 #if 0
204 /* If a game is running in @glk, warn the user that they will quit the currently
205 running game if they open a new one. Returns TRUE if no game was running.
206 Returns FALSE if the user cancelled. Returns TRUE and shuts down the running
207 game if the user wishes to continue. */
208 static gboolean
209 confirm_open_new_game(ChimaraGlk *glk)
210 {
211         g_return_val_if_fail(glk && CHIMARA_IS_GLK(glk), FALSE);
212         
213         GtkWindow *window = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(glk)));
214         
215         if(chimara_glk_get_running(glk)) {
216                 GtkWidget *dialog = gtk_message_dialog_new(window,
217                     GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
218                     GTK_MESSAGE_WARNING,
219                     GTK_BUTTONS_CANCEL,
220                     _("Are you sure you want to open a new game?"));
221                 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
222                     _("If you open a new game, you will quit the one you are currently playing."));
223                 gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_OPEN, GTK_RESPONSE_OK);
224                 gint response = gtk_dialog_run(GTK_DIALOG(dialog));
225                 gtk_widget_destroy(dialog);
226                 
227                 if(response != GTK_RESPONSE_OK)
228                         return FALSE;
229
230                 chimara_glk_stop(glk);
231                 chimara_glk_wait(glk);
232         }
233         return TRUE;
234 }
235 #endif
236
237 void
238 on_stop_activate(GtkAction *action, ChimaraPlayer *player)
239 {
240         chimara_glk_stop(CHIMARA_GLK(player->glk));
241 }
242
243 void
244 on_copy_activate(GtkAction *action, ChimaraPlayer *player)
245 {
246         GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(player));
247         /* Call "copy clipboard" on any widget that defines it */
248         if(GTK_IS_LABEL(focus) || GTK_IS_ENTRY(focus) || GTK_IS_TEXT_VIEW(focus))
249                 g_signal_emit_by_name(focus, "copy-clipboard");
250 }
251
252 void
253 on_paste_activate(GtkAction *action, ChimaraPlayer *player)
254 {
255         GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(player));
256         /* Call "paste clipboard" on any widget that defines it */
257         if(GTK_IS_ENTRY(focus) || GTK_IS_TEXT_VIEW(focus))
258                 g_signal_emit_by_name(focus, "paste-clipboard");
259 }
260
261 void
262 on_toolbar_toggled(GtkToggleAction *action, ChimaraPlayer *player)
263 {
264         if(gtk_toggle_action_get_active(action))
265                 gtk_widget_show(player->toolbar);
266         else
267                 gtk_widget_hide(player->toolbar);
268 }
269
270 void
271 on_undo_activate(GtkAction *action, ChimaraPlayer *player)
272 {
273         chimara_glk_feed_line_input(CHIMARA_GLK(player->glk), "undo");
274 }
275
276 void 
277 on_save_activate(GtkAction *action, ChimaraPlayer *player)
278 {
279         chimara_glk_feed_line_input(CHIMARA_GLK(player->glk), "save");
280 }
281
282 void 
283 on_restore_activate(GtkAction *action, ChimaraPlayer *player)
284 {
285         chimara_glk_feed_line_input(CHIMARA_GLK(player->glk), "restore");
286 }
287
288 void 
289 on_restart_activate(GtkAction *action, ChimaraPlayer *player)
290 {
291         chimara_glk_feed_line_input(CHIMARA_GLK(player->glk), "restart");
292 }
293
294 void 
295 on_quit_activate(GtkAction *action, ChimaraPlayer *player)
296 {
297         chimara_glk_feed_line_input(CHIMARA_GLK(player->glk), "quit");
298 }
299