fix some conversion warnings.
[calf.git] / src / custom_ctl.cpp
blob030c46bd401a5ca2494ec76cb93f192cb7015548
1 /* Calf DSP Library
2 * Custom controls (line graph, knob).
3 * Copyright (C) 2007 Krzysztof Foltman
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18 * Boston, MA 02111-1307, USA.
20 #include "config.h"
21 #include <calf/custom_ctl.h>
22 #include <gdk/gdkkeysyms.h>
23 #include <cairo/cairo.h>
24 #include <math.h>
25 #include <gdk/gdk.h>
28 I don't really know how to do it, or if it can be done this way.
29 struct calf_ui_type_module
31 GTypeModule *module;
33 calf_ui_type_module()
35 module = g_type_module_new();
36 g_type_module_set_name(module, "calf_custom_ctl");
37 g_type_module_use(module);
39 ~calf_ui_type_module()
41 g_type_module_unuse(module);
45 static calf_ui_type_module type_module;
48 static gboolean
49 calf_line_graph_expose (GtkWidget *widget, GdkEventExpose *event)
51 g_assert(CALF_IS_LINE_GRAPH(widget));
53 CalfLineGraph *lg = CALF_LINE_GRAPH(widget);
54 //int ox = widget->allocation.x + 1, oy = widget->allocation.y + 1;
55 int ox = 1, oy = 1;
56 int sx = widget->allocation.width - 2, sy = widget->allocation.height - 2;
58 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(widget->window));
60 GdkColor sc = { 0, 0, 0, 0 };
62 gdk_cairo_set_source_color(c, &sc);
63 cairo_rectangle(c, ox, oy, sx, sy);
64 cairo_clip_preserve(c);
65 cairo_fill(c);
66 cairo_impl cimpl;
67 cimpl.context = c;
68 cairo_select_font_face(c, "Bitstream Vera Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
69 cairo_set_font_size(c, 9);
71 if (lg->source) {
72 float pos = 0;
73 bool vertical = false;
74 cairo_set_line_width(c, 1);
75 std::string legend;
76 for(int phase = 1; phase <= 2; phase++)
78 for(int gn = 0; legend = std::string(), cairo_set_source_rgba(c, 1, 1, 1, 0.5), lg->source->get_gridline(lg->source_id, gn, pos, vertical, legend, &cimpl); gn++)
80 cairo_text_extents_t tx;
81 if (!legend.empty())
82 cairo_text_extents(c, legend.c_str(), &tx);
83 if (vertical)
85 float x = floor(ox + pos * sx) + 0.5;
86 if (phase == 1)
88 cairo_move_to(c, x, oy);
89 cairo_line_to(c, x, oy + sy);
90 cairo_stroke(c);
92 if (phase == 2 && !legend.empty()) {
94 cairo_set_source_rgba(c, 1.0, 1.0, 1.0, 0.75);
95 cairo_move_to(c, x - (tx.x_bearing + tx.width / 2.0), oy + sy - 2);
96 cairo_show_text(c, legend.c_str());
99 else
101 float y = floor(oy + sy / 2 - (sy / 2 - 1) * pos) + 0.5;
102 if (phase == 1)
104 cairo_move_to(c, ox, y);
105 cairo_line_to(c, ox + sx, y);
106 cairo_stroke(c);
108 if (phase == 2 && !legend.empty()) {
109 cairo_set_source_rgba(c, 1.0, 1.0, 1.0, 0.75);
110 cairo_move_to(c, ox + sx - 2 - tx.width, y + tx.height/2 - 1);
111 cairo_show_text(c, legend.c_str());
116 float *data = new float[2 * sx];
117 GdkColor sc2 = { 0, 0, 65535, 0 };
118 gdk_cairo_set_source_color(c, &sc2);
119 cairo_set_line_join(c, CAIRO_LINE_JOIN_MITER);
120 cairo_set_line_width(c, 1);
121 for(int gn = 0; lg->source->get_graph(lg->source_id, gn, data, 2 * sx, &cimpl); gn++)
123 for (int i = 0; i < 2 * sx; i++)
125 int y = (int)(oy + sy / 2 - (sy / 2 - 1) * data[i]);
126 //if (y < oy) y = oy;
127 //if (y >= oy + sy) y = oy + sy - 1;
128 if (i)
129 cairo_line_to(c, ox + i * 0.5, y);
130 else
131 cairo_move_to(c, ox, y);
133 cairo_stroke(c);
135 delete []data;
136 float x, y;
137 int size = 0;
138 GdkColor sc3 = { 0, 32767, 65535, 0 };
139 gdk_cairo_set_source_color(c, &sc3);
140 for(int gn = 0; lg->source->get_dot(lg->source_id, gn, x, y, size = 3, &cimpl); gn++)
142 int yv = (int)(oy + sy / 2 - (sy / 2 - 1) * y);
143 cairo_arc(c, ox + x * sx, yv, size, 0, 2 * M_PI);
144 cairo_fill(c);
148 cairo_destroy(c);
150 gtk_paint_shadow(widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN, NULL, widget, NULL, ox - 1, oy - 1, sx + 2, sy + 2);
151 // printf("exposed %p %d+%d\n", widget->window, widget->allocation.x, widget->allocation.y);
153 return TRUE;
156 void calf_line_graph_set_square(CalfLineGraph *graph, bool is_square)
158 g_assert(CALF_IS_LINE_GRAPH(graph));
159 graph->is_square = is_square;
162 static void
163 calf_line_graph_size_request (GtkWidget *widget,
164 GtkRequisition *requisition)
166 g_assert(CALF_IS_LINE_GRAPH(widget));
168 // CalfLineGraph *lg = CALF_LINE_GRAPH(widget);
169 requisition->width = 40;
170 requisition->height = 40;
173 static void
174 calf_line_graph_size_allocate (GtkWidget *widget,
175 GtkAllocation *allocation)
177 g_assert(CALF_IS_LINE_GRAPH(widget));
178 CalfLineGraph *lg = CALF_LINE_GRAPH(widget);
180 GtkWidgetClass *parent_class = (GtkWidgetClass *) g_type_class_peek_parent( CALF_LINE_GRAPH_GET_CLASS( lg ) );
182 // if( lg->cache_surface )
183 // cairo_surface_destroy( lg->cache_surface );
184 // lg->cache_surface = NULL;
185 if( lg->cache_pixmap )
186 g_object_unref( G_OBJECT( lg->cache_pixmap ) );
187 lg->cache_pixmap = NULL;
189 widget->allocation = *allocation;
190 GtkAllocation &a = widget->allocation;
191 if (lg->is_square)
193 if (a.width > a.height)
195 a.x += (a.width - a.height) / 2;
196 a.width = a.height;
198 if (a.width < a.height)
200 a.y += (a.height - a.width) / 2;
201 a.height = a.width;
204 parent_class->size_allocate( widget, allocation );
207 static void
208 calf_line_graph_class_init (CalfLineGraphClass *klass)
210 // GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
211 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
212 widget_class->expose_event = calf_line_graph_expose;
213 widget_class->size_request = calf_line_graph_size_request;
214 widget_class->size_allocate = calf_line_graph_size_allocate;
217 static void
218 calf_line_graph_init (CalfLineGraph *self)
220 GtkWidget *widget = GTK_WIDGET(self);
221 //GTK_WIDGET_SET_FLAGS (widget, GTK_NO_WINDOW);
222 //widget->requisition.width = 40;
223 //widget->requisition.height = 40;
224 gtk_widget_set_size_request( widget, 40, 40 );
225 self->cache_pixmap = NULL;
228 GtkWidget *
229 calf_line_graph_new()
231 return GTK_WIDGET( g_object_new (CALF_TYPE_LINE_GRAPH, NULL ));
234 GType
235 calf_line_graph_get_type (void)
237 static GType type = 0;
238 if (!type) {
239 static const GTypeInfo type_info = {
240 sizeof(CalfLineGraphClass),
241 NULL, /* base_init */
242 NULL, /* base_finalize */
243 (GClassInitFunc)calf_line_graph_class_init,
244 NULL, /* class_finalize */
245 NULL, /* class_data */
246 sizeof(CalfLineGraph),
247 0, /* n_preallocs */
248 (GInstanceInitFunc)calf_line_graph_init
251 GTypeInfo *type_info_copy = new GTypeInfo(type_info);
253 for (int i = 0; ; i++) {
254 char *name = g_strdup_printf("CalfLineGraph%u%d", ((unsigned int)(intptr_t)calf_line_graph_class_init) >> 16, i);
255 if (g_type_from_name(name)) {
256 free(name);
257 continue;
259 type = g_type_register_static( GTK_TYPE_DRAWING_AREA,
260 name,
261 type_info_copy,
262 (GTypeFlags)0);
263 free(name);
264 break;
267 return type;
270 ///////////////////////////////////////// vu meter ///////////////////////////////////////////////
272 static gboolean
273 calf_vumeter_expose (GtkWidget *widget, GdkEventExpose *event)
275 g_assert(CALF_IS_VUMETER(widget));
278 CalfVUMeter *vu = CALF_VUMETER(widget);
279 //int ox = widget->allocation.x + 1, oy = widget->allocation.y + 1;
280 int ox = 1, oy = 1;
281 int sx = widget->allocation.width - 2, sy = widget->allocation.height - 2;
283 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(widget->window));
285 GdkColor sc = { 0, 0, 0, 0 };
286 gdk_cairo_set_source_color(c, &sc);
287 cairo_rectangle(c, ox, oy, sx, sy);
288 cairo_fill(c);
289 cairo_set_line_width(c, 1);
291 CalfVUMeterMode mode = vu->mode;
293 for (int x = ox; x <= ox + sx; x += 3)
295 float ts = (x - ox) * 1.0 / sx;
296 float r = 0.f, g = 0.f, b = 0.f;
297 switch(mode)
299 case VU_STANDARD:
300 default:
301 if (ts < 0.75)
302 r = ts / 0.75, g = 1, b = 0;
303 else
304 r = 1, g = 1 - (ts - 0.75) / 0.25, b = 0;
305 if (vu->value < ts || vu->value <= 0)
306 r *= 0.5, g *= 0.5, b *= 0.5;
307 break;
308 case VU_MONOCHROME_REVERSE:
309 r = 1, g = 1, b = 0;
310 if (!(vu->value < ts) || vu->value >= 1.0)
311 r *= 0.5, g *= 0.5, b *= 0.5;
312 break;
313 case VU_MONOCHROME:
314 r = 1, g = 1, b = 0;
315 if (vu->value < ts || vu->value <= 0)
316 r *= 0.5, g *= 0.5, b *= 0.5;
317 break;
319 GdkColor sc2 = { 0, (guint16)(65535 * r), (guint16)(65535 * g), (guint16)(65535 * b) };
320 gdk_cairo_set_source_color(c, &sc2);
321 cairo_move_to(c, x, oy);
322 cairo_line_to(c, x, oy + sy + 1);
323 cairo_move_to(c, x, oy + sy);
324 cairo_line_to(c, x, oy );
325 cairo_stroke(c);
328 cairo_destroy(c);
330 gtk_paint_shadow(widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN, NULL, widget, NULL, ox - 1, oy - 1, sx + 2, sy + 2);
331 printf("exposed %p %d+%d\n", widget->window, widget->allocation.x, widget->allocation.y);
333 return TRUE;
336 static void
337 calf_vumeter_size_request (GtkWidget *widget,
338 GtkRequisition *requisition)
340 g_assert(CALF_IS_VUMETER(widget));
342 requisition->width = 50;
343 requisition->height = 14;
346 static void
347 calf_vumeter_size_allocate (GtkWidget *widget,
348 GtkAllocation *allocation)
350 g_assert(CALF_IS_VUMETER(widget));
352 widget->allocation = *allocation;
355 static void
356 calf_vumeter_class_init (CalfVUMeterClass *klass)
358 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
359 widget_class->expose_event = calf_vumeter_expose;
360 widget_class->size_request = calf_vumeter_size_request;
361 //widget_class->size_allocate = calf_vumeter_size_allocate;
364 static void
365 calf_vumeter_init (CalfVUMeter *self)
367 GtkWidget *widget = GTK_WIDGET(self);
368 //GTK_WIDGET_SET_FLAGS (widget, GTK_NO_WINDOW);
369 widget->requisition.width = 50;
370 widget->requisition.height = 15;
371 self->value = 0.5;
374 GtkWidget *
375 calf_vumeter_new()
377 return GTK_WIDGET( g_object_new (CALF_TYPE_VUMETER, NULL ));
380 GType
381 calf_vumeter_get_type (void)
383 static GType type = 0;
384 if (!type) {
385 static const GTypeInfo type_info = {
386 sizeof(CalfVUMeterClass),
387 NULL, /* base_init */
388 NULL, /* base_finalize */
389 (GClassInitFunc)calf_vumeter_class_init,
390 NULL, /* class_finalize */
391 NULL, /* class_data */
392 sizeof(CalfVUMeter),
393 0, /* n_preallocs */
394 (GInstanceInitFunc)calf_vumeter_init
397 GTypeInfo *type_info_copy = new GTypeInfo(type_info);
399 for (int i = 0; ; i++) {
400 char *name = g_strdup_printf("CalfVUMeter%u%d", ((unsigned int)(intptr_t)calf_vumeter_class_init) >> 16, i);
401 if (g_type_from_name(name)) {
402 free(name);
403 continue;
405 type = g_type_register_static( GTK_TYPE_DRAWING_AREA,
406 name,
407 type_info_copy,
408 (GTypeFlags)0);
409 free(name);
410 break;
413 return type;
416 extern void calf_vumeter_set_value(CalfVUMeter *meter, float value)
418 if (value != meter->value)
420 meter->value = value;
421 gtk_widget_queue_draw(GTK_WIDGET(meter));
425 extern float calf_vumeter_get_value(CalfVUMeter *meter)
427 return meter->value;
430 extern void calf_vumeter_set_mode(CalfVUMeter *meter, CalfVUMeterMode mode)
432 if (mode != meter->mode)
434 meter->mode = mode;
435 gtk_widget_queue_draw(GTK_WIDGET(meter));
439 extern CalfVUMeterMode calf_vumeter_get_mode(CalfVUMeter *meter)
441 return meter->mode;
444 ///////////////////////////////////////// knob ///////////////////////////////////////////////
446 static gboolean
447 calf_knob_expose (GtkWidget *widget, GdkEventExpose *event)
449 g_assert(CALF_IS_KNOB(widget));
451 CalfKnob *self = CALF_KNOB(widget);
452 GdkWindow *window = widget->window;
453 GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
455 // printf("adjustment = %p value = %f\n", adj, adj->value);
456 int ox = widget->allocation.x, oy = widget->allocation.y;
458 ox += (widget->allocation.width - 40) / 2;
459 oy += (widget->allocation.height - 40) / 2;
461 int phase = (int)((adj->value - adj->lower) * 64 / (adj->upper - adj->lower));
462 // skip middle phase except for true middle value
463 if (self->knob_type == 1 && phase == 32) {
464 double pt = (adj->value - adj->lower) * 2.0 / (adj->upper - adj->lower) - 1.0;
465 if (pt < 0)
466 phase = 31;
467 if (pt > 0)
468 phase = 33;
470 // endless knob: skip 90deg highlights unless the value is really a multiple of 90deg
471 if (self->knob_type == 3 && !(phase % 16)) {
472 if (phase == 64)
473 phase = 0;
474 double nom = adj->lower + phase * (adj->upper - adj->lower) / 64.0;
475 double diff = (adj->value - nom) / (adj->upper - adj->lower);
476 if (diff > 0.0001)
477 phase = (phase + 1) % 64;
478 if (diff < -0.0001)
479 phase = (phase + 63) % 64;
481 gdk_draw_pixbuf(GDK_DRAWABLE(widget->window), widget->style->fg_gc[0], CALF_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget))->knob_image, phase * 40, self->knob_type * 40, ox, oy, 40, 40, GDK_RGB_DITHER_NORMAL, 0, 0);
482 // printf("exposed %p %d+%d\n", widget->window, widget->allocation.x, widget->allocation.y);
483 if (gtk_widget_is_focus(widget))
485 gtk_paint_focus(widget->style, window, GTK_STATE_NORMAL, NULL, widget, NULL, ox, oy, 40, 40);
488 return TRUE;
491 static void
492 calf_knob_size_request (GtkWidget *widget,
493 GtkRequisition *requisition)
495 g_assert(CALF_IS_KNOB(widget));
497 // width/height is hardwired at 40px now
498 requisition->width = 40;
499 requisition->height = 40;
502 static void
503 calf_knob_incr (GtkWidget *widget, int dir_down)
505 g_assert(CALF_IS_KNOB(widget));
506 CalfKnob *self = CALF_KNOB(widget);
507 GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
509 int oldstep = (int)(0.5f + (adj->value - adj->lower) / adj->step_increment);
510 int step;
511 int nsteps = (int)(0.5f + (adj->upper - adj->lower) / adj->step_increment); // less 1 actually
512 if (dir_down)
513 step = oldstep - 1;
514 else
515 step = oldstep + 1;
516 if (self->knob_type == 3 && step >= nsteps)
517 step %= nsteps;
518 if (self->knob_type == 3 && step < 0)
519 step = nsteps - (nsteps - step) % nsteps;
521 // trying to reduce error cumulation here, by counting from lowest or from highest
522 float value = adj->lower + step * double(adj->upper - adj->lower) / nsteps;
523 gtk_range_set_value(GTK_RANGE(widget), value);
524 // printf("step %d:%d nsteps %d value %f:%f\n", oldstep, step, nsteps, oldvalue, value);
527 static gboolean
528 calf_knob_key_press (GtkWidget *widget, GdkEventKey *event)
530 g_assert(CALF_IS_KNOB(widget));
531 CalfKnob *self = CALF_KNOB(widget);
532 GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
534 switch(event->keyval)
536 case GDK_Home:
537 gtk_range_set_value(GTK_RANGE(widget), adj->lower);
538 return TRUE;
540 case GDK_End:
541 gtk_range_set_value(GTK_RANGE(widget), adj->upper);
542 return TRUE;
544 case GDK_Up:
545 calf_knob_incr(widget, 0);
546 return TRUE;
548 case GDK_Down:
549 calf_knob_incr(widget, 1);
550 return TRUE;
552 case GDK_Shift_L:
553 case GDK_Shift_R:
554 self->start_value = gtk_range_get_value(GTK_RANGE(widget));
555 self->start_y = self->last_y;
556 return TRUE;
559 return FALSE;
562 static gboolean
563 calf_knob_key_release (GtkWidget *widget, GdkEventKey *event)
565 g_assert(CALF_IS_KNOB(widget));
566 CalfKnob *self = CALF_KNOB(widget);
568 if(event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R)
570 self->start_value = gtk_range_get_value(GTK_RANGE(widget));
571 self->start_y = self->last_y;
572 return TRUE;
575 return FALSE;
578 static gboolean
579 calf_knob_button_press (GtkWidget *widget, GdkEventButton *event)
581 g_assert(CALF_IS_KNOB(widget));
582 CalfKnob *self = CALF_KNOB(widget);
584 // CalfKnob *lg = CALF_KNOB(widget);
585 gtk_widget_grab_focus(widget);
586 gtk_grab_add(widget);
587 self->start_x = event->x;
588 self->start_y = event->y;
589 self->start_value = gtk_range_get_value(GTK_RANGE(widget));
591 return TRUE;
594 static gboolean
595 calf_knob_button_release (GtkWidget *widget, GdkEventButton *event)
597 g_assert(CALF_IS_KNOB(widget));
599 if (GTK_WIDGET_HAS_GRAB(widget))
600 gtk_grab_remove(widget);
601 return FALSE;
604 static inline float endless(float value)
606 if (value >= 0)
607 return fmod(value, 1.f);
608 else
609 return fmod(1.f - fmod(1.f - value, 1.f), 1.f);
612 static inline float deadzone(float value, float incr, float scale)
614 float dzw = 10 / scale;
615 if (value >= 0.501)
616 value += dzw;
617 if (value < 0.499)
618 value -= dzw;
620 value += incr;
622 if (value >= (0.5 - dzw) && value <= (0.5 + dzw))
623 return 0.5;
624 if (value < 0.5)
625 return value + dzw;
626 return value - dzw;
629 static gboolean
630 calf_knob_pointer_motion (GtkWidget *widget, GdkEventMotion *event)
632 g_assert(CALF_IS_KNOB(widget));
633 CalfKnob *self = CALF_KNOB(widget);
635 float scale = (event->state & GDK_SHIFT_MASK) ? 1000 : 100;
636 gboolean moved = FALSE;
638 if (GTK_WIDGET_HAS_GRAB(widget))
640 if (self->knob_type == 3)
642 gtk_range_set_value(GTK_RANGE(widget), endless(self->start_value - (event->y - self->start_y) / scale));
644 else
645 if (self->knob_type == 1)
647 gtk_range_set_value(GTK_RANGE(widget), deadzone(self->start_value, -(event->y - self->start_y) / scale, scale));
649 else
651 gtk_range_set_value(GTK_RANGE(widget), self->start_value - (event->y - self->start_y) / scale);
653 moved = TRUE;
655 self->last_y = event->y;
656 return moved;
659 static gboolean
660 calf_knob_scroll (GtkWidget *widget, GdkEventScroll *event)
662 calf_knob_incr(widget, event->direction);
663 return TRUE;
666 static void
667 calf_knob_class_init (CalfKnobClass *klass)
669 // GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
670 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
671 widget_class->expose_event = calf_knob_expose;
672 widget_class->size_request = calf_knob_size_request;
673 widget_class->button_press_event = calf_knob_button_press;
674 widget_class->button_release_event = calf_knob_button_release;
675 widget_class->motion_notify_event = calf_knob_pointer_motion;
676 widget_class->key_press_event = calf_knob_key_press;
677 widget_class->key_release_event = calf_knob_key_release;
678 widget_class->scroll_event = calf_knob_scroll;
679 GError *error = NULL;
680 klass->knob_image = gdk_pixbuf_new_from_file(PKGLIBDIR "/knob.png", &error);
681 g_assert(klass->knob_image != NULL);
684 static void
685 calf_knob_init (CalfKnob *self)
687 GtkWidget *widget = GTK_WIDGET(self);
688 GTK_WIDGET_SET_FLAGS (GTK_WIDGET(self), GTK_CAN_FOCUS);
689 widget->requisition.width = 40;
690 widget->requisition.height = 40;
693 GtkWidget *
694 calf_knob_new()
696 GtkAdjustment *adj = (GtkAdjustment *)gtk_adjustment_new(0, 0, 1, 0.01, 0.5, 0);
697 return calf_knob_new_with_adjustment(adj);
700 static gboolean calf_knob_value_changed(gpointer obj)
702 GtkWidget *widget = (GtkWidget *)obj;
703 gtk_widget_queue_draw(widget);
704 return FALSE;
707 GtkWidget *calf_knob_new_with_adjustment(GtkAdjustment *_adjustment)
709 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_KNOB, NULL ));
710 if (widget) {
711 gtk_range_set_adjustment(GTK_RANGE(widget), _adjustment);
712 gtk_signal_connect(GTK_OBJECT(widget), "value-changed", G_CALLBACK(calf_knob_value_changed), widget);
714 return widget;
717 GType
718 calf_knob_get_type (void)
720 static GType type = 0;
721 if (!type) {
723 static const GTypeInfo type_info = {
724 sizeof(CalfKnobClass),
725 NULL, /* base_init */
726 NULL, /* base_finalize */
727 (GClassInitFunc)calf_knob_class_init,
728 NULL, /* class_finalize */
729 NULL, /* class_data */
730 sizeof(CalfKnob),
731 0, /* n_preallocs */
732 (GInstanceInitFunc)calf_knob_init
735 for (int i = 0; ; i++) {
736 char *name = g_strdup_printf("CalfKnob%u%d",
737 ((unsigned int)(intptr_t)calf_knob_class_init) >> 16, i);
738 if (g_type_from_name(name)) {
739 free(name);
740 continue;
742 type = g_type_register_static(GTK_TYPE_RANGE,
743 name,
744 &type_info,
745 (GTypeFlags)0);
746 free(name);
747 break;
750 return type;