3 * Copyright (C) 2007-2010 Krzysztof Foltman, Torben Hohn, Markus Schmidt
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
22 #include <calf/ctl_knob.h>
23 #include <gdk/gdkkeysyms.h>
24 #include <cairo/cairo.h>
30 ///////////////////////////////////////// knob ///////////////////////////////////////////////
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;
55 // endless knob: skip 90deg highlights unless the value is really a multiple of 90deg
56 if (self
->knob_type
== 3 && !(phase
% 16)) {
59 double nom
= adj
->lower
+ phase
* (adj
->upper
- adj
->lower
) / 64.0;
60 double diff
= (adj
->value
- nom
) / (adj
->upper
- adj
->lower
);
62 phase
= (phase
+ 1) % 64;
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);
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
;
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
);
99 int nsteps
= (int)(0.5f
+ (adj
->upper
- adj
->lower
) / adj
->step_increment
); // less 1 actually
104 if (self
->knob_type
== 3 && 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);
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
)
125 gtk_range_set_value(GTK_RANGE(widget
), adj
->lower
);
129 gtk_range_set_value(GTK_RANGE(widget
), adj
->upper
);
133 calf_knob_incr(widget
, 0);
137 calf_knob_incr(widget
, 1);
142 self
->start_value
= gtk_range_get_value(GTK_RANGE(widget
));
143 self
->start_y
= self
->last_y
;
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
;
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
));
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
);
197 static inline float endless(float value
)
200 return fmod(value
, 1.f
);
202 return fmod(1.f
- fmod(1.f
- value
, 1.f
), 1.f
);
205 static inline float deadzone(GtkWidget
*widget
, float value
, float incr
)
214 float nv
= ov
+ incr
;
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
));
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
));
245 gtk_range_set_value(GTK_RANGE(widget
), self
->start_value
- (event
->y
- self
->start_y
) / scale
);
249 self
->last_y
= event
->y
;
254 calf_knob_scroll (GtkWidget
*widget
, GdkEventScroll
*event
)
256 calf_knob_incr(widget
, event
->direction
);
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
);
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;
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
);
305 GtkWidget
*calf_knob_new_with_adjustment(GtkAdjustment
*_adjustment
)
307 GtkWidget
*widget
= GTK_WIDGET( g_object_new (CALF_TYPE_KNOB
, NULL
));
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
);
316 calf_knob_get_type (void)
318 static GType type
= 0;
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 */
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
)) {
340 type
= g_type_register_static(GTK_TYPE_RANGE
,