# 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

from __future__ import division

import gtk, gnome.ui, gnome.canvas
import mpd
import soundcard
import cfg
import runtime

class PianoKeyboard(gnome.canvas.Canvas):
    def __init__(self, lowest_tone, highest_tone, size):
        """
        The lowest and highest tones should be without accidentals.
        """
        gnome.canvas.Canvas.__init__(self)
        self.m_lowest_tone = lowest_tone
        self.m_highest_tone = highest_tone
        lowp = mpd.MusicalPitch.new_from_notename(lowest_tone)
        if lowp.m_accidental_i != 0:
            lowp.m_accidental_i = 0
        highp = mpd.MusicalPitch.new_from_notename(highest_tone)
        if highp.m_accidental_i != 0:
            highp.m_accidental_i = 0
        assert mpd.compare_notenames(lowest_tone, highest_tone) < 0
        self.m_whites = []
        self.m_blacks = []
        for i in range(lowp.semitone_pitch(), highp.semitone_pitch()+1):
            p = mpd.MusicalPitch.new_from_int(i)
            if p.m_accidental_i == 0:
                self.m_whites.append(p.semitone_pitch())
                if i+1 < highp.semitone_pitch()+1:
                    if p.m_notename_i in (0, 1, 3, 4, 5):
                        self.m_blacks.append(p.semitone_pitch()+1)
                    else:
                        self.m_blacks.append(None)
        self.m_keyboard = self.root().add(gnome.canvas.CanvasGroup)
        self.m_w, self.m_h, self.m_h2 = size
        for x in range(len(self.m_whites)):
            g = self.m_keyboard.add(gnome.canvas.CanvasGroup)
            g.add(gnome.canvas.CanvasRect,
                  x1=x*self.m_w, y1=0,
                  x2=(x+1)*self.m_w, y2=self.m_h,
                  fill_color='white', outline_color='black')
            g.connect('event', self.on_event, self.m_whites[x])
        for x in range(len(self.m_blacks)):
            if self.m_blacks[x]:
                g = self.m_keyboard.add(gnome.canvas.CanvasGroup)
                g.add(gnome.canvas.CanvasRect,
                      x1=(x+0.65)*self.m_w, y1=0,
                      x2=(x+1.35)*self.m_w, y2=self.m_h2,
                      fill_color='black', outline_color='black')
                g.connect('event', self.on_event, self.m_blacks[x])
        self.set_size_request(len(self.m_whites)*self.m_w+1, self.m_h+1)
        self.set_scroll_region(0, 0, len(self.m_whites)*self.m_w+1, self.m_h+1)
    def grab_focus_first_sensitive_button(self):
        pass #dummy
    def on_event(self, group, event, midi_int):
        if event.type == gtk.gdk.BUTTON_PRESS:
            self.on_button_press_event(group, event, midi_int)

class PianoOctaveWithAccelName(PianoKeyboard):
    def __init__(self, callback, keys):
        PianoKeyboard.__init__(self, 'c', 'b', (30, 100, 60))
        self.m_callback = callback
        for x in range(7):
            g = self.m_keyboard.add(gnome.canvas.CanvasGroup)
            g.add(gnome.canvas.CanvasText,
                  text=keys[[0, 2, 4, 5, 7, 9, 11][x]],
                  x=(x+0.5)*self.m_w,
                  y=self.m_h-20,
                  font="Sans 24",
                  fill_color='black')
            g.connect('event', self.on_event,
                   mpd.notename_to_int(['c', 'd', 'e', 'f', 'g', 'a', 'b'][x]))
        for x in range(5):
            g = self.m_keyboard.add(gnome.canvas.CanvasGroup)
            g.add(gnome.canvas.CanvasText, text=keys[[1, 3, 6, 8, 10][x]],
                  x=[1, 2, 4, 5, 6][x]*self.m_w,
                  y=self.m_h2-20,
                  font="Sans 24", fill_color='white')
            g.connect('event', self.on_event,
                mpd.notename_to_int(['cis', 'dis', 'fis', 'gis', 'ais'][x]))
    def on_button_press_event(self, group, event, midi_int):
        if event.button == 3:
            soundcard.play_note(cfg.get_int('config/preferred_instrument'),
                           4, 0, midi_int,
                           cfg.get_int('config/preferred_instrument_velocity'))
        elif event.button == 1:
            self.m_callback(mpd.int_to_notename(midi_int))


class IntervalPianoWidget(PianoKeyboard):
    def __init__(self, callback):
        PianoKeyboard.__init__(self, "c,", "b''", (15, 50, 30))
        self.m_clicked_tones = []
        self.m_callback = callback
        self.m_marks = {}
    def set_first_note(self, tone):
        for key in self.m_marks.keys():
            self.m_marks[key].destroy()
            del self.m_marks[key]
        self.m_clicked_tones = [tone.semitone_pitch()]
        self.mark(self.m_clicked_tones[-1])
    def on_button_press_event(self, group, event, midi_int):
        """
        The callback function is only called if we have an interval.
        """
        if self.m_clicked_tones:
            interval = midi_int - self.m_clicked_tones[-1]
            if event.button == 1:
                self.m_clicked_tones.append(midi_int)
            self.m_callback(event.button, interval, midi_int)
    def forget_last_tone(self):
        self.m_clicked_tones.pop()
    def know_directions(self):
        return 1
    def clear(self):
        for m in self.m_marks.values():
            m.destroy()
        self.m_marks = {}
        self.m_clicked_tones = []
    def mark(self, midi_int):
        x, y = self.midi_int_to_key(midi_int)
        if (x, y) not in self.m_marks:
            g = self.m_keyboard.add(gnome.canvas.CanvasGroup)
            g.connect('event', self.on_event, midi_int)
            self.m_marks[x, y] = g
            if y == 1:
                h1 = (self.m_h - self.m_h2)*0.25 + self.m_h2
                h2 = (self.m_h - self.m_h2)*0.75 + self.m_h2
                g.add(gnome.canvas.CanvasEllipse,
                      x1=x*self.m_w+3, x2=(x+1)*self.m_w-3,
                      y1=h1, y2=h2,
                      outline_color='black', fill_color='red')
            elif y == 0:
                g.add(gnome.canvas.CanvasEllipse,
                      x1=(x+0.75)*self.m_w, x2=(x+1.25)*self.m_w,
                      y1=10, y2=20,
                      outline_color='white', fill_color='red')
        else:
            self.unmark(midi_int)
        self.update_now()
    def unmark(self, midi_int):
        x, y = self.midi_int_to_key(midi_int)
        if (x, y) in self.m_marks:
            self.m_marks[x, y].destroy()
            del self.m_marks[x, y]
        else:
            print "unmarking", x, y, "when there is no mark"
        self.update_now()
    def midi_int_to_key(self, i):
        if i in self.m_whites:
            return self.m_whites.index(i), 1
        else:
            assert i in self.m_blacks
            return self.m_blacks.index(i), 0


class IntervalButtonsWidget(gtk.Table, cfg.ConfigUtils):
    def __init__(self, exname, name, callback, sensicallback, vars_to_watch):
        gtk.Table.__init__(self, 1, 1, True)
        cfg.ConfigUtils.__init__(self, exname)
        self.m_name = name
        self.get_sensitive_buttons = sensicallback
        self.m_callback = callback
        self.m_buttons = {}
        self.new_int_button(_("Minor\nsecond"), 1, 0, 1, 0, 2)
        self.new_int_button(_("Major\nsecond"),  2, 0, 1, 2, 4)
        self.new_int_button(_("Minor\nthird"),   3, 0, 1, 4, 6)
        self.new_int_button(_("Major\nthird"),    4, 0, 1, 6, 8)
        self.new_int_button(_("Perfect\nfourth"),    5, 1, 2, 0, 2)
        self.new_int_button(_("Diminished\nfifth"),    6, 1, 2, 2, 4)
        self.new_int_button(_("Perfect\nfifth"),    7, 1, 2, 4, 6)
        self.new_int_button(_("Minor\nsixth"),  8, 1, 2, 6, 8)
        self.new_int_button(_("Major\nsixth"),   9, 2, 3, 0, 2)
        self.new_int_button(_("Minor\nseventh"),10, 2, 3, 2, 4)
        self.new_int_button(_("Major\nseventh"), 11, 2, 3, 4, 6)
        self.new_int_button(_("Perfect\noctave"),   12, 2, 3, 6, 8)
        self.new_int_button(_("Minor\nninth"),  13, 3, 4, 0, 2)
        self.new_int_button(_("Major\nninth"),   14, 3, 4, 2, 4)
        self.new_int_button(_("Minor\ndecim"), 15, 3, 4, 4, 6)
        self.new_int_button(_("Major\ndecim"),  16, 3, 4, 6, 8)
        self.m_lowest_tone = mpd.LOWEST_NOTENAME
        self.m_highest_tone = mpd.HIGHEST_NOTENAME
        self.add_watch('disable_unused_intervals', self.intervals_changed)
        for var in vars_to_watch:
            self.add_watch(var, self.intervals_changed)
        self.intervals_changed()
    def intervals_changed(self, s=None):
        if self.get_bool('disable_unused_intervals'):
            self.set_sensitivity(self.get_sensitive_buttons())
        else:
            for x in range(1, 17):
                self.m_buttons[x].set_sensitive(True)
    def set_sensitivity(self, make_active):
        for x in range(1, 17):
            self.m_buttons[x].set_sensitive(x in make_active)
    def new_int_button(self, txt, nr, x1, x2, y1, y2):
        # buttonwidget calls m_callback with None as midi_int because it
        # does not know if you mean interval up or down when you click
        # the buttons
        self.m_buttons[nr] = b = gtk.Button(txt)
        l=b.get_child().set_justify(gtk.JUSTIFY_CENTER)
        self.attach(b, x1, x2, y1, y2)
        b.connect('clicked',
                  lambda s, nr=nr, self=self:self.m_callback(1, nr, None))
        b.set_data('interval', nr)
        b.connect('event', self._abc)
    def _abc(self, button, event):
        if event.type == gtk.gdk.BUTTON_RELEASE and event.button == 3:
            self.m_callback(3, button.get_data('interval'), None)
    def set_first_note(self, note):
        self.m_first_note = int(note)
    def know_directions(self):
        return 0
    def clear(self):
        pass
    def show(self):
        self.show_all()
    def grab_focus_first_sensitive_button(self):
        if self.get_bool('disable_unused_intervals'):
            self.m_buttons[self.get_sensitive_buttons()[0]].grab_focus()
        else:
            self.m_buttons[1].grab_focus()

class AbstractGuitarWidget(gnome.canvas.Canvas):
    def __init__(self, callback, strings,
                            string_thickness=(1, 1, 1, 1, 1, 1)):
        assert len(strings) == len(string_thickness)
        gnome.canvas.Canvas.__init__(self, 1)
        self.m_callback = callback
        self.m_guitar = self.root().add(gnome.canvas.CanvasGroup)
        self.m_guitar.connect('event', self.event_callback)
        self.m_clicked_tones = []
        self.m_shadow = None
        self.m_mark_dict = {}
        self.m_fretdist = (20, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 30, 30)
        self.m_stringdist = 17
        self.m_numstring = len(strings)
        self.m_neckborder = 6
        self.m_neckl = 0
        self.m_xlist = []
        self.m_lowest_tone = mpd.int_to_notename(
            min(map(mpd.notename_to_int, strings)))
        self.m_highest_tone = mpd.int_to_notename(
            len(self.m_fretdist) - 1 + max(map(mpd.notename_to_int, strings)))
        self.m_stringtuning = map(mpd.notename_to_int, strings)
        tmp = 0
        for x in self.m_fretdist:
            tmp = tmp + x
            self.m_xlist.append(tmp)
        tmp = self.m_neckborder + self.m_stringdist/2
        self.m_ylist = [tmp]
        for y in range(self.m_numstring-1):
            tmp = tmp + self.m_stringdist
            self.m_ylist.append(tmp)
        for x in self.m_fretdist:
            self.m_neckl = self.m_neckl + x
        self.m_neckl = self.m_neckl + 2
        self.m_neckw = self.m_neckborder \
                       + (self.m_numstring-1)*self.m_stringdist \
                       + 1 + self.m_neckborder
        self.m_guitar.add(gnome.canvas.CanvasRect, x1=0, x2=self.m_neckl,
                          y1=0, y2=self.m_neckw,
                      fill_color='black')
        px = self.m_fretdist[0]
        self.m_guitar.add(gnome.canvas.CanvasRect, x1=px, x2=px+1,
                          y1=0, y2=self.m_neckw, fill_color='white')
        self.m_guitar.add(gnome.canvas.CanvasRect, x1=px+1, x2=px+5,
                          y1=0, y2=self.m_neckw, fill_color='gray')
        self.m_guitar.add(gnome.canvas.CanvasRect, x1=px+5, x2=px+6,
                          y1=0, y2=self.m_neckw, fill_color='dark gray')
        for w in self.m_fretdist[1:]:
            px = px + w
            self.m_guitar.add(gnome.canvas.CanvasRect, x1=px, x2=px+1, y1=0, y2=self.m_neckw,
                              fill_color='white')
            self.m_guitar.add(gnome.canvas.CanvasRect, x1=px+1, x2=px+2,
                              y1=0, y2=self.m_neckw, fill_color='gray')
            self.m_guitar.add(gnome.canvas.CanvasRect, x1=px+2, x2=px+3,
                              y1=0, y2=self.m_neckw, fill_color='dark gray')
        for y in range(self.m_numstring):
            # the strings
            if string_thickness[y] == 1:
                self.m_guitar.add(gnome.canvas.CanvasRect, x1=0, x2=self.m_neckl,
                  y1=self.m_neckborder+y*self.m_stringdist,
                  y2=self.m_neckborder+y*self.m_stringdist+1,
                  fill_color='gray')
            elif string_thickness[y] == 2:
                self.m_guitar.add(gnome.canvas.CanvasRect, x1=0, x2=self.m_neckl,
                  y1=self.m_neckborder+y*self.m_stringdist,
                  y2=self.m_neckborder+y*self.m_stringdist+1,
                  fill_color='white')
                self.m_guitar.add(gnome.canvas.CanvasRect, x1=0, x2=self.m_neckl,
                  y1=self.m_neckborder+y*self.m_stringdist+1,
                  y2=self.m_neckborder+y*self.m_stringdist+2,
                  fill_color='dark gray')
            else:
                self.m_guitar.add(gnome.canvas.CanvasRect, x1=0, x2=self.m_neckl,
                  y1=self.m_neckborder+y*self.m_stringdist-1,
                  y2=self.m_neckborder+y*self.m_stringdist,
                  fill_color='white')

                self.m_guitar.add(gnome.canvas.CanvasRect, x1=0, x2=self.m_neckl,
                  y1=self.m_neckborder+y*self.m_stringdist,
                  y2=self.m_neckborder+y*self.m_stringdist+1,
                  fill_color='gray')
                self.m_guitar.add(gnome.canvas.CanvasRect, x1=0, x2=self.m_neckl,
                  y1=self.m_neckborder+y*self.m_stringdist+1,
                  y2=self.m_neckborder+y*self.m_stringdist+2,
                  fill_color='dark gray')

        self.set_size_request(self.m_neckl, self.m_neckw)
        self.set_scroll_region(0, 0, self.m_neckl, self.m_neckw)
        self.show()
    def grab_focus_first_sensitive_button(self):
        pass #dummy
    def forget_last_tone(self):
        #FIXME this could be shared with the other instrument widgets.
        self.m_clicked_tones.pop()
    def white_dot(self, x, y):
        dot = self.m_guitar.add(gnome.canvas.CanvasGroup)
        dot.add(gnome.canvas.CanvasEllipse, x1=-5, x2=5, y1=-4, y2=6, fill_color='white')
        dot.move(self.m_xlist[x] + self.m_fretdist[x+1] / 2 + 1,
                 self.m_ylist[y])
    def event_callback(self, group, event):
        xp, yp = self.event2xy(event)
        if event.type == gtk.gdk.MOTION_NOTIFY:
            if xp is not None and yp is not None:
                self.shadow(xp, yp)
            else:
                if self.m_shadow:
                    self.m_shadow.destroy()
                    self.m_shadow = None
        if event.type == gtk.gdk.BUTTON_PRESS and xp is not None and yp is not None:
            midi_int = self.m_stringtuning[yp] + xp
            if self.m_clicked_tones:
                interval = midi_int - self.m_clicked_tones[-1]
                if event.button == 1:
                    self.m_clicked_tones.append(midi_int)
                self.m_callback(event.button, interval, midi_int)
    def event2xy(self, event):
        x = event.x - self.m_guitar.get_bounds()[0]
        xp = yp = None
        for idx in range(len(self.m_xlist)):
            if 0 <= x < self.m_xlist[idx]:
                xp = idx
                break
        for idx in range(len(self.m_ylist)):
            if 0 <= event.y < self.m_ylist[idx]:
                yp = idx
                break
        return xp, yp
    def shadow(self, x, y):
        if self.m_shadow:
            self.m_shadow.destroy()
        self.m_shadow = self.m_guitar.add(gnome.canvas.CanvasGroup)
        self.m_shadow.set_data('name', 'red_dot')
        self.m_shadow.add(gnome.canvas.CanvasEllipse, x1=-5, x2=5,
                      y1=-5, y2=5, outline_color='black', fill_color='red')
        self.m_shadow.move(self.m_xlist[x]-self.m_fretdist[x]/2,
                         self.m_ylist[y]-self.m_stringdist/2)
        self.m_shadow.set_data('x', x)
        self.m_shadow.set_data('y', y)
    def set_first_note(self, note):
        assert isinstance(note, mpd.MusicalPitch)
        self.clear()
        self.mark_note(int(note))
        self.m_clicked_tones = [int(note)]
    def mark_note(self, note):
        for idx in range(len(self.m_stringtuning)):
            if self.m_stringtuning[idx] <= note < self.m_stringtuning[idx] + len(self.m_fretdist):
                self._set_mark(note - self.m_stringtuning[idx], idx)
    def _set_mark(self, x, y):
        assert (x, y) not in self.m_mark_dict
        mark = self.m_guitar.add(gnome.canvas.CanvasGroup)
        self.m_mark_dict[(x, y)] = mark
        mark.add(gnome.canvas.CanvasEllipse, x1=-7, x2=7,
                 y1=-7, y2=7, outline_color='black', fill_color='green')
        mark.move(self.m_xlist[x]-self.m_fretdist[x]/2,
                  self.m_ylist[y]-self.m_stringdist/2)
    def clear(self):
        for m in self.m_mark_dict.values():
            m.destroy()
        self.m_mark_dict = {}
        self.m_clicked_tones = []
    def know_directions(self):
        return 1


class GuitarWidget(AbstractGuitarWidget):
    def __init__(self, callback, strings, string_thickness):
        AbstractGuitarWidget.__init__(self, callback, strings, string_thickness)
        for x, y in ((2, 2), (4, 2), (6, 2), (8, 2), (11, 1), (11, 3)):
            self.white_dot(x, y)


class AccordionWidget(gnome.canvas.Canvas):
    def __init__(self, callback, keyboard_system):
        gnome.canvas.Canvas.__init__(self, 1)
        self.m_callback = callback
        self.m_clicked_tones = []
        self.m_mark_dict = {}
        self.m_notenames_norwegian = (("g,", "bes,", "des", "e",
                 "g", "bes", "des'", "e'", "g'", "bes'", "des''", "e''",
                 "g''", "bes''", "des'''", "e'''", "g'''", "bes'''", "des''''"),
                ("f,", "aes,", "b,", "d",
                 "f", "aes", "b", "d'", "f'", "aes'", "b'", "d''", "f''",
                 "aes''", "b''", "d'''", "f'''", "aes'''", "b'''", "d''''"),
                ("fis,", "a,", "c", "ees",
                 "fis", "a", "c'", "ees'", "fis'", "a'", "c''", "ees''",
                 "fis''", "a''", "c'''", "ees'''", "fis'''", "a'''", "c''''"),
                ("e,", "g,", "bes,", "des",
                 "e", "g", "bes", "des'", "e'", "g'", "bes'", "des''", "e''",
                 "g''", "bes''", "des'''", "e'''", "g'''", "bes'''", "des''''"),
                ("f,", "aes,", "b,", "d",
                 "f", "aes", "b", "d'", "f'", "aes'", "b'", "d''", "f''",
                 "aes''", "b''", "d'''", "f'''", "aes'''", "b'''"))
        self.m_notenames_swedish = (
                ("e,", "g,", "bes,", "des",
                 "e", "g", "bes", "des'", "e'", "g'", "bes'", "des''", "e''",
                 "g''", "bes''", "des'''", "e'''", "g'''", "bes'''"),
                ("dis,", "fis,", "a,", "c", "ees", "fis", "a", "c'",
                 "ees'", "fis'", "a'", "c''", "ees''", "fis''", "a''",
                 "c'''", "ees'''", "fis'''", "a'''", "c''''"),
                ("f,", "aes,", "b,", "d",
                 "f", "aes", "b", "d'", "f'", "aes'", "b'", "d''", "f''",
                 "aes''", "b''", "d'''", "f'''", "aes'''", "b'''"),
                ("e,", "g,", "bes,", "des",
                 "e", "g", "bes", "des'", "e'", "g'", "bes'", "des''", "e''",
                 "g''", "bes''", "des'''", "e'''", "g'''", "bes'''", "des''''"),
                ("fis,", "a,", "c", "ees",
                 "fis", "a", "c'", "ees'", "fis'", "a'", "c''", "ees''",
                 "fis''", "a''", "c'''", "ees'''", "fis'''", "a'''", "c''''"),
                 )
        self.m_notenames_finnish = (
                ("dis,", "fis,", "a,", "c", "ees", "fis", "a", "c'",
                 "ees'", "fis'", "a'", "c''", "ees''", "fis''", "a''",
                 "c'''", "ees'''", "fis'''", "a'''", "c''''"),
                ("d,", "f,", "aes,", "b,", "d",
                 "f", "aes", "b", "d'", "f'", "aes'", "b'", "d''", "f''",
                 "aes''", "b''", "d'''", "f'''", "aes'''", "b'''"),
                ("e,", "g,", "bes,", "des",
                 "e", "g", "bes", "des'", "e'", "g'", "bes'", "des''", "e''",
                 "g''", "bes''", "des'''", "e'''", "g'''", "bes'''", "des''''"),
                ("dis,", "fis,", "a,", "c", "ees",
                 "fis", "a", "c'", "ees'", "fis'", "a'", "c''", "ees''",
                 "fis''", "a''", "c'''", "ees'''", "fis'''", "a'''", "c''''"),
                ("f,", "aes,", "b,", "d",
                 "f", "aes", "b", "d'", "f'", "aes'", "b'", "d''", "f''",
                 "aes''", "b''", "d'''", "f'''", "aes'''", "b'''", "d''''"),
                 )
        self.m_notenames = {'norwegian': self.m_notenames_norwegian,
                            'swedish': self.m_notenames_swedish,
                       'finnish': self.m_notenames_finnish}[keyboard_system]
        self.m_lowest_tone = mpd.HIGHEST_NOTENAME
        self.m_highest_tone = mpd.LOWEST_NOTENAME
        for v in self.m_notenames:
            if mpd.compare_notenames(self.m_lowest_tone, v[0]) > 0:
                self.m_lowest_tone = v[0]
            if mpd.compare_notenames(self.m_highest_tone, v[-1]) < 0:
                self.m_highest_tone = v[-1]

        self.m_keyboard = kb = self.root().add(gnome.canvas.CanvasGroup)
        self.m_button_radius = 9
        self.m_button_xdist = 20
        self.m_button_ydist = 18
        lb = -self.m_button_radius
        rb = len(self.m_notenames[0])*self.m_button_xdist+self.m_button_radius
        bb = len(self.m_notenames)*self.m_button_ydist
        tb = -self.m_button_ydist
        bw = 7
        kb.add(gnome.canvas.CanvasPolygon, points=(lb-bw, -self.m_button_ydist,
                                  lb-bw, tb,
                                  lb-bw, bb-bw,
                                  lb, bb,
                                  rb, bb,
                                  rb+bw, bb-bw,
                                  rb+bw, -self.m_button_ydist),
                     fill_color='black', outline_color='black')
        for row in range(len(self.m_notenames)):
            sx = (not row % 2) * self.m_button_xdist / 2
            for notename in self.m_notenames[row]:
                btn = kb.add(gnome.canvas.CanvasGroup)
                btn.set_data('midi_int', mpd.notename_to_int(notename))
                btn.connect('event', self.event_callback, mpd.notename_to_int(notename))
                if len(notename) >= 3 and notename[1:][:2] in ["is", "es"]:
                    color = "dark gray"
                    border = "black"
                else:
                    color = "white"
                    border = "black"
                btn.add(gnome.canvas.CanvasEllipse, x1=-self.m_button_radius,
                        x2=self.m_button_radius, y1=-self.m_button_radius,
                        y2=self.m_button_radius, fill_color=color,
                        outline_color=border)
                if (notename[1:][:2] not in ("is", "es")) \
                   and (notename[0] in ("c", "f")):
                    btn.add(gnome.canvas.CanvasLine, points=(-self.m_button_radius/2, -2,
                         self.m_button_radius/2+1, -2), fill_color="gray")
                    btn.add(gnome.canvas.CanvasLine, points=(-self.m_button_radius/2, 2,
                         self.m_button_radius/2+1, 2), fill_color="gray")
                btn.move(sx, row*self.m_button_ydist)
                sx = sx + self.m_button_xdist
        self.set_scroll_region(lb-bw, 0, rb+bw*2, bb)
        # FIXME +10 just to get finish system not clip.
        # Fix this for 2.0
        self.set_size_request(-lb+bw+rb+bw+10, self.m_button_ydist*2 + bb)
    def grab_focus_first_sensitive_button(self):
        pass #dummy
    def event_callback(self, group, event, midi_int):
        if event.type == gtk.gdk.BUTTON_PRESS:
            if self.m_clicked_tones:
                interval = midi_int - self.m_clicked_tones[-1]
                if event.button == 1:
                    self.m_clicked_tones.append(midi_int)
                self.m_callback(event.button, interval, midi_int)
    def set_first_note(self, note):
        self.clear()
        self.m_clicked_tones = [int(note)]
        self.mark_note(self.m_clicked_tones[-1])
        self.queue_draw()
    def forget_last_tone(self):
        self.m_clicked_tones.pop()
    def mark_note(self, midi_int):
        for rownum in range(len(self.m_notenames)):
            for n in range(len(self.m_notenames[rownum])):
                if mpd.notename_to_int(self.m_notenames[rownum][n]) == midi_int:
                    g = self.m_keyboard.add(gnome.canvas.CanvasEllipse, x1=-5, x2=5,
                      y1=-5, y2=5, outline_color='black', fill_color='red')
                    g.connect('event', self.event_callback,
                              mpd.notename_to_int(self.m_notenames[rownum][n]))

                    g.move((not rownum % 2) * self.m_button_xdist / 2 \
                           + n*self.m_button_xdist, rownum*self.m_button_ydist)
                    self.m_mark_dict[(rownum, n)] = g
    def clear(self):
        for m in self.m_mark_dict.values():
            m.destroy()
        self.m_mark_dict = {}
        self.m_clicked_tones = []
    def know_directions(self):
        return 1

if runtime.has_gnome_canvas():
    inputwidget_names = [_("Buttons"), _("Piano"), _("Guitar"), _("Bass"),
                 _("5 string bass"), _("6 string bass"),
                 _("Accordion B griff"), _("Accordion C griff"),
                 _("Accordion (system used in Finland)")]
else:
    inputwidget_names = [_("Buttons")]

def name_to_inputwidget(name, callback):
    if name == _("Piano"):
        w = IntervalPianoWidget(callback)
    elif name == _("Accordion B griff"):
        w = AccordionWidget(callback, 'norwegian')
    elif name == _("Accordion C griff"):
        w = AccordionWidget(callback, 'swedish')
    elif name == _("Accordion (system used in Finland)"):
        w = AccordionWidget(callback, 'finnish')
    elif name == _("Guitar"):
        w = GuitarWidget(callback, ["e'", "b", "g", "d", "a,", "e,"],
                                   (1, 1, 1, 2, 2, 3))
    elif name == _("Bass"):
        w = AbstractGuitarWidget(callback, ["g,", "d,", "a,,", "e,,"],
                                 (1, 1, 2, 3))
    elif name == _("5 string bass"):
        w = AbstractGuitarWidget(callback, ["g,", "d,", "a,,", "e,,", "b,,,"],
                                 (1, 1, 2, 3, 3))
    elif name == _("6 string bass"):
        w = AbstractGuitarWidget(callback, ["c", "g,", "d,", "a,,", "e,,", "b,,,"],
                                 (1, 1, 2, 2, 3, 3))
    else:#if name == _("Buttons"):
        w = IntervalButtonsWidget(callback)
    return w

