Esempio n. 1
0
class UICallbacks:
    tmpconfpath = os.path.expanduser("~/.hatari/.tmp.cfg")
    def __init__(self):
        # Hatari and configuration
        self.hatari = Hatari()
        error = self.hatari.is_compatible()
        if error:
            ErrorDialog(None).run(error)
            sys.exit(-1)
            
        self.config = HatariConfigMapping(self.hatari)
        try:
            self.config.validate()
        except (KeyError, AttributeError):
            NoteDialog(None).run("Loading Hatari configuration failed!\nRetrying after saving Hatari configuration.")
            self.hatari.save_config()
            self.config = HatariConfigMapping(self.hatari)
            self.config.validate()
        
        # windows are created when needed
        self.mainwin = None
        self.hatariwin = None
        self.debugui = None
        self.panels = {}

        # dialogs are created when needed
        self.aboutdialog = None
        self.inputdialog = None
        self.tracedialog = None
        self.resetdialog = None
        self.quitdialog = None
        self.killdialog = None

        self.floppydialog = None
        self.harddiskdialog = None
        self.displaydialog = None
        self.joystickdialog = None
        self.machinedialog = None
        self.peripheraldialog = None
        self.sounddialog = None
        self.pathdialog = None
        
        # used by run()
        self.memstate = None
        self.floppy = None
        self.io_id = None

        # TODO: Hatari UI own configuration settings save/load
        self.tracepoints = None

    def _reset_config_dialogs(self):
        self.floppydialog = None
        self.harddiskdialog = None
        self.displaydialog = None
        self.joystickdialog = None
        self.machinedialog = None
        self.peripheraldialog = None
        self.sounddialog = None
        self.pathdialog = None
        
    # ---------- create UI ----------------
    def create_ui(self, accelgroup, menu, toolbars, fullscreen, embed):
        "create_ui(menu, toolbars, fullscreen, embed)"
        # add horizontal elements
        hbox = gtk.HBox()
        if toolbars["left"]:
            hbox.pack_start(toolbars["left"], False, True)
        if embed:
            self._add_uisocket(hbox)
        if toolbars["right"]:
            hbox.pack_start(toolbars["right"], False, True)
        # add vertical elements
        vbox = gtk.VBox()
        if menu:
            vbox.add(menu)
        if toolbars["top"]:
            vbox.pack_start(toolbars["top"], False, True)
        vbox.add(hbox)
        if toolbars["bottom"]:
            vbox.pack_start(toolbars["bottom"], False, True)
        # put them to main window
        mainwin = gtk.Window(gtk.WINDOW_TOPLEVEL)
        mainwin.set_title("%s %s" % (UInfo.name, UInfo.version))
        mainwin.set_icon_from_file(UInfo.icon)
        if accelgroup:
            mainwin.add_accel_group(accelgroup)
        if fullscreen:
            mainwin.fullscreen()
        mainwin.add(vbox)
        mainwin.show_all()
        # for run and quit callbacks
        self.killdialog = KillDialog(mainwin)
        mainwin.connect("delete_event", self.quit)
        self.mainwin = mainwin
    
    def _add_uisocket(self, box):
        # add Hatari parent container to given box
        socket = gtk.Socket()
        # without this, closing Hatari would remove the socket widget
        socket.connect("plug-removed", lambda obj: True)
        socket.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("black"))
        socket.set_events(gtk.gdk.ALL_EVENTS_MASK)
        socket.set_flags(gtk.CAN_FOCUS)
        # set max Hatari window size = desktop size
        self.config.set_desktop_size(gtk.gdk.screen_width(), gtk.gdk.screen_height())
        # set initial embedded hatari size
        width, height = self.config.get_window_size()
        socket.set_size_request(width, height)
        # no resizing for the Hatari window
        box.pack_start(socket, False, False)
        self.hatariwin = socket

    # ------- run callback -----------
    def _socket_cb(self, fd, event):
        if event != gobject.IO_IN:
            # hatari process died, make sure Hatari instance notices
            self.hatari.kill()
            return False
        width, height = self.hatari.get_embed_info()
        print("New size = %d x %d" % (width, height))
        oldwidth, oldheight = self.hatariwin.get_size_request()
        self.hatariwin.set_size_request(width, height)
        if width < oldwidth or height < oldheight:
            # force also mainwin smaller (it automatically grows)
            self.mainwin.resize(width, height)
        return True
    
    def run(self, widget = None):
        if not self.killdialog.run(self.hatari):
            return
        if self.io_id:
            gobject.source_remove(self.io_id)
        args = ["--configfile"]
        # whether to use Hatari config or unsaved Hatari UI config?
        if self.config.is_changed():
            args += [self.config.save_tmp(self.tmpconfpath)]
        else:
            args += [self.config.get_path()]
        if self.memstate:
            args += self.memstate
        # only way to change boot order is to specify disk on command line
        if self.floppy:
            args += self.floppy
        if self.hatariwin:
            size = self.hatariwin.window.get_size()
            self.hatari.run(args, self.hatariwin.window)
            # get notifications of Hatari window size changes
            self.hatari.enable_embed_info()
            socket = self.hatari.get_control_socket().fileno()
            events = gobject.IO_IN | gobject.IO_HUP | gobject.IO_ERR
            self.io_id = gobject.io_add_watch(socket, events, self._socket_cb)
            # all keyboard events should go to Hatari window
            self.hatariwin.grab_focus()
        else:
            self.hatari.run(args)

    def set_floppy(self, floppy):
        self.floppy = floppy

    # ------- quit callback -----------
    def quit(self, widget, arg = None):
        # due to Gtk API, needs to return True when *not* quitting
        if not self.killdialog.run(self.hatari):
            return True
        if self.io_id:
            gobject.source_remove(self.io_id)
        if self.config.is_changed():
            if not self.quitdialog:
                self.quitdialog = QuitSaveDialog(self.mainwin)
            if not self.quitdialog.run(self.config):
                return True
        gtk.main_quit()
        if os.path.exists(self.tmpconfpath):
            os.unlink(self.tmpconfpath)
        # continue to mainwin destroy if called by delete_event
        return False

    # ------- pause callback -----------
    def pause(self, widget):
        if widget.get_active():
            self.hatari.pause()
        else:
            self.hatari.unpause()

    # dialogs
    # ------- reset callback -----------
    def reset(self, widget):
        if not self.resetdialog:
            self.resetdialog = ResetDialog(self.mainwin)
        self.resetdialog.run(self.hatari)

    # ------- about callback -----------
    def about(self, widget):
        if not self.aboutdialog:
            self.aboutdialog = AboutDialog(self.mainwin)
        self.aboutdialog.run()

    # ------- input callback -----------
    def inputs(self, widget):
        if not self.inputdialog:
            self.inputdialog = InputDialog(self.mainwin)
        self.inputdialog.run(self.hatari)

    # ------- floppydisk callback -----------
    def floppydisk(self, widget):
        if not self.floppydialog:
            self.floppydialog = FloppyDialog(self.mainwin)
        self.floppydialog.run(self.config)

    # ------- harddisk callback -----------
    def harddisk(self, widget):
        if not self.harddiskdialog:
            self.harddiskdialog = HardDiskDialog(self.mainwin)
        self.harddiskdialog.run(self.config)

    # ------- display callback -----------
    def display(self, widget):
        if not self.displaydialog:
            self.displaydialog = DisplayDialog(self.mainwin)
        self.displaydialog.run(self.config)

    # ------- joystick callback -----------
    def joystick(self, widget):
        if not self.joystickdialog:
            self.joystickdialog = JoystickDialog(self.mainwin)
        self.joystickdialog.run(self.config)

    # ------- machine callback -----------
    def machine(self, widget):
        if not self.machinedialog:
            self.machinedialog = MachineDialog(self.mainwin)
        if self.machinedialog.run(self.config):
            self.hatari.trigger_shortcut("coldreset")

    # ------- peripheral callback -----------
    def peripheral(self, widget):
        if not self.peripheraldialog:
            self.peripheraldialog = PeripheralDialog(self.mainwin)
        self.peripheraldialog.run(self.config)

    # ------- sound callback -----------
    def sound(self, widget):
        if not self.sounddialog:
            self.sounddialog = SoundDialog(self.mainwin)
        self.sounddialog.run(self.config)

    # ------- path callback -----------
    def path(self, widget):
        if not self.pathdialog:
            self.pathdialog = PathDialog(self.mainwin)
        self.pathdialog.run(self.config)

    # ------- debug callback -----------
    def debugger(self, widget):
        if not self.debugui:
            self.debugui = HatariDebugUI(self.hatari)
        self.debugui.show()

    # ------- trace callback -----------
    def trace(self, widget):
        if not self.tracedialog:
            self.tracedialog = TraceDialog(self.mainwin)
        self.tracepoints = self.tracedialog.run(self.hatari, self.tracepoints)

    # ------ snapshot load/save callbacks ---------
    def load(self, widget):
        path = os.path.expanduser("~/.hatari/hatari.sav")
        filename = get_open_filename("Select snapshot", self.mainwin, path)
        if filename:
            self.memstate = ["--memstate", filename]
            self.run()
            return True
        return False

    def save(self, widget):
        self.hatari.trigger_shortcut("savemem")

    # ------ config load/save callbacks ---------
    def config_load(self, widget):
        path = self.config.get_path()
        filename = get_open_filename("Select configuration file", self.mainwin, path)
        if filename:
            self.hatari.change_option("--configfile %s" % filename)
            self.config.load(filename)
            self._reset_config_dialogs()
            return True
        return False

    def config_save(self, widget):
        path = self.config.get_path()
        filename = get_save_filename("Save configuration as...", self.mainwin, path)
        if filename:
            self.config.save_as(filename)
            return True
        return False

    # ------- fast forward callback -----------
    def set_fastforward(self, widget):
        self.config.set_fastforward(widget.get_active())

    def get_fastforward(self):
        return self.config.get_fastforward()

    # ------- fullscreen callback -----------
    def set_fullscreen(self, widget):
        # if user can select this, Hatari isn't in fullscreen
        self.hatari.change_option("--fullscreen")

    # ------- screenshot callback -----------
    def screenshot(self, widget):
        self.hatari.trigger_shortcut("screenshot")

    # ------- record callbacks -----------
    def recanim(self, widget):
        self.hatari.trigger_shortcut("recanim")

    def recsound(self, widget):
        self.hatari.trigger_shortcut("recsound")

    # ------- insert key special callback -----------
    def keypress(self, widget, code):
        self.hatari.insert_event("keypress %s" % code)

    def textinsert(self, widget, text):
        HatariTextInsert(self.hatari, text)

    # ------- panel callback -----------
    def panel(self, action, box):
        title = action.get_name()
        if title not in self.panels:
            window = gtk.Window(gtk.WINDOW_TOPLEVEL)
            window.set_transient_for(self.mainwin)
            window.set_icon_from_file(UInfo.icon)
            window.set_title(title)
            window.add(box)
            window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
            window.connect("delete_event", window_hide_cb)
            self.panels[title] = window
        else:
            window = self.panels[title]
        window.show_all()
        window.deiconify()