# GNU Solfege - eartraining for GNOME
# Copyright (C) 2000, 2001, 2002  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
import gnome.uiconsts
import soundcard
from i18n import _
import cfg

#  Prefixes used in this module:
#  t  pack into a table, the first five parameters are table, x1, x2, y1, y2
#  n  a widget that get its state stored in ~/.gnome/Solfege
#  b  the widget is packed into the first argument


def tLabel(table, x1, x2, y1, y2, text="", xalign=0.0, yalign=0.5, xoptions=gtk.EXPAND|gtk.FILL, yoptions=gtk.EXPAND|gtk.FILL, xpadding=0, ypadding=0):
    label = gtk.GtkLabel(text)
    label.set_alignment(xalign, yalign)
    table.attach(label, x1, x2, y1, y2, xoptions=xoptions, yoptions=yoptions, xpadding=xpadding, ypadding=ypadding)
    return label

def bLabel(pack_into, label, expand=gtk.TRUE, fill=gtk.TRUE):
    b = gtk.GtkLabel(label)
    b.show()
    pack_into.pack_start(b, expand, fill)
    return b

def bButton(pack_into, label, callback=None, expand=gtk.TRUE, fill=gtk.TRUE):
    b = gtk.GtkButton(label)
    b.show()
    if callback:
        b.connect('clicked', callback)
    pack_into.pack_start(b, expand, fill)
    return b

class nSpinButton(gtk.GtkSpinButton, cfg.ConfigUtils):#FIXME (??what is there to fix???)
    def __init__(self, exname, name, adj, climb_rate=1, digits=1):
        gtk.GtkSpinButton.__init__(self, adj, climb_rate, digits)
        cfg.ConfigUtils.__init__(self, exname)
        self.m_name = name
        self.set_digits(0)
        self.show()
        self.set_value(self.get_float(self.m_name))
        if self.get_value() != self.get_float(self.m_name):
            self.set_float(self.m_name, self.get_value())
        self.connect('changed', self.on_changed)
        self._watch_id = self.add_watch(self.m_name, self._watch_cb)
    def _watch_cb(self, name):
        gtk.GtkSpinButton.set_value(self, self.get_float(name))
    def set_value(self, value):
        gtk.GtkSpinButton.set_value(self, value)
        self.set_float(self.m_name, value)
    def on_changed(self, _o):
        self.set_float(self.m_name, self.get_value())

def tSpinButton(table, x1, x2, y1, y2,
                value, lower, upper, step_incr=1, page_incr=10, callback=None):
    adj = gtk.GtkAdjustment(value, lower, upper, step_incr, page_incr)
    spin = gtk.GtkSpinButton(adj, digits=0)
    if callback:
        spin.connect('changed', callback)
    table.attach(spin, x1, x2, y1, y2)
    return spin

def bHBox(pack_into, expand=gtk.TRUE, fill=gtk.TRUE, padding=0):
    b = gtk.GtkHBox()
    b.show()
    pack_into.pack_start(b, expand, fill, padding)
    return b

def bVBox(pack_into, expand=gtk.TRUE, fill=gtk.TRUE, padding=0):
    b = gtk.GtkVBox()
    pack_into.pack_start(b, expand, fill, padding)
    return b

class nCheckButton(gtk.GtkCheckButton, cfg.ConfigUtils):
    def __init__(self, exname, name, label=None, default_value=0, callback=None):
        gtk.GtkCheckButton.__init__(self, label)
        #cfg.ConfigUtils.__init__(self, exname)
        cfg.ConfigUtils.__dict__['__init__'](self, exname)
        self.m_name = name
        self.m_callback = callback
        self.show()
        if default_value:
            s = "true"
        else:
            s = "false"
        self.set_bool(self.m_name, self.get_bool(self.m_name+"="+s))
        if self.get_bool(self.m_name):
            self.set_active(1)
        self._clicked_id = self.connect('clicked', self.on_clicked)
        self._watch_id = self.add_watch(self.m_name, self._watch_cb)
    def _watch_cb(self, name):
        self.set_active(self.get_bool(name))
    def on_clicked(self, _o):
        self.set_bool(self.m_name, self.get_active())
        if self.m_callback:
            self.m_callback(_o)
 
def RadioButton(group, label, callback=None):
    rdb = gtk.GtkRadioButton(group, label)
    if callback:
        rdb.connect('toggled', callback)
    rdb.show()
    return rdb

class nCombo(gtk.GtkCombo, cfg.ConfigUtils):
    def __init__(self, exname, name, default, popdown_strings):
        """
        Be aware that the value of the entry, is stored as an integer
        popdown_strings.index(entry.get_text()), so if popdown_strings
        changes when upgrading the program, the value of the combo
        might change.

        Despite this problems, I do it this way, because if we store
        the actual value of the entry, we get into trouble when running
        the program with other locale settings.
        """
        gtk.GtkCombo.__init__(self)
        #cfg.ConfigUtils.__init__(self, exname)
        cfg.ConfigUtils.__dict__['__init__'](self, exname)
        self.popdown_strings = popdown_strings
        self.m_name = name
        self.set_value_in_list(gtk.TRUE, gtk.FALSE)
        self.set_popdown_strings(popdown_strings)
        i = self.get_int_with_default(name, -1)
        if i == -1:
            i = popdown_strings.index(default)
        self.entry.set_text(popdown_strings[i])
        self.entry.connect("changed", self.entry_changed)
        self.entry.set_editable(gtk.FALSE)
        self.show()
    def entry_changed(self, entry):
        self.set_int(self.m_name, self.popdown_strings.index(entry.get_text()))

class PercussionNameCombo(gtk.GtkCombo, cfg.ConfigUtils):
    def __init__(self, exname, name, default):
        gtk.GtkCombo.__init__(self)
        cfg.ConfigUtils.__init__(self, exname)
        self.m_name = name
        self.set_value_in_list(gtk.TRUE, gtk.FALSE)
        self.set_popdown_strings(soundcard.percussion_names)
        i = self.get_int(name)
        if not i:
            i = soundcard.percussionname_to_int(default)
            self.set_int(name, i)
        self.entry.set_text(soundcard.int_to_percussionname(i))
        self.entry.connect("changed", self.entry_changed)
        self.entry.set_editable(gtk.FALSE)
        self.show()
    def entry_changed(self, entry):
        self.set_int(self.m_name,
                     soundcard.percussionname_to_int(entry.get_text()))

class SolfegeDialog(gtk.GtkDialog):
    def __init__(self, heading, txt, buttons=(), callback=None):
        gtk.GtkDialog.__init__(self)
        self.m_callback = callback
        self.vbox.set_border_width(gnome.uiconsts.PAD)
        self.connect('delete_event', self.destroy)

        label = bLabel(self.vbox, heading)
        label.set_name('Heading1')

        label = bLabel(self.vbox, txt)
        label.set_justify(gtk.JUSTIFY_LEFT)
        for text in buttons:
            b = gtk.GtkButton(text)
            b.set_data('user_data', text)
            b.connect('clicked', self.click)
            self.action_area.pack_start(b)
            b.show()
    def click(self, button):
        if self.m_callback:
            self.m_callback(button.get_data('user_data'))
        self.destroy()


class FlashBar(gtk.GtkFrame):
    def __init__(self):
        gtk.GtkFrame.__init__(self)
        self.set_shadow_type(gtk.SHADOW_IN)
        #UGH
        self.set_usize(358, 40)
        self.__stack = []
        import widgets
        self.__label = widgets.HarmonicProgressionLabel('')
        self.add(self.__label)
        self.__timeout = None
    def flash(self, txt):
        """Display a message that is automatically removed after some time.
        If we flash a new message before the old flashed message are removed,
        we old flashed message are removed.
        """
        if self.__timeout:
            gtk.timeout_remove(self.__timeout)
        self.__label.set_text(txt)
        def f(self=self):
            self.__timeout = None
            if self.__stack:
                self.__label.set_text(self.__stack[-1])
            else:
                self.__label.set_text('')
        self.__timeout = gtk.timeout_add(2000, f)
    def push(self, txt):
        # stop any flashing before we push
        if self.__timeout:
            gtk.timeout_remove(self.__timeout)
            self.__timeout = None
        self.__stack.append(txt)
        self.__label.set_text(txt, '')
    def pop(self):
        """If a message is being flashed right now, that flashing is
        not affected, but the message below the flashed message is removed.
        """
        self.__stack.pop()
        if not self.__timeout:
            if self.__stack:
                self.__label.set_text(self.__stack[-1])
    def clear(self):
        self.__stack = []
        if not self.__timeout:
            self.__label.set_text('')
