+ Filter: move implementation of get_changed_offsets to .cpp file
[calf.git] / src / ctl_keyboard.cpp
blob993391284986b5d3a57ed60f335b9591be3544da
1 /* Calf DSP Library
2 * Barely started keyboard widget. Planned to be usable as
3 * a ruler for curves, and possibly as input widget in future
4 * as well (that's what event sink interface is for, at least).
6 * Copyright (C) 2008 Krzysztof Foltman
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General
19 * Public License along with this program; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
21 * Boston, MA 02111-1307, USA.
23 #include <calf/ctl_keyboard.h>
24 #include <stdint.h>
25 #include <malloc.h>
27 static const int semitones_b[] = { 1, 3, -1, 6, 8, 10, -1 };
28 static const int semitones_w[] = { 0, 2, 4, 5, 7, 9, 11 };
31 GtkWidget *
32 calf_keyboard_new()
34 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_KEYBOARD, NULL ));
35 return widget;
38 static gboolean
39 calf_keyboard_expose (GtkWidget *widget, GdkEventExpose *event)
41 g_assert(CALF_IS_KEYBOARD(widget));
43 GdkColor scWhiteKey = { 0, 65535, 65535, 65535 };
44 GdkColor scBlackKey = { 0, 0, 0, 0 };
45 GdkColor scOutline = { 0, 0, 0, 0 };
46 CalfKeyboard *self = CALF_KEYBOARD(widget);
47 GdkWindow *window = widget->window;
48 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
49 int sy = widget->allocation.height - 1;
50 cairo_set_line_join(c, CAIRO_LINE_JOIN_MITER);
51 cairo_set_line_width(c, 1);
53 for (int i = 0; i < self->nkeys; i++)
55 CalfKeyboard::KeyInfo ki = { 0.5 + 12 * i, 0.5, 12, sy, 12 * (i / 7) + semitones_w[i % 7], false };
56 cairo_new_path(c);
57 gdk_cairo_set_source_color(c, &scWhiteKey);
58 if (!self->sink->pre_draw(c, ki))
60 cairo_rectangle(c, ki.x, ki.y, ki.width, ki.height);
61 cairo_fill_preserve(c);
62 gdk_cairo_set_source_color(c, &scOutline);
63 if (!self->sink->pre_draw_outline(c, ki))
64 cairo_stroke(c);
65 else
66 cairo_new_path(c);
67 self->sink->post_draw(c, ki);
71 for (int i = 0; i < self->nkeys - 1; i++)
73 if ((1 << (i % 7)) & 59)
75 CalfKeyboard::KeyInfo ki = { 8.5 + 12 * i, 0.5, 8, sy * 3 / 5, 12 * (i / 7) + semitones_b[i % 7], true };
76 cairo_new_path(c);
77 cairo_rectangle(c, ki.x, ki.y, ki.width, ki.height);
78 gdk_cairo_set_source_color(c, &scBlackKey);
79 if (!self->sink->pre_draw(c, ki))
81 cairo_fill(c);
82 self->sink->post_draw(c, ki);
87 self->sink->post_all(c);
89 cairo_destroy(c);
91 return TRUE;
94 static void
95 calf_keyboard_realize(GtkWidget *widget)
97 GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
99 GdkWindowAttr attributes;
100 attributes.event_mask = GDK_EXPOSURE_MASK | GDK_BUTTON1_MOTION_MASK |
101 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
102 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK;
103 attributes.x = widget->allocation.x;
104 attributes.y = widget->allocation.y;
105 attributes.width = widget->allocation.width;
106 attributes.height = widget->allocation.height;
107 attributes.wclass = GDK_INPUT_OUTPUT;
108 attributes.window_type = GDK_WINDOW_CHILD;
110 widget->window = gdk_window_new(gtk_widget_get_parent_window (widget), &attributes, GDK_WA_X | GDK_WA_Y);
112 gdk_window_set_user_data(widget->window, widget);
113 widget->style = gtk_style_attach(widget->style, widget->window);
116 static void
117 calf_keyboard_size_request (GtkWidget *widget,
118 GtkRequisition *requisition)
120 CalfKeyboard *self = CALF_KEYBOARD(widget);
121 g_assert(CALF_IS_KEYBOARD(widget));
123 requisition->width = 12 * self->nkeys + 1;
124 requisition->height = 32;
127 static void
128 calf_keyboard_size_allocate (GtkWidget *widget,
129 GtkAllocation *allocation)
131 // CalfKeyboard *self = CALF_KEYBOARD(widget);
132 g_assert(CALF_IS_KEYBOARD(widget));
133 widget->allocation = *allocation;
134 widget->allocation.width = widget->requisition.width;
136 if (GTK_WIDGET_REALIZED(widget))
137 gdk_window_move_resize(widget->window,
138 allocation->x + (allocation->width - widget->allocation.width) / 2, allocation->y,
139 widget->allocation.width, allocation->height );
142 static gboolean
143 calf_keyboard_key_press (GtkWidget *widget, GdkEventKey *event)
145 g_assert(CALF_IS_KEYBOARD(widget));
146 CalfKeyboard *self = CALF_KEYBOARD(widget);
147 if (!self->sink)
148 return FALSE;
149 return FALSE;
153 calf_keyboard_pos_to_note (CalfKeyboard *kb, int x, int y, int *vel = NULL)
155 // first try black keys
156 if (y <= kb->parent.allocation.height * 3 / 5 && x >= 0 && (x - 8) % 12 < 8)
158 int blackkey = (x - 8) / 12;
159 if (blackkey < kb->nkeys && (59 & (1 << (blackkey % 7))))
161 return semitones_b[blackkey % 7] + 12 * (blackkey / 7);
164 // if not a black key, then which white one?
165 int whitekey = x / 12;
166 // semitones within octave + 12 semitones per octave
167 return semitones_w[whitekey % 7] + 12 * (whitekey / 7);
170 static gboolean
171 calf_keyboard_button_press (GtkWidget *widget, GdkEventButton *event)
173 g_assert(CALF_IS_KEYBOARD(widget));
174 CalfKeyboard *self = CALF_KEYBOARD(widget);
175 if (!self->interactive)
176 return FALSE;
177 gtk_widget_grab_focus(widget);
178 int vel = 127;
179 self->last_key = calf_keyboard_pos_to_note(self, (int)event->x, (int)event->y, &vel);
180 if (self->last_key != -1)
181 self->sink->note_on(self->last_key, vel);
182 return FALSE;
185 static gboolean
186 calf_keyboard_button_release (GtkWidget *widget, GdkEventButton *event)
188 g_assert(CALF_IS_KEYBOARD(widget));
189 CalfKeyboard *self = CALF_KEYBOARD(widget);
190 if (!self->interactive)
191 return FALSE;
192 if (self->last_key != -1)
193 self->sink->note_off(self->last_key);
194 return FALSE;
197 static gboolean
198 calf_keyboard_pointer_motion (GtkWidget *widget, GdkEventMotion *event)
200 g_assert(CALF_IS_KEYBOARD(widget));
201 CalfKeyboard *self = CALF_KEYBOARD(widget);
202 if (!self->interactive)
203 return FALSE;
204 int vel = 127;
205 int key = calf_keyboard_pos_to_note(self, (int)event->x, (int)event->y, &vel);
206 if (key != self->last_key)
208 if (self->last_key != -1)
209 self->sink->note_off(self->last_key);
210 self->last_key = key;
211 if (self->last_key != -1)
212 self->sink->note_on(self->last_key, vel);
214 return FALSE;
217 static void
218 calf_keyboard_class_init (CalfKeyboardClass *klass)
220 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
221 widget_class->realize = calf_keyboard_realize;
222 widget_class->size_allocate = calf_keyboard_size_allocate;
223 widget_class->expose_event = calf_keyboard_expose;
224 widget_class->size_request = calf_keyboard_size_request;
225 widget_class->button_press_event = calf_keyboard_button_press;
226 widget_class->button_release_event = calf_keyboard_button_release;
227 widget_class->motion_notify_event = calf_keyboard_pointer_motion;
228 widget_class->key_press_event = calf_keyboard_key_press;
229 // widget_class->scroll_event = calf_keyboard_scroll;
232 static void
233 calf_keyboard_init (CalfKeyboard *self)
235 static CalfKeyboard::EventAdapter default_sink;
236 GtkWidget *widget = GTK_WIDGET(self);
237 g_assert(CALF_IS_KEYBOARD(widget));
238 GTK_WIDGET_SET_FLAGS (GTK_WIDGET(self), GTK_CAN_FOCUS);
239 self->nkeys = 7 * 3 + 1;
240 self->sink = &default_sink;
241 self->last_key = -1;
244 GType
245 calf_keyboard_get_type (void)
247 static GType type = 0;
248 if (!type) {
250 static const GTypeInfo type_info = {
251 sizeof(CalfKeyboardClass),
252 NULL, /* base_init */
253 NULL, /* base_finalize */
254 (GClassInitFunc)calf_keyboard_class_init,
255 NULL, /* class_finalize */
256 NULL, /* class_data */
257 sizeof(CalfKeyboard),
258 0, /* n_preallocs */
259 (GInstanceInitFunc)calf_keyboard_init
262 for (int i = 0; ; i++) {
263 char *name = g_strdup_printf("CalfKeyboard%u%d",
264 ((unsigned int)(intptr_t)calf_keyboard_class_init) >> 16, i);
265 if (g_type_from_name(name)) {
266 free(name);
267 continue;
269 type = g_type_register_static(GTK_TYPE_WIDGET,
270 name,
271 &type_info,
272 (GTypeFlags)0);
273 free(name);
274 break;
277 return type;