# GNU Solfege - eartraining for GNOME
# Copyright (C) 2000, 2001, 2002, 2003  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 2 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, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

import sys
import gtk, gnome, gnome.ui
import gu, mpd
import configureoutput
import widgets
from instrumentselector import ReadOnlyInstrumentSelector
import cfg
import soundcard

class ConfigWindow(gnome.ui.PropertyBox, cfg.ConfigUtils):
    def on_destroy(self, widget):
        self.m_app.m_ui.g_config_window.destroy()
        self.m_app.m_ui.g_config_window = None
    def __init__(self, app):
        gnome.ui.PropertyBox.__init__(self)
        cfg.ConfigUtils.__init__(self, 'configwindow')
        self.m_app = app
        self._is_alive = 0
        self.__apply_tag = self.connect('apply', self.on_apply)
        self.connect('help', self.on_help)
        self.connect('destroy', self.on_destroy)
        ########
        # midi #
        ########
        vbox = gtk.VBox()
        self.append_page(vbox, gtk.Label(_("Midi stuff")))
        hbox = gu.bHBox(vbox, gtk.FALSE)
        hbox.set_border_width(gnome.ui.PAD)

        table = gtk.Table(1, 1, gtk.FALSE)
        hbox.pack_start(table, gtk.FALSE)

        gu.tLabel(table, 0, 1, 0, 1, _("Slow bpm:"), 1.0,
                  xoptions=gtk.FILL, xpadding=gnome.ui.PAD)
        gu.tLabel(table, 0, 1, 1, 2, _("Default bpm:"), 1.0, 0.5,
                  xoptions=gtk.FILL, xpadding=gnome.ui.PAD)
        gu.tLabel(table, 0, 1, 2, 3, _("Arpeggio bpm:"), 1.0, 0.5,
                  xoptions=gtk.FILL, xpadding=gnome.ui.PAD)
        self.g_slow_bpm = gu.tSpinButton(table, 1, 2, 0, 1,
                                         self.get_int('config/slow_bpm'),
                                         10, 500, 1, 10,
                                         self.on_changed)
        self.g_default_bpm = gu.tSpinButton(table, 1, 2, 1, 2,
                                            self.get_int('config/default_bpm'),
                                            10, 500, 1, 10,
                                            self.on_changed)
        self.g_arpeggio_bpm = gu.tSpinButton(table, 1, 2, 2, 3,
                                           self.get_int('config/arpeggio_bpm'),
                                             10, 500, 1, 10, self.on_changed)
        self.g_instrsel = ReadOnlyInstrumentSelector(_("Preferred instrument"),
               'config', 'preferred_instrument', self.on_changed)
        self.g_instrsel.set_border_width(gnome.ui.PAD)
        hbox.pack_start(self.g_instrsel, gtk.FALSE)
        ########
        # user #
        ########
        box = gtk.VBox()
        box.set_border_width(gnome.ui.PAD)
        self.append_page(box, gtk.Label(_("User")))
        hbox = gu.bHBox(box, gtk.FALSE)

        table = gtk.Table(2, 3, gtk.FALSE)
        hbox.pack_start(table, gtk.FALSE)
        gu.tLabel(table, 0, 1, 0, 1, _("Highest note user can sing:"), 1.0,
                  xoptions=gtk.FILL, xpadding=gnome.ui.PAD_SMALL)
        gu.tLabel(table, 0, 1, 1, 2, _("Lowest note user can sing:"), 1.0,
                  xoptions=gtk.FILL, xpadding=gnome.ui.PAD_SMALL)

        self.g_highest_singable = widgets.NotenameSpinButton(
            self.get_string('user/highest_pitch'))
        self.g_highest_singable.connect('value-changed', self.on_changed)
        table.attach(self.g_highest_singable, 1, 2 , 0, 1)
        self.g_lowest_singable = widgets.NotenameSpinButton(
            self.get_string('user/lowest_pitch'))
        self.g_lowest_singable.connect('value-changed', self.on_changed)
        table.attach(self.g_lowest_singable, 1, 2, 1, 2)
        widgets.NotenameRangeController(
                  self.g_lowest_singable, self.g_highest_singable,
                  mpd.LOWEST_NOTENAME, mpd.HIGHEST_NOTENAME)

        hbox = gu.bHBox(box, gtk.FALSE)
        self.g_sex_male = gtk.RadioButton(None, _("Male"))
        self.g_sex_male.connect('clicked', self.on_changed)
        hbox.pack_start(self.g_sex_male, gtk.FALSE)
        self.g_sex_female = gtk.RadioButton(self.g_sex_male, _("Female"))
        self.g_sex_female.connect('clicked', self.on_changed)
        hbox.pack_start(self.g_sex_female, gtk.FALSE)
        if self.get_string('user/sex') == 'female':
            self.g_sex_female.set_active(gtk.TRUE)
        #######
        # gui #
        #######
        vbox = gtk.VBox()
        vbox.set_border_width(gnome.ui.PAD)
        self.append_page(vbox, gtk.Label(_("Gui")))

        hbox = gu.bHBox(vbox, gtk.FALSE)
        hbox.set_spacing(gnome.ui.PAD)

        frame = gtk.Frame(_("Main toolbar"))
        hbox.pack_start(frame, gtk.FALSE)
        table = gtk.Table()
        table.set_border_width(gnome.ui.PAD)
        frame.add(table)

        self.g_toolbar_icons = gu.RadioButton(None, _("Icons"), self.on_changed)
        table.attach(self.g_toolbar_icons, 0, 1, 0, 1)
        self.g_toolbar_text = gu.RadioButton(self.g_toolbar_icons, _("Text"), self.on_changed)
        table.attach(self.g_toolbar_text, 1, 2, 0, 1)
        self.g_toolbar_both = gu.RadioButton(self.g_toolbar_icons, _("Both"), self.on_changed)
        table.attach(self.g_toolbar_both, 2, 3, 0, 1)
        sep = gtk.HSeparator()
        table.attach(sep, 0, 3, 1, 2)
        self.__dict__['g_toolbar_'+self.get_string('gui/toolbar_style=icons')].set_active(gtk.TRUE)
        self.g_toolbar_show = gu.RadioButton(None, _("Show"), self.on_changed)
        table.attach(self.g_toolbar_show, 0, 1, 2, 3)
        self.g_toolbar_hide = gu.RadioButton(self.g_toolbar_show, _("Hide"), self.on_changed)
        table.attach(self.g_toolbar_hide, 1, 2, 2, 3)
        if self.get_bool('gui/toolbar_visible'):
            self.g_toolbar_show.set_active(gtk.TRUE)
        else:
            self.g_toolbar_hide.set_active(gtk.TRUE)
        self.add_watch('gui/toolbar_visible', self._watch_toolbar_radios)

        frame = gtk.Frame(_("Navigation toolbar"))
        hbox.pack_start(frame, gtk.FALSE)
        table = gtk.Table()
        table.set_border_width(gnome.ui.PAD)
        frame.add(table)

        self.g_navbar_icons = gu.RadioButton(None, _("Icons"), self.on_changed)
        table.attach(self.g_navbar_icons, 0, 1, 0, 1)
        self.g_navbar_text = gu.RadioButton(self.g_navbar_icons, _("Text"), self.on_changed)
        table.attach(self.g_navbar_text, 1, 2, 0, 1)
        self.g_navbar_both = gu.RadioButton(self.g_navbar_icons, _("Both"), self.on_changed)
        table.attach(self.g_navbar_both, 2, 3, 0, 1)
        sep = gtk.HSeparator()
        table.attach(sep, 0, 3, 1, 2)
        self.__dict__['g_navbar_'+self.get_string('gui/navbar_style=icons')].set_active(gtk.TRUE)
        self.g_navbar_show = gu.RadioButton(None, _("Show"), self.on_changed)
        table.attach(self.g_navbar_show, 0, 1, 2, 3)
        self.g_navbar_hide = gu.RadioButton(self.g_navbar_show, _("Hide"), self.on_changed)
        table.attach(self.g_navbar_hide, 1, 2, 2, 3)
        if self.get_bool('gui/navbar_visible'):
            self.g_navbar_show.set_active(gtk.TRUE)
        else:
            self.g_navbar_hide.set_active(gtk.TRUE)
        self.add_watch('gui/navbar_visible', self._watch_navbar_radios)

        self.g_mainwin_user_resizeable = gtk.CheckButton(
              _("User resizeable main window"))
        self.g_mainwin_user_resizeable.connect('clicked', self.on_changed)
        self.g_mainwin_user_resizeable.set_active(
                        self.get_bool('gui/mainwin_user_resizeable'))
        self.add_watch('gui/mainwin_user_resizeable',
                lambda key: self.g_mainwin_user_resizeable.set_active(
                            self.get_bool('gui/mainwin_user_resizeable')))
        vbox.pack_start(self.g_mainwin_user_resizeable, gtk.FALSE)

        self._is_alive = 1

        hbox = gu.bHBox(vbox, gtk.FALSE)
        hbox.set_spacing(gnome.ui.PAD_SMALL)
        hbox.pack_start(gtk.Label(_("Web browser:")), gtk.FALSE)
        self.g_web_browser = gtk.Entry()
        self.g_web_browser.set_text(self.get_string("config/web_browser"))
        self.g_web_browser.connect('changed', self.on_changed)
        hbox.pack_start(self.g_web_browser)

        hbox = gu.bHBox(vbox, gtk.FALSE)
        hbox.set_spacing(gnome.ui.PAD_SMALL)
        hbox.pack_start(gtk.Label(_("Mail program:")), gtk.FALSE)
        self.g_mail_program = gtk.Entry()
        self.g_mail_program.set_text(self.get_string("config/mua"))
        self.g_mail_program.connect('changed', self.on_changed)
        hbox.pack_start(self.g_mail_program)

        ############
        # Practise #
        ############
        vbox = gtk.VBox()
        vbox.set_border_width(gnome.ui.PAD)
        self.append_page(vbox, gtk.Label(_("Practise")))

        self.g_picky_on_new_question = gtk.CheckButton(_("Not allow new question before the old is solved"))
        self.g_picky_on_new_question.set_active(self.get_bool('config/picky_on_new_question'))
        vbox.pack_start(self.g_picky_on_new_question, gtk.FALSE)

        self.g_autorepeat_if_wrong = gtk.CheckButton(_("Repeat question if the answer was wrong"))
        self.g_autorepeat_if_wrong.set_active(self.get_bool('config/auto_repeat_question_if_wrong_answer'))
        vbox.pack_start(self.g_autorepeat_if_wrong, gtk.FALSE)

        self.g_picky_on_new_question.connect('clicked', self.on_changed)
        self.g_autorepeat_if_wrong.connect('clicked', self.on_changed)

        #########
        # sound #
        #########
        if sys.platform == 'win32':
            self.create_win32_sound_page()
        else:
            self.create_linux_sound_page()
        self.show_all()
    def _watch_toolbar_radios(self, key):
        if self.get_bool('gui/toolbar_visible'):
            self.g_toolbar_show.set_active(gtk.TRUE)
        else:
            self.g_toolbar_hide.set_active(gtk.TRUE)
    def _watch_navbar_radios(self, key):
        if self.get_bool('gui/navbar_visible'):
            self.g_navbar_show.set_active(gtk.TRUE)
        else:
            self.g_navbar_hide.set_active(gtk.TRUE)
    def create_linux_sound_page(self):
        vbox = gtk.VBox()
        vbox.set_border_width(gnome.ui.PAD)
        self.append_page(vbox, gtk.Label(_("Sound setup")))
        txt = gtk.Label(_("""Solfege has two ways to play music. It can use /dev/music or an external midi player program. The preferred way is to use /dev/music since this offers features that will be utilized more in later versions of this program."""))
        txt.set_line_wrap(1)
        txt.set_justify(gtk.JUSTIFY_FILL)
        txt.set_alignment(0.0, 0.0)
        vbox.pack_start(txt, gtk.FALSE)
        self.g_fakesynth_radio = gu.RadioButton(None, _("No sound"), None)
        vbox.pack_start(self.g_fakesynth_radio, gtk.FALSE)
        hbox = gu.bHBox(vbox, gtk.FALSE)
        self.g_device_radio = gu.RadioButton(self.g_fakesynth_radio,
              _("Use device"), None)
        hbox.pack_start(self.g_device_radio, gtk.FALSE)
        combo = gtk.Combo()
        combo.set_popdown_strings(('/dev/sequencer', '/dev/sequencer2',
                                   '/dev/music'))
        self.g_device_entry = combo.entry
        self.g_device_entry.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(combo, gtk.FALSE)
        hbox.pack_start(self.g_synth_num, gtk.FALSE)

        hbox = gu.bHBox(vbox, gtk.FALSE)
        self.g_midiplayer_radio = gu.RadioButton(self.g_fakesynth_radio,
             _("Use external midiplayer"), None)
        hbox.pack_start(self.g_midiplayer_radio, gtk.FALSE)
        combo = gtk.Combo()
        combo.set_popdown_strings(('drvmidi -i d %s',
                                   'timidity -idqq %s',
                                   'playmidi -f %s'))
        self.g_midiplayer_entry = combo.entry
        hbox.pack_start(combo, gtk.FALSE)
        self.g_midiplayer_entry.set_text(self.get_string("sound/commandline"))
        if self.get_string("sound/type") == "external-midiplayer":
            self.g_midiplayer_radio.set_active(gtk.TRUE)
        elif self.get_string("sound/type") == "sequencer-device":
            self.g_device_radio.set_active(gtk.TRUE)
        else:
            self.g_fakesynth_radio.set_active(gtk.TRUE)

        # checkbox to enable awe support
        self.g_awe_checkbutton = gtk.CheckButton(_("My sound card is Sound Blaster AWE32, AWE64 or pnp32"))
        self.g_awe_checkbutton.set_active(self.get_string('sound/card_info') == 'awe')
        if not configureoutput.HAVE_LINUX_AWE_VOICE_H:
            self.g_awe_checkbutton.set_sensitive(gtk.FALSE)
        vbox.pack_start(self.g_awe_checkbutton, gtk.FALSE)

        gu.bButton(vbox, _("Sound test. Hit 'Apply' before testing."), self.on_sound_test, gtk.FALSE, gtk.FALSE)

        self.g_fakesynth_radio.connect('clicked', self.on_changed)
        self.g_midiplayer_radio.connect('clicked', self.on_changed)
        self.g_device_radio.connect('clicked', self.on_changed)
        self.g_midiplayer_entry.connect('changed', self.on_changed)
        self.g_device_entry.connect('changed', self.on_changed)
        self.g_synth_num.connect('value-changed', self.on_changed)
        self.g_awe_checkbutton.connect('clicked', self.on_changed)
    def create_win32_sound_page(self):
        vbox = gtk.VBox()
        vbox.set_border_width(gnome.ui.PAD)
        self.append_page(vbox, gtk.Label(_("Sound setup")))
        txt = gtk.Label(_("""Solfege has two ways to play music. It is recommended to use Windows multimedia output. An external midiplayer is only useful if your soundcard lack a hardware synth, and 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)
        vbox.pack_start(txt, gtk.FALSE)
        self.g_fakesynth_radio = gu.RadioButton(None, _("No sound"), None)
        vbox.pack_start(self.g_fakesynth_radio, gtk.FALSE)
        hbox = gu.bHBox(vbox, gtk.FALSE)
        self.g_device_radio = gu.RadioButton(self.g_fakesynth_radio,
              _("Windows multimedia output, synth number:"), None)
        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_radio, gtk.FALSE)
        hbox.pack_start(self.g_synth_num, gtk.FALSE)

        hbox = gu.bHBox(vbox, gtk.FALSE)
        self.g_midiplayer_radio = gu.RadioButton(self.g_fakesynth_radio,
             _("Use external midiplayer"), None)
        hbox.pack_start(self.g_midiplayer_radio, gtk.FALSE)
        combo = gtk.Combo()
        combo.set_popdown_strings(('midiplayer.exe %s',
                                   'timidity -idqq %s',
                                   'playmidi -f %s'))
        self.g_midiplayer_entry = combo.entry
        hbox.pack_start(combo, gtk.FALSE)
        self.g_midiplayer_entry.set_text(self.get_string("sound/commandline"))
        if self.get_string("sound/type") == "external-midiplayer":
            self.g_midiplayer_radio.set_active(gtk.TRUE)
        elif self.get_string("sound/type") == "winsynth":
            self.g_device_radio.set_active(gtk.TRUE)
        else:
            self.g_fakesynth_radio.set_active(gtk.TRUE)

        gu.bButton(vbox, _("Sound test. Hit 'Apply' before testing."), self.on_sound_test)

        self.g_fakesynth_radio.connect('clicked', self.on_changed)
        self.g_midiplayer_radio.connect('clicked', self.on_changed)
        self.g_synth_num.connect('changed', self.on_changed)
        self.g_device_radio.connect('clicked', self.on_changed)
        self.g_midiplayer_entry.connect('changed', self.on_changed)
    def on_sound_test(self, w):
        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)
    def on_help(self, *v):
        #FIXME It is stupid to display this help in the main window.
        self.m_app.handle_href("preferences-window.html")
    def on_changed(self, *_o):
        if self._is_alive:
            self.set_modified(gtk.TRUE)
    def on_apply(self, _o, pagenum):
        card_info = ""
        if pagenum == 0:
            self.set_int('config/slow_bpm', int(self.g_slow_bpm.get_text()))
            self.set_int('config/default_bpm',
                         int(self.g_default_bpm.get_text()))
            self.set_int('config/arpeggio_bpm',
                         int(self.g_arpeggio_bpm.get_text()))
            self.set_int('config/preferred_instrument',
                         self.g_instrsel.m_instrument)
            self.set_int('config/preferred_instrument_velocity',
                         self.g_instrsel.g_velocity.get_value_as_int())
        if pagenum == 1:
            self.set_string('user/lowest_pitch',
                mpd.int_to_notename(self.g_lowest_singable.get_value()))
            self.set_string('user/highest_pitch',
                mpd.int_to_notename(self.g_highest_singable.get_value()))
            if self.g_sex_male.get_active():
                self.set_string('user/sex', 'male')
            else:
                self.set_string('user/sex', 'female')
        if pagenum == 2:
            if self.g_toolbar_text.get_active():
                self.m_app.m_ui.g_toolbar.set_style(gtk.TOOLBAR_TEXT)
                self.set_string('gui/toolbar_style', 'text')
            elif self.g_toolbar_icons.get_active():
                self.m_app.m_ui.g_toolbar.set_style(gtk.TOOLBAR_ICONS)
                self.set_string('gui/toolbar_style', 'icons')
            elif self.g_toolbar_both.get_active():
                self.m_app.m_ui.g_toolbar.set_style(gtk.TOOLBAR_BOTH)
                self.set_string('gui/toolbar_style', 'both')
            if self.g_toolbar_show.get_active():
                self.m_app.m_ui.get_dock_item_by_name('toolbar').show()
                self.set_bool('gui/toolbar_visible', 1)
            else:
                self.set_bool('gui/toolbar_visible', 0)
                self.m_app.m_ui.get_dock_item_by_name('toolbar').hide()

            if self.g_navbar_text.get_active():
                self.m_app.m_ui.g_navbar.set_style(gtk.TOOLBAR_TEXT)
                self.set_string('gui/navbar_style', 'text')
            elif self.g_navbar_icons.get_active():
                self.m_app.m_ui.g_navbar.set_style(gtk.TOOLBAR_ICONS)
                self.set_string('gui/navbar_style', 'icons')
            elif self.g_navbar_both.get_active():
                self.m_app.m_ui.g_navbar.set_style(gtk.TOOLBAR_BOTH)
                self.set_string('gui/navbar_style', 'both')
            if self.g_navbar_show.get_active():
                self.m_app.m_ui.get_dock_item_by_name('navbar').show()
                self.set_bool('gui/navbar_visible', 1)
            else:
                self.m_app.m_ui.get_dock_item_by_name('navbar').hide()
                self.set_bool('gui/navbar_visible', 0)
            self.set_bool('gui/mainwin_user_resizeable',
                          self.g_mainwin_user_resizeable.get_active())
            self.m_app.m_ui.set_resizable(self.get_bool('gui/mainwin_user_resizeable'))
            self.set_string("config/web_browser", self.g_web_browser.get_text())
            self.set_string("config/mua", self.g_mail_program.get_text())
        if pagenum == 3:
            self.set_bool('config/picky_on_new_question',
                          self.g_picky_on_new_question.get_active())
            self.set_bool('config/auto_repeat_question_if_wrong_answer',
                          self.g_autorepeat_if_wrong.get_active())
        if pagenum == 4:
            if soundcard.synth:
                soundcard.synth.close()
            if self.g_midiplayer_radio.get_active():
                soundcard.initialise_external_midiplayer(
                      self.g_midiplayer_entry.get_text())
            elif self.g_device_radio.get_active():
                if sys.platform != 'win32' and configureoutput.HAVE_LINUX_AWE_VOICE_H and \
                    self.g_awe_checkbutton.get_active():
                      card_info = "awe"
                else:
                     card_info = ""
                try:
                    if sys.platform == 'win32':
                        soundcard.initialise_winsynth(self.g_synth_num.get_value_as_int())
                    else:
                        soundcard.initialise_devicefile(
                            self.g_device_entry.get_text(),
                            self.g_synth_num.get_value_as_int(),
                            card_info)
                except (soundcard.SoundInitException, OSError), e:
                    self.m_app.display_sound_init_error_message(e)
                    return
            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_entry.get_text())
                self.set_string("sound/card_info", card_info)
            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
                self.g_synth_num.set_value(soundcard.synth.m_devnum)
            self.set_string("sound/commandline",
                            self.g_midiplayer_entry.get_text())
        if pagenum == -1:
            pass
    def _watch_toggle_callback(self, widget, key):
        widget.set_active(self.get_bool(key))

