# GNU Solfege - ear training for GNOME
# Copyright (C) 2000, 2001, 2002, 2003, 2004  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 gtk, gnome, soundcard
import gu, os
import cfg

class MidiInstrumentMenu(gtk.Menu):
    def __init__(self, callback):
        gtk.Menu.__init__(self)
        self.m_callback = callback
        for x in range(len(soundcard.instrument_names)):
            if x % 8 == 0:
                menuitem = gtk.MenuItem(soundcard.instrument_sections[x/8])
                submenu = gtk.Menu()
                self.append(menuitem)
                menuitem.set_submenu(submenu)
                menuitem.show()
            item = gtk.MenuItem(soundcard.instrument_names[x])
            item.connect('activate', self.on_activate)
            item.set_data('instrument', x)
            submenu.append(item)
            item.show()
        self.show()
    def on_activate(self, menuitem):
        self.m_callback(menuitem.get_data('instrument'))


class nInstrumentSelector(gtk.VBox, cfg.ConfigUtils):
    def __init__(self, exname, name, sizegroup):
        gtk.VBox.__init__(self)
        cfg.ConfigUtils.__dict__['__init__'](self, exname)
        self.m_name = name
        hbox = gu.bHBox(self)
        hbox.set_spacing(gnome.ui.PAD_SMALL)
        label = gtk.Label(_('_Instrument:'))
        label.set_alignment(0.0, 0.5)
        sizegroup.add_widget(label)
        hbox.pack_start(label, False, False)

        self.g_button = gtk.Button(
              soundcard.instrument_names[self.get_int(self.m_name)])
        self.g_button.connect('clicked', self.on_btnclick)
        label.set_use_underline(True)
        label.set_mnemonic_widget(self.g_button)
        hbox.pack_start(self.g_button)

        self.g_menu = MidiInstrumentMenu(self.on_instrument_selected)
        self.m_instrument = self.get_int('config/preferred_instrument')
        if self.get_int(self.m_name+'_velocity')==0:
            self.set_int(self.m_name+'_velocity',
                      self.get_int('config/preferred_instrument_velocity'))

        hbox = gtk.HBox()
        hbox.set_spacing(6)
        self.pack_start(hbox)

        label = gtk.Label(_("_Velocity:"))
        label.set_use_underline(True)
        label.set_alignment(0.0, 0.5)
        sizegroup.add_widget(label)
        hbox.pack_start(label, False)
        self.g_velocity = gu.nSpinButton(exname, self.m_name+'_velocity', gtk.Adjustment(self.get_int(self.m_name+'_velocity'), 0, 127, 1, 10), digits=0)
        label.set_mnemonic_widget(self.g_velocity)
        hbox.pack_start(self.g_velocity, True)

    def on_btnclick(self, *argv):
        self.g_menu.popup(None, None, None, 1, 0)
    def on_instrument_selected(self, instrument=None):
        self.set_int(self.m_name, instrument)
        self.g_button.get_children()[0].set_text(soundcard.instrument_names[instrument])
        self.m_instrument = instrument
        self.play_selected_instrument()
    def play_selected_instrument(self, _o=None):
        soundcard.play_note(self.m_instrument, 4, 0, 60,
                            self.g_velocity.get_value_as_int())

    def show(self):
        self.show_all()

class ReadOnlyInstrumentSelector(gtk.Frame, cfg.ConfigUtils):
    def __init__(self, txt, section, name, on_change_callback=None):
        """
        ReadOnlyInstrumentSelector is used by ConfigWindow, because
        the config data should not be changed before the user press
        'apply' or 'ok'. The exercises' config page should use
        the InstrumentSelector class
        
        section: what section in config file to use, typically an exercise name
        name: name and name_velocity are used in configfile
        """
        gtk.Frame.__init__(self, txt)
        #cfg.ConfigUtils.__init__(self, section)
        cfg.ConfigUtils.__dict__['__init__'](self, section)
        self.m_name = name
        self.m_on_change_callback = on_change_callback
        vbox = gtk.VBox()
        vbox.set_border_width(gnome.ui.PAD_SMALL)
        self.add(vbox)
        self.g_button = gu.bButton(vbox,
              soundcard.instrument_names[self.get_int(self.m_name)],
              self.on_btnclick)
        self.g_menu = MidiInstrumentMenu(self.on_instrument_selected)
        self.m_instrument = self.get_int('config/preferred_instrument')
        if self.get_int(self.m_name+'_velocity')==0:
            self.set_int(self.m_name+'_velocity',
                      self.get_int('config/preferred_instrument_velocity'))
        hbox = gu.bHBox(vbox)
        hbox.set_spacing(gnome.ui.PAD_SMALL)
        label = gtk.Label(_("Velocity:"))
        label.set_alignment(1.0, 0.5)
        hbox.pack_start(label)
        self.g_velocity = gtk.SpinButton(gtk.Adjustment(self.get_int(self.m_name+'_velocity'), 0, 127, 1, 10), digits=0)
        self.g_velocity.connect('value-changed', self.spin_callback)
        ihbox = gu.bHBox(hbox, False)
        ihbox.pack_start(self.g_velocity, False)
        btn = gu.bButton(ihbox, None, self.play_selected_instrument, False)
        im = gtk.Image()
        im.set_from_file(os.path.join("graphics", "test-sound.png"))
        btn.add(im)
        self.show_all()
    def on_btnclick(self, *argv):
        self.g_menu.popup(None, None, None, 1, 0)
    def on_instrument_selected(self, instrument=None):
        self.g_button.get_children()[0].set_text(soundcard.instrument_names[instrument])
        self.m_instrument = instrument
        self.play_selected_instrument()
        if self.m_on_change_callback:
            self.m_on_change_callback()
    def spin_callback(self, spin):
        if self.m_on_change_callback:
            self.m_on_change_callback()
    def play_selected_instrument(self, _o=None):
        soundcard.play_note(self.m_instrument, 4, 0, 60,
                            self.g_velocity.get_value_as_int())

class InstrumentSelector(ReadOnlyInstrumentSelector):
    def __init__(self, txt, section, name, on_change_callback=None):
        ReadOnlyInstrumentSelector.__init__(self, txt, section, name, on_change_callback)
        self.add_watch(self.m_name, self._instr_cb)
        self.add_watch(self.m_name+'_velocity', self._velocity_cb)
    def _instr_cb(self, name):
        self.g_button.get_children()[0].set_text(
              soundcard.instrument_names[self.get_int(name)])
    def _velocity_cb(self, name):
        self.g_velocity.set_value(self.get_float(name))
    def on_instrument_selected(self, instrument=None):
        ReadOnlyInstrumentSelector.on_instrument_selected(self, instrument)
        self.set_int(self.m_name, instrument)
    def spin_callback(self, p=None):
        ReadOnlyInstrumentSelector.spin_callback(self, p)
        self.set_int(self.m_name+'_velocity', p.get_value_as_int())

class InstrumentConfigurator(gtk.VBox, cfg.ConfigUtils):
    def __init__(self, exname, num):
        gtk.VBox.__init__(self)
        #cfg.ConfigUtils.__init__(self, exname)
        cfg.ConfigUtils.__dict__['__init__'](self, exname)
        assert num in (2, 3)
        self.m_num = num
        self.g_override_default_instrument_checkbutton \
            = gu.nCheckButton(exname, 'override_default_instrument',
                 _("_Change midi instrument for this exercise"),
                 callback=self.update_instrument_override)
        self.pack_start(self.g_override_default_instrument_checkbutton,
                        False)
        hbox = gu.bHBox(self)
        hbox.set_spacing(gnome.ui.PAD_SMALL)

        self.g_instrsel_high = InstrumentSelector(_("Highest instrument"),
                                                 exname, 'highest_instrument')
        hbox.pack_start(self.g_instrsel_high, False)
        if num == 3:
            self.g_instrsel_middle = InstrumentSelector(_("Middle instrument"),
                                            exname, 'middle_instrument')
            hbox.pack_start(self.g_instrsel_middle, False)
        else:
            self.g_instrsel_middle = None
        self.g_instrsel_low = InstrumentSelector(_("Lowest instrument"),
                                                exname, 'lowest_instrument')
        hbox.pack_start(self.g_instrsel_low, False)
        self.update_instrument_override()
    def update(self):
        self.update_instrument_override()
        self.g_instrsel_high.update()
        if self.m_num == 3:
            self.g_instrsel_middle.update()
        self.g_instrsel_low.update()
    def update_instrument_override(self, _o=None):
        self.g_override_default_instrument_checkbutton.set_active(
                self.get_bool('override_default_instrument'))
        self.g_instrsel_high.set_sensitive(
               self.g_override_default_instrument_checkbutton.get_active())
        self.g_instrsel_low.set_sensitive(
               self.g_override_default_instrument_checkbutton.get_active())
        if self.g_instrsel_middle:
            self.g_instrsel_middle.set_sensitive(
               self.g_override_default_instrument_checkbutton.get_active())
    def show(self):
        self.show_all()

