# 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

if __name__ == '__main__':
    import sys
    #sys.path.insert(0, "/home/tom/pygtk19914/lib/python2.2/site-packages")

    import pygtk
    pygtk.require("2.0")
    import sys
    sys.path.append(".")

import gtk
import mpd, gu, cfg
import gobject

# FIXME This was not defined in gtk-types.defs yet
INPUT_ERROR=-1
DELAY1 = 500
DELAY2 = 50

class NotenameSpinButton(gtk.HBox):
    def __init__(self, default_value):
        gtk.HBox.__init__(self)
        self.m_value = mpd.notename_to_int(default_value)
        self.g_label = gtk.Label(mpd.int_to_user_notename(self.m_value))
        self.g_label.set_use_markup(1)
        frame = gtk.Frame()
        e = gtk.EventBox()
        e.set_name("NotenameSpinButtonLabel")
        frame.add(e)
        self.pack_start(frame, gtk.FALSE)
        self.g_label.set_size_request(60, -1)#FIXME hardcoded value
        frame.set_shadow_type(gtk.SHADOW_IN)
        e.add(self.g_label)
        vbox = gtk.VBox()
        self.pack_start(vbox, gtk.FALSE)
        # up
        self.g_up = gtk.Arrow(gtk.ARROW_UP, gtk.SHADOW_OUT)
        eb1 = gtk.EventBox()
        eb1.connect('button-press-event', self.on_up_press)
        eb1.connect('button-release-event', self.on_up_release)
        frame = gtk.Frame()
        frame.set_shadow_type(gtk.SHADOW_OUT)
        frame.add(self.g_up)
        eb1.add(frame)
        vbox.pack_start(eb1)
        # down
        self.g_down = gtk.Arrow(gtk.ARROW_DOWN, gtk.SHADOW_ETCHED_IN)
        eb2 = gtk.EventBox()
        eb2.connect('button-press-event', self.on_down_press)
        eb2.connect('button-release-event', self.on_down_release)
        frame = gtk.Frame()
        frame.set_shadow_type(gtk.SHADOW_OUT)
        frame.add(self.g_down)
        eb2.add(frame)
        vbox.pack_start(eb2)
        self.m_timeout = None
    def on_up_press(self, eb, ev):
        if self.m_timeout:
            gtk.timeout_remove(self.m_timeout)
            self.m_timeout = None
        if ev.type == gtk.gdk.BUTTON_PRESS:
            if self.m_value < 127:
                self.up()
            if self.m_value < 127:
                self.m_timeout = gtk.timeout_add(DELAY1, self.on_up_timeout)
    def on_up_release(self, eb, ev):
        if self.m_timeout:
            gtk.timeout_remove(self.m_timeout)
            self.m_timeout = None
    def on_up_timeout(self, *v):
        if self.m_value < 127:
            self.up()
            self.m_timeout = gtk.timeout_add(DELAY2, self.on_up_timeout)
    def up(self):
        self.m_value += 1
        self.g_label.set_text(mpd.int_to_user_notename(self.m_value))
        self.g_label.set_use_markup(1)
        self.emit('value-changed', self.m_value)
    def on_down_press(self, eb, ev):
        if self.m_timeout:
            gtk.timeout_remove(self.m_timeout)
            self.m_timeout = None
        if ev.type == gtk.gdk.BUTTON_PRESS:
            if self.m_value > 0:
                self.down()
            if self.m_value > 0:
                self.m_timeout = gtk.timeout_add(DELAY1, self.on_down_timeout)
    def on_down_release(self, eb, ev):
        if self.m_timeout:
            gtk.timeout_remove(self.m_timeout)
            self.m_timeout = None
    def on_down_timeout(self, *v):
        if self.m_value > 0:
            self.down()
            self.m_timeout = gtk.timeout_add(DELAY2, self.on_down_timeout)
    def down(self):
        self.m_value -= 1
        self.g_label.set_text(mpd.int_to_user_notename(self.m_value))
        self.g_label.set_use_markup(1)
        self.emit('value-changed', self.m_value)
    def get_value(self):
        return self.m_value
    def set_value(self, val):
        self.m_value = val
        self.g_label.set_text(mpd.int_to_user_notename(val))
        self.g_label.set_use_markup(1)

gobject.signal_new('value-changed', NotenameSpinButton,
                   gobject.SIGNAL_RUN_FIRST,
                   gobject.TYPE_NONE,
                   (gobject.TYPE_PYOBJECT,))


class OLDNotenameSpinButton(gtk.SpinButton):
    def __init__(self, default_value):
        #FIXME values are _BROKEN_SPIN too large to workaround problem with
        # spinbutton and input/output signals.
        #adj = gtk.Adjustment(mpd.notename_to_int(default_value)+_BROKEN_SPIN, 1.0+_BROKEN_SPIN, 127.0+_BROKEN_SPIN, 1.0, 12.0, 0.0)
        adj = gtk.Adjustment(mpd.notename_to_int(default_value), 1.0, 127.0, 1.0, 12.0, 0.0)
        gtk.SpinButton.__init__(self, adj,  0, 0)
        self.set_update_policy(gtk.UPDATE_IF_VALID)
        self.connect('input', self.on_input_signal)
        self.connect('output', self.on_output_signal)
        self.__tmpval = 1.0
    def on_input_signal(self, spin, cob):
        try:# test if the text field is a valid notename
            mpd.notename_to_int(spin.get_text())
            return gtk.TRUE
        except:
            return INPUT_ERROR
    def Xon_input_signal(self, spin, cob):#FIXME old workaround. broken by pygtk 1.99.14 or .15
        valid_notename = None
        try:
            i = mpd.notename_to_int(spin.get_text())
            spin.set_value(i+_BROKEN_SPIN)
            return gtk.TRUE
        except:
            return INPUT_ERROR
    def on_output_signal(self, spin):
        if self.get_value() - int(self.get_value()) > 0.001:
            spin.set_value(self.__tmpval)
            return gtk.TRUE
        if spin.get_text() != mpd.int_to_user_notename(spin.get_value_as_int()):
            spin.set_text(mpd.int_to_user_notename(spin.get_value_as_int()))
            self.__tmpval = spin.get_value_as_int()
        return gtk.TRUE
    def Xon_output_signal(self, spin):
        n = spin.get_value_as_int()
        if _BROKEN_SPIN <= n <= 127 + _BROKEN_SPIN:
            if spin.get_text() != mpd.int_to_notename(n-_BROKEN_SPIN):
                self.set_text(mpd.int_to_notename(n-_BROKEN_SPIN))
        return gtk.TRUE

class NotenameRangeController(object):
    def __init__(self, spin_low, spin_high, lowest_value, highest_value):
        self.g_spin_low = spin_low
        self.g_spin_low.connect('value-changed', self.on_low_changed)
        self.g_spin_high = spin_high
        self.g_spin_high.connect('value-changed', self.on_high_changed)
        self.m_lowest_value = mpd.notename_to_int(lowest_value)
        self.m_highest_value = mpd.notename_to_int(highest_value)
    def on_low_changed(self, widget, *v):
        if widget.get_value() > self.g_spin_high.get_value():
            self.g_spin_low.set_value(self.g_spin_high.get_value())
        elif widget.get_value() < self.m_lowest_value:
            self.g_spin_low.set_value(self.m_lowest_value)
    def on_high_changed(self, widget, *v):
        if widget.get_value() < self.g_spin_low.get_value():
            self.g_spin_high.set_value(self.g_spin_low.get_value())
        elif widget.get_value() > self.m_highest_value:
            self.g_spin_high.set_value(self.m_highest_value)

class nNotenameRangeController(NotenameRangeController, cfg.ConfigUtils):
    def __init__(self, spin_low, spin_high, lowest_value, highest_value,
                 exname, name_low, name_high):
        NotenameRangeController.__init__(self, spin_low, spin_high,
                lowest_value, highest_value)
        cfg.ConfigUtils.__init__(self, exname)
        self.m_name_low = name_low
        self.m_name_high = name_high
        self.g_spin_low.set_value(mpd.notename_to_int(self.get_string(self.m_name_low)))
        self.g_spin_high.set_value(mpd.notename_to_int(self.get_string(self.m_name_high)))
    def set_range(self, lowest_value, highest_value):
        """
        make a separate function for NotenameRangeController if we need it.
        """
        assert mpd.compare_notenames(lowest_value, highest_value) <= 0
        self.m_lowest_value = mpd.notename_to_int(lowest_value)
        self.m_highest_value = mpd.notename_to_int(highest_value)
        if self.m_lowest_value > self.g_spin_low.get_value():
            self.set_string(self.m_name_low, lowest_value)
            self.g_spin_low.set_value(self.m_lowest_value)
        if self.m_highest_value < self.g_spin_high.get_value():
            self.set_string(self.m_name_high, highest_value)
            self.g_spin_high.set_value(self.m_highest_value)

def _test():
    w = gtk.Window(gtk.WINDOW_TOPLEVEL)
    w.set_size_request(100, 100)
    vbox = gtk.VBox()
    w.add(vbox)
    #####
    ns  = NotenameSpinButton("cis''")
    ns.show()
    vbox.pack_start(ns, gtk.FALSE)
    #####
    adj = gtk.Adjustment(mpd.notename_to_int('c'), 1.0, 127.0, 1.0, 12.0, 0.0)
    n = NotenameSpinButton("d'")
    vbox.pack_start(n)

    hbox = gtk.HBox()
    vbox.pack_start(hbox)
    s1 = NotenameSpinButton("c")
    s2 = NotenameSpinButton("c'")
    hbox.pack_start(s1)
    hbox.pack_start(s2)
    con = NotenameRangeController(s1, s2, "c,", "c''")

    l = gtk.Entry()
    l.set_text("test<sup>3</sup>")
    vbox.pack_start(l)
    w.show_all()
    w.connect('destroy', gtk.mainquit)
    gtk.mainloop()
if __name__ == '__main__':
    sys.path.append(".")
    #import src.i18n#, gettext, os, configureoutput
    import gettext
    x=gettext.install('solfege', 'share/locale', unicode=1)
    _test()
