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)
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(ignore_errors=True)
70 css_file = _maybe(self.prefs_settings.get_value('css-file'))
72 css_file = 'style.css'
73 self.glk.set_css_from_file(css_file)
75 vbox = builder.get_object('vbox')
76 vbox.pack_end(self.glk, True, True, 0)
77 vbox.pack_start(menubar, False, False, 0)
78 vbox.pack_start(toolbar, False, False, 0)
80 builder.connect_signals(self)
82 self.glk.connect('notify::program-name', self.change_window_title)
83 self.glk.connect('notify::story-name', self.change_window_title)
85 # Create preferences window
88 def change_window_title(self, glk, pspec, data=None):
89 if glk.props.program_name is None:
91 elif glk.props.story_name is None:
92 title = "{interp} - Chimara".format(interp=glk.props.program_name)
94 title = "{interp} - {story} - Chimara".format(
95 interp=glk.props.program_name,
96 story=glk.props.story_name)
97 self.window.props.title = title
99 def on_open_activate(self, action, data=None):
100 if not self.confirm_open_new_game():
103 dialog = Gtk.FileChooserDialog(_('Open Game'), self.window,
104 Gtk.FileChooserAction.OPEN,
105 (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
106 Gtk.STOCK_OPEN, Gtk.ResponseType.ACCEPT))
108 # Get last opened path
109 path = _maybe(self.state_settings.get_value('last-open-path'))
111 dialog.set_current_folder(path)
113 response = dialog.run()
115 if response != Gtk.ResponseType.ACCEPT:
118 gamefile = dialog.get_file()
119 self.search_for_graphics_file(gamefile.get_path())
121 self.glk.run_game_file(gamefile)
122 except GLib.Error as e:
123 error_dialog(self.window, _('Could not open game file {filename}: {errmsg}').format(filename=gamefile.get_path(), errmsg=e.message))
126 path = dialog.get_current_folder()
128 self.state_settings.last_open_path = path
130 # Add file to recent files list
131 manager = Gtk.RecentManager.get_default()
132 uri = gamefile.get_uri()
133 manager.add_item(uri)
137 def on_recent_item_activated(self, chooser, data=None):
138 if not self.confirm_open_new_game():
141 uri = chooser.get_current_uri()
142 gamefile = Gio.file_new_for_uri(uri)
144 self.search_for_graphics_file(gamefile.get_path())
146 self.glk.run_game_file(gamefile)
147 except GLib.Error as e:
148 error_dialog(self.window,
149 _('Could not open game file {filename}: {errmsg}').format(
150 filename=gamefile.get_basename(),
154 # Add file to recent files list again, this updates it to most recently
156 manager = Gtk.RecentManager.get_default()
157 manager.add_item(uri)
159 def on_stop_activate(self, action, data=None):
162 def on_quit_chimara_activate(self, action, data=None):
165 def on_copy_activate(self, action, data=None):
166 focus = self.window.get_focus()
167 # Call "copy clipboard" on any widget that defines it
168 if (isinstance(focus, Gtk.Label)
169 or isinstance(focus, Gtk.Entry)
170 or isinstance(focus, Gtk.TextView)):
171 focus.emit('copy-clipboard')
173 def on_paste_activate(self, action, data=None):
174 focus = self.window.get_focus()
175 # Call "paste clipboard" on any widget that defines it
176 if isinstance(focus, Gtk.Entry) or isinstance(focus, Gtk.TextView):
177 focus.emit('paste-clipboard')
179 def on_preferences_activate(self, action, data=None):
180 self.prefswindow.present()
182 def on_toolbar_toggled(self, action, data=None):
183 if action.get_active():
188 def on_undo_activate(self, action, data=None):
189 self.glk.feed_line_input('undo')
191 def on_save_activate(self, action, data=None):
192 self.glk.feed_line_input('save')
194 def on_restore_activate(self, action, data=None):
195 self.glk.feed_line_input('restore')
197 def on_restart_activate(self, action, data=None):
198 self.glk.feed_line_input('restart')
200 def on_quit_activate(self, action, data=None):
201 self.glk.feed_line_input('quit')
203 def on_about_activate(self, action, data=None):
204 self.aboutwindow.set_version(config.PACKAGE_VERSION)
205 self.aboutwindow.present()
207 def on_window_delete_event(self, widget, event, data=None):
211 def confirm_open_new_game(self):
213 If a game is running in the Glk widget, warn the user that they will
214 quit the currently running game if they open a new one. Returns True if
215 no game was running. Returns False if the user cancelled. Returns True
216 and shuts down the running game if the user wishes to continue.
218 if not self.glk.props.running:
221 dialog = Gtk.MessageDialog(self.window,
222 Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
223 Gtk.MessageType.WARNING, Gtk.ButtonsType.CANCEL,
224 _("Are you sure you want to open a new game?"))
225 dialog.format_secondary_text(
226 _("If you open a new game, you will quit the one you are "
227 "currently playing."))
228 dialog.add_button(Gtk.STOCK_OPEN, Gtk.ResponseType.OK)
229 response = dialog.run()
232 if response != Gtk.ResponseType.OK:
239 def search_for_graphics_file(self, filename):
240 """Internal function: See if there is a corresponding graphics file"""
242 # First get the name of the story file
243 base = os.path.basename(filename)
244 base_noext = os.path.splitext(base)[0]
246 # Check in the stored resource path, if set
247 resource_path = _maybe(self.prefs_settings.get_value('resource-path'))
249 # Otherwise check in the current directory
250 if resource_path is None:
251 resource_path = os.path.dirname(filename)
253 blorbfile = os.path.join(resource_path, base_noext + '.blb')
254 if os.path.exists(blorbfile):
255 self.glk.graphics_file = blorbfile
257 # Various signal handlers for GtkBuilder file
258 def gtk_widget_hide(self, widget, *args):
259 return Gtk.Widget.hide(widget)
261 def gtk_widget_hide_on_delete(self, widget, *args):
262 return Gtk.Widget.hide_on_delete(widget)
264 def dummy_handler(self, *args):
267 on_resource_file_set = dummy_handler
268 on_interpreter_cell_changed = dummy_handler
269 on_toggle_underline = dummy_handler
270 on_toggle_italic = dummy_handler
271 on_toggle_bold = dummy_handler
272 on_toggle_justify = dummy_handler
273 on_toggle_right = dummy_handler
274 on_toggle_center = dummy_handler
275 on_toggle_left = dummy_handler
276 on_background_color_set = dummy_handler
277 on_foreground_color_set = dummy_handler
278 on_font_set = dummy_handler
279 on_css_filechooser_file_set = dummy_handler
283 """Gets a maybe value from a GVariant - not handled in PyGI"""
284 v = variant.get_maybe()
290 def error_dialog(parent, message):
291 dialog = Gtk.MessageDialog(parent, Gtk.DialogFlags.DESTROY_WITH_PARENT,
292 Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, message)
296 if __name__ == '__main__':
300 player.window.show_all()
302 if len(sys.argv) == 3:
303 player.glk.props.graphics_file = sys.argv[2]
304 if len(sys.argv) >= 2:
306 player.glk.run_game(sys.argv[1])
307 except GLib.Error as e:
308 error_dialog(player.window,
309 _("Error starting Glk library: {errmsg}").format(