def on_preset_button_clicked(self, widget): def on_ok(preset_name): if preset_name == '': return preset = {} for name in self.plugin.config_default_values: if name == 'presets': continue if name == 'preview_provider': preset[name] = self.xml.get_object(name).get_active() continue widget = self.xml.get_object(name) preset[name] = widget.get_text() preset = {preset_name: preset} presets = dict(self.plugin.config['presets'].items() + preset.items()) if preset_name not in self.plugin.config['presets'].keys(): iter_ = self.preset_liststore.append((preset_name, )) self.plugin.config['presets'] = presets self.set_modal(False) InputDialog(_('Save as Preset'), _('Please type a name for this preset'), is_modal=True, ok_handler=on_ok)
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()
def inputs(self, widget): if not self.inputdialog: self.inputdialog = InputDialog(self.mainwin) self.inputdialog.run(self.hatari)
def __init__(self, partition): assert partition.type not in (parted.PARTITION_METADATA, parted.PARTITION_EXTENDED) self.format_as = '' self.mount_as = '' self.label = '' self.encrypt = False self.enc_passphrase = '' # Initiate encryption status dictionary (even for non-encrypted partitions) self.enc_status = get_status(partition.path) self.grub = False self.disk_type = '' self.partition = partition self.path = partition.path self.length = partition.getLength() self.size_percent = max(1, round(80*self.length/partition.disk.device.getLength(), 1)) self.size_mb = int(partition.getLength('B') / 1024 / 1024) self.size = to_human_readable(partition.getLength('B')) try: # This will crash on USB sticks self.flags = partition.getFlagsAsString().split(', ') except: self.flags = [] # if not normal partition with /dev/sdXN path, set its name to '' and discard it from model self.name = self.path if partition.number != -1 else '' encrypted = is_encrypted(self.path) try: if encrypted: self.type = 'luks' else: self.type = partition.fileSystem.type for fs in ('swap', 'hfs', 'ufs'): # normalize fs variations (parted.filesystem.fileSystemType.keys()) if fs in self.type: self.type = fs self.style = self.type except AttributeError: # non-formatted partitions self.type = { parted.PARTITION_LVM: 'LVM', parted.PARTITION_SWAP: 'swap', parted.PARTITION_RAID: 'RAID', # Empty space on Extended partition is recognized as this parted.PARTITION_PALO: 'PALO', parted.PARTITION_PREP: 'PReP', parted.PARTITION_LOGICAL: _('Logical partition'), parted.PARTITION_EXTENDED: _('Extended partition'), parted.PARTITION_FREESPACE: _('Free space'), parted.PARTITION_HPSERVICE: 'HP Service', parted.PARTITION_MSFT_RESERVED: 'MSFT Reserved', }.get(partition.type, _('Unknown')) self.style = { parted.PARTITION_SWAP: 'swap', parted.PARTITION_FREESPACE: 'freespace', }.get(partition.type, '') if "swap" in self.type: self.mount_as = SWAP_MOUNT_POINT if "fat" in self.type and installer.setup.gptonefi: if has_efi_installed(self.path): self.mount_as = EFI_MOUNT_POINT installer.setup.efi_partition = self.path else: if installer.setup.efi_partition is None: if 'boot' in self.flags: self.mount_as = EFI_MOUNT_POINT elif 'boot' in self.flags: self.mount_as = BOOT_MOUNT_POINT # identify partition's label and used space mount_point = '' try: if encrypted: if not is_connected(self): # Ask for password for encrypted partition and save it in enc_password pwd = InputDialog(title=_("Encryption password"), text="{}\n\n{}".format(_("Password for the encrypted partition:"), self.path), is_password=True) enc_passphrase = pwd.show() self.enc_passphrase = enc_passphrase mapped_drv = connect_block_device(self) if is_connected(self): self.enc_status = get_status(mapped_drv) self.path = mapped_drv self.name = mapped_drv part_label = get_partition_label(mapped_drv) self.mount_device(mapped_drv) else: part_label = get_partition_label(self.path) if not "swap" in self.type: self.mount_device(self.path) # Get size and other info size, free, self.used_percent, mount_point = getoutput("df {0} | grep '^{0}' | awk '{{print $2,$4,$5,$6}}' | tail -1".format(self.path)).split() except ValueError: print('WARNING: Partition {} or type {} failed to mount!'.format(self.path, self.type)) self.os_fs_info, self.label, self.free_space, self.used_percent = ': '+self.type, '', '', 0 else: self.size_mb = int(int(size) / 1024) self.size = to_human_readable(int(size) * 1024) # for mountable partitions, more accurate than the getLength size above self.free_space = to_human_readable(int(free) * 1024) # df returns values in 1024B-blocks by default self.used_percent = self.used_percent.strip('%') or 0 # Had to rewrite label: multiple user errors label = '' if path_exists(mount_point, 'etc/'): try: label = get_release_name(mount_point) if label == '': label = getoutput('uname -s') set_home = False if installer.setup.oem_setup: # OEM setup: check if this partition is mounted as root #print((">> %s" % get_mount_point(self.path))) if get_mount_point(self.path) == '/': installer.setup.root_partition = self.path set_home = True else: # Save this partition as a partition to mount as root if installer.setup.root_partition is None: installer.setup.root_partition = self.path set_home = True else: # Empty the root_partition and home_partition variables to show that this # is a multi-boot system and no root or home partition may be pre-mounted installer.setup.root_partition = '' installer.setup.home_partition = '' # Get home partition from fstab if set_home: fstab = join(mount_point, 'etc/fstab') if path_exists(fstab): fstab_cont = '' with open(fstab, 'r') as f: fstab_cont = f.read() fstab_str = re.search("([a-z0-9-/]+)\s+%s" % HOME_MOUNT_POINT, fstab_cont).group(1) installer.setup.home_partition = get_partition_path_from_string(fstab_str) except: label = 'Unix' elif path_exists(mount_point, 'Windows/servicing/Version'): try: label = 'Windows ' + { '6.4':'10', '6.3':'8.1', '6.2':'8', '6.1':'7', '6.0':'Vista', '5.2':'XP Pro x64', '5.1':'XP', '5.0':'2000', '4.9':'ME', '4.1':'98', '4.0':'95', }.get(getoutput('ls {}/Windows/servicing/Version'.format(mount_point))[:3], '') except: label = 'Windows' elif path_exists(mount_point, 'Boot/BCD'): label = 'Windows recovery' elif path_exists(mount_point, 'Windows/System32'): label = 'Windows' elif path_exists(mount_point, 'System/Library/CoreServices/SystemVersion.plist'): label = 'Mac OS X' elif self.mount_as == EFI_MOUNT_POINT: # Label on fat cannot be longer than 11 characters label = 'EFI' if part_label == '': self.label = label else: self.label = part_label self.os_fs_info = ': {0.label} ({0.type}; {0.size}; {0.free_space})'.format(self) if self.label else ': ' + self.type finally: if TMP_MOUNTPOINT in mount_point: shell_exec("umount -f %s" % self.path) self.html_name = self.name.split('/')[-1] self.html_description = self.label if (self.size_percent < 10 and len(self.label) > 5): self.html_description = "%s..." % self.label[0:5] if (self.size_percent < 5): #Not enough space, don't write the name self.html_name = "" self.html_description = "" self.color = { # colors approximately from gparted (find matching set in usr/share/disk-partitions.html) 'btrfs': '#636363', 'exfat': '#47872a', 'ext2': '#2582a0', 'ext3': '#2582a0', 'ext4': '#21619e', 'fat16': '#47872a', 'fat32': '#47872a', 'hfs': '#636363', 'jfs': '#636363', 'swap': '#be3a37', 'ntfs': '#66a6a8', 'reiserfs': '#636363', 'ufs': '#636363', 'xfs': '#636363', 'zfs': '#636363', 'luks': '#3E3B4D', parted.PARTITION_EXTENDED: '#a9a9a9', }.get(self.type, '#a9a9a9')
def __init__(self, partition): assert partition.type not in (parted.PARTITION_METADATA, parted.PARTITION_EXTENDED) self.format_as = '' self.mount_as = '' self.label = '' self.encrypt = False self.enc_passphrase = '' # Initiate encryption status dictionary (even for non-encrypted partitions) self.enc_status = get_status(partition.path) self.grub = False self.disk_type = '' self.partition = partition self.path = partition.path self.length = partition.getLength() self.size_percent = max( 1, round(80 * self.length / partition.disk.device.getLength(), 1)) self.size_mb = int(partition.getLength('B') / 1024 / 1024) self.size = to_human_readable(partition.getLength('B')) try: # This will crash on USB sticks self.flags = partition.getFlagsAsString().split(', ') except: self.flags = [] # if not normal partition with /dev/sdXN path, set its name to '' and discard it from model self.name = self.path if partition.number != -1 else '' encrypted = is_encrypted(self.path) try: if encrypted: self.type = 'luks' else: self.type = partition.fileSystem.type for fs in ( 'swap', 'hfs', 'ufs' ): # normalize fs variations (parted.filesystem.fileSystemType.keys()) if fs in self.type: self.type = fs self.style = self.type except AttributeError: # non-formatted partitions self.type = { parted.PARTITION_LVM: 'LVM', parted.PARTITION_SWAP: 'swap', parted.PARTITION_RAID: 'RAID', # Empty space on Extended partition is recognized as this parted.PARTITION_PALO: 'PALO', parted.PARTITION_PREP: 'PReP', parted.PARTITION_LOGICAL: _('Logical partition'), parted.PARTITION_EXTENDED: _('Extended partition'), parted.PARTITION_FREESPACE: _('Free space'), parted.PARTITION_HPSERVICE: 'HP Service', parted.PARTITION_MSFT_RESERVED: 'MSFT Reserved', }.get(partition.type, _('Unknown')) self.style = { parted.PARTITION_SWAP: 'swap', parted.PARTITION_FREESPACE: 'freespace', }.get(partition.type, '') if "swap" in self.type: self.mount_as = SWAP_MOUNT_POINT if "fat" in self.type and installer.setup.gptonefi: if has_efi_installed(self.path): self.mount_as = EFI_MOUNT_POINT installer.setup.efi_partition = self.path else: if installer.setup.efi_partition is None: if 'boot' in self.flags: self.mount_as = EFI_MOUNT_POINT elif 'boot' in self.flags: self.mount_as = BOOT_MOUNT_POINT # identify partition's label and used space mount_point = '' try: if encrypted: if not is_connected(self): # Ask for password for encrypted partition and save it in enc_password pwd = InputDialog( title=_("Encryption password"), text="{}\n\n{}".format( _("Password for the encrypted partition:"), self.path), is_password=True) enc_passphrase = pwd.show() self.enc_passphrase = enc_passphrase mapped_drv = connect_block_device(self) if is_connected(self): self.enc_status = get_status(mapped_drv) self.path = mapped_drv self.name = mapped_drv part_label = get_partition_label(mapped_drv) self.mount_device(mapped_drv) else: part_label = get_partition_label(self.path) if not "swap" in self.type: self.mount_device(self.path) # Get size and other info size, free, self.used_percent, mount_point = getoutput( "df {0} | grep '^{0}' | awk '{{print $2,$4,$5,$6}}' | tail -1". format(self.path)).split() except ValueError: print('WARNING: Partition {} or type {} failed to mount!'.format( self.path, self.type)) self.os_fs_info, self.label, self.free_space, self.used_percent = ': ' + self.type, '', '', 0 else: self.size_mb = int(int(size) / 1024) self.size = to_human_readable( int(size) * 1024 ) # for mountable partitions, more accurate than the getLength size above self.free_space = to_human_readable( int(free) * 1024) # df returns values in 1024B-blocks by default self.used_percent = self.used_percent.strip('%') or 0 # Had to rewrite label: multiple user errors label = '' if path_exists(mount_point, 'etc/'): try: label = get_release_name(mount_point) if label == '': label = getoutput('uname -s') set_home = False if installer.setup.oem_setup: # OEM setup: check if this partition is mounted as root #print((">> %s" % get_mount_point(self.path))) if get_mount_point(self.path) == '/': installer.setup.root_partition = self.path set_home = True else: # Save this partition as a partition to mount as root if installer.setup.root_partition is None: installer.setup.root_partition = self.path set_home = True else: # Empty the root_partition and home_partition variables to show that this # is a multi-boot system and no root or home partition may be pre-mounted installer.setup.root_partition = '' installer.setup.home_partition = '' # Get home partition from fstab if set_home: fstab = join(mount_point, 'etc/fstab') if path_exists(fstab): fstab_cont = '' with open(fstab, 'r') as f: fstab_cont = f.read() fstab_str = re.search( "([a-z0-9-/]+)\s+%s" % HOME_MOUNT_POINT, fstab_cont).group(1) installer.setup.home_partition = get_partition_path_from_string( fstab_str) except: label = 'Unix' elif path_exists(mount_point, 'Windows/servicing/Version'): label = 'Windows ' + { '6.4': '10', '6.3': '8.1', '6.2': '8', '6.1': '7', '6.0': 'Vista', '5.2': 'XP Pro x64', '5.1': 'XP', '5.0': '2000', '4.9': 'ME', '4.1': '98', '4.0': '95', }.get( getoutput('ls {}/Windows/servicing/Version'.format( mount_point))[:3], '') elif path_exists(mount_point, 'Boot/BCD'): label = 'Windows recovery' elif path_exists(mount_point, 'Windows/System32'): label = 'Windows' elif path_exists( mount_point, 'System/Library/CoreServices/SystemVersion.plist'): label = 'Mac OS X' elif self.mount_as == EFI_MOUNT_POINT: # Label on fat cannot be longer than 11 characters label = 'EFI' if part_label == '': self.label = label else: self.label = part_label self.os_fs_info = ': {0.label} ({0.type}; {0.size}; {0.free_space})'.format( self) if self.label else ': ' + self.type finally: if TMP_MOUNTPOINT in mount_point: shell_exec("umount -f %s" % self.path) self.html_name = self.name.split('/')[-1] self.html_description = self.label if (self.size_percent < 10 and len(self.label) > 5): self.html_description = "%s..." % self.label[0:5] if (self.size_percent < 5): #Not enough space, don't write the name self.html_name = "" self.html_description = "" self.color = { # colors approximately from gparted (find matching set in usr/share/disk-partitions.html) 'btrfs': '#636363', 'exfat': '#47872a', 'ext2': '#2582a0', 'ext3': '#2582a0', 'ext4': '#21619e', 'fat16': '#47872a', 'fat32': '#47872a', 'hfs': '#636363', 'jfs': '#636363', 'swap': '#be3a37', 'ntfs': '#66a6a8', 'reiserfs': '#636363', 'ufs': '#636363', 'xfs': '#636363', 'zfs': '#636363', 'luks': '#3E3B4D', parted.PARTITION_EXTENDED: '#a9a9a9', }.get(self.type, '#a9a9a9')