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 if os.path.exists('chimara-config'):
19 keyfile = 'chimara-config'
21 keyfile = os.path.expanduser('~/.chimara/config')
23 # This only works on my custom-built gobject-introspection; opened
25 backend = Gio.keyfile_settings_backend_new(keyfile, "/org/chimara-if/player/", None)
26 except AttributeError:
28 self.prefs_settings = Gio.Settings('org.chimara-if.player.preferences', backend=backend)
29 self.state_settings = Gio.Settings('org.chimara-if.player.state', backend=backend)
31 builder = Gtk.Builder()
32 builder.add_from_file('chimara.ui')
33 self.window = builder.get_object('chimara')
34 self.aboutwindow = builder.get_object('aboutwindow')
35 self.prefswindow = builder.get_object('prefswindow')
36 actiongroup = builder.get_object('actiongroup')
38 # Set the default value of the "View/Toolbar" menu item upon creation
39 # of a new window to the "show-toolbar-default" setting, but bind the
40 # setting one-way only - we don't want toolbars to disappear suddenly
41 toolbar_action = builder.get_object('toolbar')
42 toolbar_action.active = \
43 self.state_settings.get_boolean('show-toolbar-default')
44 self.state_settings.bind('show-toolbar-default', toolbar_action,
45 'active', Gio.SettingsBindFlags.SET)
47 filt = Gtk.RecentFilter()
48 for pattern in ['*.z[1-8]', '*.[zg]lb', '*.[zg]blorb', '*.ulx', '*.blb',
50 filt.add_pattern(pattern)
51 recent = builder.get_object('recent')
52 recent.add_filter(filt)
54 uimanager = Gtk.UIManager()
55 uimanager.add_ui_from_file('chimara.menus')
56 uimanager.insert_action_group(actiongroup, 0)
57 menubar = uimanager.get_widget('/menubar')
58 toolbar = uimanager.get_widget('/toolbar')
59 toolbar.no_show_all = True
60 if toolbar_action.active:
65 # Connect the accelerators
66 accels = uimanager.get_accel_group()
67 self.window.add_accel_group(accels)
69 self.glk = Chimara.IF()
70 self.glk.props.ignore_errors = True
71 css_file = _maybe(self.prefs_settings.get_value('css-file'))
73 css_file = 'style.css'
74 self.glk.set_css_from_file(css_file)
76 vbox = builder.get_object('vbox')
77 vbox.pack_end(self.glk, True, True, 0)
78 vbox.pack_start(menubar, False, False, 0)
79 vbox.pack_start(toolbar, False, False, 0)
81 builder.connect_signals(self)
83 self.glk.connect('notify::program-name', self.change_window_title)
84 self.glk.connect('notify::story-name', self.change_window_title)
86 # Create preferences window
89 def change_window_title(self, glk, pspec, data=None):
90 if glk.props.program_name is None:
92 elif glk.props.story_name is None:
93 title = "{interp} - Chimara".format(interp=glk.props.program_name)
95 title = "{interp} - {story} - Chimara".format(
96 interp=glk.props.program_name,
97 story=glk.props.story_name)
98 self.window.props.title = title
100 def on_open_activate(self, action, data=None):
101 if not self.confirm_open_new_game():
104 dialog = Gtk.FileChooserDialog(_('Open Game'), self.window,
105 Gtk.FileChooserAction.OPEN,
106 (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
107 Gtk.STOCK_OPEN, Gtk.ResponseType.ACCEPT))
109 # Get last opened path
110 path = _maybe(self.state_settings.get_value('last-open-path'))
112 dialog.set_current_folder(path)
114 response = dialog.run()
116 if response != Gtk.ResponseType.ACCEPT:
119 gamefile = dialog.get_file()
120 self.search_for_graphics_file(gamefile.get_path())
122 self.glk.run_game_file(gamefile)
123 except GLib.Error as e:
124 error_dialog(self.window, _('Could not open game file {filename}: {errmsg}').format(filename=gamefile.get_path(), errmsg=e.message))
127 path = dialog.get_current_folder()
129 self.state_settings.last_open_path = path
131 # Add file to recent files list
132 manager = Gtk.RecentManager.get_default()
133 uri = gamefile.get_uri()
134 manager.add_item(uri)
138 def on_recent_item_activated(self, chooser, data=None):
139 if not self.confirm_open_new_game():
142 uri = chooser.get_current_uri()
143 gamefile = Gio.file_new_for_uri(uri)
145 self.search_for_graphics_file(gamefile.get_path())
147 self.glk.run_game_file(gamefile)
148 except GLib.Error as e:
149 error_dialog(self.window,
150 _('Could not open game file {filename}: {errmsg}').format(
151 filename=gamefile.get_basename(),
155 # Add file to recent files list again, this updates it to most recently
157 manager = Gtk.RecentManager.get_default()
158 manager.add_item(uri)
160 def on_stop_activate(self, action, data=None):
163 def on_quit_chimara_activate(self, action, data=None):
166 def on_copy_activate(self, action, data=None):
167 focus = self.window.get_focus()
168 # Call "copy clipboard" on any widget that defines it
169 if (isinstance(focus, Gtk.Label)
170 or isinstance(focus, Gtk.Entry)
171 or isinstance(focus, Gtk.TextView)):
172 focus.emit('copy-clipboard')
174 def on_paste_activate(self, action, data=None):
175 focus = self.window.get_focus()
176 # Call "paste clipboard" on any widget that defines it
177 if isinstance(focus, Gtk.Entry) or isinstance(focus, Gtk.TextView):
178 focus.emit('paste-clipboard')
180 def on_preferences_activate(self, action, data=None):
181 self.prefswindow.present()
183 def on_toolbar_toggled(self, action, data=None):
184 if action.get_active():
189 def on_undo_activate(self, action, data=None):
190 self.glk.feed_line_input('undo')
192 def on_save_activate(self, action, data=None):
193 self.glk.feed_line_input('save')
195 def on_restore_activate(self, action, data=None):
196 self.glk.feed_line_input('restore')
198 def on_restart_activate(self, action, data=None):
199 self.glk.feed_line_input('restart')
201 def on_quit_activate(self, action, data=None):
202 self.glk.feed_line_input('quit')
204 def on_about_activate(self, action, data=None):
205 self.aboutwindow.set_version(config.PACKAGE_VERSION)
206 self.aboutwindow.present()
208 def on_window_delete_event(self, widget, event, data=None):
212 def confirm_open_new_game(self):
214 If a game is running in the Glk widget, warn the user that they will
215 quit the currently running game if they open a new one. Returns True if
216 no game was running. Returns False if the user cancelled. Returns True
217 and shuts down the running game if the user wishes to continue.
219 if not self.glk.props.running:
222 dialog = Gtk.MessageDialog(self.window,
223 Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
224 Gtk.MessageType.WARNING, Gtk.ButtonsType.CANCEL,
225 _("Are you sure you want to open a new game?"))
226 dialog.format_secondary_text(
227 _("If you open a new game, you will quit the one you are "
228 "currently playing."))
229 dialog.add_button(Gtk.STOCK_OPEN, Gtk.ResponseType.OK)
230 response = dialog.run()
233 if response != Gtk.ResponseType.OK:
240 def search_for_graphics_file(self, filename):
241 """Internal function: See if there is a corresponding graphics file"""
243 # First get the name of the story file
244 base = os.path.basename(filename)
245 base_noext = os.path.splitext(base)[0]
247 # Check in the stored resource path, if set
248 resource_path = _maybe(self.prefs_settings.get_value('resource-path'))
250 # Otherwise check in the current directory
251 if resource_path is None:
252 resource_path = os.path.dirname(filename)
254 blorbfile = os.path.join(resource_path, base_noext + '.blb')
255 if os.path.exists(blorbfile):
256 self.glk.graphics_file = blorbfile
258 # Various signal handlers for GtkBuilder file
259 def gtk_widget_hide(self, *args):
260 return Gtk.Widget.hide(*args)
262 def gtk_widget_hide_on_delete(self, *args):
263 return Gtk.Widget.hide_on_delete(*args)
265 def dummy_handler(self, *args):
268 on_resource_file_set = dummy_handler
269 on_interpreter_cell_changed = dummy_handler
270 on_toggle_underline = dummy_handler
271 on_toggle_italic = dummy_handler
272 on_toggle_bold = dummy_handler
273 on_toggle_justify = dummy_handler
274 on_toggle_right = dummy_handler
275 on_toggle_center = dummy_handler
276 on_toggle_left = dummy_handler
277 on_background_color_set = dummy_handler
278 on_foreground_color_set = dummy_handler
279 on_font_set = dummy_handler
280 on_css_filechooser_file_set = dummy_handler
284 """Gets a maybe value from a GVariant - not handled in PyGI"""
285 v = variant.get_maybe()
291 def error_dialog(parent, message):
292 dialog = Gtk.MessageDialog(parent, Gtk.DialogFlags.DESTROY_WITH_PARENT,
293 Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, message)
297 if __name__ == '__main__':
301 player.window.show_all()
303 if len(sys.argv) == 3:
304 player.glk.props.graphics_file = sys.argv[2]
305 if len(sys.argv) >= 2:
307 player.glk.run_game(sys.argv[1])
308 except GLib.Error as e:
309 error_dialog(player.window,
310 _("Error starting Glk library: {errmsg}").format(