fix crash when copy'ing latent plugins
[ardour.git] / gtk2_ardour / gtk_pianokeyboard.c
1 /*-
2  * Copyright (c) 2007, 2008 Edward Tomasz Napiera�ła <trasz@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 /*
28  * This is piano_keyboard, piano keyboard-like GTK+ widget.  It contains
29  * no MIDI-specific code.
30  *
31  * For questions and comments, contact Edward Tomasz Napierala <trasz@FreeBSD.org>.
32  */
33
34 #include <assert.h>
35 #include <string.h>
36 #include <stdint.h>
37 #include <cairo/cairo.h>
38
39 #include <gtk/gtk.h>
40 #include <gdk/gdkkeysyms.h>
41
42 #include "gtk_pianokeyboard.h"
43
44 #define PIANO_KEYBOARD_DEFAULT_WIDTH 730
45 #define PIANO_KEYBOARD_DEFAULT_HEIGHT 70
46
47 enum {
48         NOTE_ON_SIGNAL,
49         NOTE_OFF_SIGNAL,
50         REST_SIGNAL,
51         LAST_SIGNAL
52 };
53
54 static guint    piano_keyboard_signals[LAST_SIGNAL] = { 0 };
55
56 static void
57 draw_keyboard_cue(PianoKeyboard *pk, cairo_t* cr)
58 {
59         int             w = pk->notes[0].w;
60         int             h = pk->notes[0].h;
61
62         int             first_note_in_lower_row = (pk->octave + 5) * 12;
63         int             last_note_in_lower_row = (pk->octave + 6) * 12 - 1;
64         int             first_note_in_higher_row = (pk->octave + 6) * 12;
65         int             last_note_in_higher_row = (pk->octave + 7) * 12 + 4;
66
67         cairo_set_source_rgb (cr, 1.0f, 0.0f, 0.0f);
68         cairo_move_to (cr, pk->notes[first_note_in_lower_row].x + 3, h - 6);
69         cairo_line_to (cr, pk->notes[last_note_in_lower_row].x + w - 3, h - 6);
70         cairo_stroke (cr);
71
72         cairo_set_source_rgb (cr, 0.0f, 0.0f, 1.0f);
73         cairo_move_to (cr, pk->notes[first_note_in_higher_row].x + 3, h - 9);
74         cairo_line_to (cr, pk->notes[last_note_in_higher_row].x + w - 3, h - 9);
75         cairo_stroke (cr);
76 }
77
78 static void
79 queue_note_draw (PianoKeyboard* pk, int note)
80 {
81         GdkWindow* w = GTK_WIDGET(pk)->window;
82
83         if (w) {
84                 GdkRectangle r;
85
86                 r.x = pk->notes[note].x;
87                 r.y = 0;
88                 r.width = pk->notes[note].w;
89                 r.height = pk->notes[note].h;
90
91                 gdk_window_invalidate_rect (w, &r, TRUE);
92         }
93 }
94
95 static void
96 draw_note(PianoKeyboard *pk, cairo_t* cr, int note)
97 {
98         int             is_white = pk->notes[note].white;
99
100         int             x = pk->notes[note].x;
101         int             w = pk->notes[note].w;
102         int             h = pk->notes[note].h;
103
104         if (pk->notes[note].pressed || pk->notes[note].sustained) {
105                 if (is_white) {
106                         cairo_set_source_rgb (cr, 0.60f, 0.60f, 0.60f);
107                 } else {
108                         cairo_set_source_rgb (cr, 0.50f, 0.50f, 0.50f);
109                 }
110         } else {
111                 if (is_white) {
112                         cairo_set_source_rgb (cr, 1.0f, 1.0f, 1.0f);
113                 } else {
114                         cairo_set_source_rgb (cr, 0.0f, 0.0f, 0.0f);
115                 }
116         }
117
118         cairo_set_line_width (cr, 1.0);
119
120         cairo_rectangle (cr, x, 0, w, h);
121         cairo_fill (cr);
122
123         cairo_set_source_rgb(cr, 0.0f, 0.0f, 0.0f); /* black outline */
124         cairo_rectangle (cr, x, 0, w, h);
125         cairo_stroke (cr);
126
127         if (pk->enable_keyboard_cue) {
128                 draw_keyboard_cue (pk, cr);
129         }
130
131         /* We need to redraw black keys that partially obscure the white one. */
132         if (note < NNOTES - 2 && !pk->notes[note + 1].white) {
133                 draw_note(pk, cr, note + 1);
134         }
135
136         if (note > 0 && !pk->notes[note - 1].white) {
137                 draw_note(pk, cr, note - 1);
138         }
139 }
140
141 static int
142 press_key(PianoKeyboard *pk, int key)
143 {
144         assert(key >= 0);
145         assert(key < NNOTES);
146
147         pk->maybe_stop_sustained_notes = 0;
148
149         /* This is for keyboard autorepeat protection. */
150         if (pk->notes[key].pressed)
151                 return 0;
152
153         if (pk->sustain_new_notes)
154                 pk->notes[key].sustained = 1;
155         else
156                 pk->notes[key].sustained = 0;
157
158         if (pk->monophonic && pk->last_key != key) {
159                 pk->notes[pk->last_key].pressed   = 0;
160                 pk->notes[pk->last_key].sustained = 0;
161                 queue_note_draw(pk, pk->last_key);
162         }
163         pk->last_key = key;
164
165         pk->notes[key].pressed = 1;
166
167         g_signal_emit_by_name(GTK_WIDGET(pk), "note-on", key);
168         queue_note_draw(pk, key);
169
170         return 1;
171 }
172
173 static int
174 release_key(PianoKeyboard *pk, int key)
175 {
176         assert(key >= 0);
177         assert(key < NNOTES);
178
179         pk->maybe_stop_sustained_notes = 0;
180
181         if (!pk->notes[key].pressed)
182                 return 0;
183
184         if (pk->sustain_new_notes)
185                 pk->notes[key].sustained = 1;
186
187         pk->notes[key].pressed = 0;
188
189         if (pk->notes[key].sustained)
190                 return 0;
191
192         g_signal_emit_by_name(GTK_WIDGET(pk), "note-off", key);
193         queue_note_draw(pk, key);
194
195         return 1;
196 }
197
198 static void
199 rest (PianoKeyboard* pk)
200 {
201         g_signal_emit_by_name(GTK_WIDGET(pk), "rest");
202 }
203
204 static void
205 stop_unsustained_notes(PianoKeyboard *pk)
206 {
207         int             i;
208
209         for (i = 0; i < NNOTES; i++) {
210                 if (pk->notes[i].pressed && !pk->notes[i].sustained) {
211                         pk->notes[i].pressed = 0;
212                         g_signal_emit_by_name(GTK_WIDGET(pk), "note-off", i);
213                         queue_note_draw(pk, i);
214                 }
215         }
216 }
217
218 static void
219 stop_sustained_notes(PianoKeyboard *pk)
220 {
221         int             i;
222
223         for (i = 0; i < NNOTES; i++) {
224                 if (pk->notes[i].sustained) {
225                         pk->notes[i].pressed = 0;
226                         pk->notes[i].sustained = 0;
227                         g_signal_emit_by_name(GTK_WIDGET(pk), "note-off", i);
228                         queue_note_draw(pk, i);
229                 }
230         }
231 }
232
233 static int
234 key_binding(PianoKeyboard *pk, const char *key)
235 {
236         gpointer notused, note;
237         gboolean found;
238
239         assert(pk->key_bindings != NULL);
240
241         found = g_hash_table_lookup_extended(pk->key_bindings, key, &notused, &note);
242
243         if (!found)
244                 return -1;
245
246         return (intptr_t)note;
247 }
248
249 static void
250 bind_key(PianoKeyboard *pk, const char *key, int note)
251 {
252         assert(pk->key_bindings != NULL);
253
254         g_hash_table_insert(pk->key_bindings, (const gpointer)key, (gpointer)((intptr_t)note));
255 }
256
257 static void
258 clear_notes(PianoKeyboard *pk)
259 {
260         assert(pk->key_bindings != NULL);
261
262         g_hash_table_remove_all(pk->key_bindings);
263 }
264
265 static void
266 bind_keys_qwerty(PianoKeyboard *pk)
267 {
268         clear_notes(pk);
269
270         bind_key(pk, "space", 128);
271
272         /* Lower keyboard row - "zxcvbnm". */
273         bind_key(pk, "z", 12);  /* C0 */
274         bind_key(pk, "s", 13);
275         bind_key(pk, "x", 14);
276         bind_key(pk, "d", 15);
277         bind_key(pk, "c", 16);
278         bind_key(pk, "v", 17);
279         bind_key(pk, "g", 18);
280         bind_key(pk, "b", 19);
281         bind_key(pk, "h", 20);
282         bind_key(pk, "n", 21);
283         bind_key(pk, "j", 22);
284         bind_key(pk, "m", 23);
285
286         /* Upper keyboard row, first octave - "qwertyu". */
287         bind_key(pk, "q", 24);
288         bind_key(pk, "2", 25);
289         bind_key(pk, "w", 26);
290         bind_key(pk, "3", 27);
291         bind_key(pk, "e", 28);
292         bind_key(pk, "r", 29);
293         bind_key(pk, "5", 30);
294         bind_key(pk, "t", 31);
295         bind_key(pk, "6", 32);
296         bind_key(pk, "y", 33);
297         bind_key(pk, "7", 34);
298         bind_key(pk, "u", 35);
299
300         /* Upper keyboard row, the rest - "iop". */
301         bind_key(pk, "i", 36);
302         bind_key(pk, "9", 37);
303         bind_key(pk, "o", 38);
304         bind_key(pk, "0", 39);
305         bind_key(pk, "p", 40);
306 }
307
308 static void
309 bind_keys_qwertz(PianoKeyboard *pk)
310 {
311         bind_keys_qwerty(pk);
312
313         /* The only difference between QWERTY and QWERTZ is that the "y" and "z" are swapped together. */
314         bind_key(pk, "y", 12);
315         bind_key(pk, "z", 33);
316 }
317
318 static void
319 bind_keys_azerty(PianoKeyboard *pk)
320 {
321         clear_notes(pk);
322
323         bind_key(pk, "space", 128);
324
325         /* Lower keyboard row - "wxcvbn,". */
326         bind_key(pk, "w", 12);  /* C0 */
327         bind_key(pk, "s", 13);
328         bind_key(pk, "x", 14);
329         bind_key(pk, "d", 15);
330         bind_key(pk, "c", 16);
331         bind_key(pk, "v", 17);
332         bind_key(pk, "g", 18);
333         bind_key(pk, "b", 19);
334         bind_key(pk, "h", 20);
335         bind_key(pk, "n", 21);
336         bind_key(pk, "j", 22);
337         bind_key(pk, "comma", 23);
338
339         /* Upper keyboard row, first octave - "azertyu". */
340         bind_key(pk, "a", 24);
341         bind_key(pk, "eacute", 25);
342         bind_key(pk, "z", 26);
343         bind_key(pk, "quotedbl", 27);
344         bind_key(pk, "e", 28);
345         bind_key(pk, "r", 29);
346         bind_key(pk, "parenleft", 30);
347         bind_key(pk, "t", 31);
348         bind_key(pk, "minus", 32);
349         bind_key(pk, "y", 33);
350         bind_key(pk, "egrave", 34);
351         bind_key(pk, "u", 35);
352
353         /* Upper keyboard row, the rest - "iop". */
354         bind_key(pk, "i", 36);
355         bind_key(pk, "ccedilla", 37);
356         bind_key(pk, "o", 38);
357         bind_key(pk, "agrave", 39);
358         bind_key(pk, "p", 40);
359 }
360
361 static gint
362 keyboard_event_handler(GtkWidget *mk, GdkEventKey *event, gpointer ignored)
363 {
364         int             note;
365         char            *key;
366         guint           keyval;
367         GdkKeymapKey    kk;
368         PianoKeyboard   *pk = PIANO_KEYBOARD(mk);
369
370         (void) ignored;
371
372         /* We're not using event->keyval, because we need keyval with level set to 0.
373            E.g. if user holds Shift and presses '7', we want to get a '7', not '&'. */
374         kk.keycode = event->hardware_keycode;
375         kk.level = 0;
376         kk.group = 0;
377
378         keyval = gdk_keymap_lookup_key(NULL, &kk);
379
380         key = gdk_keyval_name(gdk_keyval_to_lower(keyval));
381
382         if (key == NULL) {
383                 g_message("gtk_keyval_name() returned NULL; please report this.");
384                 return FALSE;
385         }
386
387         note = key_binding(pk, key);
388
389         if (note < 0) {
390                 /* Key was not bound.  Maybe it's one of the keys handled in jack-keyboard.c. */
391                 return FALSE;
392         }
393
394         if (note == 128) {
395                 if (event->type == GDK_KEY_RELEASE) {
396                         rest (pk);
397                 }
398
399                 return TRUE;
400         }
401
402         note += pk->octave * 12;
403
404         assert(note >= 0);
405         assert(note < NNOTES);
406
407         if (event->type == GDK_KEY_PRESS) {
408                 press_key(pk, note);
409
410         } else if (event->type == GDK_KEY_RELEASE) {
411                 release_key(pk, note);
412         }
413
414         return TRUE;
415 }
416
417 static int
418 get_note_for_xy(PianoKeyboard *pk, int x, int y)
419 {
420         int             height = GTK_WIDGET(pk)->allocation.height;
421         int             note;
422
423         if (y <= height / 2) {
424                 for (note = 0; note < NNOTES - 1; note++) {
425                         if (pk->notes[note].white)
426                                 continue;
427
428                         if (x >= pk->notes[note].x && x <= pk->notes[note].x + pk->notes[note].w)
429                                 return note;
430                 }
431         }
432
433         for (note = 0; note < NNOTES - 1; note++) {
434                 if (!pk->notes[note].white)
435                         continue;
436
437                 if (x >= pk->notes[note].x && x <= pk->notes[note].x + pk->notes[note].w)
438                         return note;
439         }
440
441         return -1;
442 }
443
444 static gboolean
445 mouse_button_event_handler(PianoKeyboard *pk, GdkEventButton *event, gpointer ignored)
446 {
447         int             x = event->x;
448         int             y = event->y;
449
450         int             note = get_note_for_xy(pk, x, y);
451
452         (void) ignored;
453
454         if (event->button != 1)
455                 return TRUE;
456
457         if (event->type == GDK_BUTTON_PRESS) {
458                 /* This is possible when you make the window a little wider and then click
459                    on the grey area. */
460                 if (note < 0) {
461                         return TRUE;
462                 }
463
464                 if (pk->note_being_pressed_using_mouse >= 0)
465                         release_key(pk, pk->note_being_pressed_using_mouse);
466
467                 press_key(pk, note);
468                 pk->note_being_pressed_using_mouse = note;
469
470         } else if (event->type == GDK_BUTTON_RELEASE) {
471                 if (note >= 0) {
472                         release_key(pk, note);
473
474                 } else {
475                         if (pk->note_being_pressed_using_mouse >= 0)
476                                 release_key(pk, pk->note_being_pressed_using_mouse);
477                 }
478
479                 pk->note_being_pressed_using_mouse = -1;
480
481         }
482
483         return TRUE;
484 }
485
486 static gboolean
487 mouse_motion_event_handler(PianoKeyboard *pk, GdkEventMotion *event, gpointer ignored)
488 {
489         int             note;
490
491         (void) ignored;
492
493         if ((event->state & GDK_BUTTON1_MASK) == 0)
494                 return TRUE;
495
496         note = get_note_for_xy(pk, event->x, event->y);
497
498         if (note != pk->note_being_pressed_using_mouse && note >= 0) {
499
500                 if (pk->note_being_pressed_using_mouse >= 0)
501                         release_key(pk, pk->note_being_pressed_using_mouse);
502                 press_key(pk, note);
503                 pk->note_being_pressed_using_mouse = note;
504         }
505
506         return TRUE;
507 }
508
509 static gboolean
510 piano_keyboard_expose(GtkWidget *widget, GdkEventExpose *event)
511 {
512         int i;
513         PianoKeyboard *pk = PIANO_KEYBOARD(widget);
514         cairo_t* cr = gdk_cairo_create (GDK_DRAWABLE (GTK_WIDGET(pk)->window));
515
516         gdk_cairo_region (cr, event->region);
517         cairo_clip (cr);
518
519         for (i = 0; i < NNOTES; i++) {
520                 GdkRectangle r;
521
522                 r.x = pk->notes[i].x;
523                 r.y = 0;
524                 r.width = pk->notes[i].w;
525                 r.height = pk->notes[i].h;
526
527                 switch (gdk_region_rect_in (event->region, &r)) {
528                 case GDK_OVERLAP_RECTANGLE_PART:
529                 case GDK_OVERLAP_RECTANGLE_IN:
530                         draw_note (pk, cr, i);
531                         break;
532                 default:
533                         break;
534                 }
535         }
536
537         cairo_destroy (cr);
538
539         return TRUE;
540 }
541
542 static void
543 piano_keyboard_size_request(GtkWidget* w, GtkRequisition *requisition)
544 {
545         (void) w;
546
547         requisition->width = PIANO_KEYBOARD_DEFAULT_WIDTH;
548         requisition->height = PIANO_KEYBOARD_DEFAULT_HEIGHT;
549 }
550
551 static void
552 recompute_dimensions(PianoKeyboard *pk)
553 {
554         int             number_of_white_keys = (NNOTES - 1) * (7.0 / 12.0);
555
556         int             key_width;
557         int             black_key_width;
558         int             useful_width;
559
560         int             note;
561         int             white_key = 0;
562         int             note_in_octave;
563
564         int             width = GTK_WIDGET(pk)->allocation.width;
565         int             height = GTK_WIDGET(pk)->allocation.height;
566
567         key_width = width / number_of_white_keys;
568         black_key_width = key_width * 0.8;
569         useful_width = number_of_white_keys * key_width;
570         pk->widget_margin = (width - useful_width) / 2;
571
572         for (note = 0, white_key = 0; note < NNOTES - 2; note++) {
573                 note_in_octave = note % 12;
574
575                 if (note_in_octave == 1 || note_in_octave == 3 || note_in_octave == 6 ||
576                         note_in_octave == 8 || note_in_octave == 10) {
577
578                         /* This note is black key. */
579                         pk->notes[note].x = pk->widget_margin + white_key * key_width - black_key_width / 2;
580                         pk->notes[note].w = black_key_width;
581                         pk->notes[note].h = height / 2;
582                         pk->notes[note].white = 0;
583
584                         continue;
585                 }
586
587                 /* This note is white key. */
588                 pk->notes[note].x = pk->widget_margin + white_key * key_width;
589                 pk->notes[note].w = key_width;
590                 pk->notes[note].h = height;
591                 pk->notes[note].white = 1;
592
593                 white_key++;
594         }
595 }
596
597 static void
598 piano_keyboard_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
599 {
600         /* XXX: Are these two needed? */
601         g_return_if_fail(widget != NULL);
602         g_return_if_fail(allocation != NULL);
603
604         widget->allocation = *allocation;
605
606         recompute_dimensions(PIANO_KEYBOARD(widget));
607
608         if (GTK_WIDGET_REALIZED(widget)) {
609                 gdk_window_move_resize (widget->window, allocation->x, allocation->y, allocation->width, allocation->height);
610         }
611 }
612
613 static void
614 piano_keyboard_class_init(PianoKeyboardClass *klass)
615 {
616         GtkWidgetClass  *widget_klass;
617
618         /* Set up signals. */
619         piano_keyboard_signals[NOTE_ON_SIGNAL] = g_signal_new ("note-on",
620                 G_TYPE_FROM_CLASS (klass), (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
621                 0, NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
622
623         piano_keyboard_signals[NOTE_OFF_SIGNAL] = g_signal_new ("note-off",
624                 G_TYPE_FROM_CLASS (klass), (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
625                 0, NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
626
627         piano_keyboard_signals[REST_SIGNAL] = g_signal_new ("rest",
628                 G_TYPE_FROM_CLASS (klass), (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
629                 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
630
631         widget_klass = (GtkWidgetClass*) klass;
632
633         widget_klass->expose_event = piano_keyboard_expose;
634         widget_klass->size_request = piano_keyboard_size_request;
635         widget_klass->size_allocate = piano_keyboard_size_allocate;
636 }
637
638 static void
639 piano_keyboard_init(GtkWidget *mk)
640 {
641         gtk_widget_add_events(mk, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK);
642
643         g_signal_connect(G_OBJECT(mk), "button-press-event", G_CALLBACK(mouse_button_event_handler), NULL);
644         g_signal_connect(G_OBJECT(mk), "button-release-event", G_CALLBACK(mouse_button_event_handler), NULL);
645         g_signal_connect(G_OBJECT(mk), "motion-notify-event", G_CALLBACK(mouse_motion_event_handler), NULL);
646         g_signal_connect(G_OBJECT(mk), "key-press-event", G_CALLBACK(keyboard_event_handler), NULL);
647         g_signal_connect(G_OBJECT(mk), "key-release-event", G_CALLBACK(keyboard_event_handler), NULL);
648 }
649
650 GType
651 piano_keyboard_get_type(void)
652 {
653         static GType mk_type = 0;
654
655         if (!mk_type) {
656                 static const GTypeInfo mk_info = {
657                         sizeof(PianoKeyboardClass),
658                         NULL, /* base_init */
659                         NULL, /* base_finalize */
660                         (GClassInitFunc) piano_keyboard_class_init,
661                         NULL, /* class_finalize */
662                         NULL, /* class_data */
663                         sizeof (PianoKeyboard),
664                         0,    /* n_preallocs */
665                         (GInstanceInitFunc) piano_keyboard_init,
666                         0,    /* value_table */
667                 };
668
669                 mk_type = g_type_register_static(GTK_TYPE_DRAWING_AREA, "PianoKeyboard", &mk_info, (GTypeFlags)0);
670         }
671
672         return mk_type;
673 }
674
675 GtkWidget *
676 piano_keyboard_new(void)
677 {
678         GtkWidget *widget = (GtkWidget*)gtk_type_new(piano_keyboard_get_type());
679
680         PianoKeyboard *pk = PIANO_KEYBOARD(widget);
681
682         pk->maybe_stop_sustained_notes = 0;
683         pk->sustain_new_notes = 0;
684         pk->enable_keyboard_cue = 0;
685         pk->octave = 4;
686         pk->note_being_pressed_using_mouse = -1;
687         pk->last_key = 0;
688         pk->monophonic = FALSE;
689
690         memset((void *)pk->notes, 0, sizeof(struct PKNote) * NNOTES);
691
692         pk->key_bindings = g_hash_table_new(g_str_hash, g_str_equal);
693         bind_keys_qwerty(pk);
694
695         return widget;
696 }
697
698 void
699 piano_keyboard_set_keyboard_cue(PianoKeyboard *pk, int enabled)
700 {
701         pk->enable_keyboard_cue = enabled;
702 }
703
704 void
705 piano_keyboard_set_monophonic(PianoKeyboard *pk, gboolean monophonic)
706 {
707         pk->monophonic = monophonic;
708 }
709
710 void
711 piano_keyboard_sustain_press(PianoKeyboard *pk)
712 {
713         if (!pk->sustain_new_notes) {
714                 pk->sustain_new_notes = 1;
715                 pk->maybe_stop_sustained_notes = 1;
716         }
717 }
718
719 void
720 piano_keyboard_sustain_release(PianoKeyboard *pk)
721 {
722         if (pk->maybe_stop_sustained_notes)
723                 stop_sustained_notes(pk);
724
725         pk->sustain_new_notes = 0;
726 }
727
728 void
729 piano_keyboard_set_note_on(PianoKeyboard *pk, int note)
730 {
731         if (pk->notes[note].pressed == 0) {
732                 pk->notes[note].pressed = 1;
733                 queue_note_draw (pk, note);
734         }
735 }
736
737 void
738 piano_keyboard_set_note_off(PianoKeyboard *pk, int note)
739 {
740         if (pk->notes[note].pressed || pk->notes[note].sustained) {
741                 pk->notes[note].pressed = 0;
742                 pk->notes[note].sustained = 0;
743                 queue_note_draw (pk, note);
744         }
745 }
746
747 void
748 piano_keyboard_set_octave(PianoKeyboard *pk, int octave)
749 {
750         stop_unsustained_notes(pk);
751         pk->octave = octave;
752         gtk_widget_queue_draw(GTK_WIDGET(pk));
753 }
754
755 gboolean
756 piano_keyboard_set_keyboard_layout(PianoKeyboard *pk, const char *layout)
757 {
758         assert(layout);
759
760         if (!g_ascii_strcasecmp(layout, "QWERTY")) {
761                 bind_keys_qwerty(pk);
762
763         } else if (!g_ascii_strcasecmp(layout, "QWERTZ")) {
764                 bind_keys_qwertz(pk);
765
766         } else if (!g_ascii_strcasecmp(layout, "AZERTY")) {
767                 bind_keys_azerty(pk);
768
769         } else {
770                 /* Unknown layout name. */
771                 return TRUE;
772         }
773
774         return FALSE;
775 }