# GNU Solfege - free ear training software
# Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008  Tom Cato Amundsen
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import sys
import os
import gobject, gtk
import gu, mpd
import notenamespinbutton
from instrumentselector import nInstrumentSelector, InstrumentConfigurator
import cfg
import osutils
import soundcard
import languages
import winlang

if sys.platform == 'win32':
    try:
        from soundcard import winmidi
    except ImportError, e:
        print "Loading winmidi.pyd failed:"
        print e
        winmidi = None


class ConfigWindow(gtk.Dialog, cfg.ConfigUtils):
    def on_destroy(self, widget, e):
        self.m_app.m_ui.g_config_window.destroy()
        self.m_app.m_ui.g_config_window = None
    def __init__(self, app):
        gtk.Dialog.__init__(self, _("GNU Solfege Preferences"),
             app.m_ui, 0,
             (gtk.STOCK_HELP, gtk.RESPONSE_HELP, gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
        cfg.ConfigUtils.__init__(self, 'configwindow')
        self.connect('response', self.apply_and_close)
        # We do this so that the window is only hidden when the
        # user click on the close button provided by the window manager.
        self.connect('delete-event', self.on_destroy)#lambda w, e: True)
        self.set_default_size(400, 400)
        self.m_app = app
        self.g_notebook = gtk.Notebook()
        self.vbox.pack_start(self.g_notebook)
        
        ########
        # midi #
        ########
        page_vbox = gu.hig_dlg_vbox()
        self.g_notebook.append_page(page_vbox, gtk.Label(_("Instruments")))

        vbox, category_vbox = gu.hig_category_vbox(_("Tempo"))
        page_vbox.pack_start(vbox, False)
        sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)

        tempo_hbox = gtk.HBox(spacing=6)
        self.g_default_bpm = gu.nSpinButton('config', 'default_bpm',
            gtk.Adjustment(self.get_int('config/default_bpm'), 10, 500, 1, 10))
        self.g_arpeggio_bpm = gu.nSpinButton('config', 'arpeggio_bpm',
            gtk.Adjustment(self.get_int('config/arpeggio_bpm'), 10, 500, 1, 10))
        for text, widget in [("_Default:",self.g_default_bpm), ("A_rpeggio:",self.g_arpeggio_bpm)]:
            label = gtk.Label(text)
            label.set_alignment(0.0, 0.5)
            label.set_use_underline(True)
            tempo_hbox.pack_start(label, expand=False)
            label.set_mnemonic_widget(widget)
            tempo_hbox.pack_start(widget, expand=False)
            label = gtk.Label(_("BPM"))
            label.set_alignment(0.0, 0.5)
            label.set_tooltip_text(_("Beats per minute"))
            tempo_hbox.pack_start(label, expand=True)        
        category_vbox.pack_start(tempo_hbox, False)

        box, category_vbox = gu.hig_category_vbox(_("Preferred Instrument"))
        page_vbox.pack_start(box, False)
        self.g_instrsel = nInstrumentSelector('config',
                        'preferred_instrument', sizegroup)
        category_vbox.pack_start(self.g_instrsel, False)

        box, category_vbox = gu.hig_category_vbox(_("Chord Instruments"))
        page_vbox.pack_start(box, False)
        self.g_instrument_configurator  \
              = InstrumentConfigurator("config", 3,
                    _("Use different instruments for chords and harmonic intervals"))
        category_vbox.pack_start(self.g_instrument_configurator, False)

        vbox, category_box = gu.hig_category_vbox(_("Percussion Instruments"))
        page_vbox.pack_start(vbox, False)
        category_box.pack_start(gu.hig_label_widget(
            _("Count in:"),
            gu.PercussionNameButton("config", "countin_perc", "Claves"),
            sizegroup, True, True))
        category_box.pack_start(gu.hig_label_widget(
            _("Rhythm:"),
            gu.PercussionNameButton("config", "rhythm_perc", "Side Stick"),
            sizegroup, True, True))
        ########
        # user #
        ########
        page_vbox = gu.hig_dlg_vbox()
        self.g_notebook.append_page(page_vbox, gtk.Label(_("User")))

        box, category_vbox = gu.hig_category_vbox(_("User's Vocal Range"))
        page_vbox.pack_start(box, False)
        sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)

        self.g_highest_singable = notenamespinbutton.NotenameSpinButton(
            self.get_string('user/highest_pitch'))
        box = gu.hig_label_widget(_("Highest pitch:"),
                                  self.g_highest_singable,
                                  sizegroup)
        category_vbox.pack_start(box)

        self.g_lowest_singable = notenamespinbutton.NotenameSpinButton(
            self.get_string('user/lowest_pitch'))
        box = gu.hig_label_widget(_("Lowest pitch:"),
                                  self.g_lowest_singable,
                                  sizegroup)
        category_vbox.pack_start(box)
        notenamespinbutton.nNotenameRangeController(
                  self.g_lowest_singable, self.g_highest_singable,
                  mpd.LOWEST_NOTENAME, mpd.HIGHEST_NOTENAME,
                  'user', 'lowest_pitch', 'highest_pitch')


        box, category_vbox = gu.hig_category_vbox(_("Sex"))
        page_vbox.pack_start(box, False)
        self.g_sex_male = gtk.RadioButton(None, _("_Male"))
        self.g_sex_male.connect('toggled', lambda w: self.set_string('user/sex', 'male'))
        category_vbox.pack_start(self.g_sex_male, False)
        self.g_sex_female = gtk.RadioButton(self.g_sex_male, _("_Female or child"))
        self.g_sex_female.connect('toggled', lambda w: self.set_string('user/sex', 'female'))
        category_vbox.pack_start(self.g_sex_female, False)
        if self.get_string('user/sex') == 'female':
            self.g_sex_female.set_active(True)
        #####################
        # external programs #
        #####################
        page_vbox = gu.hig_dlg_vbox()
        self.g_notebook.append_page(page_vbox, gtk.Label(_("External Programs")))

        box, category_vbox = gu.hig_category_vbox(_("Converters"))
        page_vbox.pack_start(box, False)
        sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)

        # midi_to_wav
        self.g_wav_convertor = gtk.ComboBoxEntry(
            gtk.ListStore(gobject.TYPE_STRING))
        self.g_wav_convertor.get_model().append(("timidity",))
        #
        self.g_wav_convertor_options = gtk.ComboBoxEntry(
            gtk.ListStore(gobject.TYPE_STRING))
        self.g_wav_convertor_options.get_model().append(("-Ow %(in)s -o %(out)s",))
        #
        category_vbox.pack_start(
            gu.hig_label_widget(_("MIDI to WAV:"),
               (self.g_wav_convertor, self.g_wav_convertor_options),
               sizegroup, True, True))
        #
        self.g_wav_convertor.child.set_text(
            self.get_string("app/midi_to_wav_cmd"))
        self.g_wav_convertor_options.child.set_text(
            self.get_string("app/midi_to_wav_cmd_options"))
        self.g_wav_convertor.connect('changed',
            lambda w: self.set_string('app/midi_to_wav_cmd',
                w.child.get_text()))
        self.g_wav_convertor_options.connect('changed',
            lambda w: self.set_string('app/midi_to_wav_cmd_options',
                w.child.get_text()))
        # wav_to_mp3
        self.g_mp3_convertor = gtk.ComboBoxEntry(
            gtk.ListStore(gobject.TYPE_STRING))
        self.g_mp3_convertor.get_model().append(("lame",))
        #
        self.g_mp3_convertor_options = gtk.ComboBoxEntry(
            gtk.ListStore(gobject.TYPE_STRING))
        self.g_mp3_convertor_options.get_model().append(("%(in)s %(out)s",))
        #
        category_vbox.pack_start(
            gu.hig_label_widget(_("WAV to MP3:"),
            (self.g_mp3_convertor, self.g_mp3_convertor_options),
            sizegroup, True, True))
        #
        self.g_mp3_convertor.child.set_text(
            self.get_string("app/wav_to_mp3_cmd"))
        self.g_mp3_convertor_options.child.set_text(
            self.get_string("app/wav_to_mp3_cmd_options"))
        self.g_mp3_convertor.connect('changed', 
            lambda w: self.set_string('app/wav_to_mp3_cmd',
                w.child.get_text()))
        self.g_mp3_convertor_options.connect('changed', 
            lambda w: self.set_string('app/wav_to_mp3_cmd_options',
                w.child.get_text()))
        # wav_to_ogg
        self.g_ogg_convertor = gtk.ComboBoxEntry(
            gtk.ListStore(gobject.TYPE_STRING))
        self.g_ogg_convertor.get_model().append(("oggenc",))
        #
        self.g_ogg_convertor_options = gtk.ComboBoxEntry(
            gtk.ListStore(gobject.TYPE_STRING))
        self.g_ogg_convertor_options.get_model().append(("%(in)s",))
        #
        category_vbox.pack_start(
            gu.hig_label_widget(_("WAV to OGG:"),
            (self.g_ogg_convertor, self.g_ogg_convertor_options),
            sizegroup, True, True))
        #
        self.g_ogg_convertor.child.set_text(
            self.get_string("app/wav_to_ogg_cmd"))
        self.g_ogg_convertor_options.child.set_text(
            self.get_string("app/wav_to_ogg_cmd_options"))
        self.g_ogg_convertor.connect('changed', 
            lambda w: self.set_string('app/wav_to_ogg_cmd',
                w.child.get_text()))
        self.g_ogg_convertor_options.connect('changed', 
            lambda w: self.set_string('app/wav_to_ogg_cmd_options',
                w.child.get_text()))

        self.add_gui_for_external_programs(page_vbox)
        ########
        # Misc #
        ########
        sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)

        box, category_vbox = gu.hig_category_vbox(_("Miscellaneous"))
        page_vbox.pack_start(box, False)

        # CSound_("Musical MIDI Accompaniment:")
        for bin, label, bins in (
            ("csound", _("CSound:"), osutils.find_csound_executables()),
            ("mma", "MMA:", osutils.find_mma_executables(
                cfg.get_list("app/win32_ignore_drives"))),
            ("lilypond-book", _("Lilypond-book"),
             osutils.find_progs(("lilypond-book", "lilypond-book.py"))),
            ("latex", "Latex:", osutils.find_progs(("latex",))),
            ):
            liststore = gtk.ListStore(gobject.TYPE_STRING)
            for p in bins:
                liststore.append((p,))
            cbox_entry = gtk.ComboBoxEntry(liststore)
            cbox_entry.child.set_text(self.get_string("programs/%(bin)s" % locals()))
            def csound_changed_cb(widget, bin):
                self.set_string('programs/%s' % bin, widget.child.get_text())
                widget.warning.props.visible = not bool(
                    osutils.find_progs((cfg.get_string('programs/%s' % bin),)))
            cbox_entry.warning = gtk.Image()
            cbox_entry.warning.set_tooltip_text(_("Not found. Much of GNU Solfege will run fine without this program. You will get a message when the program is required, and the user manual will explain what you need it for."))
            cbox_entry.warning.set_from_stock(gtk.STOCK_DIALOG_WARNING,
                                              gtk.ICON_SIZE_SMALL_TOOLBAR)
            box = gu.hig_label_widget(label,
                                      [cbox_entry, cbox_entry.warning],
                                      sizegroup, True, True)
            category_vbox.pack_start(box)
            cbox_entry.warning.props.no_show_all = True
            csound_changed_cb(cbox_entry, bin)
            cbox_entry.connect('changed', csound_changed_cb, bin)
        # Mail program
        liststore = gtk.ListStore(gobject.TYPE_STRING)
        for n in osutils.find_progs(('mutt', 'thunderbird', 'evolution')):
            if os.path.split(n)[1] == 'mutt':
                n = "xterm -e %s" % n
            liststore.append((n,))
        self.g_mail_program = gtk.ComboBoxEntry(liststore)
        self.g_mail_program.child.set_text(self.get_string("config/mua"))
        self.g_mail_program.child.connect('changed', lambda w: self.set_string('config/mua', self.g_mail_program.child.get_text()))
        box = gu.hig_label_widget(_("_Mail:"), self.g_mail_program, sizegroup,
            True, True)
        category_vbox.pack_start(box)

        #######
        # Gui #
        #######
        page_vbox = gu.hig_dlg_vbox()
        self.g_notebook.append_page(page_vbox, gtk.Label(_("Interface")))

        box, category_vbox = gu.hig_category_vbox(_("Interface"))
        page_vbox.pack_start(box, False)

        b = gu.nCheckButton('gui', 'web_browser_as_help_browser',
            _("_Display the user manual in web browser"))
        category_vbox.pack_start(b, False)
        self.g_mainwin_user_resizeable = gu.nCheckButton('gui',
          'mainwin_user_resizeable', _("_Resizeable main window"))
        category_vbox.pack_start(self.g_mainwin_user_resizeable, False)

        # Combobox to select language
        hbox = gtk.HBox()
        hbox.set_spacing(6)
        label = gtk.Label()
        label.set_text_with_mnemonic(_("Select _language:"))
        hbox.pack_start(label, False)
        self.g_language = gtk.combo_box_new_text()
        for n in languages.languages:
            self.g_language.append_text(n)
        label.set_mnemonic_widget(self.g_language)
        if sys.platform == 'win32':
            lang = winlang.win32_get_langenviron()
            if lang in languages.languages:
                idx = languages.languages.index(lang)
            else:
                idx = 0
        else:
            if cfg.get_string('app/lc_messages') in languages.languages:
                idx = languages.languages.index(cfg.get_string('app/lc_messages'))
            else:
                idx = 0
        self.g_language.set_active(idx)
        def f(combobox):
            cfg.set_string('app/lc_messages', languages.languages[combobox.get_active()])
            if sys.platform == 'win32':
                if combobox.get_active():
                    lang = languages.languages[combobox.get_active()]
                    winlang.win32_put_langenviron(lang)
                else:
                    winlang.win32_put_langenviron(None)
        self.g_language.connect_after('changed', f)
        hbox.pack_start(self.g_language, False)
        category_vbox.pack_start(hbox)
        l = gtk.Label(_("You have to restart the program for the language change to take effect."))
        l.set_alignment(0.0, 0.5)
        category_vbox.pack_start(l)

        ############
        # Practise #
        ############
        page_vbox = gu.hig_dlg_vbox()
        self.g_notebook.append_page(page_vbox, gtk.Label(_("Practise")))
        box, category_vbox = gu.hig_category_vbox(_("Practise"))
        page_vbox.pack_start(box, False)

        self.g_picky_on_new_question = gu.nCheckButton('config', 'picky_on_new_question', _("_Not allow new question before the old is solved"))
        category_vbox.pack_start(self.g_picky_on_new_question, False)

        self.g_autorepeat_if_wrong = gu.nCheckButton('config', 'auto_repeat_question_if_wrong_answer', _("_Repeat question if the answer was wrong"))
        category_vbox.pack_start(self.g_autorepeat_if_wrong, False)

        self.g_expert_mode = gu.nCheckButton('gui', 
                'expert_mode', _("E_xpert mode"))
        self.g_expert_mode.connect('toggled', self.m_app.reset_exercise)
        category_vbox.pack_start(self.g_expert_mode, False)


        #########
        # sound #
        #########
        if sys.platform == 'win32':
            self.create_win32_sound_page()
        else:
            self.create_linux_sound_page()
        self.show_all()
    def add_gui_for_external_programs(self, page_vbox):
        box, category_vbox = gu.hig_category_vbox(_("Audio File Players"))
        page_vbox.pack_start(box, False)
        format_info = {}
        if sys.platform != 'win32':
            format_info = {'wav': {
                 # testfile is a file in lesson-files/share
                 'testfile': 'fifth-small-220.00.wav',
                 'label': _("WAV:"),
                 # players is a list of tuples. The tuple has to or more 
                 # items. The first is the binary, and the rest is possible
                 # sets of command line options that might work.
                 # '/path/to/player', 'comandline', '
                 'players': [
                        ('gst-launch', 'playbin uri=file://%s',
                                       'filesrc location=%s ! wavparse ! alsasink'),
                        ('play', ''),
                        ('aplay', ''),
                        ('esdplay', ''),
                 ],
                }
            }
        format_info['midi'] = {
             'testfile': 'fanfare.midi',
             'label': _("MIDI:"),
             'players': [
                         ('gst-launch', 'playbin uri=file://%s',
                                        'filesrc location=lesson-files/share/fanfare.midi ! wildmidi ! alsasink'),
                         ('timidity', '-idqq %s'),
                         ('drvmidi', ''),
                         ('playmidi', ''),
             ],
            }
        format_info['mp3'] = {
             'testfile': 'fanfare.mp3',
             'label': _("MP3:"),
             'players': [
                        ('gst-launch', 'playbin uri=file://%s',
                                       'filesrc location=%s ! mad ! alsasink'),
                        ('mpg123', ''),
                        ('alsaplayer', ''),
             ],
            }
        format_info['ogg'] = {
             'testfile': 'fanfare.ogg',
             'label': _("OGG:"),
             'players': [
                        ('gst-launch', 'playbin uri=file://%s',
                                       'filesrc location=%s ! oggdemux ! vorbisdec ! audioconvert ! alsasink'),
                        ('ogg123', ''), 
                        ('alsaplayer', ''),
             ],
            }
        sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
        for formatid, format in format_info.items():
            cmd_liststore = gtk.ListStore(gobject.TYPE_STRING)
            for player_data in format['players']:
                cmd_liststore.append((player_data[0],))
            cmd_combo = gtk.ComboBoxEntry(cmd_liststore)
            cmd_combo.set_tooltip_text(_("Enter the name of the program. An absolute path is required only if the executable is not found on the PATH."))
            cmd_combo.opts = gtk.combo_box_entry_new_text()
            cmd_combo.opts.set_tooltip_text(_("The command line options required. Write %s where you want the name of the file to be played. Or omit it to have it added to the end of the string."))
            cmd_combo.child.set_text(cfg.get_string('sound/%s_player' % formatid))
            cmd_combo.opts.child.set_text(cfg.get_string('sound/%s_player_options' % formatid))
            for s in format['players']:
                if s[0] == cmd_combo.child.get_text():
                    for o in s[1:]:
                        cmd_combo.opts.append_text(o)
            
            def _changed(widget, formatid):
                if widget.get_active() != -1:
                    widget.opts.get_model().clear()
                    format = format_info[formatid]
                    for player_options in format['players'][widget.get_active()][1:]:
                        widget.opts.append_text(player_options)
                    widget.opts.child.set_text(format['players'][widget.get_active()][1])
                self.set_string('sound/%s_player' % formatid,
                                widget.child.get_text())
                widget.testbutton.set_sensitive(bool(
                    osutils.find_progs((widget.child.get_text(),))))

            def _opts_changed(widget, formatid):
                self.set_string('sound/%s_player_options' % formatid,
                                widget.child.get_text())
            cmd_combo.connect('changed', _changed, formatid)
            cmd_combo.opts.connect('changed', _opts_changed, formatid)
            testbutton = gtk.Button(_("Test"))
            testbutton.set_tooltip_text(_("This button is clickable only if the binary is found."))
            cmd_combo.testbutton = testbutton
            testbutton.connect('clicked', self.test_XXX_player,
                           formatid, format['testfile'])
            testbutton.set_sensitive(bool(
                    osutils.find_progs((cmd_combo.child.get_text(),))))
            box = gu.hig_label_widget(format['label'],
                                      [cmd_combo, cmd_combo.opts, testbutton],
                                      sizegroup, True, True)
            category_vbox.pack_start(box)
    def test_XXX_player(self, w, typeid, testfile):
        try:
            soundcard.play_mediafile(typeid, os.path.join('lesson-files/share', testfile))
        except osutils.BinaryBaseException, e:
            self.m_app.m_ui.display_error_message2(e.msg1, e.msg2)
    def create_linux_sound_page(self):
        page_vbox = gu.hig_dlg_vbox()
        self.g_notebook.append_page(page_vbox, gtk.Label(_("Sound Setup")))
        #############
        # midi setup
        #############
        box, category_vbox = gu.hig_category_vbox(_("Sound Setup"))
        page_vbox.pack_start(box)

        self.g_fakesynth_radio = gu.RadioButton(None, _("_No sound"), None)
        category_vbox.pack_start(self.g_fakesynth_radio, False)

        hbox = gu.bHBox(category_vbox, False)
        self.g_device_radio = gu.RadioButton(self.g_fakesynth_radio,
              _("Use _device"), None)
        hbox.pack_start(self.g_device_radio, False)

        liststore = gtk.ListStore(gobject.TYPE_STRING)
        for s in ('/dev/sequencer', '/dev/sequencer2', '/dev/music'):
            liststore.append((s,))
        self.g_device_file = gtk.ComboBoxEntry(liststore)
        self.g_device_file.child.set_text(self.get_string('sound/device_file'))
        self.g_synth_num = gtk.SpinButton(gtk.Adjustment(0, 0, 100, 1, 1),
                             digits=0)
        self.g_synth_num.set_value(self.get_int('sound/synth_number'))
        hbox.pack_start(self.g_device_file, False)
        hbox.pack_start(self.g_synth_num, False)

        ###
        hbox = gu.bHBox(category_vbox, False)
        self.g_midiplayer_radio = gu.RadioButton(self.g_fakesynth_radio,
             _("Use _external MIDI player"), None)
        hbox.pack_start(self.g_midiplayer_radio, False)

        if self.get_string("sound/type") == "external-midiplayer":
            self.g_midiplayer_radio.set_active(True)
        elif self.get_string("sound/type") == "sequencer-device":
            self.g_device_radio.set_active(True)
        else:
            self.g_fakesynth_radio.set_active(True)

        hbox = gtk.HButtonBox()
        category_vbox.pack_start(hbox)
        gu.bButton(category_vbox, _("_Test"), self.on_apply_and_play_test_sound)

    def create_win32_sound_page(self):
        page_vbox = gu.hig_dlg_vbox()
        self.g_notebook.append_page(page_vbox, gtk.Label(_("MIDI Setup")))
        #############
        # midi setup
        #############
        box, category_vbox = gu.hig_category_vbox(_("Sound Setup"))
        page_vbox.pack_start(box)
        txt = gtk.Label(_("""Solfege has two ways to play MIDI files. It is recommended to use Windows multimedia output. An external MIDI player can be useful if your soundcard lacks a hardware synth, in which case you have to use a program like timidity to play the music."""))
        txt.set_line_wrap(1)
        txt.set_justify(gtk.JUSTIFY_FILL)
        txt.set_alignment(0.0, 0.0)
        category_vbox.pack_start(txt, False)

        self.g_fakesynth_radio = gu.RadioButton(None, _("_No sound"), None)
        category_vbox.pack_start(self.g_fakesynth_radio, False)

        hbox = gu.bHBox(category_vbox, False)
        self.g_device_radio = gu.RadioButton(self.g_fakesynth_radio,
              _("_Windows multimedia output:"), None)
        self.g_synth = gtk.combo_box_new_text()
        if winmidi:
            for devname in winmidi.output_devices():
                #FIXME workaround of the bug
                # http://code.google.com/p/solfege/issues/detail?id=12
                if devname is None:
                    self.g_synth.append_text("FIXME bug #12")
                else:
                    self.g_synth.append_text(devname)
        self.g_synth.set_active(self.get_int('sound/synth_number') + 1)
        hbox.pack_start(self.g_device_radio, False)
        hbox.pack_start(self.g_synth, False)

        hbox = gu.bHBox(category_vbox, False)
        self.g_midiplayer_radio = gu.RadioButton(self.g_fakesynth_radio,
             _("Use _external MIDI player"), None)
        hbox.pack_start(self.g_midiplayer_radio, False)

        if self.get_string("sound/type") == "external-midiplayer":
            self.g_midiplayer_radio.set_active(True)
        elif self.get_string("sound/type") == "winsynth":
            self.g_device_radio.set_active(True)
        else:
            self.g_fakesynth_radio.set_active(True)

        gu.bButton(category_vbox, _("_Test"), self.on_apply_and_play_test_sound)
    def set_gui_from_config(self):
        if self.get_string("sound/type") == "fake-synth":
            self.g_fakesynth_radio.set_active(True)
        elif self.get_string("sound/type") == "external-midiplayer":
            self.g_midiplayer_radio.set_active(True)
        else:
            assert self.get_string("sound/type") in ("winsynth", "sequencer-device")
            self.g_device_radio.set_active(True)
        return -1
    def apply_and_close(self, w, response):
        if response ==  gtk.RESPONSE_DELETE_EVENT:
            self.set_gui_from_config()
        elif response == gtk.RESPONSE_HELP:
            self.m_app.handle_href("preferences-window.html")
            return
        else:
            if self.on_apply() == -1:
                self.set_gui_from_config()
                return
        self.hide()
    def on_apply_and_play_test_sound(self, *w):
        if self.on_apply() != -1:
            self.set_gui_from_config()
            self.play_midi_test_sound()
    def play_midi_test_sound(self):
        try:
            mpd.play_music(r"""
            \staff\relative c{
              c16 e g c e, g c e g, c e g c4
            }
            \staff{
              c4 e g8 e c4
            }
            """, 130, 0, 100)
        # Here we are only cathing exceptions we know the MidiFileSynth
        # can raise. Maybe we should catch something from the Sequencer
        # synths too?
        except osutils.BinaryBaseException, e:
            self.m_app.m_ui.display_error_message2(e.msg1, e.msg2)
    def on_apply(self, *v):
        """Returns -1 if sound init fails."""
        if soundcard.synth:
            soundcard.synth.close()
        if self.g_midiplayer_radio.get_active():
            soundcard.initialise_external_midiplayer(
                  cfg.get_string("sound/midi_player"))
            soundcard.synth.error_report_cb = self.m_app.m_ui.display_error_message
        elif self.g_device_radio.get_active():
            try:
                if sys.platform == 'win32':
                    soundcard.initialise_winsynth(self.g_synth.get_active() - 1)
                else:
                    soundcard.initialise_devicefile(
                        self.g_device_file.child.get_text(),
                        self.g_synth_num.get_value_as_int())
            except (soundcard.SoundInitException, OSError, ImportError), e:
                self.m_app.display_sound_init_error_message(e)
                return -1
        else: # no sound
            assert self.g_fakesynth_radio.get_active()
            soundcard.initialise_using_fake_synth(0)
        if self.g_midiplayer_radio.get_active():
            self.set_string("sound/type", "external-midiplayer")
        elif self.g_device_radio.get_active():
            if sys.platform == "win32":
                self.set_string("sound/type", "winsynth")
            else:
                self.set_string("sound/type", "sequencer-device")
        else:
            assert self.g_fakesynth_radio.get_active()
            self.set_string("sound/type", "fake-synth")
        if sys.platform != 'win32':
            self.set_string("sound/device_file", self.g_device_file.child.get_text())
        if soundcard.synth.m_type_major not in ('Midifile', 'Fake'):
            self.set_int("sound/synth_number", soundcard.synth.m_devnum)
            # we set the spin just in case m_devnum was changed by the
            # soundcard setup code, if it was out of range
            if sys.platform != 'win32':
                self.g_synth_num.set_value(soundcard.synth.m_devnum)
            else:
                self.g_synth.set_active(soundcard.synth.m_devnum + 1)
