bedafea8be69a33ba996554b58131c64db65489e
[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                 chimara_glk_wait(CHIMARA_GLK(self->glk));
79         }
80         
81         /* Chain up */
82         G_OBJECT_CLASS(chimara_player_parent_class)->dispose(object);
83 }
84
85 static void
86 chimara_player_finalize(GObject *object)
87 {
88         g_object_unref(CHIMARA_PLAYER(object)->glk);
89         
90         /* Chain up */
91         G_OBJECT_CLASS(chimara_player_parent_class)->finalize(object);
92 }
93
94 static void
95 chimara_player_class_init(ChimaraPlayerClass *klass)
96 {
97         /* Override methods of parent classes */
98         GObjectClass *object_class = G_OBJECT_CLASS(klass);
99         object_class->dispose = chimara_player_dispose;
100         object_class->finalize = chimara_player_finalize;
101
102         /* Private data */
103         g_type_class_add_private(klass, sizeof(ChimaraPlayerPrivate));
104 }
105
106 static void
107 chimara_player_init(ChimaraPlayer *self)
108 {       
109         GError *error = NULL;
110         ChimaraApp *theapp = chimara_app_get();
111
112         /* Set parent properties */
113         g_object_set(self,
114                 "title", _("Chimara"),
115                 "default-width", 600,
116                 "default-height", 800,
117                 NULL);
118
119         /* Construct user interface */
120         char *object_ids[] = {
121                 "actiongroup",
122                 "player-vbox",
123                 NULL
124         };
125         GtkBuilder *builder = new_builder_with_objects(object_ids);
126
127         GtkActionGroup *actiongroup = GTK_ACTION_GROUP(load_object(builder, "actiongroup"));
128
129         /* Set the default value of the "View/Toolbar" menu item upon creation of a
130          new window to the "show-toolbar-default" setting, but bind the setting
131          one-way only - we don't want toolbars to disappear suddenly */
132         GtkToggleAction *toolbar_action = GTK_TOGGLE_ACTION(load_object(builder, "toolbar"));
133         gtk_toggle_action_set_active(toolbar_action, g_settings_get_boolean(theapp->state_settings, "show-toolbar-default"));
134         g_settings_bind(theapp->state_settings, "show-toolbar-default", toolbar_action, "active", G_SETTINGS_BIND_SET);
135
136         self->glk = chimara_if_new();
137         g_object_set(self->glk,
138                                  "ignore-errors", TRUE,
139                                  /*"interpreter-number", CHIMARA_IF_ZMACHINE_TANDY_COLOR,*/
140                                  NULL);
141         char *default_css = get_data_file_path("style.css");
142         if( !chimara_glk_set_css_from_file(CHIMARA_GLK(self->glk), default_css, &error) ) {
143                 error_dialog(GTK_WINDOW(self), error, "Couldn't open default CSS file: ");
144         }
145         
146         /* DON'T UNCOMMENT THIS your eyes will burn
147          but it is a good test of programmatically altering just one style
148          chimara_glk_set_css_from_string(CHIMARA_GLK(glk),
149          "buffer.normal { font-family: 'Comic Sans MS'; }");*/
150
151         GtkUIManager *uimanager = new_ui_manager("player.menus");
152         gtk_ui_manager_insert_action_group(uimanager, actiongroup, 0);
153         gtk_ui_manager_insert_action_group(uimanager, chimara_app_get_action_group(theapp), 1);
154         GtkWidget *menubar = gtk_ui_manager_get_widget(uimanager, "/player_menu");
155         self->toolbar = gtk_ui_manager_get_widget(uimanager, "/player_toolbar");
156         gtk_widget_set_no_show_all(self->toolbar, TRUE);
157         if(gtk_toggle_action_get_active(toolbar_action))
158                 gtk_widget_show(self->toolbar);
159         else
160                 gtk_widget_hide(self->toolbar);
161         
162         /* Connect the accelerators */
163         GtkAccelGroup *accels = gtk_ui_manager_get_accel_group(uimanager);
164         gtk_window_add_accel_group(GTK_WINDOW(self), accels);
165
166         GtkBox *vbox = GTK_BOX(load_object(builder, "player-vbox"));
167         gtk_box_pack_end(vbox, self->glk, TRUE, TRUE, 0);
168         g_object_ref(self->glk); /* add an extra reference to keep it alive while
169                                                           the Glk program shuts down */
170         gtk_box_pack_start(vbox, menubar, FALSE, FALSE, 0);
171         gtk_box_pack_start(vbox, self->toolbar, FALSE, FALSE, 0);
172         gtk_container_add(GTK_CONTAINER(self), GTK_WIDGET(vbox));
173         
174         gtk_builder_connect_signals(builder, self);
175         g_signal_connect(self->glk, "notify::program-name", G_CALLBACK(change_window_title), self);
176         g_signal_connect(self->glk, "notify::story-name", G_CALLBACK(change_window_title), self);
177
178         g_object_unref(builder);
179         g_object_unref(uimanager);
180 }
181
182 /* PUBLIC FUNCTIONS */
183
184 GtkWidget *
185 chimara_player_new(void)
186 {
187     return GTK_WIDGET(g_object_new(CHIMARA_TYPE_PLAYER,
188                 "type", GTK_WINDOW_TOPLEVEL,
189                 NULL));
190 }
191
192 /* GLADE CALLBACKS */
193
194 #if 0
195 /* If a game is running in @glk, warn the user that they will quit the currently
196 running game if they open a new one. Returns TRUE if no game was running.
197 Returns FALSE if the user cancelled. Returns TRUE and shuts down the running
198 game if the user wishes to continue. */
199 static gboolean
200 confirm_open_new_game(ChimaraGlk *glk)
201 {
202         g_return_val_if_fail(glk && CHIMARA_IS_GLK(glk), FALSE);
203         
204         GtkWindow *window = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(glk)));
205         
206         if(chimara_glk_get_running(glk)) {
207                 GtkWidget *dialog = gtk_message_dialog_new(window,
208                     GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
209                     GTK_MESSAGE_WARNING,
210                     GTK_BUTTONS_CANCEL,
211                     _("Are you sure you want to open a new game?"));
212                 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
213                     _("If you open a new game, you will quit the one you are currently playing."));
214                 gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_OPEN, GTK_RESPONSE_OK);
215                 gint response = gtk_dialog_run(GTK_DIALOG(dialog));
216                 gtk_widget_destroy(dialog);
217                 
218                 if(response != GTK_RESPONSE_OK)
219                         return FALSE;
220
221                 chimara_glk_stop(glk);
222                 chimara_glk_wait(glk);
223         }
224         return TRUE;
225 }
226 #endif
227
228 void
229 on_stop_activate(GtkAction *action, ChimaraPlayer *player)
230 {
231         chimara_glk_stop(CHIMARA_GLK(player->glk));
232 }
233
234 void
235 on_copy_activate(GtkAction *action, ChimaraPlayer *player)
236 {
237         GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(player));
238         /* Call "copy clipboard" on any widget that defines it */
239         if(GTK_IS_LABEL(focus) || GTK_IS_ENTRY(focus) || GTK_IS_TEXT_VIEW(focus))
240                 g_signal_emit_by_name(focus, "copy-clipboard");
241 }
242
243 void
244 on_paste_activate(GtkAction *action, ChimaraPlayer *player)
245 {
246         GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(player));
247         /* Call "paste clipboard" on any widget that defines it */
248         if(GTK_IS_ENTRY(focus) || GTK_IS_TEXT_VIEW(focus))
249                 g_signal_emit_by_name(focus, "paste-clipboard");
250 }
251
252 void
253 on_toolbar_toggled(GtkToggleAction *action, ChimaraPlayer *player)
254 {
255         if(gtk_toggle_action_get_active(action))
256                 gtk_widget_show(player->toolbar);
257         else
258                 gtk_widget_hide(player->toolbar);
259 }
260
261 void
262 on_undo_activate(GtkAction *action, ChimaraPlayer *player)
263 {
264         chimara_glk_feed_line_input(CHIMARA_GLK(player->glk), "undo");
265 }
266
267 void 
268 on_save_activate(GtkAction *action, ChimaraPlayer *player)
269 {
270         chimara_glk_feed_line_input(CHIMARA_GLK(player->glk), "save");
271 }
272
273 void 
274 on_restore_activate(GtkAction *action, ChimaraPlayer *player)
275 {
276         chimara_glk_feed_line_input(CHIMARA_GLK(player->glk), "restore");
277 }
278
279 void 
280 on_restart_activate(GtkAction *action, ChimaraPlayer *player)
281 {
282         chimara_glk_feed_line_input(CHIMARA_GLK(player->glk), "restart");
283 }
284
285 void 
286 on_quit_activate(GtkAction *action, ChimaraPlayer *player)
287 {
288         chimara_glk_feed_line_input(CHIMARA_GLK(player->glk), "quit");
289 }
290