|
|
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.