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