+ ChangeLog: updated
[calf.git] / src / custom_ctl.cpp
blobc7e3e75b442add4722df4644d885350c371ee6a7
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>
27 I don't really know how to do it, or if it can be done this way.
28 struct calf_ui_type_module
30 GTypeModule *module;
32 calf_ui_type_module()
34 module = g_type_module_new();
35 g_type_module_set_name(module, "calf_custom_ctl");
36 g_type_module_use(module);
38 ~calf_ui_type_module()
40 g_type_module_unuse(module);
44 static calf_ui_type_module type_module;
47 static gboolean
48 calf_line_graph_expose (GtkWidget *widget, GdkEventExpose *event)
50 g_assert(CALF_IS_LINE_GRAPH(widget));
52 CalfLineGraph *lg = CALF_LINE_GRAPH(widget);
53 int ox = widget->allocation.x + 1, oy = widget->allocation.y + 1;
54 int sx = widget->allocation.width - 2, sy = widget->allocation.height - 2;
56 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(widget->window));
58 GdkColor sc = { 0, 0, 0, 0 };
60 gdk_cairo_set_source_color(c, &sc);
61 cairo_rectangle(c, ox, oy, sx, sy);
62 cairo_clip_preserve(c);
63 cairo_fill(c);
64 cairo_impl cimpl;
65 cimpl.context = c;
66 cairo_select_font_face(c, "Bitstream Vera Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
67 cairo_set_font_size(c, 9);
69 if (lg->source) {
70 float pos = 0;
71 bool vertical = false;
72 cairo_set_line_width(c, 1);
73 std::string legend;
74 for(int phase = 1; phase <= 2; phase++)
76 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++)
78 cairo_text_extents_t tx;
79 if (!legend.empty())
80 cairo_text_extents(c, legend.c_str(), &tx);
81 if (vertical)
83 float x = floor(ox + pos * sx) + 0.5;
84 if (phase == 1)
86 cairo_move_to(c, x, oy);
87 cairo_line_to(c, x, oy + sy);
88 cairo_stroke(c);
90 if (phase == 2 && !legend.empty()) {
92 cairo_set_source_rgba(c, 1.0, 1.0, 1.0, 0.75);
93 cairo_move_to(c, x - (tx.x_bearing + tx.width / 2.0), oy + sy - 2);
94 cairo_show_text(c, legend.c_str());
97 else
99 float y = floor(oy + sy / 2 - (sy / 2 - 1) * pos) + 0.5;
100 if (phase == 1)
102 cairo_move_to(c, ox, y);
103 cairo_line_to(c, ox + sx, y);
104 cairo_stroke(c);
106 if (phase == 2 && !legend.empty()) {
107 cairo_set_source_rgba(c, 1.0, 1.0, 1.0, 0.75);
108 cairo_move_to(c, ox + sx - 2 - tx.width, y + tx.height/2 - 1);
109 cairo_show_text(c, legend.c_str());
114 float *data = new float[2 * sx];
115 GdkColor sc2 = { 0, 0, 65535, 0 };
116 gdk_cairo_set_source_color(c, &sc2);
117 cairo_set_line_join(c, CAIRO_LINE_JOIN_MITER);
118 cairo_set_line_width(c, 1);
119 for(int gn = 0; lg->source->get_graph(lg->source_id, gn, data, 2 * sx, &cimpl); gn++)
121 for (int i = 0; i < 2 * sx; i++)
123 int y = (int)(oy + sy / 2 - (sy / 2 - 1) * data[i]);
124 //if (y < oy) y = oy;
125 //if (y >= oy + sy) y = oy + sy - 1;
126 if (i)
127 cairo_line_to(c, ox + i * 0.5, y);
128 else
129 cairo_move_to(c, ox, y);
131 cairo_stroke(c);
133 delete []data;
134 float x, y;
135 int size = 0;
136 GdkColor sc3 = { 0, 32767, 65535, 0 };
137 gdk_cairo_set_source_color(c, &sc3);
138 for(int gn = 0; lg->source->get_dot(lg->source_id, gn, x, y, size = 3, &cimpl); gn++)
140 int yv = (int)(oy + sy / 2 - (sy / 2 - 1) * y);
141 cairo_arc(c, ox + x * sx, yv, size, 0, 2 * M_PI);
142 cairo_fill(c);
146 cairo_destroy(c);
148 gtk_paint_shadow(widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN, NULL, widget, NULL, ox - 1, oy - 1, sx + 2, sy + 2);
149 // printf("exposed %p %d+%d\n", widget->window, widget->allocation.x, widget->allocation.y);
151 return TRUE;
154 void calf_line_graph_set_square(CalfLineGraph *graph, bool is_square)
156 g_assert(CALF_IS_LINE_GRAPH(graph));
157 graph->is_square = is_square;
160 static void
161 calf_line_graph_size_request (GtkWidget *widget,
162 GtkRequisition *requisition)
164 g_assert(CALF_IS_LINE_GRAPH(widget));
166 // CalfLineGraph *lg = CALF_LINE_GRAPH(widget);
169 static void
170 calf_line_graph_size_allocate (GtkWidget *widget,
171 GtkAllocation *allocation)
173 g_assert(CALF_IS_LINE_GRAPH(widget));
174 CalfLineGraph *lg = CALF_LINE_GRAPH(widget);
176 widget->allocation = *allocation;
177 GtkAllocation &a = widget->allocation;
178 if (lg->is_square)
180 if (a.width > a.height)
182 a.x += (a.width - a.height) / 2;
183 a.width = a.height;
185 if (a.width < a.height)
187 a.y += (a.height - a.width) / 2;
188 a.height = a.width;
193 static void
194 calf_line_graph_class_init (CalfLineGraphClass *klass)
196 // GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
197 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
198 widget_class->expose_event = calf_line_graph_expose;
199 widget_class->size_request = calf_line_graph_size_request;
200 widget_class->size_allocate = calf_line_graph_size_allocate;
203 static void
204 calf_line_graph_init (CalfLineGraph *self)
206 GtkWidget *widget = GTK_WIDGET(self);
207 GTK_WIDGET_SET_FLAGS (widget, GTK_NO_WINDOW);
208 widget->requisition.width = 40;
209 widget->requisition.height = 40;
212 GtkWidget *
213 calf_line_graph_new()
215 return GTK_WIDGET( g_object_new (CALF_TYPE_LINE_GRAPH, NULL ));
218 GType
219 calf_line_graph_get_type (void)
221 static GType type = 0;
222 if (!type) {
223 static const GTypeInfo type_info = {
224 sizeof(CalfLineGraphClass),
225 NULL, /* base_init */
226 NULL, /* base_finalize */
227 (GClassInitFunc)calf_line_graph_class_init,
228 NULL, /* class_finalize */
229 NULL, /* class_data */
230 sizeof(CalfLineGraph),
231 0, /* n_preallocs */
232 (GInstanceInitFunc)calf_line_graph_init
235 GTypeInfo *type_info_copy = new GTypeInfo(type_info);
237 for (int i = 0; ; i++) {
238 char *name = g_strdup_printf("CalfLineGraph%u%d", ((unsigned int)(intptr_t)calf_line_graph_class_init) >> 16, i);
239 if (g_type_from_name(name)) {
240 free(name);
241 continue;
243 type = g_type_register_static( GTK_TYPE_WIDGET,
244 name,
245 type_info_copy,
246 (GTypeFlags)0);
247 free(name);
248 break;
251 return type;
254 ///////////////////////////////////////// vu meter ///////////////////////////////////////////////
256 static gboolean
257 calf_vumeter_expose (GtkWidget *widget, GdkEventExpose *event)
259 g_assert(CALF_IS_VUMETER(widget));
261 CalfVUMeter *vu = CALF_VUMETER(widget);
262 int ox = widget->allocation.x + 1, oy = widget->allocation.y + 1;
263 int sx = widget->allocation.width - 2, sy = widget->allocation.height - 2;
265 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(widget->window));
267 GdkColor sc = { 0, 0, 0, 0 };
268 gdk_cairo_set_source_color(c, &sc);
269 cairo_rectangle(c, ox, oy, sx, sy);
270 cairo_fill(c);
271 cairo_set_line_width(c, 1);
273 CalfVUMeterMode mode = vu->mode;
275 for (int x = ox; x <= ox + sx; x += 3)
277 float ts = (x - ox) * 1.0 / sx;
278 float r = 0.f, g = 0.f, b = 0.f;
279 switch(mode)
281 case VU_STANDARD:
282 default:
283 if (ts < 0.75)
284 r = ts / 0.75, g = 1, b = 0;
285 else
286 r = 1, g = 1 - (ts - 0.75) / 0.25, b = 0;
287 if (vu->value < ts || vu->value <= 0)
288 r *= 0.5, g *= 0.5, b *= 0.5;
289 break;
290 case VU_MONOCHROME_REVERSE:
291 r = 1, g = 1, b = 0;
292 if (!(vu->value < ts) || vu->value >= 1.0)
293 r *= 0.5, g *= 0.5, b *= 0.5;
294 break;
295 case VU_MONOCHROME:
296 r = 1, g = 1, b = 0;
297 if (vu->value < ts || vu->value <= 0)
298 r *= 0.5, g *= 0.5, b *= 0.5;
299 break;
301 GdkColor sc2 = { 0, (guint16)(65535 * r), (guint16)(65535 * g), (guint16)(65535 * b) };
302 gdk_cairo_set_source_color(c, &sc2);
303 cairo_move_to(c, x, oy);
304 cairo_line_to(c, x, oy + sy + 1);
305 cairo_move_to(c, x, oy + sy);
306 cairo_line_to(c, x, oy );
307 cairo_stroke(c);
310 cairo_destroy(c);
312 gtk_paint_shadow(widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN, NULL, widget, NULL, ox - 1, oy - 1, sx + 2, sy + 2);
313 // printf("exposed %p %d+%d\n", widget->window, widget->allocation.x, widget->allocation.y);
315 return TRUE;
318 static void
319 calf_vumeter_size_request (GtkWidget *widget,
320 GtkRequisition *requisition)
322 g_assert(CALF_IS_VUMETER(widget));
324 requisition->width = 50;
325 requisition->height = 14;
328 static void
329 calf_vumeter_size_allocate (GtkWidget *widget,
330 GtkAllocation *allocation)
332 g_assert(CALF_IS_VUMETER(widget));
334 widget->allocation = *allocation;
337 static void
338 calf_vumeter_class_init (CalfVUMeterClass *klass)
340 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
341 widget_class->expose_event = calf_vumeter_expose;
342 widget_class->size_request = calf_vumeter_size_request;
343 widget_class->size_allocate = calf_vumeter_size_allocate;
346 static void
347 calf_vumeter_init (CalfVUMeter *self)
349 GtkWidget *widget = GTK_WIDGET(self);
350 GTK_WIDGET_SET_FLAGS (widget, GTK_NO_WINDOW);
351 widget->requisition.width = 40;
352 widget->requisition.height = 40;
353 self->value = 0.5;
356 GtkWidget *
357 calf_vumeter_new()
359 return GTK_WIDGET( g_object_new (CALF_TYPE_VUMETER, NULL ));
362 GType
363 calf_vumeter_get_type (void)
365 static GType type = 0;
366 if (!type) {
367 static const GTypeInfo type_info = {
368 sizeof(CalfVUMeterClass),
369 NULL, /* base_init */
370 NULL, /* base_finalize */
371 (GClassInitFunc)calf_vumeter_class_init,
372 NULL, /* class_finalize */
373 NULL, /* class_data */
374 sizeof(CalfVUMeter),
375 0, /* n_preallocs */
376 (GInstanceInitFunc)calf_vumeter_init
379 GTypeInfo *type_info_copy = new GTypeInfo(type_info);
381 for (int i = 0; ; i++) {
382 char *name = g_strdup_printf("CalfVUMeter%u%d", ((unsigned int)(intptr_t)calf_vumeter_class_init) >> 16, i);
383 if (g_type_from_name(name)) {
384 free(name);
385 continue;
387 type = g_type_register_static( GTK_TYPE_WIDGET,
388 name,
389 type_info_copy,
390 (GTypeFlags)0);
391 free(name);
392 break;
395 return type;
398 extern void calf_vumeter_set_value(CalfVUMeter *meter, float value)
400 if (value != meter->value)
402 meter->value = value;
403 gtk_widget_queue_draw(GTK_WIDGET(meter));
407 extern float calf_vumeter_get_value(CalfVUMeter *meter)
409 return meter->value;
412 extern void calf_vumeter_set_mode(CalfVUMeter *meter, CalfVUMeterMode mode)
414 if (mode != meter->mode)
416 meter->mode = mode;
417 gtk_widget_queue_draw(GTK_WIDGET(meter));
421 extern CalfVUMeterMode calf_vumeter_get_mode(CalfVUMeter *meter)
423 return meter->mode;
426 ///////////////////////////////////////// knob ///////////////////////////////////////////////
428 static gboolean
429 calf_knob_expose (GtkWidget *widget, GdkEventExpose *event)
431 g_assert(CALF_IS_KNOB(widget));
433 CalfKnob *self = CALF_KNOB(widget);
434 GdkWindow *window = widget->window;
435 GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
437 // printf("adjustment = %p value = %f\n", adj, adj->value);
438 int ox = widget->allocation.x, oy = widget->allocation.y;
440 ox += (widget->allocation.width - 40) / 2;
441 oy += (widget->allocation.height - 40) / 2;
443 int phase = (int)((adj->value - adj->lower) * 64 / (adj->upper - adj->lower));
444 // skip middle phase except for true middle value
445 if (self->knob_type == 1 && phase == 32) {
446 double pt = (adj->value - adj->lower) * 2.0 / (adj->upper - adj->lower) - 1.0;
447 if (pt < 0)
448 phase = 31;
449 if (pt > 0)
450 phase = 33;
452 // endless knob: skip 90deg highlights unless the value is really a multiple of 90deg
453 if (self->knob_type == 3 && !(phase % 16)) {
454 if (phase == 64)
455 phase = 0;
456 double nom = adj->lower + phase * (adj->upper - adj->lower) / 64.0;
457 double diff = (adj->value - nom) / (adj->upper - adj->lower);
458 if (diff > 0.0001)
459 phase = (phase + 1) % 64;
460 if (diff < -0.0001)
461 phase = (phase + 63) % 64;
463 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);
464 // printf("exposed %p %d+%d\n", widget->window, widget->allocation.x, widget->allocation.y);
465 if (gtk_widget_is_focus(widget))
467 gtk_paint_focus(widget->style, window, GTK_STATE_NORMAL, NULL, widget, NULL, ox, oy, 40, 40);
470 return TRUE;
473 static void
474 calf_knob_size_request (GtkWidget *widget,
475 GtkRequisition *requisition)
477 g_assert(CALF_IS_KNOB(widget));
479 // width/height is hardwired at 40px now
480 requisition->width = 40;
481 requisition->height = 40;
484 static void
485 calf_knob_incr (GtkWidget *widget, int dir_down)
487 g_assert(CALF_IS_KNOB(widget));
488 CalfKnob *self = CALF_KNOB(widget);
489 GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
491 int oldstep = (int)(0.5f + (adj->value - adj->lower) / adj->step_increment);
492 int step;
493 int nsteps = (int)(0.5f + (adj->upper - adj->lower) / adj->step_increment); // less 1 actually
494 if (dir_down)
495 step = oldstep - 1;
496 else
497 step = oldstep + 1;
498 if (self->knob_type == 3 && step >= nsteps)
499 step %= nsteps;
500 if (self->knob_type == 3 && step < 0)
501 step = nsteps - (nsteps - step) % nsteps;
503 // trying to reduce error cumulation here, by counting from lowest or from highest
504 float value = adj->lower + step * double(adj->upper - adj->lower) / nsteps;
505 gtk_range_set_value(GTK_RANGE(widget), value);
506 // printf("step %d:%d nsteps %d value %f:%f\n", oldstep, step, nsteps, oldvalue, value);
509 static gboolean
510 calf_knob_key_press (GtkWidget *widget, GdkEventKey *event)
512 g_assert(CALF_IS_KNOB(widget));
513 CalfKnob *self = CALF_KNOB(widget);
514 GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
516 switch(event->keyval)
518 case GDK_Home:
519 gtk_range_set_value(GTK_RANGE(widget), adj->lower);
520 return TRUE;
522 case GDK_End:
523 gtk_range_set_value(GTK_RANGE(widget), adj->upper);
524 return TRUE;
526 case GDK_Up:
527 calf_knob_incr(widget, 0);
528 return TRUE;
530 case GDK_Down:
531 calf_knob_incr(widget, 1);
532 return TRUE;
534 case GDK_Shift_L:
535 case GDK_Shift_R:
536 self->start_value = gtk_range_get_value(GTK_RANGE(widget));
537 self->start_y = self->last_y;
538 return TRUE;
541 return FALSE;
544 static gboolean
545 calf_knob_key_release (GtkWidget *widget, GdkEventKey *event)
547 g_assert(CALF_IS_KNOB(widget));
548 CalfKnob *self = CALF_KNOB(widget);
550 if(event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R)
552 self->start_value = gtk_range_get_value(GTK_RANGE(widget));
553 self->start_y = self->last_y;
554 return TRUE;
557 return FALSE;
560 static gboolean
561 calf_knob_button_press (GtkWidget *widget, GdkEventButton *event)
563 g_assert(CALF_IS_KNOB(widget));
564 CalfKnob *self = CALF_KNOB(widget);
566 // CalfKnob *lg = CALF_KNOB(widget);
567 gtk_widget_grab_focus(widget);
568 gtk_grab_add(widget);
569 self->start_x = event->x;
570 self->start_y = event->y;
571 self->start_value = gtk_range_get_value(GTK_RANGE(widget));
573 return TRUE;
576 static gboolean
577 calf_knob_button_release (GtkWidget *widget, GdkEventButton *event)
579 g_assert(CALF_IS_KNOB(widget));
581 if (GTK_WIDGET_HAS_GRAB(widget))
582 gtk_grab_remove(widget);
583 return FALSE;
586 static inline float endless(float value)
588 if (value >= 0)
589 return fmod(value, 1.f);
590 else
591 return fmod(1.f - fmod(1.f - value, 1.f), 1.f);
594 static inline float deadzone(float value, float incr, float scale)
596 float dzw = 10 / scale;
597 if (value >= 0.501)
598 value += dzw;
599 if (value < 0.499)
600 value -= dzw;
602 value += incr;
604 if (value >= (0.5 - dzw) && value <= (0.5 + dzw))
605 return 0.5;
606 if (value < 0.5)
607 return value + dzw;
608 return value - dzw;
611 static gboolean
612 calf_knob_pointer_motion (GtkWidget *widget, GdkEventMotion *event)
614 g_assert(CALF_IS_KNOB(widget));
615 CalfKnob *self = CALF_KNOB(widget);
617 float scale = (event->state & GDK_SHIFT_MASK) ? 1000 : 100;
618 gboolean moved = FALSE;
620 if (GTK_WIDGET_HAS_GRAB(widget))
622 if (self->knob_type == 3)
624 gtk_range_set_value(GTK_RANGE(widget), endless(self->start_value - (event->y - self->start_y) / scale));
626 else
627 if (self->knob_type == 1)
629 gtk_range_set_value(GTK_RANGE(widget), deadzone(self->start_value, -(event->y - self->start_y) / scale, scale));
631 else
633 gtk_range_set_value(GTK_RANGE(widget), self->start_value - (event->y - self->start_y) / scale);
635 moved = TRUE;
637 self->last_y = event->y;
638 return moved;
641 static gboolean
642 calf_knob_scroll (GtkWidget *widget, GdkEventScroll *event)
644 calf_knob_incr(widget, event->direction);
645 return TRUE;
648 static void
649 calf_knob_class_init (CalfKnobClass *klass)
651 // GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
652 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
653 widget_class->expose_event = calf_knob_expose;
654 widget_class->size_request = calf_knob_size_request;
655 widget_class->button_press_event = calf_knob_button_press;
656 widget_class->button_release_event = calf_knob_button_release;
657 widget_class->motion_notify_event = calf_knob_pointer_motion;
658 widget_class->key_press_event = calf_knob_key_press;
659 widget_class->key_release_event = calf_knob_key_release;
660 widget_class->scroll_event = calf_knob_scroll;
661 GError *error = NULL;
662 klass->knob_image = gdk_pixbuf_new_from_file(PKGLIBDIR "/knob.png", &error);
663 g_assert(klass->knob_image != NULL);
666 static void
667 calf_knob_init (CalfKnob *self)
669 GtkWidget *widget = GTK_WIDGET(self);
670 GTK_WIDGET_SET_FLAGS (GTK_WIDGET(self), GTK_CAN_FOCUS);
671 widget->requisition.width = 40;
672 widget->requisition.height = 40;
675 GtkWidget *
676 calf_knob_new()
678 GtkAdjustment *adj = (GtkAdjustment *)gtk_adjustment_new(0, 0, 1, 0.01, 0.5, 0);
679 return calf_knob_new_with_adjustment(adj);
682 static gboolean calf_knob_value_changed(gpointer obj)
684 GtkWidget *widget = (GtkWidget *)obj;
685 gtk_widget_queue_draw(widget);
686 return FALSE;
689 GtkWidget *calf_knob_new_with_adjustment(GtkAdjustment *_adjustment)
691 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_KNOB, NULL ));
692 if (widget) {
693 gtk_range_set_adjustment(GTK_RANGE(widget), _adjustment);
694 gtk_signal_connect(GTK_OBJECT(widget), "value-changed", G_CALLBACK(calf_knob_value_changed), widget);
696 return widget;
699 GType
700 calf_knob_get_type (void)
702 static GType type = 0;
703 if (!type) {
705 static const GTypeInfo type_info = {
706 sizeof(CalfKnobClass),
707 NULL, /* base_init */
708 NULL, /* base_finalize */
709 (GClassInitFunc)calf_knob_class_init,
710 NULL, /* class_finalize */
711 NULL, /* class_data */
712 sizeof(CalfKnob),
713 0, /* n_preallocs */
714 (GInstanceInitFunc)calf_knob_init
717 for (int i = 0; ; i++) {
718 char *name = g_strdup_printf("CalfKnob%u%d",
719 ((unsigned int)(intptr_t)calf_knob_class_init) >> 16, i);
720 if (g_type_from_name(name)) {
721 free(name);
722 continue;
724 type = g_type_register_static(GTK_TYPE_RANGE,
725 name,
726 &type_info,
727 (GTypeFlags)0);
728 free(name);
729 break;
732 return type;