|
|
1.1 root 1: #!/usr/bin/env python
2: #
3: # Classes for the Hatari UI dialogs
4: #
1.1.1.2 ! root 5: # Copyright (C) 2008-2011 by Eero Tamminen <eerot at berlios>
1.1 root 6: #
7: # This program is free software; you can redistribute it and/or modify
8: # it under the terms of the GNU General Public License as published by
9: # the Free Software Foundation; either version 2 of the License, or
10: # (at your option) any later version.
11: #
12: # This program is distributed in the hope that it will be useful,
13: # but WITHOUT ANY WARRANTY; without even the implied warranty of
14: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15: # GNU General Public License for more details.
16:
17: import os
18: # use correct version of pygtk/gtk
19: import pygtk
20: pygtk.require('2.0')
21: import gtk
22: import pango
23:
24: from uihelpers import UInfo, HatariTextInsert, create_table_dialog, \
25: table_add_entry_row, table_add_widget_row, table_add_separator, \
1.1.1.2 ! root 26: table_add_radio_rows, table_set_col_offset, create_button, FselEntry, \
! 27: FselAndEjectFactory
1.1 root 28:
29:
30: # -----------------
31: # Dialog base class
32:
33: class HatariUIDialog:
34: def __init__(self, parent):
35: "<any>Dialog(parent) -> object"
36: self.parent = parent
37: self.dialog = None
38:
39: def run(self):
40: """run() -> response. Shows dialog and returns response,
41: subclasses overriding run() require also an argument."""
42: response = self.dialog.run()
43: self.dialog.hide()
44: return response
45:
46:
47: # ---------------------------
48: # Note/Todo/Error/Ask dialogs
49:
50: class NoteDialog(HatariUIDialog):
51: button = gtk.BUTTONS_OK
52: icontype = gtk.MESSAGE_INFO
53: textpattern = "\n%s"
54: def run(self, text):
55: "run(text), show message dialog with given text"
56: dialog = gtk.MessageDialog(self.parent,
57: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
58: self.icontype, self.button, self.textpattern % text)
59: dialog.run()
60: dialog.destroy()
61:
62: class TodoDialog(NoteDialog):
63: textpattern = "\nTODO: %s"
64:
65: class ErrorDialog(NoteDialog):
66: button = gtk.BUTTONS_CLOSE
67: icontype = gtk.MESSAGE_ERROR
68: textpattern = "\nERROR: %s"
69:
70:
71: class AskDialog(HatariUIDialog):
72: def run(self, text):
73: "run(text) -> bool, show question dialog and return True if user OKed it"
74: dialog = gtk.MessageDialog(self.parent,
75: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
76: gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, text)
77: response = dialog.run()
78: dialog.destroy()
79: return (response == gtk.RESPONSE_YES)
80:
81:
82: # ---------------------------
83: # About dialog
84:
85: class AboutDialog(HatariUIDialog):
86: def __init__(self, parent):
87: dialog = gtk.AboutDialog()
88: dialog.set_transient_for(parent)
89: dialog.set_name(UInfo.name)
90: dialog.set_version(UInfo.version)
91: dialog.set_website("http://hatari.berlios.de/")
92: dialog.set_website_label("Hatari emulator www-site")
93: dialog.set_authors(["Eero Tamminen"])
94: dialog.set_artists(["The logo is from Hatari"])
95: dialog.set_logo(gtk.gdk.pixbuf_new_from_file(UInfo.logo))
96: dialog.set_translator_credits("translator-credits")
97: dialog.set_copyright(UInfo.copyright)
98: dialog.set_license("""
99: This software is licenced under GPL v2 or later.
100:
101: You can see the whole license at:
102: http://www.gnu.org/licenses/info/GPLv2.html""")
103: self.dialog = dialog
104:
105:
106: # ---------------------------
107: # Input dialog
108:
109: class InputDialog(HatariUIDialog):
110: def __init__(self, parent):
111: dialog = gtk.Dialog("Key/mouse input", parent,
112: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
113: ("Close", gtk.RESPONSE_CLOSE))
114:
115: entry = gtk.Entry()
116: entry.connect("activate", self._entry_cb)
117: insert = create_button("Insert", self._entry_cb)
1.1.1.2 ! root 118: insert.set_tooltip_text("Insert given text to Hatari window")
! 119: enter = create_button("Enter key", self._enter_cb)
! 120: enter.set_tooltip_text("Simulate Enter key press")
1.1 root 121:
122: hbox1 = gtk.HBox()
123: hbox1.add(gtk.Label("Text:"))
124: hbox1.add(entry)
125: hbox1.add(insert)
1.1.1.2 ! root 126: hbox1.add(enter)
1.1 root 127: dialog.vbox.add(hbox1)
128:
1.1.1.2 ! root 129: rclick = gtk.Button("Right click")
1.1 root 130: rclick.connect("pressed", self._rightpress_cb)
131: rclick.connect("released", self._rightrelease_cb)
1.1.1.2 ! root 132: rclick.set_tooltip_text("Simulate Atari right button press & release")
! 133: dclick = create_button("Double click", self._doubleclick_cb)
! 134: dclick.set_tooltip_text("Simulate Atari left button double-click")
1.1 root 135:
136: hbox2 = gtk.HBox()
137: hbox2.add(dclick)
1.1.1.2 ! root 138: hbox2.add(rclick)
1.1 root 139: dialog.vbox.add(hbox2)
140:
141: dialog.show_all()
142: self.dialog = dialog
143: self.entry = entry
144:
145: def _entry_cb(self, widget):
146: text = self.entry.get_text()
147: if text:
148: HatariTextInsert(self.hatari, text)
149: self.entry.set_text("")
150:
1.1.1.2 ! root 151: def _enter_cb(self, widget):
! 152: self.hatari.insert_event("keypress 28") # Enter key scancode
! 153:
1.1 root 154: def _doubleclick_cb(self, widget):
155: self.hatari.insert_event("doubleclick")
156:
157: def _rightpress_cb(self, widget):
1.1.1.2 ! root 158: self.hatari.insert_event("rightdown")
1.1 root 159:
160: def _rightrelease_cb(self, widget):
1.1.1.2 ! root 161: self.hatari.insert_event("rightup")
1.1 root 162:
163: def run(self, hatari):
164: "run(hatari), do text/mouse click input"
165: self.hatari = hatari
166: self.dialog.run()
167: self.dialog.hide()
168:
169:
170: # ---------------------------
171: # Quit and Save dialog
172:
173: class QuitSaveDialog(HatariUIDialog):
174: def __init__(self, parent):
175: dialog = gtk.Dialog("Quit and Save?", parent,
176: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
177: ("Save changes", gtk.RESPONSE_YES,
178: "Discard changes", gtk.RESPONSE_NO,
179: gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
180: dialog.vbox.add(gtk.Label("You have unsaved configuration changes:"))
181: viewport = gtk.Viewport()
182: viewport.add(gtk.Label())
183: scrolledwindow = gtk.ScrolledWindow()
184: scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
185: scrolledwindow.add(viewport)
186: dialog.vbox.add(scrolledwindow)
187: dialog.show_all()
188: self.scrolledwindow = scrolledwindow
189: self.viewport = viewport
190: self.dialog = dialog
191:
192: def run(self, config):
193: "run(config) -> False if canceled, True otherwise or if no changes"
194: changes = []
195: for key, value in config.get_changes():
196: changes.append("%s = %s" % (key, str(value)))
197: if not changes:
198: return True
199: child = self.viewport.get_child()
200: child.set_text(config.get_path() + ":\n" + "\n".join(changes))
201: width, height = child.get_size_request()
202: if height < 320:
203: self.scrolledwindow.set_size_request(width, height)
204: else:
205: self.scrolledwindow.set_size_request(-1, 320)
206: self.viewport.show_all()
207:
208: response = self.dialog.run()
209: self.dialog.hide()
210: if response == gtk.RESPONSE_CANCEL:
211: return False
212: if response == gtk.RESPONSE_YES:
213: config.save()
214: return True
215:
216:
217: # ---------------------------
218: # Kill Hatari dialog
219:
220: class KillDialog(HatariUIDialog):
221: def __init__(self, parent):
222: self.dialog = gtk.MessageDialog(parent,
223: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
224: gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL,
225: """\
226: Hatari emulator is already/still running and it needs to be terminated first. However, if it contains unsaved data, that will be lost.
227:
228: Terminate Hatari anyway?""")
229:
230: def run(self, hatari):
231: "run(hatari) -> True if Hatari killed, False if left running"
232: if not hatari.is_running():
233: return True
234: # Hatari is running, OK to kill?
235: response = self.dialog.run()
236: self.dialog.hide()
237: if response == gtk.RESPONSE_OK:
238: hatari.kill()
239: return True
240: return False
241:
242:
243: # ---------------------------
244: # Reset Hatari dialog
245:
246: class ResetDialog(HatariUIDialog):
247: COLD = 1
248: WARM = 2
249: def __init__(self, parent):
250: self.dialog = gtk.Dialog("Reset Atari?", parent,
251: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
252: ("Cold reset", self.COLD, "Warm reset", self.WARM,
253: gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
254: label = gtk.Label("\nRebooting will lose changes in currently\nrunning Atari programs.\n\nReset anyway?\n")
255: self.dialog.vbox.add(label)
256: label.show()
257:
258: def run(self, hatari):
259: "run(hatari) -> True if Hatari rebooted, False if canceled"
260: if not hatari.is_running():
261: return False
262: # Hatari is running, how to reboot?
263: response = self.dialog.run()
264: self.dialog.hide()
265: if response == self.COLD:
266: hatari.trigger_shortcut("coldreset")
267: elif response == self.WARM:
268: hatari.trigger_shortcut("warmreset")
269: else:
270: return False
271: return True
272:
273:
274: # ----------------------------------
275: # Floppy image dialog
276:
277: class FloppyDialog(HatariUIDialog):
278: def _create_dialog(self, config):
279: table, self.dialog = create_table_dialog(self.parent, "Floppy images", 4, 2)
1.1.1.2 ! root 280: factory = FselAndEjectFactory()
1.1 root 281:
282: row = 0
283: self.floppy = []
284: path = config.get_floppydir()
285: for drive in ("A", "B"):
286: label = "Disk %c:" % drive
1.1.1.2 ! root 287: fname = config.get_floppy(row)
! 288: fsel, box = factory.get(label, path, fname, gtk.FILE_CHOOSER_ACTION_OPEN)
1.1 root 289: table_add_widget_row(table, row, label, box)
1.1.1.2 ! root 290: self.floppy.append(fsel)
1.1 root 291: row += 1
292:
293: protect = gtk.combo_box_new_text()
294: for text in config.get_protection_types():
295: protect.append_text(text)
296: protect.set_active(config.get_floppy_protection())
1.1.1.2 ! root 297: protect.set_tooltip_text("Write protect floppy image contents")
1.1 root 298: table_add_widget_row(table, row, "Write protection:", protect)
299:
300: row += 1
301: slowfdc = gtk.CheckButton("Slow floppy access")
302: slowfdc.set_active(config.get_slowfdc())
1.1.1.2 ! root 303: slowfdc.set_tooltip_text("May be required by some rare game/demo")
1.1 root 304: table_add_widget_row(table, row, None, slowfdc)
305:
306: table.show_all()
307:
308: self.protect = protect
309: self.slowfdc = slowfdc
310:
311: def run(self, config):
312: "run(config), show disk image dialog"
313: if not self.dialog:
314: self._create_dialog(config)
315: response = self.dialog.run()
316: self.dialog.hide()
317:
318: if response == gtk.RESPONSE_APPLY:
319: config.lock_updates()
320: for drive in range(2):
321: config.set_floppy(drive, self.floppy[drive].get_filename())
1.1.1.2 ! root 322: config.set_floppy_protection(self.protect.get_active())
1.1 root 323: config.set_slowfdc(self.slowfdc.get_active())
324: config.flush_updates()
325:
326:
327: # ----------------------------------
328: # Hard disk dialog
329:
330: class HardDiskDialog(HatariUIDialog):
331: def _create_dialog(self, config):
332: table, self.dialog = create_table_dialog(self.parent, "Hard disks", 4, 4, "Set and reboot")
1.1.1.2 ! root 333: factory = FselAndEjectFactory()
1.1 root 334:
1.1.1.2 ! root 335: row = 0
1.1 root 336: label = "ASCI HD image:"
337: path = config.get_acsi_image()
1.1.1.2 ! root 338: fsel, box = factory.get(label, None, path, gtk.FILE_CHOOSER_ACTION_OPEN)
1.1 root 339: table_add_widget_row(table, row, label, box, True)
340: self.acsi = fsel
341: row += 1
342:
343: label = "IDE HD master image:"
344: path = config.get_idemaster_image()
1.1.1.2 ! root 345: fsel, box = factory.get(label, None, path, gtk.FILE_CHOOSER_ACTION_OPEN)
1.1 root 346: table_add_widget_row(table, row, label, box, True)
347: self.idemaster = fsel
348: row += 1
349:
350: label = "IDE HD slave image:"
351: path = config.get_ideslave_image()
1.1.1.2 ! root 352: fsel, box = factory.get(label, None, path, gtk.FILE_CHOOSER_ACTION_OPEN)
1.1 root 353: table_add_widget_row(table, row, label, box, True)
354: self.ideslave = fsel
355: row += 1
356:
357: label = "GEMDOS drive directory:"
358: path = config.get_gemdos_dir()
1.1.1.2 ! root 359: fsel, box = factory.get(label, None, path, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
1.1 root 360: table_add_widget_row(table, row, label, box, True)
361: self.gemdos = fsel
362: row += 1
363:
364: protect = gtk.combo_box_new_text()
365: for text in config.get_protection_types():
366: protect.append_text(text)
1.1.1.2 ! root 367: protect.set_tooltip_text("Write protect GEMDOS drive contents")
1.1 root 368: table_add_widget_row(table, row, "Write protection:", protect)
369: self.protect = protect
370:
371: table.show_all()
372:
373: def _get_config(self, config):
374: path = config.get_gemdos_dir()
375: if path:
376: self.gemdos.set_filename(path)
377: path = config.get_acsi_image()
378: if path:
379: self.acsi.set_filename(path)
380: path = config.get_idemaster_image()
381: if path:
382: self.idemaster.set_filename(path)
383: path = config.get_ideslave_image()
384: if path:
385: self.ideslave.set_filename(path)
386: self.protect.set_active(config.get_hd_protection())
387:
388: def _set_config(self, config):
389: config.lock_updates()
390: config.set_gemdos_dir(self.gemdos.get_filename())
391: config.set_acsi_image(self.acsi.get_filename())
392: config.set_idemaster_image(self.idemaster.get_filename())
393: config.set_ideslave_image(self.ideslave.get_filename())
394: config.set_hd_protection(self.protect.get_active())
395: config.flush_updates()
396:
397: def run(self, config):
398: "run(config) -> bool, whether to reboot"
399: if not self.dialog:
400: self._create_dialog(config)
401:
402: self._get_config(config)
403: response = self.dialog.run()
404: self.dialog.hide()
405: if response == gtk.RESPONSE_APPLY:
406: self._set_config(config)
407: return True
408: return False
409:
410:
411: # ---------------------------
412: # Display dialog
413:
414: class DisplayDialog(HatariUIDialog):
415:
416: def _create_dialog(self, config):
417:
418: skip = gtk.combo_box_new_text()
419: for text in config.get_frameskip_names():
420: skip.append_text(text)
421: skip.set_active(config.get_frameskip())
1.1.1.2 ! root 422: skip.set_tooltip_text("Set how many frames are skipped")
1.1 root 423:
424: maxw, maxh = config.get_max_size()
425: maxadjw = gtk.Adjustment(maxw, 320, 1280, 8, 40)
426: maxadjh = gtk.Adjustment(maxh, 200, 960, 8, 40)
427: scalew = gtk.HScale(maxadjw)
428: scaleh = gtk.HScale(maxadjh)
429: scalew.set_digits(0)
430: scaleh.set_digits(0)
1.1.1.2 ! root 431: scalew.set_tooltip_text("Preferred/maximum zoomed width")
! 432: scaleh.set_tooltip_text("Preferred/maximum zoomed height")
! 433:
! 434: desktop = gtk.CheckButton("Keep desktop resolution")
! 435: desktop.set_active(config.get_desktop())
! 436: desktop.set_tooltip_text("Whether to keep desktop resolution in fullscreen and (try to) scale Atari screen by an integer factor instead")
1.1 root 437:
438: borders = gtk.CheckButton("ST/STE overscan borders")
439: borders.set_active(config.get_borders())
1.1.1.2 ! root 440: borders.set_tooltip_text("Whether to show overscan borders in ST/STE low/mid-rez. Visible border area is affected by max. zoom size")
1.1 root 441:
442: statusbar = gtk.CheckButton("Show statusbar")
443: statusbar.set_active(config.get_statusbar())
1.1.1.2 ! root 444: statusbar.set_tooltip_text("Whether to show statusbar with floppy leds etc")
1.1 root 445:
446: led = gtk.CheckButton("Show overlay led")
447: led.set_active(config.get_led())
1.1.1.2 ! root 448: led.set_tooltip_text("Whether to show overlay drive led when statusbar isn't visible")
! 449:
! 450: crop = gtk.CheckButton("Remove statusbar from screen capture")
! 451: crop.set_active(config.get_crop())
! 452: crop.set_tooltip_text("Whether to crop statusbar from screenshots and video recordings")
1.1 root 453:
454: dialog = gtk.Dialog("Display settings", self.parent,
455: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
456: (gtk.STOCK_APPLY, gtk.RESPONSE_APPLY,
457: gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
458:
459: dialog.vbox.add(gtk.Label("Frameskip:"))
460: dialog.vbox.add(skip)
461: dialog.vbox.add(gtk.Label("Max zoomed size:"))
462: dialog.vbox.add(scalew)
463: dialog.vbox.add(scaleh)
1.1.1.2 ! root 464: dialog.vbox.add(desktop)
1.1 root 465: dialog.vbox.add(borders)
466: dialog.vbox.add(statusbar)
467: dialog.vbox.add(led)
1.1.1.2 ! root 468: dialog.vbox.add(crop)
1.1 root 469: dialog.vbox.show_all()
470:
471: self.dialog = dialog
472: self.skip = skip
473: self.maxw = maxadjw
474: self.maxh = maxadjh
1.1.1.2 ! root 475: self.desktop = desktop
1.1 root 476: self.borders = borders
477: self.statusbar = statusbar
478: self.led = led
1.1.1.2 ! root 479: self.crop = crop
1.1 root 480:
481: def run(self, config):
482: "run(config), show display dialog"
483: if not self.dialog:
484: self._create_dialog(config)
485: response = self.dialog.run()
486: self.dialog.hide()
487: if response == gtk.RESPONSE_APPLY:
488: config.lock_updates()
489: config.set_frameskip(self.skip.get_active())
490: config.set_max_size(self.maxw.get_value(), self.maxh.get_value())
1.1.1.2 ! root 491: config.set_desktop(self.desktop.get_active())
1.1 root 492: config.set_borders(self.borders.get_active())
493: config.set_statusbar(self.statusbar.get_active())
494: config.set_led(self.led.get_active())
1.1.1.2 ! root 495: config.set_crop(self.crop.get_active())
1.1 root 496: config.flush_updates()
497:
498:
499: # ----------------------------------
500: # Joystick dialog
501:
502: class JoystickDialog(HatariUIDialog):
503: def _create_dialog(self, config):
504: table, self.dialog = create_table_dialog(self.parent, "Joystick settings", 9, 2)
505:
506: joy = 0
507: self.joy = []
508: joytypes = config.get_joystick_types()
509: for label in config.get_joystick_names():
510: combo = gtk.combo_box_new_text()
511: for text in joytypes:
512: combo.append_text(text)
513: combo.set_active(config.get_joystick(joy))
514: widget = table_add_widget_row(table, joy, "%s:" % label, combo)
515: self.joy.append(widget)
516: joy += 1
517:
518: table.show_all()
519:
520: def run(self, config):
521: "run(config), show joystick dialog"
522: if not self.dialog:
523: self._create_dialog(config)
524: response = self.dialog.run()
525: self.dialog.hide()
526:
527: if response == gtk.RESPONSE_APPLY:
528: config.lock_updates()
529: for joy in range(6):
530: config.set_joystick(joy, self.joy[joy].get_active())
531: config.flush_updates()
532:
533:
534: # ---------------------------------------
535: # Peripherals (midi,printer,rs232) dialog
536:
537: class PeripheralDialog(HatariUIDialog):
538: def _create_dialog(self, config):
539: midi = gtk.CheckButton("Enable MIDI")
540: midi.set_active(config.get_midi())
541:
542: printer = gtk.CheckButton("Enable printer output")
543: printer.set_active(config.get_printer())
544:
545: rs232 = gtk.CheckButton("Enable RS232")
546: rs232.set_active(config.get_rs232())
547:
548: dialog = gtk.Dialog("Peripherals", self.parent,
549: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
550: (gtk.STOCK_APPLY, gtk.RESPONSE_APPLY,
551: gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
552: dialog.vbox.add(midi)
553: dialog.vbox.add(printer)
554: dialog.vbox.add(rs232)
555: dialog.vbox.show_all()
556:
557: self.dialog = dialog
558: self.printer = printer
559: self.rs232 = rs232
560: self.midi = midi
561:
562: def run(self, config):
563: "run(config), show peripherals dialog"
564: if not self.dialog:
565: self._create_dialog(config)
566: response = self.dialog.run()
567: self.dialog.hide()
568:
569: if response == gtk.RESPONSE_APPLY:
570: config.lock_updates()
571: config.set_midi(self.midi.get_active())
572: config.set_printer(self.printer.get_active())
573: config.set_rs232(self.rs232.get_active())
574: config.flush_updates()
575:
576:
577: # ---------------------------------------
578: # Path dialog
579:
580: class PathDialog(HatariUIDialog):
581: def _create_dialog(self, config):
582: paths = config.get_paths()
583: table, self.dialog = create_table_dialog(self.parent, "File path settings", len(paths), 2)
584: paths.sort()
585: row = 0
586: self.paths = []
587: for (key, path, label) in paths:
588: fsel = FselEntry(self.dialog, self._validate_fname, key)
589: fsel.set_filename(path)
590: self.paths.append((key, fsel))
591: table_add_widget_row(table, row, label, fsel.get_container())
592: row += 1
593: table.show_all()
594:
595: def _validate_fname(self, key, fname):
596: if key != "soundout":
597: return True
598: if fname.rsplit(".", 1)[-1].lower() in ("ym", "wav"):
599: return True
600: ErrorDialog(self.dialog).run("Sound output file name:\n\t%s\nneeds to end with '.ym' or '.wav'." % fname)
601: return False
602:
603: def run(self, config):
604: "run(config), show paths dialog"
605: if not self.dialog:
606: self._create_dialog(config)
607: response = self.dialog.run()
608: self.dialog.hide()
609:
610: if response == gtk.RESPONSE_APPLY:
611: paths = []
612: for key, fsel in self.paths:
613: paths.append((key, fsel.get_filename()))
614: config.set_paths(paths)
615:
616:
617: # ---------------------------
618: # Sound dialog
619:
620: class SoundDialog(HatariUIDialog):
621:
622: def _create_dialog(self, config):
623: combo = gtk.combo_box_entry_new_text()
624: for text in config.get_sound_values():
625: combo.append_text(text)
626: enabled, hz = config.get_sound()
627: self.enabled = gtk.CheckButton("Sound enabled")
628: self.enabled.set_active(enabled)
629: combo.child.set_text(hz)
630: box = gtk.HBox()
631: box.pack_start(gtk.Label("Sound frequency:"), False, False)
632: box.add(combo)
633: self.sound = combo.child
634:
635: dialog = gtk.Dialog("Sound settings", self.parent,
636: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
637: (gtk.STOCK_APPLY, gtk.RESPONSE_APPLY,
638: gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
639: dialog.vbox.add(self.enabled)
640: dialog.vbox.add(box)
641: dialog.vbox.show_all()
642: self.dialog = dialog
643:
644: def run(self, config):
645: "run(config), show sound dialog"
646: if not self.dialog:
647: self._create_dialog(config)
648: response = self.dialog.run()
649: self.dialog.hide()
650: if response == gtk.RESPONSE_APPLY:
651: enabled = self.enabled.get_active()
652: hz1 = self.sound.get_text()
653: hz2 = config.set_sound(enabled, hz1)
654: if hz2 != hz1:
655: self.sound.set_text(hz2)
656:
657:
658: # ---------------------------
659: # Trace settings dialog
660:
661: class TraceDialog(HatariUIDialog):
662: # you can get this list with:
663: # hatari --trace help 2>&1|awk '/all$/{next} /^ [^-]/ {printf("\"%s\",\n", $1)}'
664: tracepoints = [
665: "video_sync",
666: "video_res",
667: "video_color",
668: "video_border_v",
669: "video_border_h",
670: "video_addr",
671: "video_hbl",
672: "video_vbl",
673: "video_ste",
674: "mfp_exception",
675: "mfp_start",
676: "mfp_read",
677: "mfp_write",
1.1.1.2 ! root 678: "psg_read",
! 679: "psg_write",
1.1 root 680: "cpu_pairing",
681: "cpu_disasm",
682: "cpu_exception",
683: "int",
684: "fdc",
685: "ikbd_cmds",
686: "ikbd_acia",
687: "ikbd_exec",
688: "blitter",
689: "bios",
690: "xbios",
691: "gemdos",
692: "vdi",
1.1.1.2 ! root 693: "aes",
1.1 root 694: "io_read",
695: "io_write",
1.1.1.2 ! root 696: "dmasound",
! 697: "crossbar",
! 698: "videl",
! 699: "dsp_host_interface",
! 700: "dsp_host_command",
! 701: "dsp_host_ssi",
! 702: "dsp_interrupt",
! 703: "dsp_disasm",
! 704: "dsp_state"
1.1 root 705: ]
706: def __init__(self, parent):
707: self.savedpoints = "none"
708: hbox1 = gtk.HBox()
709: hbox1.add(create_button("Load", self._load_traces))
710: hbox1.add(create_button("Clear", self._clear_traces))
711: hbox1.add(create_button("Save", self._save_traces))
712: hbox2 = gtk.HBox()
713: vboxes = []
1.1.1.2 ! root 714: for idx in (0,1,2,3):
1.1 root 715: vboxes.append(gtk.VBox())
716: hbox2.add(vboxes[idx])
717:
718: count = 0
1.1.1.2 ! root 719: per_side = (len(self.tracepoints)+3)/4
1.1 root 720: self.tracewidgets = {}
721: for trace in self.tracepoints:
722: name = trace.replace("_", "-")
723: widget = gtk.CheckButton(name)
724: self.tracewidgets[trace] = widget
725: vboxes[count/per_side].pack_start(widget, False, True)
726: count += 1
727:
728: dialog = gtk.Dialog("Trace settings", parent,
729: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
730: (gtk.STOCK_APPLY, gtk.RESPONSE_APPLY,
731: gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
732: dialog.vbox.add(hbox1)
733: dialog.vbox.add(gtk.Label("Select trace points:"))
734: dialog.vbox.add(hbox2)
735: dialog.vbox.show_all()
736: self.dialog = dialog
737:
738: def _get_traces(self):
739: traces = []
740: for trace in self.tracepoints:
741: if self.tracewidgets[trace].get_active():
742: traces.append(trace)
743: if traces:
744: return ",".join(traces)
745: return "none"
746:
747: def _set_traces(self, tracepoints):
748: self._clear_traces()
749: for trace in tracepoints.split(","):
750: if trace in self.tracewidgets:
751: self.tracewidgets[trace].set_active(True)
752: else:
1.1.1.2 ! root 753: print("ERROR: unknown trace setting '%s'" % trace)
1.1 root 754:
755: def _clear_traces(self, widget = None):
756: for trace in self.tracepoints:
757: self.tracewidgets[trace].set_active(False)
758:
759: def _load_traces(self, widget):
760: # this doesn't load traces, just sets them from internal variable
761: # that run method gets from caller and sets. It's up to caller
762: # whether the saving or loading happens actually to disk
763: self._set_traces(self.savedpoints)
764:
765: def _save_traces(self, widget):
766: self.savedpoints = self._get_traces()
767:
768: def run(self, hatari, savedpoints):
769: "run(hatari,tracepoints) -> tracepoints, caller saves tracepoints"
770: self.savedpoints = savedpoints
771: while 1:
772: response = self.dialog.run()
773: if response == gtk.RESPONSE_APPLY:
774: hatari.change_option("--trace %s" % self._get_traces())
775: else:
776: self.dialog.hide()
777: return self.savedpoints
778:
779:
780: # ------------------------------------------
781: # Machine dialog for settings needing reboot
782:
783: class MachineDialog(HatariUIDialog):
784: def _machine_cb(self, widget, data):
785: if not widget.get_active():
786: return
787: machine = data.lower()
788: if machine == "ste" or machine == "st":
789: self.clocks[0].set_active(True)
790: self.cpulevel.set_active(0)
791: elif machine == "falcon":
792: self.clocks[1].set_active(True)
1.1.1.2 ! root 793: self.dsps[2].set_active(True)
1.1 root 794: self.cpulevel.set_active(3)
795: elif machine == "tt":
796: self.clocks[2].set_active(True)
797: self.cpulevel.set_active(3)
798:
799: def _create_dialog(self, config):
800: table, self.dialog = create_table_dialog(self.parent, "Machine configuration", 6, 4, "Set and reboot")
801:
802: row = 0
803: self.machines = table_add_radio_rows(table, row, "Machine:",
804: config.get_machine_types(), self._machine_cb)
805: row += 1
806:
1.1.1.2 ! root 807: self.dsps = table_add_radio_rows(table, row, "DSP type:", config.get_dsp_types())
1.1 root 808: row += 1
809:
810: # start next table column
811: row = 0
812: table_set_col_offset(table, 2)
813: self.monitors = table_add_radio_rows(table, row, "Monitor:", config.get_monitor_types())
814: row += 1
815:
816: self.clocks = table_add_radio_rows(table, row, "CPU clock:", config.get_cpuclock_types())
817: row += 1
818:
819: # fullspan at bottom
820: fullspan = True
821:
822: combo = gtk.combo_box_new_text()
823: for text in config.get_cpulevel_types():
824: combo.append_text(text)
825: self.cpulevel = table_add_widget_row(table, row, "CPU type:", combo, fullspan)
826: row += 1
827:
828: combo = gtk.combo_box_new_text()
829: for text in config.get_memory_names():
830: combo.append_text(text)
831: self.memory = table_add_widget_row(table, row, "Memory:", combo, fullspan)
832: row += 1
833:
834: label = "TOS image:"
835: fsel = self._fsel(label, gtk.FILE_CHOOSER_ACTION_OPEN)
836: self.tos = table_add_widget_row(table, row, label, fsel, fullspan)
837: row += 1
838:
839: vbox = gtk.VBox()
840: self.compatible = gtk.CheckButton("Compatible CPU")
1.1.1.2 ! root 841: self.rtc = gtk.CheckButton("Real-time clock")
1.1 root 842: self.timerd = gtk.CheckButton("Patch Timer-D")
843: vbox.add(self.compatible)
844: vbox.add(self.timerd)
1.1.1.2 ! root 845: vbox.add(self.rtc)
1.1 root 846: table_add_widget_row(table, row, "Misc.:", vbox, fullspan)
847: row += 1
848:
849: table.show_all()
850:
851: def _fsel(self, label, action):
852: fsel = gtk.FileChooserButton(label)
853: # Hatari cannot access URIs
854: fsel.set_local_only(True)
855: fsel.set_width_chars(12)
856: fsel.set_action(action)
857: return fsel
858:
859: def _get_config(self, config):
860: self.machines[config.get_machine()].set_active(True)
861: self.monitors[config.get_monitor()].set_active(True)
862: self.clocks[config.get_cpuclock()].set_active(True)
863: self.dsps[config.get_dsp()].set_active(True)
864: self.cpulevel.set_active(config.get_cpulevel())
865: self.memory.set_active(config.get_memory())
866: tos = config.get_tos()
867: if tos:
868: self.tos.set_filename(tos)
869: self.compatible.set_active(config.get_compatible())
870: self.timerd.set_active(config.get_timerd())
1.1.1.2 ! root 871: self.rtc.set_active(config.get_rtc())
1.1 root 872:
873: def _get_active_radio(self, radios):
874: idx = 0
875: for radio in radios:
876: if radio.get_active():
877: return idx
878: idx += 1
879:
880: def _set_config(self, config):
881: config.lock_updates()
882: config.set_machine(self._get_active_radio(self.machines))
883: config.set_monitor(self._get_active_radio(self.monitors))
884: config.set_cpuclock(self._get_active_radio(self.clocks))
885: config.set_dsp(self._get_active_radio(self.dsps))
886: config.set_cpulevel(self.cpulevel.get_active())
887: config.set_memory(self.memory.get_active())
888: config.set_tos(self.tos.get_filename())
889: config.set_compatible(self.compatible.get_active())
890: config.set_timerd(self.timerd.get_active())
1.1.1.2 ! root 891: config.set_rtc(self.rtc.get_active())
1.1 root 892: config.flush_updates()
893:
894: def run(self, config):
895: "run(config) -> bool, whether to reboot"
896: if not self.dialog:
897: self._create_dialog(config)
898:
899: self._get_config(config)
900: response = self.dialog.run()
901: self.dialog.hide()
902: if response == gtk.RESPONSE_APPLY:
903: self._set_config(config)
904: return True
905: return False
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.