|
|
1.1 root 1: #!/usr/bin/env python
2: #
3: # Classes for the Hatari UI dialogs
4: #
1.1.1.6 ! root 5: # Copyright (C) 2008-2012 by Eero Tamminen
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.4 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):
1.1.1.3 root 51: button = gtk.BUTTONS_OK
1.1 root 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,
1.1.1.3 root 58: self.icontype, self.button, self.textpattern % text)
1.1 root 59: dialog.run()
60: dialog.destroy()
61:
62: class TodoDialog(NoteDialog):
63: textpattern = "\nTODO: %s"
64:
65: class ErrorDialog(NoteDialog):
1.1.1.3 root 66: button = gtk.BUTTONS_CLOSE
1.1 root 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)
1.1.1.5 root 91: dialog.set_website("http://hatari.tuxfamily.org/")
1.1 root 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")
1.1.1.3 root 97: dialog.set_copyright(UInfo.copyright)
1.1 root 98: dialog.set_license("""
1.1.1.2 root 99: This software is licenced under GPL v2 or later.
1.1 root 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.4 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.4 root 126: hbox1.add(enter)
1.1 root 127: dialog.vbox.add(hbox1)
128:
1.1.1.4 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.4 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.4 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.4 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.4 root 158: self.hatari.insert_event("rightdown")
1.1 root 159:
160: def _rightrelease_cb(self, widget):
1.1.1.4 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:
1.1.1.3 root 277: class FloppyDialog(HatariUIDialog):
1.1 root 278: def _create_dialog(self, config):
1.1.1.3 root 279: table, self.dialog = create_table_dialog(self.parent, "Floppy images", 4, 2)
1.1.1.4 root 280: factory = FselAndEjectFactory()
1.1.1.3 root 281:
1.1 root 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.4 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.4 root 290: self.floppy.append(fsel)
1.1 root 291: row += 1
292:
1.1.1.3 root 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.4 root 297: protect.set_tooltip_text("Write protect floppy image contents")
1.1.1.3 root 298: table_add_widget_row(table, row, "Write protection:", protect)
299:
300: row += 1
1.1.1.5 root 301: fastfdc = gtk.CheckButton("Fast floppy access")
302: fastfdc.set_active(config.get_fastfdc())
1.1.1.6 ! root 303: fastfdc.set_tooltip_text("Can cause incompatibilities with some games/demos")
1.1.1.5 root 304: table_add_widget_row(table, row, None, fastfdc)
1.1.1.3 root 305:
1.1 root 306: table.show_all()
307:
1.1.1.3 root 308: self.protect = protect
1.1.1.5 root 309: self.fastfdc = fastfdc
1.1 root 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.4 root 322: config.set_floppy_protection(self.protect.get_active())
1.1.1.5 root 323: config.set_fastfdc(self.fastfdc.get_active())
1.1 root 324: config.flush_updates()
325:
1.1.1.3 root 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.4 root 333: factory = FselAndEjectFactory()
1.1.1.3 root 334:
1.1.1.4 root 335: row = 0
1.1.1.3 root 336: label = "ASCI HD image:"
337: path = config.get_acsi_image()
1.1.1.4 root 338: fsel, box = factory.get(label, None, path, gtk.FILE_CHOOSER_ACTION_OPEN)
1.1.1.3 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.4 root 345: fsel, box = factory.get(label, None, path, gtk.FILE_CHOOSER_ACTION_OPEN)
1.1.1.3 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.4 root 352: fsel, box = factory.get(label, None, path, gtk.FILE_CHOOSER_ACTION_OPEN)
1.1.1.3 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.4 root 359: fsel, box = factory.get(label, None, path, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
1.1.1.3 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.4 root 367: protect.set_tooltip_text("Write protect GEMDOS drive contents")
1.1.1.3 root 368: table_add_widget_row(table, row, "Write protection:", protect)
369: self.protect = protect
370:
371: table.show_all()
1.1 root 372:
1.1.1.3 root 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:
1.1 root 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.4 root 422: skip.set_tooltip_text("Set how many frames are skipped")
1.1 root 423:
1.1.1.3 root 424: maxw, maxh = config.get_max_size()
1.1.1.6 ! root 425: topw, toph = config.get_desktop_size()
! 426: maxadjw = gtk.Adjustment(maxw, 320, topw, 8, 40)
! 427: maxadjh = gtk.Adjustment(maxh, 200, toph, 8, 40)
1.1.1.3 root 428: scalew = gtk.HScale(maxadjw)
429: scaleh = gtk.HScale(maxadjh)
430: scalew.set_digits(0)
431: scaleh.set_digits(0)
1.1.1.4 root 432: scalew.set_tooltip_text("Preferred/maximum zoomed width")
433: scaleh.set_tooltip_text("Preferred/maximum zoomed height")
434:
1.1.1.6 ! root 435: desktop = gtk.CheckButton("Keep desktop resolution (for Falcon/TT)")
1.1.1.4 root 436: desktop.set_active(config.get_desktop())
437: 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 438:
1.1.1.3 root 439: borders = gtk.CheckButton("ST/STE overscan borders")
1.1 root 440: borders.set_active(config.get_borders())
1.1.1.4 root 441: 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 442:
1.1.1.3 root 443: statusbar = gtk.CheckButton("Show statusbar")
1.1 root 444: statusbar.set_active(config.get_statusbar())
1.1.1.4 root 445: statusbar.set_tooltip_text("Whether to show statusbar with floppy leds etc")
1.1 root 446:
1.1.1.3 root 447: led = gtk.CheckButton("Show overlay led")
1.1 root 448: led.set_active(config.get_led())
1.1.1.4 root 449: led.set_tooltip_text("Whether to show overlay drive led when statusbar isn't visible")
450:
451: crop = gtk.CheckButton("Remove statusbar from screen capture")
452: crop.set_active(config.get_crop())
453: crop.set_tooltip_text("Whether to crop statusbar from screenshots and video recordings")
1.1 root 454:
455: dialog = gtk.Dialog("Display settings", self.parent,
456: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
457: (gtk.STOCK_APPLY, gtk.RESPONSE_APPLY,
458: gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
459:
460: dialog.vbox.add(gtk.Label("Frameskip:"))
461: dialog.vbox.add(skip)
1.1.1.3 root 462: dialog.vbox.add(gtk.Label("Max zoomed size:"))
463: dialog.vbox.add(scalew)
464: dialog.vbox.add(scaleh)
1.1.1.4 root 465: dialog.vbox.add(desktop)
1.1 root 466: dialog.vbox.add(borders)
467: dialog.vbox.add(statusbar)
468: dialog.vbox.add(led)
1.1.1.4 root 469: dialog.vbox.add(crop)
1.1 root 470: dialog.vbox.show_all()
471:
472: self.dialog = dialog
473: self.skip = skip
1.1.1.3 root 474: self.maxw = maxadjw
475: self.maxh = maxadjh
1.1.1.4 root 476: self.desktop = desktop
1.1 root 477: self.borders = borders
478: self.statusbar = statusbar
479: self.led = led
1.1.1.4 root 480: self.crop = crop
1.1 root 481:
482: def run(self, config):
483: "run(config), show display dialog"
484: if not self.dialog:
485: self._create_dialog(config)
486: response = self.dialog.run()
487: self.dialog.hide()
488: if response == gtk.RESPONSE_APPLY:
489: config.lock_updates()
490: config.set_frameskip(self.skip.get_active())
1.1.1.3 root 491: config.set_max_size(self.maxw.get_value(), self.maxh.get_value())
1.1.1.4 root 492: config.set_desktop(self.desktop.get_active())
1.1 root 493: config.set_borders(self.borders.get_active())
494: config.set_statusbar(self.statusbar.get_active())
495: config.set_led(self.led.get_active())
1.1.1.4 root 496: config.set_crop(self.crop.get_active())
1.1 root 497: config.flush_updates()
498:
499:
500: # ----------------------------------
501: # Joystick dialog
502:
503: class JoystickDialog(HatariUIDialog):
504: def _create_dialog(self, config):
1.1.1.3 root 505: table, self.dialog = create_table_dialog(self.parent, "Joystick settings", 9, 2)
1.1 root 506:
507: joy = 0
508: self.joy = []
509: joytypes = config.get_joystick_types()
510: for label in config.get_joystick_names():
511: combo = gtk.combo_box_new_text()
512: for text in joytypes:
513: combo.append_text(text)
514: combo.set_active(config.get_joystick(joy))
515: widget = table_add_widget_row(table, joy, "%s:" % label, combo)
516: self.joy.append(widget)
517: joy += 1
518:
519: table.show_all()
520:
521: def run(self, config):
522: "run(config), show joystick dialog"
523: if not self.dialog:
524: self._create_dialog(config)
525: response = self.dialog.run()
526: self.dialog.hide()
527:
528: if response == gtk.RESPONSE_APPLY:
529: config.lock_updates()
530: for joy in range(6):
531: config.set_joystick(joy, self.joy[joy].get_active())
532: config.flush_updates()
533:
534:
535: # ---------------------------------------
536: # Peripherals (midi,printer,rs232) dialog
537:
538: class PeripheralDialog(HatariUIDialog):
539: def _create_dialog(self, config):
540: midi = gtk.CheckButton("Enable MIDI")
541: midi.set_active(config.get_midi())
542:
543: printer = gtk.CheckButton("Enable printer output")
544: printer.set_active(config.get_printer())
545:
546: rs232 = gtk.CheckButton("Enable RS232")
547: rs232.set_active(config.get_rs232())
548:
549: dialog = gtk.Dialog("Peripherals", self.parent,
550: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
551: (gtk.STOCK_APPLY, gtk.RESPONSE_APPLY,
552: gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
553: dialog.vbox.add(midi)
554: dialog.vbox.add(printer)
555: dialog.vbox.add(rs232)
556: dialog.vbox.show_all()
557:
558: self.dialog = dialog
559: self.printer = printer
560: self.rs232 = rs232
561: self.midi = midi
562:
563: def run(self, config):
564: "run(config), show peripherals dialog"
565: if not self.dialog:
566: self._create_dialog(config)
567: response = self.dialog.run()
568: self.dialog.hide()
569:
570: if response == gtk.RESPONSE_APPLY:
571: config.lock_updates()
572: config.set_midi(self.midi.get_active())
573: config.set_printer(self.printer.get_active())
574: config.set_rs232(self.rs232.get_active())
575: config.flush_updates()
576:
577:
578: # ---------------------------------------
579: # Path dialog
580:
581: class PathDialog(HatariUIDialog):
582: def _create_dialog(self, config):
583: paths = config.get_paths()
1.1.1.3 root 584: table, self.dialog = create_table_dialog(self.parent, "File path settings", len(paths), 2)
1.1 root 585: paths.sort()
586: row = 0
587: self.paths = []
588: for (key, path, label) in paths:
589: fsel = FselEntry(self.dialog, self._validate_fname, key)
590: fsel.set_filename(path)
591: self.paths.append((key, fsel))
592: table_add_widget_row(table, row, label, fsel.get_container())
593: row += 1
594: table.show_all()
595:
596: def _validate_fname(self, key, fname):
597: if key != "soundout":
598: return True
599: if fname.rsplit(".", 1)[-1].lower() in ("ym", "wav"):
600: return True
601: ErrorDialog(self.dialog).run("Sound output file name:\n\t%s\nneeds to end with '.ym' or '.wav'." % fname)
602: return False
603:
604: def run(self, config):
605: "run(config), show paths dialog"
606: if not self.dialog:
607: self._create_dialog(config)
608: response = self.dialog.run()
609: self.dialog.hide()
610:
611: if response == gtk.RESPONSE_APPLY:
612: paths = []
613: for key, fsel in self.paths:
614: paths.append((key, fsel.get_filename()))
615: config.set_paths(paths)
616:
617:
618: # ---------------------------
619: # Sound dialog
620:
621: class SoundDialog(HatariUIDialog):
622:
623: def _create_dialog(self, config):
1.1.1.6 ! root 624: enabled, curhz = config.get_sound()
! 625:
1.1 root 626: self.enabled = gtk.CheckButton("Sound enabled")
627: self.enabled.set_active(enabled)
1.1.1.6 ! root 628:
! 629: hz = gtk.combo_box_new_text()
! 630: for text in config.get_sound_values():
! 631: hz.append_text(text)
! 632: hz.set_active(curhz)
! 633: self.hz = hz
! 634:
! 635: ymmixer = gtk.combo_box_new_text()
! 636: for text in config.get_ymmixer_types():
! 637: ymmixer.append_text(text)
! 638: ymmixer.set_active(config.get_ymmixer())
! 639: self.ymmixer = ymmixer
! 640:
! 641: self.mic = gtk.CheckButton("Enable (Falcon) microphone")
! 642: self.mic.set_active(config.get_mic())
1.1 root 643:
644: dialog = gtk.Dialog("Sound settings", self.parent,
645: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
646: (gtk.STOCK_APPLY, gtk.RESPONSE_APPLY,
647: gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
648: dialog.vbox.add(self.enabled)
1.1.1.6 ! root 649: dialog.vbox.add(gtk.Label("Sound frequency::"))
! 650: dialog.vbox.add(hz)
! 651: dialog.vbox.add(gtk.Label("YM voices mixing method:"))
! 652: dialog.vbox.add(ymmixer)
! 653: dialog.vbox.add(self.mic)
1.1 root 654: dialog.vbox.show_all()
655: self.dialog = dialog
656:
657: def run(self, config):
658: "run(config), show sound dialog"
659: if not self.dialog:
660: self._create_dialog(config)
661: response = self.dialog.run()
662: self.dialog.hide()
663: if response == gtk.RESPONSE_APPLY:
1.1.1.6 ! root 664: config.lock_updates()
! 665: config.set_mic(self.mic.get_active())
! 666: config.set_ymmixer(self.ymmixer.get_active())
1.1 root 667: enabled = self.enabled.get_active()
1.1.1.6 ! root 668: hz = self.hz.get_active()
! 669: config.set_sound(enabled, hz)
! 670: config.flush_updates()
1.1 root 671:
672:
673: # ---------------------------
674: # Trace settings dialog
675:
676: class TraceDialog(HatariUIDialog):
677: # you can get this list with:
678: # hatari --trace help 2>&1|awk '/all$/{next} /^ [^-]/ {printf("\"%s\",\n", $1)}'
679: tracepoints = [
680: "video_sync",
681: "video_res",
682: "video_color",
683: "video_border_v",
684: "video_border_h",
685: "video_addr",
686: "video_hbl",
687: "video_vbl",
688: "video_ste",
689: "mfp_exception",
690: "mfp_start",
691: "mfp_read",
692: "mfp_write",
1.1.1.4 root 693: "psg_read",
694: "psg_write",
1.1 root 695: "cpu_pairing",
696: "cpu_disasm",
697: "cpu_exception",
698: "int",
699: "fdc",
700: "ikbd_cmds",
701: "ikbd_acia",
702: "ikbd_exec",
703: "blitter",
704: "bios",
705: "xbios",
706: "gemdos",
707: "vdi",
1.1.1.4 root 708: "aes",
1.1 root 709: "io_read",
710: "io_write",
1.1.1.4 root 711: "dmasound",
712: "crossbar",
713: "videl",
714: "dsp_host_interface",
715: "dsp_host_command",
716: "dsp_host_ssi",
717: "dsp_interrupt",
718: "dsp_disasm",
719: "dsp_state"
1.1 root 720: ]
721: def __init__(self, parent):
1.1.1.6 ! root 722: self.savedpoints = None
1.1 root 723: hbox1 = gtk.HBox()
724: hbox1.add(create_button("Load", self._load_traces))
725: hbox1.add(create_button("Clear", self._clear_traces))
726: hbox1.add(create_button("Save", self._save_traces))
727: hbox2 = gtk.HBox()
728: vboxes = []
1.1.1.4 root 729: for idx in (0,1,2,3):
1.1 root 730: vboxes.append(gtk.VBox())
731: hbox2.add(vboxes[idx])
732:
733: count = 0
1.1.1.4 root 734: per_side = (len(self.tracepoints)+3)/4
1.1 root 735: self.tracewidgets = {}
736: for trace in self.tracepoints:
1.1.1.2 root 737: name = trace.replace("_", "-")
738: widget = gtk.CheckButton(name)
1.1 root 739: self.tracewidgets[trace] = widget
740: vboxes[count/per_side].pack_start(widget, False, True)
741: count += 1
742:
743: dialog = gtk.Dialog("Trace settings", parent,
744: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
745: (gtk.STOCK_APPLY, gtk.RESPONSE_APPLY,
746: gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
747: dialog.vbox.add(hbox1)
748: dialog.vbox.add(gtk.Label("Select trace points:"))
749: dialog.vbox.add(hbox2)
750: dialog.vbox.show_all()
751: self.dialog = dialog
752:
753: def _get_traces(self):
754: traces = []
755: for trace in self.tracepoints:
756: if self.tracewidgets[trace].get_active():
757: traces.append(trace)
758: if traces:
759: return ",".join(traces)
760: return "none"
761:
762: def _set_traces(self, tracepoints):
763: self._clear_traces()
1.1.1.6 ! root 764: if not tracepoints:
! 765: return
1.1 root 766: for trace in tracepoints.split(","):
767: if trace in self.tracewidgets:
768: self.tracewidgets[trace].set_active(True)
769: else:
1.1.1.4 root 770: print("ERROR: unknown trace setting '%s'" % trace)
1.1 root 771:
772: def _clear_traces(self, widget = None):
773: for trace in self.tracepoints:
774: self.tracewidgets[trace].set_active(False)
775:
776: def _load_traces(self, widget):
777: # this doesn't load traces, just sets them from internal variable
778: # that run method gets from caller and sets. It's up to caller
779: # whether the saving or loading happens actually to disk
780: self._set_traces(self.savedpoints)
781:
782: def _save_traces(self, widget):
783: self.savedpoints = self._get_traces()
784:
785: def run(self, hatari, savedpoints):
786: "run(hatari,tracepoints) -> tracepoints, caller saves tracepoints"
787: self.savedpoints = savedpoints
788: while 1:
789: response = self.dialog.run()
790: if response == gtk.RESPONSE_APPLY:
791: hatari.change_option("--trace %s" % self._get_traces())
792: else:
793: self.dialog.hide()
794: return self.savedpoints
795:
796:
797: # ------------------------------------------
798: # Machine dialog for settings needing reboot
799:
800: class MachineDialog(HatariUIDialog):
1.1.1.3 root 801: def _machine_cb(self, widget, data):
802: if not widget.get_active():
803: return
804: machine = data.lower()
805: if machine == "ste" or machine == "st":
806: self.clocks[0].set_active(True)
807: self.cpulevel.set_active(0)
808: elif machine == "falcon":
809: self.clocks[1].set_active(True)
1.1.1.4 root 810: self.dsps[2].set_active(True)
1.1.1.3 root 811: self.cpulevel.set_active(3)
812: elif machine == "tt":
813: self.clocks[2].set_active(True)
814: self.cpulevel.set_active(3)
815:
1.1 root 816: def _create_dialog(self, config):
1.1.1.3 root 817: table, self.dialog = create_table_dialog(self.parent, "Machine configuration", 6, 4, "Set and reboot")
818:
819: row = 0
820: self.machines = table_add_radio_rows(table, row, "Machine:",
821: config.get_machine_types(), self._machine_cb)
822: row += 1
1.1 root 823:
1.1.1.4 root 824: self.dsps = table_add_radio_rows(table, row, "DSP type:", config.get_dsp_types())
1.1.1.3 root 825: row += 1
826:
827: # start next table column
1.1 root 828: row = 0
1.1.1.3 root 829: table_set_col_offset(table, 2)
830: self.monitors = table_add_radio_rows(table, row, "Monitor:", config.get_monitor_types())
1.1 root 831: row += 1
1.1.1.3 root 832:
833: self.clocks = table_add_radio_rows(table, row, "CPU clock:", config.get_cpuclock_types())
834: row += 1
835:
836: # fullspan at bottom
837: fullspan = True
838:
1.1 root 839: combo = gtk.combo_box_new_text()
1.1.1.3 root 840: for text in config.get_cpulevel_types():
1.1 root 841: combo.append_text(text)
1.1.1.3 root 842: self.cpulevel = table_add_widget_row(table, row, "CPU type:", combo, fullspan)
1.1 root 843: row += 1
1.1.1.3 root 844:
1.1 root 845: combo = gtk.combo_box_new_text()
846: for text in config.get_memory_names():
847: combo.append_text(text)
1.1.1.3 root 848: self.memory = table_add_widget_row(table, row, "Memory:", combo, fullspan)
1.1 root 849: row += 1
850:
851: label = "TOS image:"
852: fsel = self._fsel(label, gtk.FILE_CHOOSER_ACTION_OPEN)
1.1.1.3 root 853: self.tos = table_add_widget_row(table, row, label, fsel, fullspan)
1.1 root 854: row += 1
855:
1.1.1.3 root 856: vbox = gtk.VBox()
857: self.compatible = gtk.CheckButton("Compatible CPU")
1.1.1.4 root 858: self.rtc = gtk.CheckButton("Real-time clock")
1.1.1.3 root 859: self.timerd = gtk.CheckButton("Patch Timer-D")
860: vbox.add(self.compatible)
861: vbox.add(self.timerd)
1.1.1.4 root 862: vbox.add(self.rtc)
1.1.1.3 root 863: table_add_widget_row(table, row, "Misc.:", vbox, fullspan)
1.1 root 864: row += 1
865:
866: table.show_all()
867:
868: def _fsel(self, label, action):
869: fsel = gtk.FileChooserButton(label)
870: # Hatari cannot access URIs
871: fsel.set_local_only(True)
872: fsel.set_width_chars(12)
873: fsel.set_action(action)
874: return fsel
875:
876: def _get_config(self, config):
1.1.1.3 root 877: self.machines[config.get_machine()].set_active(True)
878: self.monitors[config.get_monitor()].set_active(True)
879: self.clocks[config.get_cpuclock()].set_active(True)
880: self.dsps[config.get_dsp()].set_active(True)
881: self.cpulevel.set_active(config.get_cpulevel())
1.1 root 882: self.memory.set_active(config.get_memory())
883: tos = config.get_tos()
884: if tos:
885: self.tos.set_filename(tos)
886: self.compatible.set_active(config.get_compatible())
887: self.timerd.set_active(config.get_timerd())
1.1.1.4 root 888: self.rtc.set_active(config.get_rtc())
1.1 root 889:
1.1.1.3 root 890: def _get_active_radio(self, radios):
891: idx = 0
892: for radio in radios:
893: if radio.get_active():
894: return idx
895: idx += 1
896:
1.1 root 897: def _set_config(self, config):
898: config.lock_updates()
1.1.1.3 root 899: config.set_machine(self._get_active_radio(self.machines))
900: config.set_monitor(self._get_active_radio(self.monitors))
901: config.set_cpuclock(self._get_active_radio(self.clocks))
902: config.set_dsp(self._get_active_radio(self.dsps))
903: config.set_cpulevel(self.cpulevel.get_active())
1.1 root 904: config.set_memory(self.memory.get_active())
905: config.set_tos(self.tos.get_filename())
906: config.set_compatible(self.compatible.get_active())
907: config.set_timerd(self.timerd.get_active())
1.1.1.4 root 908: config.set_rtc(self.rtc.get_active())
1.1 root 909: config.flush_updates()
910:
911: def run(self, config):
912: "run(config) -> bool, whether to reboot"
913: if not self.dialog:
914: self._create_dialog(config)
915:
916: self._get_config(config)
917: response = self.dialog.run()
918: self.dialog.hide()
919: if response == gtk.RESPONSE_APPLY:
920: self._set_config(config)
921: return True
922: return False
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.