5 from gi.repository import GObject, GLib, Gdk, Gio, Gtk, Chimara
8 # FIXME: Dummy translation function, for now
12 class Player(GObject.GObject):
13 __gtype_name__ = 'ChimaraPlayer'
16 super(Player, self).__init__()
18 # FIXME: should use the Keyfile backend, but that's not available from
20 self.prefs_settings = Gio.Settings('org.chimara-if.player.preferences')
21 self.state_settings = Gio.Settings('org.chimara-if.player.state')
23 builder = Gtk.Builder()
24 builder.add_from_file('chimara.ui')
25 self.window = builder.get_object('chimara')
26 self.aboutwindow = builder.get_object('aboutwindow')
27 self.prefswindow = builder.get_object('prefswindow')
28 actiongroup = builder.get_object('actiongroup')
30 # Set the default value of the "View/Toolbar" menu item upon creation
31 # of a new window to the "show-toolbar-default" setting, but bind the
32 # setting one-way only - we don't want toolbars to disappear suddenly
33 toolbar_action = builder.get_object('toolbar')
34 toolbar_action.active = \
35 self.state_settings.get_boolean('show-toolbar-default')
36 self.state_settings.bind('show-toolbar-default', toolbar_action,
37 'active', Gio.SettingsBindFlags.SET)
39 filt = Gtk.RecentFilter()
40 for pattern in ['*.z[1-8]', '*.[zg]lb', '*.[zg]blorb', '*.ulx', '*.blb',
42 filt.add_pattern(pattern)
43 recent = builder.get_object('recent')
44 recent.add_filter(filt)
46 uimanager = Gtk.UIManager()
47 uimanager.add_ui_from_file('chimara.menus')
48 uimanager.insert_action_group(actiongroup, 0)
49 menubar = uimanager.get_widget('/menubar')
50 toolbar = uimanager.get_widget('/toolbar')
51 toolbar.no_show_all = True
52 if toolbar_action.active:
57 # Connect the accelerators
58 accels = uimanager.get_accel_group()
59 self.window.add_accel_group(accels)
61 self.glk = Chimara.IF()
62 self.glk.props.ignore_errors = True
63 self.glk.set_css_from_file('style.css')
65 vbox = builder.get_object('vbox')
66 vbox.pack_end(self.glk, True, True, 0)
67 vbox.pack_start(menubar, False, False, 0)
68 vbox.pack_start(toolbar, False, False, 0)
70 builder.connect_signals(self)
72 self.glk.connect('notify::program-name', self.change_window_title)
73 self.glk.connect('notify::story-name', self.change_window_title)
75 # Create preferences window
78 def change_window_title(self, glk, pspec, data=None):
79 if glk.props.program_name is None:
81 elif glk.props.story_name is None:
82 title = "{interp} - Chimara".format(interp=glk.props.program_name)
84 title = "{interp} - {story} - Chimara".format(
85 interp=glk.props.program_name,
86 story=glk.props.story_name)
87 self.window.props.title = title
89 def on_open_activate(self, action, data=None):
90 if not self.confirm_open_new_game():
93 dialog = Gtk.FileChooserDialog(_('Open Game'), self.window,
94 Gtk.FileChooserAction.OPEN,
95 (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
96 Gtk.STOCK_OPEN, Gtk.ResponseType.ACCEPT))
98 # Get last opened path
99 path = _maybe(self.state_settings.get_value('last-open-path'))
101 dialog.set_current_folder(path)
103 response = dialog.run()
105 if response != Gtk.ResponseType.ACCEPT:
108 gamefile = dialog.get_file()
109 self.search_for_graphics_file(gamefile.get_path())
111 self.glk.run_game_file(gamefile)
112 except GLib.Error as e:
113 error_dialog(self.window, _('Could not open game file {filename}: {errmsg}').format(filename=gamefile.get_path(), errmsg=e.message))
116 path = dialog.get_current_folder()
118 self.state_settings.last_open_path = path
120 # Add file to recent files list
121 manager = Gtk.RecentManager.get_default()
122 uri = gamefile.get_uri()
123 manager.add_item(uri)
127 def on_recent_item_activated(self, chooser, data=None):
128 if not self.confirm_open_new_game():
131 uri = chooser.get_current_uri()
132 gamefile = Gio.file_new_for_uri(uri)
134 self.search_for_graphics_file(gamefile.get_path())
136 self.glk.run_game_file(gamefile)
137 except GLib.Error as e:
138 error_dialog(self.window,
139 _('Could not open game file {filename}: {errmsg}').format(
140 filename=gamefile.get_basename(),
144 # Add file to recent files list again, this updates it to most recently
146 manager = Gtk.RecentManager.get_default()
147 manager.add_item(uri)
149 def on_stop_activate(self, action, data=None):
152 def on_quit_chimara_activate(self, action, data=None):
155 def on_copy_activate(self, action, data=None):
156 focus = self.window.get_focus()
157 # Call "copy clipboard" on any widget that defines it
158 if (isinstance(focus, Gtk.Label)
159 or isinstance(focus, Gtk.Entry)
160 or isinstance(focus, Gtk.TextView)):
161 focus.emit('copy-clipboard')
163 def on_paste_activate(self, action, data=None):
164 focus = self.window.get_focus()
165 # Call "paste clipboard" on any widget that defines it
166 if isinstance(focus, Gtk.Entry) or isinstance(focus, Gtk.TextView):
167 focus.emit('paste-clipboard')
169 def on_preferences_activate(self, action, data=None):
170 self.prefswindow.present()
172 def on_toolbar_toggled(self, action, data=None):
173 if action.get_active():
178 def on_undo_activate(self, action, data=None):
179 self.glk.feed_line_input('undo')
181 def on_save_activate(self, action, data=None):
182 self.glk.feed_line_input('save')
184 def on_restore_activate(self, action, data=None):
185 self.glk.feed_line_input('restore')
187 def on_restart_activate(self, action, data=None):
188 self.glk.feed_line_input('restart')
190 def on_quit_activate(self, action, data=None):
191 self.glk.feed_line_input('quit')
193 def on_about_activate(self, action, data=None):
194 self.aboutwindow.set_version(config.PACKAGE_VERSION)
195 self.aboutwindow.present()
197 def on_window_delete_event(self, widget, event, data=None):
201 def confirm_open_new_game(self):
203 If a game is running in the Glk widget, warn the user that they will
204 quit the currently running game if they open a new one. Returns True if
205 no game was running. Returns False if the user cancelled. Returns True
206 and shuts down the running game if the user wishes to continue.
208 if not self.glk.props.running:
211 dialog = Gtk.MessageDialog(self.window,
212 Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
213 Gtk.MessageType.WARNING, Gtk.ButtonsType.CANCEL,
214 _("Are you sure you want to open a new game?"))
215 dialog.format_secondary_text(
216 _("If you open a new game, you will quit the one you are "
217 "currently playing."))
218 dialog.add_button(Gtk.STOCK_OPEN, Gtk.ResponseType.OK)
219 response = dialog.run()
222 if response != Gtk.ResponseType.OK:
229 def search_for_graphics_file(self, filename):
230 """Internal function: See if there is a corresponding graphics file"""
232 # First get the name of the story file
233 base = os.path.basename(filename)
234 base_noext = os.path.splitext(base)[0]
236 # Check in the stored resource path, if set
237 resource_path = _maybe(self.prefs_settings.get_value('resource-path'))
239 # Otherwise check in the current directory
240 if resource_path is None:
241 resource_path = os.path.dirname(filename)
243 blorbfile = os.path.join(resource_path, base_noext + '.blb')
244 if os.path.exists(blorbfile):
245 self.glk.graphics_file = blorbfile
247 # Various signal handlers for GtkBuilder file
248 def gtk_widget_hide(self, *args):
249 return Gtk.Widget.hide(*args)
251 def gtk_widget_hide_on_delete(self, *args):
252 return Gtk.Widget.hide_on_delete(*args)
254 def dummy_handler(self, *args):
257 on_resource_file_set = dummy_handler
258 on_interpreter_cell_changed = dummy_handler
259 on_toggle_underline = dummy_handler
260 on_toggle_italic = dummy_handler
261 on_toggle_bold = dummy_handler
262 on_toggle_justify = dummy_handler
263 on_toggle_right = dummy_handler
264 on_toggle_center = dummy_handler
265 on_toggle_left = dummy_handler
266 on_background_color_set = dummy_handler
267 on_foreground_color_set = dummy_handler
268 on_font_set = dummy_handler
269 on_css_filechooser_file_set = dummy_handler
273 """Gets a maybe value from a GVariant - not handled in PyGI"""
274 v = variant.get_maybe()
280 def error_dialog(parent, message):
281 dialog = Gtk.MessageDialog(parent, Gtk.DialogFlags.DESTROY_WITH_PARENT,
282 Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, message)
286 if __name__ == '__main__':
290 player.window.show_all()
292 if len(sys.argv) == 3:
293 player.glk.props.graphics_file = sys.argv[2]
294 if len(sys.argv) >= 2:
296 player.glk.run_game(sys.argv[1])
297 except GLib.Error as e:
298 error_dialog(player.window,
299 _("Error starting Glk library: {errmsg}").format(