Merge branch 'master' of https://github.com/calf-studio-gear/calf
[calf.git] / src / ctl_keyboard.cpp
blobb661dd9d51b8445eacdd31d79ed0b5e913a7e124
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., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301 USA
23 #include <calf/ctl_keyboard.h>
24 #include <stdint.h>
25 #include <stdlib.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 cairo_pattern_t *pat;
44 CalfKeyboard *self = CALF_KEYBOARD(widget);
45 GdkWindow *window = widget->window;
46 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
47 int sy = widget->allocation.height - 1;
48 cairo_set_line_join(c, CAIRO_LINE_JOIN_MITER);
49 cairo_set_line_width(c, 1);
51 for (int i = 0; i < self->nkeys; i++)
53 CalfKeyboard::KeyInfo ki = { 0.5 + 11 * i, 0.5, 11, (double)sy, 12 * (i / 7) + semitones_w[i % 7], false };
54 cairo_new_path(c);
55 if (!self->sink->pre_draw(c, ki))
57 cairo_rectangle(c, ki.x, ki.y, ki.width, ki.y + ki.height);
59 pat = cairo_pattern_create_linear (ki.x, ki.y, ki.x, ki.y + ki.height);
60 cairo_pattern_add_color_stop_rgb (pat, 0.0, 0.25, 0.25, 0.2);
61 cairo_pattern_add_color_stop_rgb (pat, 0.1, 0.957, 0.914, 0.925);
62 cairo_pattern_add_color_stop_rgb (pat, 1.0, 0.796, 0.787, 0.662);
63 cairo_set_source(c, pat);
64 cairo_fill(c);
66 cairo_set_source_rgba(c, 0, 0, 0, 0.5);
68 if (!self->sink->pre_draw_outline(c, ki))
69 cairo_stroke(c);
70 else
71 cairo_new_path(c);
72 self->sink->post_draw(c, ki);
76 for (int i = 0; i < self->nkeys - 1; i++)
78 if ((1 << (i % 7)) & 59)
80 CalfKeyboard::KeyInfo ki = { 8.5 + 11 * i, 0.5, 6, (double)sy * 3 / 5, 12 * (i / 7) + semitones_b[i % 7], true };
81 cairo_new_path(c);
82 cairo_rectangle(c, ki.x, ki.y, ki.width, ki.height);
83 // gdk_cairo_set_source_color(c, &scBlackKey);
84 if (!self->sink->pre_draw(c, ki))
86 pat = cairo_pattern_create_linear (ki.x, ki.y, ki.x, ki.height + ki.y);
87 cairo_pattern_add_color_stop_rgb (pat, 0.0, 0, 0, 0);
88 cairo_pattern_add_color_stop_rgb (pat, 0.1, 0.27, 0.27, 0.27);
89 cairo_pattern_add_color_stop_rgb (pat, 1.0, 0, 0, 0);
90 cairo_set_source(c, pat);
91 cairo_fill(c);
93 pat = cairo_pattern_create_linear (ki.x + 1, ki.y, ki.x + 1, (int)(ki.height * 0.8 + ki.y));
94 cairo_pattern_add_color_stop_rgb (pat, 0.0, 0, 0, 0);
95 cairo_pattern_add_color_stop_rgb (pat, 0.1, 0.55, 0.55, 0.55);
96 cairo_pattern_add_color_stop_rgb (pat, 0.5, 0.45, 0.45, 0.45);
97 cairo_pattern_add_color_stop_rgb (pat, 0.5001, 0.35, 0.35, 0.35);
98 cairo_pattern_add_color_stop_rgb (pat, 1.0, 0.25, 0.25, 0.25);
99 cairo_set_source(c, pat);
100 cairo_rectangle(c, ki.x + 1, ki.y, ki.width - 2, (int)(ki.height * 0.8 + ki.y));
101 cairo_fill(c);
103 self->sink->post_draw(c, ki);
108 pat = cairo_pattern_create_linear (widget->allocation.x, widget->allocation.y, widget->allocation.x, (int)(widget->allocation.height * 0.2 + widget->allocation.y));
109 cairo_pattern_add_color_stop_rgba (pat, 0.0, 0, 0, 0, 0.4);
110 cairo_pattern_add_color_stop_rgba (pat, 1.0, 0, 0, 0, 0);
111 cairo_rectangle(c, widget->allocation.x, widget->allocation.y, widget->allocation.width, (int)(widget->allocation.height * 0.2));
112 cairo_set_source(c, pat);
113 cairo_fill(c);
115 self->sink->post_all(c);
117 cairo_destroy(c);
119 return TRUE;
122 static void
123 calf_keyboard_realize(GtkWidget *widget)
125 GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
127 GdkWindowAttr attributes;
128 attributes.event_mask = GDK_EXPOSURE_MASK | GDK_BUTTON1_MOTION_MASK |
129 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
130 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK;
131 attributes.x = widget->allocation.x;
132 attributes.y = widget->allocation.y;
133 attributes.width = widget->allocation.width;
134 attributes.height = widget->allocation.height;
135 attributes.wclass = GDK_INPUT_OUTPUT;
136 attributes.window_type = GDK_WINDOW_CHILD;
138 widget->window = gdk_window_new(gtk_widget_get_parent_window (widget), &attributes, GDK_WA_X | GDK_WA_Y);
140 gdk_window_set_user_data(widget->window, widget);
141 widget->style = gtk_style_attach(widget->style, widget->window);
144 static void
145 calf_keyboard_size_request (GtkWidget *widget,
146 GtkRequisition *requisition)
148 CalfKeyboard *self = CALF_KEYBOARD(widget);
149 g_assert(CALF_IS_KEYBOARD(widget));
151 requisition->width = 11 * self->nkeys + 1;
152 requisition->height = 40;
155 static void
156 calf_keyboard_size_allocate (GtkWidget *widget,
157 GtkAllocation *allocation)
159 // CalfKeyboard *self = CALF_KEYBOARD(widget);
160 g_assert(CALF_IS_KEYBOARD(widget));
161 widget->allocation = *allocation;
162 widget->allocation.width = widget->requisition.width;
164 if (GTK_WIDGET_REALIZED(widget))
165 gdk_window_move_resize(widget->window,
166 allocation->x + (allocation->width - widget->allocation.width) / 2, allocation->y,
167 widget->allocation.width, allocation->height );
170 static gboolean
171 calf_keyboard_key_press (GtkWidget *widget, GdkEventKey *event)
173 g_assert(CALF_IS_KEYBOARD(widget));
174 CalfKeyboard *self = CALF_KEYBOARD(widget);
175 if (!self->sink)
176 return FALSE;
177 return FALSE;
181 calf_keyboard_pos_to_note (CalfKeyboard *kb, int x, int y, int *vel = NULL)
183 // first try black keys
184 if (y <= kb->parent.allocation.height * 3 / 5 && x >= 0 && (x - 8) % 12 < 8)
186 int blackkey = (x - 8) / 12;
187 if (blackkey < kb->nkeys && (59 & (1 << (blackkey % 7))))
189 return semitones_b[blackkey % 7] + 12 * (blackkey / 7);
192 // if not a black key, then which white one?
193 int whitekey = x / 12;
194 // semitones within octave + 12 semitones per octave
195 return semitones_w[whitekey % 7] + 12 * (whitekey / 7);
198 static gboolean
199 calf_keyboard_button_press (GtkWidget *widget, GdkEventButton *event)
201 g_assert(CALF_IS_KEYBOARD(widget));
202 CalfKeyboard *self = CALF_KEYBOARD(widget);
203 if (!self->interactive)
204 return FALSE;
205 gtk_widget_grab_focus(widget);
206 int vel = 127;
207 self->last_key = calf_keyboard_pos_to_note(self, (int)event->x, (int)event->y, &vel);
208 if (self->last_key != -1)
209 self->sink->note_on(self->last_key, vel);
210 return FALSE;
213 static gboolean
214 calf_keyboard_button_release (GtkWidget *widget, GdkEventButton *event)
216 g_assert(CALF_IS_KEYBOARD(widget));
217 CalfKeyboard *self = CALF_KEYBOARD(widget);
218 if (!self->interactive)
219 return FALSE;
220 if (self->last_key != -1)
221 self->sink->note_off(self->last_key);
222 return FALSE;
225 static gboolean
226 calf_keyboard_pointer_motion (GtkWidget *widget, GdkEventMotion *event)
228 g_assert(CALF_IS_KEYBOARD(widget));
229 CalfKeyboard *self = CALF_KEYBOARD(widget);
230 if (!self->interactive)
231 return FALSE;
232 int vel = 127;
233 int key = calf_keyboard_pos_to_note(self, (int)event->x, (int)event->y, &vel);
234 if (key != self->last_key)
236 if (self->last_key != -1)
237 self->sink->note_off(self->last_key);
238 self->last_key = key;
239 if (self->last_key != -1)
240 self->sink->note_on(self->last_key, vel);
242 return FALSE;
245 static void
246 calf_keyboard_class_init (CalfKeyboardClass *klass)
248 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
249 widget_class->realize = calf_keyboard_realize;
250 widget_class->size_allocate = calf_keyboard_size_allocate;
251 widget_class->expose_event = calf_keyboard_expose;
252 widget_class->size_request = calf_keyboard_size_request;
253 widget_class->button_press_event = calf_keyboard_button_press;
254 widget_class->button_release_event = calf_keyboard_button_release;
255 widget_class->motion_notify_event = calf_keyboard_pointer_motion;
256 widget_class->key_press_event = calf_keyboard_key_press;
257 // widget_class->scroll_event = calf_keyboard_scroll;
260 static void
261 calf_keyboard_init (CalfKeyboard *self)
263 static CalfKeyboard::EventAdapter default_sink;
264 GtkWidget *widget = GTK_WIDGET(self);
265 g_assert(CALF_IS_KEYBOARD(widget));
266 GTK_WIDGET_SET_FLAGS (GTK_WIDGET(self), GTK_CAN_FOCUS);
267 self->nkeys = 7 * 3 + 1;
268 self->sink = &default_sink;
269 self->last_key = -1;
272 GType
273 calf_keyboard_get_type (void)
275 static GType type = 0;
276 if (!type) {
278 static const GTypeInfo type_info = {
279 sizeof(CalfKeyboardClass),
280 NULL, /* base_init */
281 NULL, /* base_finalize */
282 (GClassInitFunc)calf_keyboard_class_init,
283 NULL, /* class_finalize */
284 NULL, /* class_data */
285 sizeof(CalfKeyboard),
286 0, /* n_preallocs */
287 (GInstanceInitFunc)calf_keyboard_init
290 for (int i = 0; ; i++) {
291 char *name = g_strdup_printf("CalfKeyboard%u%d",
292 ((unsigned int)(intptr_t)calf_keyboard_class_init) >> 16, i);
293 if (g_type_from_name(name)) {
294 free(name);
295 continue;
297 type = g_type_register_static(GTK_TYPE_WIDGET,
298 name,
299 &type_info,
300 (GTypeFlags)0);
301 free(name);
302 break;
305 return type;