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>
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 };
34 GtkWidget
*widget
= GTK_WIDGET( g_object_new (CALF_TYPE_KEYBOARD
, NULL
));
39 calf_keyboard_expose (GtkWidget
*widget
, GdkEventExpose
*event
)
41 g_assert(CALF_IS_KEYBOARD(widget
));
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 + 12 * i
, 0.5, 12, sy
, 12 * (i
/ 7) + semitones_w
[i
% 7], false };
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
);
66 cairo_set_source_rgba(c
, 0, 0, 0, 0.5);
68 if (!self
->sink
->pre_draw_outline(c
, ki
))
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 + 12 * i
, 0.5, 8, sy
* 3 / 5, 12 * (i
/ 7) + semitones_b
[i
% 7], true };
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
);
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
));
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
);
115 self
->sink
->post_all(c
);
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
);
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
= 12 * self
->nkeys
+ 1;
152 requisition
->height
= 40;
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
);
171 calf_keyboard_key_press (GtkWidget
*widget
, GdkEventKey
*event
)
173 g_assert(CALF_IS_KEYBOARD(widget
));
174 CalfKeyboard
*self
= CALF_KEYBOARD(widget
);
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);
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
)
205 gtk_widget_grab_focus(widget
);
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
);
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
)
220 if (self
->last_key
!= -1)
221 self
->sink
->note_off(self
->last_key
);
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
)
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
);
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;
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
;
273 calf_keyboard_get_type (void)
275 static GType type
= 0;
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
),
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
)) {
297 type
= g_type_register_static(GTK_TYPE_WIDGET
,