mirror of
https://github.com/libretro/hatari.git
synced 2024-11-30 19:50:27 +00:00
- Added recanim, recsound, config load/save and memory snapshot
load/save features. If config changed in UI, give a temporary config file to Hatari whenever it's re-run - Fullscreen isn't anymore a toggle as user cannot use this option if Hatari is fullscreen... Removed fullscreen config option handling as it will be changed also directly from Hatari - To support drag&drop for the UI, it forwards non-option args (floppy name) to Hatari from the UI command line - Disk image and joystick settings to separate dialogs - Add dummy paths and peripherals settings dialogs - Check that Hatari supports --control-socket argument - Make dialog run() methods more consistent - Python code compatible to Python 2.4 (I'm testing with 2.5 which accepts '()' for classes not inheriting anything) and wrapper scripts to older SH (use exit when outside func body)
This commit is contained in:
parent
e83c646bda
commit
5793fdb70b
@ -63,38 +63,49 @@ def text_to_value(text):
|
||||
# ------------------------------------------------------
|
||||
# Handle INI style configuration files as used by Hatari
|
||||
|
||||
class ConfigStore():
|
||||
defaultpath = "%s%c.hatari" % (os.getenv("HOME"), os.path.sep)
|
||||
class ConfigStore:
|
||||
defaultpath = "%s%c.hatari" % (os.path.expanduser("~"), os.path.sep)
|
||||
|
||||
def __init__(self, cfgfile, defaults = {}, miss_is_error = True):
|
||||
"ConfigStore(cfgfile[,defaults,miss_is_error])"
|
||||
self.changed = False
|
||||
self.defaults = defaults
|
||||
self.miss_is_error = miss_is_error
|
||||
path = self.get_path(cfgfile)
|
||||
if path:
|
||||
self.sections = self.load(path)
|
||||
if self.sections:
|
||||
print "Loaded configuration file:", path
|
||||
else:
|
||||
print "WARNING: configuration file '%' loading failed" % path
|
||||
path = None
|
||||
else:
|
||||
print "WARNING: configuration file '%s' missing, using defaults" % cfgfile
|
||||
self.sections = defaults
|
||||
self.original = self.get_checkpoint()
|
||||
path = self._get_fullpath(cfgfile)
|
||||
self.cfgfile = cfgfile
|
||||
self.load(path)
|
||||
|
||||
def load(self, path):
|
||||
"load(path), load given configuration file"
|
||||
if path:
|
||||
sections = self._read(path)
|
||||
if sections:
|
||||
print "Loaded configuration file:", path
|
||||
self.cfgfile = os.path.basename(path)
|
||||
self.sections = sections
|
||||
else:
|
||||
print "ERROR: configuration file '%' loading failed" % path
|
||||
return
|
||||
else:
|
||||
print "WARNING: configuration file missing, using defaults"
|
||||
self.sections = self.defaults
|
||||
self.path = path
|
||||
self.original = self.get_checkpoint()
|
||||
self.changed = False
|
||||
|
||||
def is_loaded(self):
|
||||
"is_loaded() -> True if configuration loading succeeded"
|
||||
if self.sections:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_path(self, cfgfile):
|
||||
"get_path(cfgfile) -> path or None, check first CWD & then HOME for cfgfile"
|
||||
|
||||
def get_path(self):
|
||||
"get_path() -> configuration file path"
|
||||
return self.path
|
||||
|
||||
def _get_fullpath(self, cfgfile):
|
||||
"get_fullpath(cfgfile) -> path or None, check first CWD & then HOME for cfgfile"
|
||||
# hatari.cfg can be in home or current work dir
|
||||
for path in (os.getcwd(), os.getenv("HOME")):
|
||||
for path in (os.getcwd(), os.path.expanduser("~")):
|
||||
if path:
|
||||
path = self._check_path(path, cfgfile)
|
||||
if path:
|
||||
@ -114,8 +125,8 @@ class ConfigStore():
|
||||
return testpath
|
||||
return None
|
||||
|
||||
def load(self, path):
|
||||
"load(path) -> (all keys, section2key mappings)"
|
||||
def _read(self, path):
|
||||
"_read(path) -> (all keys, section2key mappings)"
|
||||
config = open(path, "r")
|
||||
if not config:
|
||||
return ({}, {})
|
||||
@ -211,19 +222,42 @@ class ConfigStore():
|
||||
fileobj.write("\n")
|
||||
|
||||
def save(self):
|
||||
"save(), if configuration changed, save it"
|
||||
"save() -> path, if configuration changed, save it"
|
||||
if not self.changed:
|
||||
print "No configuration changes to save, skipping"
|
||||
return
|
||||
return None
|
||||
if not self.path:
|
||||
print "WARNING: no existing configuration file, trying to create one"
|
||||
if not os.path.exists(self.defaultpath):
|
||||
os.mkdir(self.defaultpath)
|
||||
self.path = "%s%c%s" % (self.defaultpath, os.path.sep, self.cfgfile)
|
||||
#fileobj = sys.stdout
|
||||
fileobj = open(self.path, "w")
|
||||
if fileobj:
|
||||
self.write(fileobj)
|
||||
print "Saved configuration file:", self.path
|
||||
else:
|
||||
if not fileobj:
|
||||
print "ERROR: opening '%s' for saving failed" % self.path
|
||||
return None
|
||||
self.write(fileobj)
|
||||
print "Saved configuration file:", self.path
|
||||
self.changed = False
|
||||
return path
|
||||
|
||||
def save_as(self, path):
|
||||
"save_as(path) -> path, save configuration to given file and select it"
|
||||
assert(path)
|
||||
if not os.path.exists(os.path.dirname(path)):
|
||||
os.makedirs(os.path.dirname(path))
|
||||
self.path = path
|
||||
self.changed = True
|
||||
self.save()
|
||||
return path
|
||||
|
||||
def save_tmp(self, path):
|
||||
"save_tmp(path) -> path, save configuration to given file without selecting it"
|
||||
if not os.path.exists(os.path.dirname(path)):
|
||||
os.makedirs(os.path.dirname(path))
|
||||
fileobj = open(path, "w")
|
||||
if not fileobj:
|
||||
print "ERROR: opening '%s' for saving failed" % path
|
||||
return None
|
||||
self.write(fileobj)
|
||||
print "Saved temporary configuration file:", path
|
||||
return path
|
||||
|
@ -23,7 +23,8 @@ import pango
|
||||
|
||||
from config import ConfigStore
|
||||
from uihelpers import UInfo, create_button, create_toggle, \
|
||||
create_table_dialog, table_add_entry_row, table_add_widget_row
|
||||
create_table_dialog, table_add_entry_row, table_add_widget_row, \
|
||||
get_save_filename
|
||||
from dialogs import TodoDialog, ErrorDialog, AskDialog, KillDialog
|
||||
|
||||
|
||||
@ -34,7 +35,7 @@ def dialog_apply_cb(widget, dialog):
|
||||
# -------------
|
||||
# Table dialogs
|
||||
|
||||
class SaveDialog():
|
||||
class SaveDialog:
|
||||
def __init__(self, parent):
|
||||
entry = gtk.Entry()
|
||||
entry.set_width_chars(12)
|
||||
@ -51,14 +52,11 @@ class SaveDialog():
|
||||
self.address.connect("activate", dialog_apply_cb, self.dialog)
|
||||
self.length = table_add_entry_row(table, 2, "Number of bytes:", 6)
|
||||
self.length.connect("activate", dialog_apply_cb, self.dialog)
|
||||
|
||||
|
||||
def _select_file_cb(self, widget):
|
||||
buttons = (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
|
||||
fsel = gtk.FileChooserDialog("Select save file", self.dialog, gtk.FILE_CHOOSER_ACTION_SAVE, buttons)
|
||||
fsel.set_local_only(True)
|
||||
if fsel.run() == gtk.RESPONSE_OK:
|
||||
self.file.set_text(fsel.get_filename())
|
||||
fsel.destroy()
|
||||
filename = get_save_filename("Select save file", self.dialog)
|
||||
if filename:
|
||||
self.file.set_text(filename)
|
||||
|
||||
def run(self, address):
|
||||
"run(address) -> (filename,address,length), all as strings"
|
||||
@ -96,7 +94,7 @@ class SaveDialog():
|
||||
return (filename, address, length)
|
||||
|
||||
|
||||
class LoadDialog():
|
||||
class LoadDialog:
|
||||
def __init__(self, parent):
|
||||
chooser = gtk.FileChooserButton('Select a File')
|
||||
chooser.set_local_only(True) # Hatari cannot access URIs
|
||||
@ -132,7 +130,7 @@ class LoadDialog():
|
||||
return (filename, address)
|
||||
|
||||
|
||||
class OptionsDialog():
|
||||
class OptionsDialog:
|
||||
def __init__(self, parent):
|
||||
table, self.dialog = create_table_dialog(parent, "Debugger UI options", 1)
|
||||
self.lines = table_add_entry_row(table, 0, "Memdump/disasm lines:", 2)
|
||||
@ -178,7 +176,7 @@ class Constants:
|
||||
|
||||
# class for the memory address entry, view (label) and
|
||||
# the logic for memory dump modes and moving in memory
|
||||
class MemoryAddress():
|
||||
class MemoryAddress:
|
||||
# class variables
|
||||
debug_output = None
|
||||
hatari = None
|
||||
@ -346,7 +344,7 @@ class MemoryAddress():
|
||||
|
||||
|
||||
# the Hatari debugger UI class and methods
|
||||
class HatariDebugUI():
|
||||
class HatariDebugUI:
|
||||
|
||||
def __init__(self, hatariobj, do_destroy = False):
|
||||
self.address = MemoryAddress(hatariobj)
|
||||
|
@ -29,7 +29,7 @@ from uihelpers import UInfo, HatariTextInsert, create_table_dialog, \
|
||||
# -----------------
|
||||
# Dialog base class
|
||||
|
||||
class HatariUIDialog():
|
||||
class HatariUIDialog:
|
||||
def __init__(self, parent):
|
||||
"<any>Dialog(parent) -> object"
|
||||
self.parent = parent
|
||||
@ -182,14 +182,14 @@ class QuitSaveDialog(HatariUIDialog):
|
||||
self.dialog = dialog
|
||||
|
||||
def run(self, config):
|
||||
"run(config) -> RESPONSE_CANCEL if dialog is canceled"
|
||||
"run(config) -> False if canceled, True otherwise or if no changes"
|
||||
changes = []
|
||||
for key, value in config.get_changes():
|
||||
changes.append("%s = %s" % (key, str(value)))
|
||||
if not changes:
|
||||
return gtk.RESPONSE_NO
|
||||
return True
|
||||
child = self.viewport.get_child()
|
||||
child.set_text("\n".join(changes))
|
||||
child.set_text(config.get_path() + ":\n" + "\n".join(changes))
|
||||
width, height = child.get_size_request()
|
||||
if height < 320:
|
||||
self.scrolledwindow.set_size_request(width, height)
|
||||
@ -199,9 +199,11 @@ class QuitSaveDialog(HatariUIDialog):
|
||||
|
||||
response = self.dialog.run()
|
||||
self.dialog.hide()
|
||||
if response == gtk.RESPONSE_CANCEL:
|
||||
return False
|
||||
if response == gtk.RESPONSE_YES:
|
||||
config.save()
|
||||
return response
|
||||
return True
|
||||
|
||||
|
||||
# ---------------------------
|
||||
@ -218,16 +220,16 @@ Hatari emulator is already/still running and it needs to be terminated first. Ho
|
||||
Terminate Hatari anyway?""")
|
||||
|
||||
def run(self, hatari):
|
||||
"run(hatari) -> False if Hatari killed, True if left running"
|
||||
"run(hatari) -> True if Hatari killed, False if left running"
|
||||
if not hatari.is_running():
|
||||
return False
|
||||
return True
|
||||
# Hatari is running, OK to kill?
|
||||
response = self.dialog.run()
|
||||
self.dialog.hide()
|
||||
if response == gtk.RESPONSE_OK:
|
||||
hatari.kill()
|
||||
return False
|
||||
return True
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# ---------------------------
|
||||
@ -260,9 +262,58 @@ class ResetDialog(HatariUIDialog):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
# ----------------------------------
|
||||
# Disk image dialog
|
||||
|
||||
class DiskDialog(HatariUIDialog):
|
||||
def _create_dialog(self, config):
|
||||
table, self.dialog = create_table_dialog(self.parent, "Disk image settings", 9)
|
||||
|
||||
row = 0
|
||||
self.floppy = []
|
||||
path = config.get_floppydir()
|
||||
for drive in ("A", "B"):
|
||||
label = "Disk %c:" % drive
|
||||
fsel = gtk.FileChooserButton(label)
|
||||
# Hatari cannot access URIs
|
||||
fsel.set_local_only(True)
|
||||
fsel.set_width_chars(12)
|
||||
filename = config.get_floppy(row)
|
||||
if filename:
|
||||
fsel.set_filename(filename)
|
||||
elif path:
|
||||
fsel.set_current_folder(path)
|
||||
self.floppy.append(fsel)
|
||||
|
||||
eject = create_button("Eject", self._eject, fsel)
|
||||
box = gtk.HBox()
|
||||
box.pack_start(fsel)
|
||||
box.pack_start(eject, False, False)
|
||||
table_add_widget_row(table, row, label, box)
|
||||
row += 1
|
||||
|
||||
table.show_all()
|
||||
|
||||
def _eject(self, widget, fsel):
|
||||
fsel.unselect_all()
|
||||
|
||||
def run(self, config):
|
||||
"run(config), show disk image dialog"
|
||||
if not self.dialog:
|
||||
self._create_dialog(config)
|
||||
response = self.dialog.run()
|
||||
self.dialog.hide()
|
||||
|
||||
if response == gtk.RESPONSE_APPLY:
|
||||
config.lock_updates()
|
||||
for drive in range(2):
|
||||
config.set_floppy(drive, self.floppy[drive].get_filename())
|
||||
config.flush_updates()
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# Hatari screen dialog
|
||||
# Display dialog
|
||||
|
||||
class DisplayDialog(HatariUIDialog):
|
||||
|
||||
@ -325,8 +376,83 @@ class DisplayDialog(HatariUIDialog):
|
||||
config.flush_updates()
|
||||
|
||||
|
||||
# ----------------------------------
|
||||
# Joystick dialog
|
||||
|
||||
class JoystickDialog(HatariUIDialog):
|
||||
def _create_dialog(self, config):
|
||||
table, self.dialog = create_table_dialog(self.parent, "Joystick settings", 9)
|
||||
|
||||
joy = 0
|
||||
self.joy = []
|
||||
joytypes = config.get_joystick_types()
|
||||
for label in config.get_joystick_names():
|
||||
combo = gtk.combo_box_new_text()
|
||||
for text in joytypes:
|
||||
combo.append_text(text)
|
||||
combo.set_active(config.get_joystick(joy))
|
||||
widget = table_add_widget_row(table, joy, "%s:" % label, combo)
|
||||
self.joy.append(widget)
|
||||
joy += 1
|
||||
|
||||
table.show_all()
|
||||
|
||||
def run(self, config):
|
||||
"run(config), show joystick dialog"
|
||||
if not self.dialog:
|
||||
self._create_dialog(config)
|
||||
response = self.dialog.run()
|
||||
self.dialog.hide()
|
||||
|
||||
if response == gtk.RESPONSE_APPLY:
|
||||
config.lock_updates()
|
||||
for joy in range(6):
|
||||
config.set_joystick(joy, self.joy[joy].get_active())
|
||||
config.flush_updates()
|
||||
|
||||
|
||||
# ---------------------------------------
|
||||
# Peripherals (midi,printer,rs232) dialog
|
||||
|
||||
class PeripheralDialog(HatariUIDialog):
|
||||
def _create_dialog(self, config):
|
||||
table, self.dialog = create_table_dialog(self.parent, "Peripheral settings", 9)
|
||||
print "TODO: get midi/printer/rs232 stuff"
|
||||
table.show_all()
|
||||
|
||||
def run(self, config):
|
||||
"run(config), show peripherals dialog"
|
||||
if not self.dialog:
|
||||
self._create_dialog(config)
|
||||
response = self.dialog.run()
|
||||
self.dialog.hide()
|
||||
|
||||
if response == gtk.RESPONSE_APPLY:
|
||||
print "TODO: set midi/printer/rs232 stuff"
|
||||
|
||||
|
||||
# ---------------------------------------
|
||||
# Path dialog
|
||||
|
||||
class PathDialog(HatariUIDialog):
|
||||
def _create_dialog(self, config):
|
||||
table, self.dialog = create_table_dialog(self.parent, "Path settings", 2)
|
||||
print "TODO: get path stuff"
|
||||
table.show_all()
|
||||
|
||||
def run(self, config):
|
||||
"run(config), show paths dialog"
|
||||
if not self.dialog:
|
||||
self._create_dialog(config)
|
||||
response = self.dialog.run()
|
||||
self.dialog.hide()
|
||||
|
||||
if response == gtk.RESPONSE_APPLY:
|
||||
print "TODO: set path stuff"
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# Hatari sound dialog
|
||||
# Sound dialog
|
||||
|
||||
class SoundDialog(HatariUIDialog):
|
||||
|
||||
@ -349,7 +475,7 @@ class SoundDialog(HatariUIDialog):
|
||||
self.dialog = dialog
|
||||
|
||||
def run(self, config):
|
||||
"run(config), show display dialog"
|
||||
"run(config), show sound dialog"
|
||||
if not self.dialog:
|
||||
self._create_dialog(config)
|
||||
response = self.dialog.run()
|
||||
@ -465,77 +591,10 @@ class TraceDialog(HatariUIDialog):
|
||||
return self.savedpoints
|
||||
|
||||
|
||||
# ----------------------------------
|
||||
# Peripherals (disk/joystick) dialog
|
||||
|
||||
class PeripheralsDialog(HatariUIDialog):
|
||||
def _create_dialog(self, config):
|
||||
table, self.dialog = create_table_dialog(self.parent, "Peripheral settings", 9)
|
||||
|
||||
row = 0
|
||||
self.floppy = []
|
||||
path = config.get_floppydir()
|
||||
for drive in ("A", "B"):
|
||||
label = "Disk %c:" % drive
|
||||
fsel = gtk.FileChooserButton(label)
|
||||
# Hatari cannot access URIs
|
||||
fsel.set_local_only(True)
|
||||
fsel.set_width_chars(12)
|
||||
filename = config.get_floppy(row)
|
||||
if filename:
|
||||
fsel.set_filename(filename)
|
||||
elif path:
|
||||
fsel.set_current_folder(path)
|
||||
self.floppy.append(fsel)
|
||||
|
||||
eject = create_button("Eject", self._eject, fsel)
|
||||
box = gtk.HBox()
|
||||
box.pack_start(fsel)
|
||||
box.pack_start(eject, False, False)
|
||||
table_add_widget_row(table, row, label, box)
|
||||
row += 1
|
||||
|
||||
table_add_separator(table, row)
|
||||
row += 1
|
||||
|
||||
joy = 0
|
||||
self.joy = []
|
||||
joytypes = config.get_joystick_types()
|
||||
for label in config.get_joystick_names():
|
||||
combo = gtk.combo_box_new_text()
|
||||
for text in joytypes:
|
||||
combo.append_text(text)
|
||||
combo.set_active(config.get_joystick(joy))
|
||||
widget = table_add_widget_row(table, row + joy, "%s:" % label, combo)
|
||||
self.joy.append(widget)
|
||||
joy += 1
|
||||
|
||||
# TODO: add printer, serial, midi, RTC to peripherals?
|
||||
table.show_all()
|
||||
|
||||
def _eject(self, widget, fsel):
|
||||
fsel.unselect_all()
|
||||
|
||||
def run(self, config):
|
||||
"run() -> file name, file name for given disk"
|
||||
if not self.dialog:
|
||||
self._create_dialog(config)
|
||||
response = self.dialog.run()
|
||||
self.dialog.hide()
|
||||
|
||||
if response == gtk.RESPONSE_APPLY:
|
||||
config.lock_updates()
|
||||
for drive in range(2):
|
||||
config.set_floppy(drive, self.floppy[drive].get_filename())
|
||||
for joy in range(6):
|
||||
config.set_joystick(joy, self.joy[joy].get_active())
|
||||
config.flush_updates()
|
||||
|
||||
|
||||
# ----------------------------------------
|
||||
# Setup dialog for settings needing reboot
|
||||
# Machine dialog for settings needing reboot
|
||||
|
||||
class SetupDialog(HatariUIDialog):
|
||||
class MachineDialog(HatariUIDialog):
|
||||
def __init__(self, parent):
|
||||
self.setups = []
|
||||
self.parent = parent
|
||||
@ -547,7 +606,7 @@ class SetupDialog(HatariUIDialog):
|
||||
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||
("Set and reboot", gtk.RESPONSE_APPLY,
|
||||
gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
|
||||
self.editdialog = EditSetupDialog(dialog)
|
||||
self.editdialog = EditMachineDialog(dialog)
|
||||
|
||||
box1 = gtk.HBox()
|
||||
box1.add(create_button("Add", self._add_setup, config))
|
||||
@ -641,7 +700,7 @@ class SetupDialog(HatariUIDialog):
|
||||
self.label.set_text("\n".join(info))
|
||||
|
||||
def run(self, config):
|
||||
"run() -> bool, whether to reboot"
|
||||
"run(config) -> bool, whether to reboot"
|
||||
if not self.dialog:
|
||||
self._create_dialog(config)
|
||||
|
||||
@ -655,7 +714,7 @@ class SetupDialog(HatariUIDialog):
|
||||
# ----------------------------------------------
|
||||
# Dialog for adding/editing setup configurations
|
||||
|
||||
class EditSetupDialog(HatariUIDialog):
|
||||
class EditMachineDialog(HatariUIDialog):
|
||||
def _create_dialog(self, config):
|
||||
table, self.dialog = create_table_dialog(self.parent, "Add/edit setup", 9)
|
||||
|
||||
|
@ -25,7 +25,7 @@ from config import ConfigStore
|
||||
|
||||
|
||||
# Running Hatari instance
|
||||
class Hatari():
|
||||
class Hatari:
|
||||
"running hatari instance and methods for communicating with it"
|
||||
basepath = "/tmp/hatari-ui-" + str(os.getpid())
|
||||
logpath = basepath + ".log"
|
||||
@ -184,10 +184,10 @@ class Hatari():
|
||||
self._set_embed_env(env, parent_win)
|
||||
# callers need to take care of confirming quitting
|
||||
args = [self.hataribin, "--confirm-quit", "off"]
|
||||
if extra_args:
|
||||
args += extra_args
|
||||
if self.server:
|
||||
args += ["--control-socket", self.controlpath]
|
||||
if extra_args:
|
||||
args += extra_args
|
||||
print "RUN:", args
|
||||
os.execvpe(self.hataribin, args, env)
|
||||
|
||||
@ -436,17 +436,6 @@ class HatariConfigMapping(ConfigStore):
|
||||
self.set("[Screen]", "nSpec512Threshold", value)
|
||||
self._change_option("--spec512 %d" % value)
|
||||
|
||||
# ------------ show fullscreen ---------------
|
||||
def get_fullscreen(self):
|
||||
return self.get("[Screen]", "bFullScreen")
|
||||
|
||||
def set_fullscreen(self, value):
|
||||
self.set("[Screen]", "bFullScreen", value)
|
||||
if value:
|
||||
self._change_option("--fullscreen")
|
||||
else:
|
||||
self._change_option("--window")
|
||||
|
||||
# ------------ show borders ---------------
|
||||
def get_borders(self):
|
||||
return self.get("[Screen]", "bAllowOverscan")
|
||||
|
@ -4,14 +4,15 @@ path=${0%/*}
|
||||
name=${0##*/}
|
||||
|
||||
# example setup for Hatari UI
|
||||
$path/$name.py --right "about,|,run,pause,|,reset,debug,|,quit" --embed
|
||||
return
|
||||
$path/$name.py --right "about,|,run,pause,forward,|,reset,|,quit" --embed $*
|
||||
exit 0
|
||||
|
||||
# test setup without embedding, dupplicate toggles
|
||||
$path/$name.py --top "about,run,pause,quit" \
|
||||
--panel "Testpanel,pause,>,close" \
|
||||
--bottom "sound,|,pause,|,fast,|,Testpanel"
|
||||
return
|
||||
--bottom "sound,|,pause,|,fast,|,Testpanel" \
|
||||
$*
|
||||
exit 0
|
||||
|
||||
# test setup with embedding and all available controls
|
||||
$path/$name.py --embed \
|
||||
@ -20,5 +21,6 @@ $path/$name.py --embed \
|
||||
--panel "Keys,F1=59,F2=60,F3=61,F4=62,F5=63,F6=64,F7=65,F8=66,F9=67,F10=68,>,Macro=Test,Undo=97,Help=98,Enter=114,>,close" \
|
||||
--panel "Misc,fast,|,full,|,sound,>,shot,>,close" \
|
||||
--bottom "fast,full,Misc,Keys,input,devices,display,debug,trace" \
|
||||
--right "fast,full,Misc,Keys,input,devices,display,Help=98"
|
||||
return
|
||||
--right "fast,full,Misc,Keys,input,devices,display,Help=98" \
|
||||
$*
|
||||
exit 0
|
||||
|
@ -29,10 +29,11 @@ import gobject
|
||||
from debugui import HatariDebugUI
|
||||
from hatari import Hatari, HatariConfigMapping
|
||||
from uihelpers import UInfo, create_button, create_toolbutton, create_toggle, \
|
||||
HatariTextInsert
|
||||
from dialogs import AboutDialog, InputDialog, KillDialog, QuitSaveDialog, \
|
||||
ResetDialog, SetupDialog, TraceDialog, PeripheralsDialog, ErrorDialog, \
|
||||
DisplayDialog, SoundDialog
|
||||
HatariTextInsert, get_open_filename, get_save_filename
|
||||
from dialogs import AboutDialog, TodoDialog, ErrorDialog, InputDialog, \
|
||||
KillDialog, QuitSaveDialog, ResetDialog, TraceDialog, DiskDialog, \
|
||||
DisplayDialog, JoystickDialog, MachineDialog, PeripheralDialog, \
|
||||
PathDialog, SoundDialog
|
||||
|
||||
|
||||
# helper functions to match callback args
|
||||
@ -45,7 +46,8 @@ def window_hide_cb(window, arg):
|
||||
# Class with Hatari and configuration instances which methods are
|
||||
# called to change those (with additional dialogs or directly).
|
||||
# Owns the application window and socket widget embedding Hatari.
|
||||
class UICallbacks():
|
||||
class UICallbacks:
|
||||
tmpconfpath = os.path.expanduser("~/.hatari/.tmp.cfg")
|
||||
def __init__(self):
|
||||
# Hatari and configuration
|
||||
self.hatari = Hatari()
|
||||
@ -59,47 +61,59 @@ class UICallbacks():
|
||||
self.hatariwin = None
|
||||
self.debugui = None
|
||||
self.panels = {}
|
||||
|
||||
# dialogs are created when needed
|
||||
self.aboutdialog = None
|
||||
self.resetdialog = None
|
||||
self.inputdialog = None
|
||||
self.devicesdialog = None
|
||||
self.displaydialog = None
|
||||
self.sounddialog = None
|
||||
self.pastedialog = None
|
||||
self.quitdialog = None
|
||||
self.setupdialog = None
|
||||
self.tracedialog = None
|
||||
self.resetdialog = None
|
||||
self.quitdialog = None
|
||||
self.killdialog = None
|
||||
|
||||
# TODO: Hatari UI configuration settings save/load
|
||||
self.tracepoints = None
|
||||
self.diskdialog = 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.diskdialog = 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.add(toolbars["left"])
|
||||
hbox.pack_start(toolbars["left"], False, True)
|
||||
if embed:
|
||||
self._add_uisocket(hbox)
|
||||
if toolbars["right"]:
|
||||
#hbox.add(toolbars["right"])
|
||||
hbox.pack_start(toolbars["right"], False, True)
|
||||
# add vertical elements
|
||||
vbox = gtk.VBox()
|
||||
if menu:
|
||||
vbox.add(menu)
|
||||
if toolbars["top"]:
|
||||
#vbox.add(toolbars["top"])
|
||||
vbox.pack_start(toolbars["top"], False, True)
|
||||
vbox.add(hbox)
|
||||
if toolbars["bottom"]:
|
||||
#vbox.add(toolbars["bottom"])
|
||||
vbox.pack_start(toolbars["bottom"], False, True)
|
||||
# put them to main window
|
||||
mainwin = gtk.Window(gtk.WINDOW_TOPLEVEL)
|
||||
@ -145,15 +159,25 @@ class UICallbacks():
|
||||
# force also mainwin smaller (it automatically grows)
|
||||
self.mainwin.resize(width, height)
|
||||
return True
|
||||
|
||||
def run(self, widget=None):
|
||||
if self.killdialog.run(self.hatari):
|
||||
|
||||
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
|
||||
if self.floppy:
|
||||
args += self.floppy
|
||||
if self.hatariwin:
|
||||
size = self.hatariwin.window.get_size()
|
||||
self.hatari.run(None, self.hatariwin.window)
|
||||
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()
|
||||
@ -162,7 +186,42 @@ class UICallbacks():
|
||||
# all keyboard events should go to Hatari window
|
||||
self.hatariwin.grab_focus()
|
||||
else:
|
||||
self.hatari.run()
|
||||
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):
|
||||
@ -176,46 +235,11 @@ class UICallbacks():
|
||||
self.inputdialog = InputDialog(self.mainwin)
|
||||
self.inputdialog.run(self.hatari)
|
||||
|
||||
# ------- pause callback -----------
|
||||
def pause(self, widget):
|
||||
if widget.get_active():
|
||||
self.hatari.pause()
|
||||
else:
|
||||
self.hatari.unpause()
|
||||
|
||||
# ------- reset callback -----------
|
||||
def reset(self, widget):
|
||||
if not self.resetdialog:
|
||||
self.resetdialog = ResetDialog(self.mainwin)
|
||||
self.resetdialog.run(self.hatari)
|
||||
|
||||
# ------- setup callback -----------
|
||||
def setup(self, widget):
|
||||
if not self.setupdialog:
|
||||
self.setupdialog = SetupDialog(self.mainwin)
|
||||
if self.setupdialog.run(self.config):
|
||||
self.hatari.trigger_shortcut("warmreset")
|
||||
|
||||
# ------- quit callback -----------
|
||||
def quit(self, widget, arg = None):
|
||||
if 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 self.quitdialog.run(self.config) == gtk.RESPONSE_CANCEL:
|
||||
return True
|
||||
gtk.main_quit()
|
||||
# continue to mainwin destroy if called by delete_event
|
||||
return False
|
||||
|
||||
# ------- devices callback -----------
|
||||
def devices(self, widget):
|
||||
if not self.devicesdialog:
|
||||
self.devicesdialog = PeripheralsDialog(self.mainwin)
|
||||
self.devicesdialog.run(self.config)
|
||||
# ------- disk callback -----------
|
||||
def disk(self, widget):
|
||||
if not self.diskdialog:
|
||||
self.diskdialog = DiskDialog(self.mainwin)
|
||||
self.diskdialog.run(self.config)
|
||||
|
||||
# ------- display callback -----------
|
||||
def display(self, widget):
|
||||
@ -223,6 +247,37 @@ class UICallbacks():
|
||||
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:
|
||||
@ -235,6 +290,38 @@ class UICallbacks():
|
||||
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())
|
||||
@ -244,22 +331,20 @@ class UICallbacks():
|
||||
|
||||
# ------- fullscreen callback -----------
|
||||
def set_fullscreen(self, widget):
|
||||
self.config.set_fullscreen(widget.get_active())
|
||||
|
||||
def get_fullscreen(self):
|
||||
return self.config.get_fullscreen()
|
||||
|
||||
# ------- sound callback -----------
|
||||
def sound(self, widget):
|
||||
if not self.sounddialog:
|
||||
self.sounddialog = SoundDialog(self.mainwin)
|
||||
self.sounddialog.run(self.config)
|
||||
# if user can select this, Hatari isn't in fullscreen
|
||||
self.hatari.change_option("--fullscreen")
|
||||
|
||||
# ------- screenshot callback -----------
|
||||
def screenshot(self, widget):
|
||||
print "TODO: Support converting screenshot to PNG and giving its name?"
|
||||
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)
|
||||
@ -286,39 +371,51 @@ class UICallbacks():
|
||||
window.deiconify()
|
||||
|
||||
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# class for creating menus, toolbars and panels
|
||||
# and managing actions bound to them
|
||||
class UIActions:
|
||||
def __init__(self):
|
||||
cb = self.callbacks = UICallbacks()
|
||||
|
||||
|
||||
self.actions = gtk.ActionGroup("All")
|
||||
|
||||
# name, icon ID, label, accel, tooltip, callback
|
||||
self.actions.add_toggle_actions((
|
||||
("pause", gtk.STOCK_MEDIA_PAUSE, "Pause", "<Ctrl>P", "Pause Hatari to save battery", cb.pause),
|
||||
# TODO: how to know when these are changed from inside Hatari?
|
||||
("fast", gtk.STOCK_MEDIA_FORWARD, "FastForward", "<Ctrl>F", "Whether to fast forward Hatari (needs fast machine)", cb.set_fastforward, cb.get_fastforward()),
|
||||
("full", gtk.STOCK_FULLSCREEN, "Fullscreen", "<Ctrl>U", "Toggle whether Hatari is fullscreen", cb.set_fullscreen, cb.get_fullscreen())
|
||||
("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((
|
||||
("about", gtk.STOCK_INFO, "About", "<Ctrl>A", "Hatari UI information", cb.about),
|
||||
("shot", gtk.STOCK_MEDIA_RECORD, "Screenshot", "<Ctrl>C", "Take a screenshot", cb.screenshot),
|
||||
("quit", gtk.STOCK_QUIT, "Quit", None, "Quit Hatari UI", cb.quit),
|
||||
("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),
|
||||
|
||||
("sound", gtk.STOCK_YES, "Sound...", "<Ctrl>S", "Sound settings", cb.sound),
|
||||
("display", gtk.STOCK_PROPERTIES, "Display...", "<Ctrl>D", "Display settings", cb.display),
|
||||
("devices", gtk.STOCK_FLOPPY, "Peripherals...", "<Ctrl>H", "Floppy and joystick settings", cb.devices),
|
||||
("machine", gtk.STOCK_PREFERENCES, "Machine...", "<Ctrl>M", "Hatari st/e/tt/falcon configuration", cb.setup),
|
||||
("disk", gtk.STOCK_FLOPPY, "Disks...", "<Ctrl>D", "Floppy settings", cb.disk),
|
||||
("display", gtk.STOCK_PREFERENCES, "Display...", "<Ctrl>Y", "Display settings", cb.display),
|
||||
("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...", None, "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)
|
||||
("trace", gtk.STOCK_EXECUTE, "Trace settings...", "<Ctrl>T", "Hatari tracing setup", cb.trace),
|
||||
|
||||
("about", gtk.STOCK_INFO, "About", "<Ctrl>I", "Hatari UI information", cb.about)
|
||||
))
|
||||
self.action_names = [x.get_name() for x in self.actions.list_actions()]
|
||||
|
||||
@ -326,6 +423,11 @@ class UIActions:
|
||||
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"
|
||||
@ -402,7 +504,6 @@ class UIActions:
|
||||
"Simulate Atari key press/release and string inserting"
|
||||
if not textcode:
|
||||
return (None, None)
|
||||
# TODO: icon for the key button
|
||||
widget = gtk.ToolButton(gtk.STOCK_PASTE)
|
||||
widget.set_label(name)
|
||||
try:
|
||||
@ -480,10 +581,12 @@ class UIActions:
|
||||
|
||||
def _get_menu(self):
|
||||
allmenus = (
|
||||
("File", ("about", None, "shot", None, "quit")),
|
||||
("Emulation", ("run", "pause", None, "fast", None, "full", None, "input", None, "reset")),
|
||||
("Setup", ("sound", "display", "devices", "machine")),
|
||||
("Debug", ("debug", "trace"))
|
||||
("File", ("load", "save", None, "shot", "recanim", "recsound", None, "quit")),
|
||||
("Emulation", ("run", "pause", "forward", None, "full", None, "input", None, "reset")),
|
||||
("Devices", ("disk", "display", "joystick", "machine", "device", "sound")),
|
||||
("Configuration", ("path", None, "lconfig", "sconfig")),
|
||||
("Debug", ("debug", "trace")),
|
||||
("Help", ("about",))
|
||||
)
|
||||
bar = gtk.MenuBar()
|
||||
|
||||
@ -496,7 +599,7 @@ class UIActions:
|
||||
return bar
|
||||
|
||||
# ------------- run the whole UI -------------
|
||||
def run(self, havemenu, fullscreen, embed):
|
||||
def run(self, floppy, havemenu, fullscreen, embed):
|
||||
accelgroup = None
|
||||
# create menu?
|
||||
if havemenu:
|
||||
@ -519,6 +622,7 @@ class UIActions:
|
||||
toolbars[side] = self._get_container(self.toolbars[side], True)
|
||||
|
||||
self.callbacks.create_ui(accelgroup, menu, toolbars, fullscreen, embed)
|
||||
self.callbacks.set_floppy(floppy)
|
||||
|
||||
# ugly, Hatari socket window ID can be gotten only
|
||||
# after Socket window is realized by gtk_main()
|
||||
@ -532,7 +636,7 @@ def usage(actions, msg=None):
|
||||
uiname = "%s %s" % (UInfo.name, UInfo.version)
|
||||
print "\n%s" % uiname
|
||||
print "=" * len(uiname)
|
||||
print "\nUsage: %s [options]" % name
|
||||
print "\nUsage: %s [options] [floppy image]" % name
|
||||
print "\nOptions:"
|
||||
print "\t-h, --help\t\tthis help"
|
||||
print "\t-n, --nomenu\t\tomit menus"
|
||||
@ -562,7 +666,7 @@ For example:
|
||||
\t%s --embed \\
|
||||
\t-t "about,run,pause,quit" \\
|
||||
\t-p "MyPanel,Macro=Test,Undo=97,Help=98,>,F1=59,F2=60,F3=61,F4=62,>,close" \\
|
||||
\t-r "paste,debug,trace,setup,MyPanel" \\
|
||||
\t-r "paste,debug,trace,machine,MyPanel" \\
|
||||
\t-b "sound,|,fastforward,|,fullscreen"
|
||||
|
||||
if no options are given, the UI uses basic controls.
|
||||
@ -578,12 +682,10 @@ def main():
|
||||
try:
|
||||
longopts = ["embed", "fullscreen", "nomenu", "help",
|
||||
"left=", "right=", "top=", "bottom=", "panel="]
|
||||
opts, args = getopt.getopt(sys.argv[1:], "efnhl:r:t:b:p:", longopts)
|
||||
opts, floppies = getopt.getopt(sys.argv[1:], "efnhl:r:t:b:p:", longopts)
|
||||
del longopts
|
||||
except getopt.GetoptError, err:
|
||||
usage(actions, err)
|
||||
if args:
|
||||
usage(actions, "arguments given although only options accepted")
|
||||
|
||||
menu = True
|
||||
embed = False
|
||||
@ -615,7 +717,13 @@ def main():
|
||||
if error:
|
||||
usage(actions, error)
|
||||
|
||||
actions.run(menu, fullscreen, embed)
|
||||
if len(floppies) > 1:
|
||||
usage(actions, "multiple floppy images given: %s" % str(floppies))
|
||||
if floppies:
|
||||
if not os.path.exists(floppies[0]):
|
||||
usage(actions, "floppy image '%s' doesn't exist" % floppies[0])
|
||||
|
||||
actions.run(floppies, menu, fullscreen, embed)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -39,7 +39,7 @@ class UInfo:
|
||||
logo = "hatari.png"
|
||||
icon = "hatari-icon.png"
|
||||
# path to the directory where the called script resides
|
||||
path = sys.argv[0][:sys.argv[0].rfind(os.path.sep)]
|
||||
path = os.path.dirname(sys.argv[0])
|
||||
|
||||
def __init__(self, path = None):
|
||||
"UIinfo([path]), set suitable paths for resources from CWD and path"
|
||||
@ -60,7 +60,7 @@ class UInfo:
|
||||
# --------------------------------------------------------
|
||||
# auxiliary class+callback to be used with the PasteDialog
|
||||
|
||||
class HatariTextInsert():
|
||||
class HatariTextInsert:
|
||||
def __init__(self, hatari, text):
|
||||
self.index = 0
|
||||
self.text = text
|
||||
@ -167,3 +167,34 @@ def table_add_separator(table, row):
|
||||
"table_add_separator(table,row)"
|
||||
widget = gtk.HSeparator()
|
||||
table.attach(widget, 0, 2, row, row+1, gtk.FILL)
|
||||
|
||||
|
||||
# -----------------------------
|
||||
# File selection helpers
|
||||
|
||||
def get_open_filename(title, parent, path = None):
|
||||
buttons = (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
|
||||
fsel = gtk.FileChooserDialog(title, parent, gtk.FILE_CHOOSER_ACTION_OPEN, buttons)
|
||||
fsel.set_local_only(True)
|
||||
if path:
|
||||
fsel.set_filename(path)
|
||||
if fsel.run() == gtk.RESPONSE_OK:
|
||||
filename = fsel.get_filename()
|
||||
else:
|
||||
filename = None
|
||||
fsel.destroy()
|
||||
return filename
|
||||
|
||||
def get_save_filename(title, parent, path = None):
|
||||
buttons = (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
|
||||
fsel = gtk.FileChooserDialog(title, parent, gtk.FILE_CHOOSER_ACTION_SAVE, buttons)
|
||||
fsel.set_local_only(True)
|
||||
fsel.set_do_overwrite_confirmation(True)
|
||||
if path:
|
||||
fsel.set_filename(path)
|
||||
if fsel.run() == gtk.RESPONSE_OK:
|
||||
filename = fsel.get_filename()
|
||||
else:
|
||||
filename = None
|
||||
fsel.destroy()
|
||||
return filename
|
||||
|
Loading…
Reference in New Issue
Block a user