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