# GNU Solfege - free ear training software
# Copyright (C) 2000, 2001, 2002, 2003, 2004, 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 gtk
import gobject
import mpd
import cfg

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()
        frame.add(e)
        self.pack_start(frame, 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, 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:
            gobject.source_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 = gobject.timeout_add(DELAY1, self.on_up_timeout)
    def on_up_release(self, eb, ev):
        if self.m_timeout:
            gobject.source_remove(self.m_timeout)
            self.m_timeout = None
    def on_up_timeout(self, *v):
        if self.m_value < 127:
            self.up()
            self.m_timeout = gobject.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:
            gobject.source_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 = gobject.timeout_add(DELAY1, self.on_down_timeout)
    def on_down_release(self, eb, ev):
        if self.m_timeout:
            gobject.source_remove(self.m_timeout)
            self.m_timeout = None
    def on_down_timeout(self, *v):
        if self.m_value > 0:
            self.down()
            self.m_timeout = gobject.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 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
        low = mpd.notename_to_int(self.get_string(self.m_name_low))
        high = mpd.notename_to_int(self.get_string(self.m_name_high))
        if low > high:
            low = high
        self.g_spin_low.set_value(low)
        self.g_spin_high.set_value(high)
    def on_low_changed(self, w, v):
        NotenameRangeController.on_low_changed(self, w, v)
        self.set_string(self.m_name_low, mpd.int_to_notename(self.g_spin_low.get_value()))
    def on_high_changed(self, w, v):
        NotenameRangeController.on_high_changed(self, w, v)
        self.set_string(self.m_name_high, mpd.int_to_notename(self.g_spin_high.get_value()))
    def set_range(self, lowest_value, highest_value):
        """
        Set the lowest and highest tone allowed.
        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)

