more gtk_signal_connect -> g_signal_connect
[calf.git] / src / ctl_knob.cpp
blobe362b24359a166ee46544356b354443e60c01c9f
1 /* Calf DSP Library
2 * Knob control.
3 * Copyright (C) 2007-2010 Krzysztof Foltman, Torben Hohn, Markus Schmidt
4 * and others
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA
21 #include "config.h"
22 #include <calf/ctl_knob.h>
23 #include <gdk/gdkkeysyms.h>
24 #include <cairo/cairo.h>
25 #include <malloc.h>
26 #include <math.h>
27 #include <stdint.h>
28 #include <gdk/gdk.h>
30 ///////////////////////////////////////// knob ///////////////////////////////////////////////
32 static gboolean
33 calf_knob_expose (GtkWidget *widget, GdkEventExpose *event)
35 g_assert(CALF_IS_KNOB(widget));
37 CalfKnob *self = CALF_KNOB(widget);
38 GdkWindow *window = widget->window;
39 GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
41 // printf("adjustment = %p value = %f\n", adj, adj->value);
42 int ox = widget->allocation.x, oy = widget->allocation.y;
43 ox += (widget->allocation.width - self->knob_size * 20) / 2;
44 oy += (widget->allocation.height - self->knob_size * 20) / 2;
46 int phase = (int)((adj->value - adj->lower) * 64 / (adj->upper - adj->lower));
47 // skip middle phase except for true middle value
48 if (self->knob_type == 1 && phase == 32) {
49 double pt = (adj->value - adj->lower) * 2.0 / (adj->upper - adj->lower) - 1.0;
50 if (pt < 0)
51 phase = 31;
52 if (pt > 0)
53 phase = 33;
55 // endless knob: skip 90deg highlights unless the value is really a multiple of 90deg
56 if (self->knob_type == 3 && !(phase % 16)) {
57 if (phase == 64)
58 phase = 0;
59 double nom = adj->lower + phase * (adj->upper - adj->lower) / 64.0;
60 double diff = (adj->value - nom) / (adj->upper - adj->lower);
61 if (diff > 0.0001)
62 phase = (phase + 1) % 64;
63 if (diff < -0.0001)
64 phase = (phase + 63) % 64;
66 gdk_draw_pixbuf(GDK_DRAWABLE(widget->window), widget->style->fg_gc[0], CALF_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget))->knob_image[self->knob_size - 1], phase * self->knob_size * 20, self->knob_type * self->knob_size * 20, ox, oy, self->knob_size * 20, self->knob_size * 20, GDK_RGB_DITHER_NORMAL, 0, 0);
67 // printf("exposed %p %d+%d\n", widget->window, widget->allocation.x, widget->allocation.y);
68 if (gtk_widget_is_focus(widget))
70 gtk_paint_focus(widget->style, window, GTK_STATE_NORMAL, NULL, widget, NULL, ox, oy, self->knob_size * 20, self->knob_size * 20);
73 return TRUE;
76 static void
77 calf_knob_size_request (GtkWidget *widget,
78 GtkRequisition *requisition)
80 g_assert(CALF_IS_KNOB(widget));
82 CalfKnob *self = CALF_KNOB(widget);
84 // width/height is hardwired at 40px now
85 // is now chooseable by "size" value in XML (1-4)
86 requisition->width = 20 * self->knob_size;
87 requisition->height = 20 * self->knob_size;
90 static void
91 calf_knob_incr (GtkWidget *widget, int dir_down)
93 g_assert(CALF_IS_KNOB(widget));
94 CalfKnob *self = CALF_KNOB(widget);
95 GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
97 int oldstep = (int)(0.5f + (adj->value - adj->lower) / adj->step_increment);
98 int step;
99 int nsteps = (int)(0.5f + (adj->upper - adj->lower) / adj->step_increment); // less 1 actually
100 if (dir_down)
101 step = oldstep - 1;
102 else
103 step = oldstep + 1;
104 if (self->knob_type == 3 && step >= nsteps)
105 step %= nsteps;
106 if (self->knob_type == 3 && step < 0)
107 step = nsteps - (nsteps - step) % nsteps;
109 // trying to reduce error cumulation here, by counting from lowest or from highest
110 float value = adj->lower + step * double(adj->upper - adj->lower) / nsteps;
111 gtk_range_set_value(GTK_RANGE(widget), value);
112 // printf("step %d:%d nsteps %d value %f:%f\n", oldstep, step, nsteps, oldvalue, value);
115 static gboolean
116 calf_knob_key_press (GtkWidget *widget, GdkEventKey *event)
118 g_assert(CALF_IS_KNOB(widget));
119 CalfKnob *self = CALF_KNOB(widget);
120 GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
122 switch(event->keyval)
124 case GDK_Home:
125 gtk_range_set_value(GTK_RANGE(widget), adj->lower);
126 return TRUE;
128 case GDK_End:
129 gtk_range_set_value(GTK_RANGE(widget), adj->upper);
130 return TRUE;
132 case GDK_Up:
133 calf_knob_incr(widget, 0);
134 return TRUE;
136 case GDK_Down:
137 calf_knob_incr(widget, 1);
138 return TRUE;
140 case GDK_Shift_L:
141 case GDK_Shift_R:
142 self->start_value = gtk_range_get_value(GTK_RANGE(widget));
143 self->start_y = self->last_y;
144 return TRUE;
147 return FALSE;
150 static gboolean
151 calf_knob_key_release (GtkWidget *widget, GdkEventKey *event)
153 g_assert(CALF_IS_KNOB(widget));
154 CalfKnob *self = CALF_KNOB(widget);
156 if(event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R)
158 self->start_value = gtk_range_get_value(GTK_RANGE(widget));
159 self->start_y = self->last_y;
160 return TRUE;
163 return FALSE;
166 static gboolean
167 calf_knob_button_press (GtkWidget *widget, GdkEventButton *event)
169 g_assert(CALF_IS_KNOB(widget));
170 CalfKnob *self = CALF_KNOB(widget);
172 if (event->type == GDK_2BUTTON_PRESS) {
173 gtk_range_set_value(GTK_RANGE(widget), self->default_value);
176 // CalfKnob *lg = CALF_KNOB(widget);
177 gtk_widget_grab_focus(widget);
178 gtk_grab_add(widget);
179 self->start_x = event->x;
180 self->last_y = self->start_y = event->y;
181 self->start_value = gtk_range_get_value(GTK_RANGE(widget));
183 return TRUE;
186 static gboolean
187 calf_knob_button_release (GtkWidget *widget, GdkEventButton *event)
189 g_assert(CALF_IS_KNOB(widget));
190 CalfKnob *self = CALF_KNOB(widget);
192 if (GTK_WIDGET_HAS_GRAB(widget))
193 gtk_grab_remove(widget);
194 return FALSE;
197 static inline float endless(float value)
199 if (value >= 0)
200 return fmod(value, 1.f);
201 else
202 return fmod(1.f - fmod(1.f - value, 1.f), 1.f);
205 static inline float deadzone(GtkWidget *widget, float value, float incr)
207 // map to dead zone
208 float ov = value;
209 if (ov > 0.5)
210 ov = 0.1 + ov;
211 if (ov < 0.5)
212 ov = ov - 0.1;
214 float nv = ov + incr;
216 if (nv > 0.6)
217 return nv - 0.1;
218 if (nv < 0.4)
219 return nv + 0.1;
220 return 0.5;
223 static gboolean
224 calf_knob_pointer_motion (GtkWidget *widget, GdkEventMotion *event)
226 g_assert(CALF_IS_KNOB(widget));
227 CalfKnob *self = CALF_KNOB(widget);
229 float scale = (event->state & GDK_SHIFT_MASK) ? 1000 : 100;
230 gboolean moved = FALSE;
232 if (GTK_WIDGET_HAS_GRAB(widget))
234 if (self->knob_type == 3)
236 gtk_range_set_value(GTK_RANGE(widget), endless(self->start_value - (event->y - self->start_y) / scale));
238 else
239 if (self->knob_type == 1)
241 gtk_range_set_value(GTK_RANGE(widget), deadzone(GTK_WIDGET(widget), self->start_value, -(event->y - self->start_y) / scale));
243 else
245 gtk_range_set_value(GTK_RANGE(widget), self->start_value - (event->y - self->start_y) / scale);
247 moved = TRUE;
249 self->last_y = event->y;
250 return moved;
253 static gboolean
254 calf_knob_scroll (GtkWidget *widget, GdkEventScroll *event)
256 calf_knob_incr(widget, event->direction);
257 return TRUE;
260 static void
261 calf_knob_class_init (CalfKnobClass *klass)
263 // GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
264 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
265 widget_class->expose_event = calf_knob_expose;
266 widget_class->size_request = calf_knob_size_request;
267 widget_class->button_press_event = calf_knob_button_press;
268 widget_class->button_release_event = calf_knob_button_release;
269 widget_class->motion_notify_event = calf_knob_pointer_motion;
270 widget_class->key_press_event = calf_knob_key_press;
271 widget_class->key_release_event = calf_knob_key_release;
272 widget_class->scroll_event = calf_knob_scroll;
273 GError *error = NULL;
274 klass->knob_image[0] = gdk_pixbuf_new_from_file(PKGLIBDIR "/knob1.png", &error);
275 klass->knob_image[1] = gdk_pixbuf_new_from_file(PKGLIBDIR "/knob2.png", &error);
276 klass->knob_image[2] = gdk_pixbuf_new_from_file(PKGLIBDIR "/knob3.png", &error);
277 klass->knob_image[3] = gdk_pixbuf_new_from_file(PKGLIBDIR "/knob4.png", &error);
278 klass->knob_image[4] = gdk_pixbuf_new_from_file(PKGLIBDIR "/knob5.png", &error);
279 g_assert(klass->knob_image != NULL);
282 static void
283 calf_knob_init (CalfKnob *self)
285 GtkWidget *widget = GTK_WIDGET(self);
286 GTK_WIDGET_SET_FLAGS (GTK_WIDGET(self), GTK_CAN_FOCUS);
287 widget->requisition.width = 40;
288 widget->requisition.height = 40;
291 GtkWidget *
292 calf_knob_new()
294 GtkAdjustment *adj = (GtkAdjustment *)gtk_adjustment_new(0, 0, 1, 0.01, 0.5, 0);
295 return calf_knob_new_with_adjustment(adj);
298 static gboolean calf_knob_value_changed(gpointer obj)
300 GtkWidget *widget = (GtkWidget *)obj;
301 gtk_widget_queue_draw(widget);
302 return FALSE;
305 GtkWidget *calf_knob_new_with_adjustment(GtkAdjustment *_adjustment)
307 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_KNOB, NULL ));
308 if (widget) {
309 gtk_range_set_adjustment(GTK_RANGE(widget), _adjustment);
310 g_signal_connect(GTK_OBJECT(widget), "value-changed", G_CALLBACK(calf_knob_value_changed), widget);
312 return widget;
315 GType
316 calf_knob_get_type (void)
318 static GType type = 0;
319 if (!type) {
321 static const GTypeInfo type_info = {
322 sizeof(CalfKnobClass),
323 NULL, /* base_init */
324 NULL, /* base_finalize */
325 (GClassInitFunc)calf_knob_class_init,
326 NULL, /* class_finalize */
327 NULL, /* class_data */
328 sizeof(CalfKnob),
329 0, /* n_preallocs */
330 (GInstanceInitFunc)calf_knob_init
333 for (int i = 0; ; i++) {
334 char *name = g_strdup_printf("CalfKnob%u%d",
335 ((unsigned int)(intptr_t)calf_knob_class_init) >> 16, i);
336 if (g_type_from_name(name)) {
337 free(name);
338 continue;
340 type = g_type_register_static(GTK_TYPE_RANGE,
341 name,
342 &type_info,
343 (GTypeFlags)0);
344 free(name);
345 break;
348 return type;