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>
25 #if !defined(__APPLE__)
33 ///////////////////////////////////////// knob ///////////////////////////////////////////////
36 calf_knob_expose (GtkWidget
*widget
, GdkEventExpose
*event
)
38 g_assert(CALF_IS_KNOB(widget
));
40 float widths
[6] = {0, 2, 4, 4.5, 4.5, 5.5};
41 float margins
[6] = {0, 2, 3, 5.5, 5, 7.5};
42 float pins_m
[6] = {0, 4, 8, 12, 11, 21};
43 float pins_s
[6] = {0, 3, 4, 4, 4, 5};
45 CalfKnob
*self
= CALF_KNOB(widget
);
46 GtkAdjustment
*adj
= gtk_range_get_adjustment(GTK_RANGE(widget
));
47 cairo_t
*ctx
= gdk_cairo_create(GDK_DRAWABLE(widget
->window
));
49 int ox
= widget
->allocation
.x
, oy
= widget
->allocation
.y
;
50 ox
+= (widget
->allocation
.width
- self
->knob_size
* 20) / 2;
51 oy
+= (widget
->allocation
.height
- self
->knob_size
* 20) / 2;
52 int size
= self
->knob_size
* 20;
54 int from
= self
->knob_type
== 3 ? 270 : 135;
55 int to
= self
->knob_type
== 3 ? -90 : 45;
56 int phase
= (adj
->value
- adj
->lower
) * 270 / (adj
->upper
- adj
->lower
) + 135;
61 cairo_rectangle(ctx
, ox
, oy
, size
+ size
/ 2, size
+ size
/ 2);
64 switch (self
->knob_type
) {
72 if (adj
->value
< 0.5) {
75 phase
= (adj
->value
- adj
->lower
) * 270 / (adj
->upper
- adj
->lower
) -225;
88 phase
= (adj
->value
- adj
->lower
) * 360 / (adj
->upper
- adj
->lower
) + -90;
93 if (self
->knob_type
== 1 && phase
== 270) {
94 double pt
= (adj
->value
- adj
->lower
) * 2.0 / (adj
->upper
- adj
->lower
) - 1.0;
101 static const double dash
[] = {2, 1};
102 cairo_set_dash(ctx
, dash
, 2, 0);
103 cairo_set_line_width(ctx
, widths
[self
->knob_size
]);
106 gdk_draw_pixbuf(GDK_DRAWABLE(widget
->window
),
107 widget
->style
->fg_gc
[0],
108 CALF_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget
))->knob_image
[self
->knob_size
- 1],
110 gdk_pixbuf_get_width(CALF_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget
))->knob_image
[self
->knob_size
- 1]),
111 gdk_pixbuf_get_height(CALF_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget
))->knob_image
[self
->knob_size
- 1]),
112 GDK_RGB_DITHER_NORMAL
, 0, 0);
116 cairo_arc_negative (ctx
, ox
+ rad
, oy
+ rad
, rad
- margins
[self
->knob_size
], from
* (M_PI
/ 180.), to
* (M_PI
/ 180.));
118 cairo_arc (ctx
, ox
+ rad
, oy
+ rad
, rad
- margins
[self
->knob_size
], from
* (M_PI
/ 180.), to
* (M_PI
/ 180.));
119 cairo_set_source_rgb(ctx
, 0, 0.1, 0.1);
123 float pos1
= (rad
- margins
[self
->knob_size
] + widths
[self
->knob_size
] / 2.) / rad
;
124 float pos2
= (rad
- margins
[self
->knob_size
]) / rad
;
125 float pos3
= (rad
- margins
[self
->knob_size
] - widths
[self
->knob_size
] / 2.) / rad
;
126 cairo_pattern_t
*pat
= cairo_pattern_create_radial(ox
+ rad
, oy
+ rad
, 0, ox
+ rad
, oy
+ rad
, rad
);
127 cairo_pattern_add_color_stop_rgba(pat
, pos1
, 0, 0.9, 1, 0.75);
128 cairo_pattern_add_color_stop_rgba(pat
, pos2
, 0, 1, 1, 1.);
129 cairo_pattern_add_color_stop_rgba(pat
, pos3
, 0, 0.9, 1, 0.75);
130 cairo_set_source(ctx
, pat
);
132 cairo_arc_negative (ctx
, ox
+ rad
, oy
+ rad
, rad
- margins
[self
->knob_size
], start
* (M_PI
/ 180.), phase
* (M_PI
/ 180.));
134 cairo_arc (ctx
, ox
+ rad
, oy
+ rad
, rad
- margins
[self
->knob_size
], start
* (M_PI
/ 180.), phase
* (M_PI
/ 180.));
138 float x
= ox
+ rad
+ (rad
- margins
[self
->knob_size
]) * cos(phase
* (M_PI
/ 180.));
139 float y
= oy
+ rad
+ (rad
- margins
[self
->knob_size
]) * sin(phase
* (M_PI
/ 180.));
141 cairo_arc_negative (ctx
, ox
+ rad
, oy
+ rad
, rad
- margins
[self
->knob_size
], from
* (M_PI
/ 180.), to
* (M_PI
/ 180.));
143 cairo_arc (ctx
, ox
+ rad
, oy
+ rad
, rad
- margins
[self
->knob_size
], from
* (M_PI
/ 180.), to
* (M_PI
/ 180.));
144 pat
= cairo_pattern_create_radial(x
, y
, widths
[self
->knob_size
] / 2, x
, y
, widths
[self
->knob_size
]);
145 cairo_pattern_add_color_stop_rgba(pat
, 0, 1, 1, 1, 1);
146 cairo_pattern_add_color_stop_rgba(pat
, 1, 0, 0.5, 0.8, 0.);
147 cairo_set_source(ctx
, pat
);
151 cairo_rectangle(ctx
, ox
, oy
, size
, size
);
152 pat
= cairo_pattern_create_radial(x
, y
, 0, x
, y
, widths
[self
->knob_size
] * 1.5);
153 cairo_pattern_add_color_stop_rgba(pat
, 0, 0.8, 1, 1, 0.7);
154 cairo_pattern_add_color_stop_rgba(pat
, 1, 0, 0.75, 1, 0.);
155 cairo_set_source(ctx
, pat
);
160 cairo_arc_negative (ctx
, ox
+ rad
, oy
+ rad
, rad
- margins
[self
->knob_size
] + widths
[self
->knob_size
], start
* (M_PI
/ 180.), phase
* (M_PI
/ 180.));
162 cairo_arc (ctx
, ox
+ rad
, oy
+ rad
, rad
- margins
[self
->knob_size
], start
* (M_PI
/ 180.), phase
* (M_PI
/ 180.));
163 pos1
= (rad
- margins
[self
->knob_size
] + widths
[self
->knob_size
]) / rad
;
164 pos2
= (rad
- margins
[self
->knob_size
]) / rad
;
165 pos3
= (rad
- margins
[self
->knob_size
] - widths
[self
->knob_size
]) / rad
;
166 pat
= cairo_pattern_create_radial(ox
+ rad
, oy
+ rad
, 0, ox
+ rad
, oy
+ rad
, rad
);
167 cairo_pattern_add_color_stop_rgba(pat
, pos1
, 0, 1, 1, 0.);
168 cairo_pattern_add_color_stop_rgba(pat
, pos2
, 0.8, 1, 1, 0.6);
169 cairo_pattern_add_color_stop_rgba(pat
, pos3
, 0, 1, 1, 0.);
170 cairo_set_source(ctx
, pat
);
171 cairo_set_line_width(ctx
, widths
[self
->knob_size
] * 2.);
175 cairo_pattern_destroy(pat
);
178 float x1
= ox
+ rad
+ (rad
- pins_m
[self
->knob_size
]) * cos(phase
* (M_PI
/ 180.));
179 float y1
= oy
+ rad
+ (rad
- pins_m
[self
->knob_size
]) * sin(phase
* (M_PI
/ 180.));
180 float x2
= ox
+ rad
+ (rad
- pins_s
[self
->knob_size
] - pins_m
[self
->knob_size
]) * cos(phase
* (M_PI
/ 180.));
181 float y2
= oy
+ rad
+ (rad
- pins_s
[self
->knob_size
] - pins_m
[self
->knob_size
]) * sin(phase
* (M_PI
/ 180.));
182 cairo_move_to(ctx
, x1
, y1
);
183 cairo_line_to(ctx
, x2
, y2
);
184 cairo_set_dash(ctx
, dash
, 0, 0);
186 cairo_set_source_rgba(ctx
, col
, col
, col
,0.5);
187 cairo_set_line_width(ctx
, widths
[self
->knob_size
] / 2.);
195 calf_knob_size_request (GtkWidget
*widget
,
196 GtkRequisition
*requisition
)
198 g_assert(CALF_IS_KNOB(widget
));
200 CalfKnob
*self
= CALF_KNOB(widget
);
202 // width/height is hardwired at 40px now
203 // is now chooseable by "size" value in XML (1-4)
204 requisition
->width
= 20 * self
->knob_size
;
205 requisition
->height
= 20 * self
->knob_size
;
209 calf_knob_incr (GtkWidget
*widget
, int dir_down
)
211 g_assert(CALF_IS_KNOB(widget
));
212 CalfKnob
*self
= CALF_KNOB(widget
);
213 GtkAdjustment
*adj
= gtk_range_get_adjustment(GTK_RANGE(widget
));
215 int oldstep
= (int)(0.5f
+ (adj
->value
- adj
->lower
) / adj
->step_increment
);
217 int nsteps
= (int)(0.5f
+ (adj
->upper
- adj
->lower
) / adj
->step_increment
); // less 1 actually
222 if (self
->knob_type
== 3 && step
>= nsteps
)
224 if (self
->knob_type
== 3 && step
< 0)
225 step
= nsteps
- (nsteps
- step
) % nsteps
;
227 // trying to reduce error cumulation here, by counting from lowest or from highest
228 float value
= adj
->lower
+ step
* double(adj
->upper
- adj
->lower
) / nsteps
;
229 gtk_range_set_value(GTK_RANGE(widget
), value
);
230 // printf("step %d:%d nsteps %d value %f:%f\n", oldstep, step, nsteps, oldvalue, value);
234 calf_knob_key_press (GtkWidget
*widget
, GdkEventKey
*event
)
236 g_assert(CALF_IS_KNOB(widget
));
237 CalfKnob
*self
= CALF_KNOB(widget
);
238 GtkAdjustment
*adj
= gtk_range_get_adjustment(GTK_RANGE(widget
));
240 switch(event
->keyval
)
243 gtk_range_set_value(GTK_RANGE(widget
), adj
->lower
);
247 gtk_range_set_value(GTK_RANGE(widget
), adj
->upper
);
251 calf_knob_incr(widget
, 0);
255 calf_knob_incr(widget
, 1);
260 self
->start_value
= gtk_range_get_value(GTK_RANGE(widget
));
261 self
->start_y
= self
->last_y
;
269 calf_knob_key_release (GtkWidget
*widget
, GdkEventKey
*event
)
271 g_assert(CALF_IS_KNOB(widget
));
272 CalfKnob
*self
= CALF_KNOB(widget
);
274 if(event
->keyval
== GDK_Shift_L
|| event
->keyval
== GDK_Shift_R
)
276 self
->start_value
= gtk_range_get_value(GTK_RANGE(widget
));
277 self
->start_y
= self
->last_y
;
285 calf_knob_button_press (GtkWidget
*widget
, GdkEventButton
*event
)
287 g_assert(CALF_IS_KNOB(widget
));
288 CalfKnob
*self
= CALF_KNOB(widget
);
290 if (event
->type
== GDK_2BUTTON_PRESS
) {
291 gtk_range_set_value(GTK_RANGE(widget
), self
->default_value
);
294 // CalfKnob *lg = CALF_KNOB(widget);
295 gtk_widget_grab_focus(widget
);
296 gtk_grab_add(widget
);
297 self
->start_x
= event
->x
;
298 self
->last_y
= self
->start_y
= event
->y
;
299 self
->start_value
= gtk_range_get_value(GTK_RANGE(widget
));
305 calf_knob_button_release (GtkWidget
*widget
, GdkEventButton
*event
)
307 g_assert(CALF_IS_KNOB(widget
));
309 if (GTK_WIDGET_HAS_GRAB(widget
))
310 gtk_grab_remove(widget
);
314 static inline float endless(float value
)
317 return fmod(value
, 1.f
);
319 return fmod(1.f
- fmod(1.f
- value
, 1.f
), 1.f
);
322 static inline float deadzone(GtkWidget
*widget
, float value
, float incr
)
331 float nv
= ov
+ incr
;
341 calf_knob_pointer_motion (GtkWidget
*widget
, GdkEventMotion
*event
)
343 g_assert(CALF_IS_KNOB(widget
));
344 CalfKnob
*self
= CALF_KNOB(widget
);
346 float scale
= (event
->state
& GDK_SHIFT_MASK
) ? 2500 : 250;
347 gboolean moved
= FALSE
;
349 if (GTK_WIDGET_HAS_GRAB(widget
))
351 if (self
->knob_type
== 3)
353 gtk_range_set_value(GTK_RANGE(widget
), endless(self
->start_value
- (event
->y
- self
->start_y
) / scale
));
356 if (self
->knob_type
== 1)
358 gtk_range_set_value(GTK_RANGE(widget
), deadzone(GTK_WIDGET(widget
), self
->start_value
, -(event
->y
- self
->start_y
) / scale
));
362 gtk_range_set_value(GTK_RANGE(widget
), self
->start_value
- (event
->y
- self
->start_y
) / scale
);
366 self
->last_y
= event
->y
;
371 calf_knob_scroll (GtkWidget
*widget
, GdkEventScroll
*event
)
373 calf_knob_incr(widget
, event
->direction
);
378 calf_knob_class_init (CalfKnobClass
*klass
)
380 // GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
381 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS(klass
);
382 widget_class
->expose_event
= calf_knob_expose
;
383 widget_class
->size_request
= calf_knob_size_request
;
384 widget_class
->button_press_event
= calf_knob_button_press
;
385 widget_class
->button_release_event
= calf_knob_button_release
;
386 widget_class
->motion_notify_event
= calf_knob_pointer_motion
;
387 widget_class
->key_press_event
= calf_knob_key_press
;
388 widget_class
->key_release_event
= calf_knob_key_release
;
389 widget_class
->scroll_event
= calf_knob_scroll
;
390 GError
*error
= NULL
;
391 klass
->knob_image
[0] = gdk_pixbuf_new_from_file(PKGLIBDIR
"/knob1.png", &error
);
392 klass
->knob_image
[1] = gdk_pixbuf_new_from_file(PKGLIBDIR
"/knob2.png", &error
);
393 klass
->knob_image
[2] = gdk_pixbuf_new_from_file(PKGLIBDIR
"/knob3.png", &error
);
394 klass
->knob_image
[3] = gdk_pixbuf_new_from_file(PKGLIBDIR
"/knob4.png", &error
);
395 klass
->knob_image
[4] = gdk_pixbuf_new_from_file(PKGLIBDIR
"/knob5.png", &error
);
396 g_assert(klass
->knob_image
!= NULL
);
400 calf_knob_init (CalfKnob
*self
)
402 GtkWidget
*widget
= GTK_WIDGET(self
);
403 GTK_WIDGET_SET_FLAGS (GTK_WIDGET(self
), GTK_CAN_FOCUS
);
404 widget
->requisition
.width
= 40;
405 widget
->requisition
.height
= 40;
411 GtkAdjustment
*adj
= (GtkAdjustment
*)gtk_adjustment_new(0, 0, 1, 0.01, 0.5, 0);
412 return calf_knob_new_with_adjustment(adj
);
415 static gboolean
calf_knob_value_changed(gpointer obj
)
417 GtkWidget
*widget
= (GtkWidget
*)obj
;
418 gtk_widget_queue_draw(widget
);
422 GtkWidget
*calf_knob_new_with_adjustment(GtkAdjustment
*_adjustment
)
424 GtkWidget
*widget
= GTK_WIDGET( g_object_new (CALF_TYPE_KNOB
, NULL
));
426 gtk_range_set_adjustment(GTK_RANGE(widget
), _adjustment
);
427 g_signal_connect(GTK_OBJECT(widget
), "value-changed", G_CALLBACK(calf_knob_value_changed
), widget
);
433 calf_knob_get_type (void)
435 static GType type
= 0;
438 static const GTypeInfo type_info
= {
439 sizeof(CalfKnobClass
),
440 NULL
, /* base_init */
441 NULL
, /* base_finalize */
442 (GClassInitFunc
)calf_knob_class_init
,
443 NULL
, /* class_finalize */
444 NULL
, /* class_data */
447 (GInstanceInitFunc
)calf_knob_init
450 for (int i
= 0; ; i
++) {
451 char *name
= g_strdup_printf("CalfKnob%u%d",
452 ((unsigned int)(intptr_t)calf_knob_class_init
) >> 16, i
);
453 if (g_type_from_name(name
)) {
457 type
= g_type_register_static(GTK_TYPE_RANGE
,