def __init__(self): cb = self.callbacks = UICallbacks() self.help = UIHelp() self.actions = gtk.ActionGroup("All") # name, icon ID, label, accel, tooltip, callback self.actions.add_toggle_actions(( # TODO: how to know when these are changed from inside Hatari? ("recanim", gtk.STOCK_MEDIA_RECORD, "Record animation", "<Ctrl>A", "Record animation", cb.recanim), ("recsound", gtk.STOCK_MEDIA_RECORD, "Record sound", "<Ctrl>W", "Record YM/Wav", cb.recsound), ("pause", gtk.STOCK_MEDIA_PAUSE, "Pause", "<Ctrl>P", "Pause Hatari to save battery", cb.pause), ("forward", gtk.STOCK_MEDIA_FORWARD, "Forward", "<Ctrl>F", "Whether to fast forward Hatari (needs fast machine)", cb.set_fastforward, cb.get_fastforward()) )) # name, icon ID, label, accel, tooltip, callback self.actions.add_actions(( ("load", gtk.STOCK_OPEN, "Load snapshot...", "<Ctrl>L", "Load emulation snapshot", cb.load), ("save", gtk.STOCK_SAVE, "Save snapshot", "<Ctrl>S", "Save emulation snapshot", cb.save), ("shot", gtk.STOCK_MEDIA_RECORD, "Grab screenshot", "<Ctrl>G", "Grab a screenshot", cb.screenshot), ("quit", gtk.STOCK_QUIT, "Quit", "<Ctrl>Q", "Quit Hatari UI", cb.quit), ("run", gtk.STOCK_MEDIA_PLAY, "Run", "<Ctrl>R", "(Re-)run Hatari", cb.run), ("full", gtk.STOCK_FULLSCREEN, "Fullscreen", "<Ctrl>U", "Toggle whether Hatari is fullscreen", cb.set_fullscreen), ("input", gtk.STOCK_SPELL_CHECK, "Inputs...", "<Ctrl>N", "Simulate text input and mouse clicks", cb.inputs), ("reset", gtk.STOCK_REFRESH, "Reset...", "<Ctrl>E", "Warm or cold reset Hatari", cb.reset), ("display", gtk.STOCK_PREFERENCES, "Display...", "<Ctrl>Y", "Display settings", cb.display), ("floppy", gtk.STOCK_FLOPPY, "Floppies...", "<Ctrl>D", "Floppy images", cb.floppydisk), ("harddisk", gtk.STOCK_HARDDISK, "Hard disks...", "<Ctrl>H", "Hard disk images and directories", cb.harddisk), ("joystick", gtk.STOCK_CONNECT, "Joysticks...", "<Ctrl>J", "Joystick settings", cb.joystick), ("machine", gtk.STOCK_HARDDISK, "Machine...", "<Ctrl>M", "Hatari st/e/tt/falcon configuration", cb.machine), ("device", gtk.STOCK_PRINT, "Peripherals...", "<Ctrl>V", "Toggle Midi, Printer, RS232 peripherals", cb.peripheral), ("sound", gtk.STOCK_PROPERTIES, "Sound...", "<Ctrl>O", "Sound settings", cb.sound), ("path", gtk.STOCK_DIRECTORY, "Paths...", None, "Device & save file paths", cb.path), ("lconfig", gtk.STOCK_OPEN, "Load config...", "<Ctrl>C", "Load configuration", self.config_load), ("sconfig", gtk.STOCK_SAVE_AS, "Save config as...", None, "Save configuration", cb.config_save), ("debug", gtk.STOCK_FIND, "Debugger...", "<Ctrl>B", "Activate Hatari debugger", cb.debugger), ("trace", gtk.STOCK_EXECUTE, "Trace settings...", "<Ctrl>T", "Hatari tracing setup", cb.trace), ("manual", None, "Hatari manual", None, None, self.help.view_hatari_manual), ("compatibility", None, "Hatari compatibility list", None, None, self.help.view_hatari_compatibility), ("release", None, "Hatari release notes", None, None, self.help.view_hatari_releasenotes), ("todo", None, "Hatari TODO", None, None, self.help.view_hatari_todo), ("bugs", None, "Report a bug", None, None, self.help.view_hatari_bugs), ("mails", None, "Hatari mailing lists", None, None, self.help.view_hatari_mails), ("changes", None, "Latest Hatari changes", None, None, self.help.view_hatari_repository), ("authors", None, "Hatari authors", None, None, self.help.view_hatari_authors), ("hatari", None, "Hatari home page", None, None, self.help.view_hatari_page), ("hatariui", None, "Hatari UI home page", None, None, self.help.view_hatariui_page), ("about", gtk.STOCK_INFO, "Hatari UI info", "<Ctrl>I", "Hatari UI information", cb.about) )) self.action_names = [x.get_name() for x in self.actions.list_actions()] # no actions set yet to panels or toolbars self.toolbars = {} self.panels = []
def __init__(self): cb = self.callbacks = UICallbacks() self.help = UIHelp() self.actions = gtk.ActionGroup("All") # name, icon ID, label, accel, tooltip, callback self.actions.add_toggle_actions(( # TODO: how to know when these are changed from inside Previous? ("recanim", gtk.STOCK_MEDIA_RECORD, "Record animation", "<Ctrl>A", "Record animation", cb.recanim), ("recsound", gtk.STOCK_MEDIA_RECORD, "Record sound", "<Ctrl>W", "Record YM/Wav", cb.recsound), ("pause", gtk.STOCK_MEDIA_PAUSE, "Pause", "<Ctrl>P", "Pause Previous to save battery", cb.pause), ("forward", gtk.STOCK_MEDIA_FORWARD, "Forward", "<Ctrl>F", "Whether to fast forward Previous (needs fast machine)", cb.set_fastforward, cb.get_fastforward()) )) # name, icon ID, label, accel, tooltip, callback self.actions.add_actions(( ("load", gtk.STOCK_OPEN, "Load snapshot...", "<Ctrl>L", "Load emulation snapshot", cb.load), ("save", gtk.STOCK_SAVE, "Save snapshot", "<Ctrl>S", "Save emulation snapshot", cb.save), ("shot", gtk.STOCK_MEDIA_RECORD, "Grab screenshot", "<Ctrl>G", "Grab a screenshot", cb.screenshot), ("quit", gtk.STOCK_QUIT, "Quit", "<Ctrl>Q", "Quit Previous UI", cb.quit), ("run", gtk.STOCK_MEDIA_PLAY, "Run", "<Ctrl>R", "(Re-)run Previous", cb.run), ("full", gtk.STOCK_FULLSCREEN, "Fullscreen", "<Ctrl>U", "Toggle whether Previous is fullscreen", cb.set_fullscreen), ("input", gtk.STOCK_SPELL_CHECK, "Inputs...", "<Ctrl>N", "Simulate text input and mouse clicks", cb.inputs), ("reset", gtk.STOCK_REFRESH, "Reset...", "<Ctrl>E", "Warm or cold reset Previous", cb.reset), ("display", gtk.STOCK_PREFERENCES, "Display...", "<Ctrl>Y", "Display settings", cb.display), ("floppy", gtk.STOCK_FLOPPY, "Floppies...", "<Ctrl>D", "Floppy images", cb.floppydisk), ("harddisk", gtk.STOCK_HARDDISK, "Hard disks...", "<Ctrl>H", "Hard disk images and directories", cb.harddisk), ("joystick", gtk.STOCK_CONNECT, "Joysticks...", "<Ctrl>J", "Joystick settings", cb.joystick), ("machine", gtk.STOCK_HARDDISK, "Machine...", "<Ctrl>M", "Previous st/e/tt/falcon configuration", cb.machine), ("device", gtk.STOCK_PRINT, "Peripherals...", "<Ctrl>V", "Toggle Midi, Printer, RS232 peripherals", cb.peripheral), ("sound", gtk.STOCK_PROPERTIES, "Sound...", "<Ctrl>O", "Sound settings", cb.sound), ("path", gtk.STOCK_DIRECTORY, "Paths...", None, "Device & save file paths", cb.path), ("lconfig", gtk.STOCK_OPEN, "Load config...", "<Ctrl>C", "Load configuration", self.config_load), ("sconfig", gtk.STOCK_SAVE_AS, "Save config as...", None, "Save configuration", cb.config_save), ("debug", gtk.STOCK_FIND, "Debugger...", "<Ctrl>B", "Activate Previous debugger", cb.debugger), ("trace", gtk.STOCK_EXECUTE, "Trace settings...", "<Ctrl>T", "Previous tracing setup", cb.trace), ("manual", None, "Previous manual", None, None, self.help.view_previous_manual), ("compatibility", None, "Previous compatibility list", None, None, self.help.view_previous_compatibility), ("release", None, "Previous release notes", None, None, self.help.view_previous_releasenotes), ("todo", None, "Previous TODO", None, None, self.help.view_previous_todo), ("bugs", None, "Report a bug", None, None, self.help.view_previous_bugs), ("mails", None, "Previous mailing lists", None, None, self.help.view_previous_mails), ("changes", None, "Latest Previous changes", None, None, self.help.view_previous_repository), ("authors", None, "Previous authors", None, None, self.help.view_previous_authors), ("previous", None, "Previous home page", None, None, self.help.view_previous_page), ("previousui", None, "Previous UI home page", None, None, self.help.view_previousui_page), ("about", gtk.STOCK_INFO, "Previous UI info", "<Ctrl>I", "Previous UI information", cb.about) )) self.action_names = [x.get_name() for x in self.actions.list_actions()] # no actions set yet to panels or toolbars self.toolbars = {} self.panels = []
class UIActions: def __init__(self): cb = self.callbacks = UICallbacks() self.help = UIHelp() self.actions = gtk.ActionGroup("All") # name, icon ID, label, accel, tooltip, callback self.actions.add_toggle_actions(( # TODO: how to know when these are changed from inside Hatari? ("recanim", gtk.STOCK_MEDIA_RECORD, "Record animation", "<Ctrl>A", "Record animation", cb.recanim), ("recsound", gtk.STOCK_MEDIA_RECORD, "Record sound", "<Ctrl>W", "Record YM/Wav", cb.recsound), ("pause", gtk.STOCK_MEDIA_PAUSE, "Pause", "<Ctrl>P", "Pause Hatari to save battery", cb.pause), ("forward", gtk.STOCK_MEDIA_FORWARD, "Forward", "<Ctrl>F", "Whether to fast forward Hatari (needs fast machine)", cb.set_fastforward, cb.get_fastforward()) )) # name, icon ID, label, accel, tooltip, callback self.actions.add_actions(( ("load", gtk.STOCK_OPEN, "Load snapshot...", "<Ctrl>L", "Load emulation snapshot", cb.load), ("save", gtk.STOCK_SAVE, "Save snapshot", "<Ctrl>S", "Save emulation snapshot", cb.save), ("shot", gtk.STOCK_MEDIA_RECORD, "Grab screenshot", "<Ctrl>G", "Grab a screenshot", cb.screenshot), ("quit", gtk.STOCK_QUIT, "Quit", "<Ctrl>Q", "Quit Hatari UI", cb.quit), ("run", gtk.STOCK_MEDIA_PLAY, "Run", "<Ctrl>R", "(Re-)run Hatari", cb.run), ("full", gtk.STOCK_FULLSCREEN, "Fullscreen", "<Ctrl>U", "Toggle whether Hatari is fullscreen", cb.set_fullscreen), ("input", gtk.STOCK_SPELL_CHECK, "Inputs...", "<Ctrl>N", "Simulate text input and mouse clicks", cb.inputs), ("reset", gtk.STOCK_REFRESH, "Reset...", "<Ctrl>E", "Warm or cold reset Hatari", cb.reset), ("display", gtk.STOCK_PREFERENCES, "Display...", "<Ctrl>Y", "Display settings", cb.display), ("floppy", gtk.STOCK_FLOPPY, "Floppies...", "<Ctrl>D", "Floppy images", cb.floppydisk), ("harddisk", gtk.STOCK_HARDDISK, "Hard disks...", "<Ctrl>H", "Hard disk images and directories", cb.harddisk), ("joystick", gtk.STOCK_CONNECT, "Joysticks...", "<Ctrl>J", "Joystick settings", cb.joystick), ("machine", gtk.STOCK_HARDDISK, "Machine...", "<Ctrl>M", "Hatari st/e/tt/falcon configuration", cb.machine), ("device", gtk.STOCK_PRINT, "Peripherals...", "<Ctrl>V", "Toggle Midi, Printer, RS232 peripherals", cb.peripheral), ("sound", gtk.STOCK_PROPERTIES, "Sound...", "<Ctrl>O", "Sound settings", cb.sound), ("path", gtk.STOCK_DIRECTORY, "Paths...", None, "Device & save file paths", cb.path), ("lconfig", gtk.STOCK_OPEN, "Load config...", "<Ctrl>C", "Load configuration", self.config_load), ("sconfig", gtk.STOCK_SAVE_AS, "Save config as...", None, "Save configuration", cb.config_save), ("debug", gtk.STOCK_FIND, "Debugger...", "<Ctrl>B", "Activate Hatari debugger", cb.debugger), ("trace", gtk.STOCK_EXECUTE, "Trace settings...", "<Ctrl>T", "Hatari tracing setup", cb.trace), ("manual", None, "Hatari manual", None, None, self.help.view_hatari_manual), ("compatibility", None, "Hatari compatibility list", None, None, self.help.view_hatari_compatibility), ("release", None, "Hatari release notes", None, None, self.help.view_hatari_releasenotes), ("todo", None, "Hatari TODO", None, None, self.help.view_hatari_todo), ("mails", None, "Hatari mailing lists", None, None, self.help.view_hatari_mails), ("changes", None, "Latest Hatari changes", None, None, self.help.view_hatari_repository), ("authors", None, "Hatari authors", None, None, self.help.view_hatari_authors), ("hatari", None, "Hatari home page", None, None, self.help.view_hatari_page), ("hatariui", None, "Hatari UI home page", None, None, self.help.view_hatariui_page), ("about", gtk.STOCK_INFO, "Hatari UI info", "<Ctrl>I", "Hatari UI information", cb.about) )) self.action_names = [x.get_name() for x in self.actions.list_actions()] # no actions set yet to panels or toolbars self.toolbars = {} self.panels = [] def config_load(self, widget): # user loads a new configuration? if self.callbacks.config_load(widget): print("TODO: reset toggle actions") # ----- toolbar / panel additions --------- def set_actions(self, action_str, place): "set_actions(actions,place) -> error string, None if all OK" actions = action_str.split(",") for action in actions: if action in self.action_names: # regular action continue if action in self.panels: # user specified panel continue if action in ("close", ">"): if place != "panel": return "'close' and '>' can be only in a panel" continue if action == "|": # divider continue if action.find("=") >= 0: # special keycode/string action continue return "unrecognized action '%s'" % action if place in ("left", "right", "top", "bottom"): self.toolbars[place] = actions return None if place == "panel": if len(actions) < 3: return "panel has too few items to be useful" return None return "unknown actions position '%s'" % place def add_panel(self, spec): "add_panel(panel_specification) -> error string, None if all is OK" offset = spec.find(",") if offset <= 0: return "invalid panel specification '%s'" % spec name, panelcontrols = spec[:offset], spec[offset+1:] error = self.set_actions(panelcontrols, "panel") if error: return error if ",>," in panelcontrols: box = gtk.VBox() splitcontrols = panelcontrols.split(",>,") for controls in splitcontrols: box.add(self._get_container(controls.split(","))) else: box = self._get_container(panelcontrols.split(",")) self.panels.append(name) self.actions.add_actions( ((name, gtk.STOCK_ADD, name, None, name, self.callbacks.panel),), box ) return None def list_actions(self): yield ("|", "Separator between controls") yield (">", "Start next toolbar row in panel windows") # generate the list from action information for act in self.actions.list_actions(): note = act.get_property("tooltip") if not note: note = act.get_property("label") yield(act.get_name(), note) yield ("<panel name>", "Button for the specified panel window") yield ("<name>=<string/code>", "Synthetize string or single key <code>") # ------- panel special actions ----------- def _close_cb(self, widget): widget.get_toplevel().hide() # ------- key special action ----------- def _create_key_control(self, name, textcode): "Simulate Atari key press/release and string inserting" if not textcode: return None widget = gtk.ToolButton(gtk.STOCK_PASTE) widget.set_label(name) try: # part after "=" converts to an int? code = int(textcode, 0) widget.connect("clicked", self.callbacks.keypress, code) tip = "keycode: %d" % code except ValueError: # no, assume a string macro is wanted instead widget.connect("clicked", self.callbacks.textinsert, textcode) tip = "string '%s'" % textcode widget.set_tooltip_text("Insert " + tip) return widget def _get_container(self, actions, horiz = True): "return Gtk container with the specified actions or None for no actions" if not actions: return None #print("ACTIONS:", actions) if len(actions) > 1: bar = gtk.Toolbar() if horiz: bar.set_orientation(gtk.ORIENTATION_HORIZONTAL) else: bar.set_orientation(gtk.ORIENTATION_VERTICAL) bar.set_style(gtk.TOOLBAR_BOTH) # disable overflow menu to get toolbar sized correctly for panels bar.set_show_arrow(False) else: bar = None for action in actions: #print(action) offset = action.find("=") if offset >= 0: # handle "<name>=<keycode>" action specification name = action[:offset] text = action[offset+1:] widget = self._create_key_control(name, text) elif action == "|": widget = gtk.SeparatorToolItem() elif action == "close": if bar: widget = create_toolbutton(gtk.STOCK_CLOSE, self._close_cb) else: widget = create_button("Close", self._close_cb) else: widget = self.actions.get_action(action).create_tool_item() if not widget: continue if bar: if action != "|": widget.set_expand(True) bar.insert(widget, -1) if bar: return bar return widget # ------------- handling menu ------------- def _add_submenu(self, bar, title, items): submenu = gtk.Menu() for name in items: if name: action = self.actions.get_action(name) item = action.create_menu_item() else: item = gtk.SeparatorMenuItem() submenu.add(item) baritem = gtk.MenuItem(title, False) baritem.set_submenu(submenu) bar.add(baritem) def _get_menu(self): allmenus = ( ("File", ("load", "save", None, "shot", "recanim", "recsound", None, "quit")), ("Emulation", ("run", "pause", "forward", None, "full", None, "input", None, "reset")), ("Devices", ("display", "floppy", "harddisk", "joystick", "machine", "device", "sound")), ("Configuration", ("path", None, "lconfig", "sconfig")), ("Debug", ("debug", "trace")), ("Help", ("manual", "compatibility", "release", "todo", None, "mails", "changes", None, "authors", "hatari", "hatariui", "about",)) ) bar = gtk.MenuBar() for title, items in allmenus: self._add_submenu(bar, title, items) if self.panels: self._add_submenu(bar, "Panels", self.panels) return bar # ------------- run the whole UI ------------- def run(self, floppy, havemenu, fullscreen, embed): accelgroup = None # create menu? if havemenu: # this would steal keys from embedded Hatari if not embed: accelgroup = gtk.AccelGroup() for action in self.actions.list_actions(): action.set_accel_group(accelgroup) menu = self._get_menu() else: menu = None # create toolbars toolbars = { "left":None, "right":None, "top":None, "bottom":None} for side in ("left", "right"): if side in self.toolbars: toolbars[side] = self._get_container(self.toolbars[side], False) for side in ("top", "bottom"): if side in self.toolbars: toolbars[side] = self._get_container(self.toolbars[side], True) self.callbacks.create_ui(accelgroup, menu, toolbars, fullscreen, embed) self.help.set_mainwin(self.callbacks.mainwin) self.callbacks.set_floppy(floppy) # ugly, Hatari socket window ID can be gotten only # after Socket window is realized by gtk_main() gobject.idle_add(self.callbacks.run) gtk.main()