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
));
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 };
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
))
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 };
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
))
82 self
->sink
->post_draw(c
, ki
);
87 self
->sink
->post_all(c
);
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
);
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;
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
);
143 calf_keyboard_key_press (GtkWidget
*widget
, GdkEventKey
*event
)
145 g_assert(CALF_IS_KEYBOARD(widget
));
146 CalfKeyboard
*self
= CALF_KEYBOARD(widget
);
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);
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
)
177 gtk_widget_grab_focus(widget
);
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
);
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
)
192 if (self
->last_key
!= -1)
193 self
->sink
->note_off(self
->last_key
);
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
)
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
);
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;
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
;
245 calf_keyboard_get_type (void)
247 static GType type
= 0;
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
),
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
)) {
269 type
= g_type_register_static(GTK_TYPE_WIDGET
,