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