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 <calf/drawingutils.h>
24 #include <gdk/gdkkeysyms.h>
25 #include <cairo/cairo.h>
26 #if !defined(__APPLE__)
36 #define range01(tick) std::min(1., std::max(0., tick))
38 ///////////////////////////////////////// knob ///////////////////////////////////////////////
41 calf_knob_get_color (CalfKnob
*self
, float deg
, float phase
, float start
, float last
, float tickw
)
45 //printf ("get color: phase %.2f deg %.2f\n", phase, deg);
46 if (self
->type
== 0) {
48 if (deg
> phase
or phase
== start
)
52 if (self
->type
== 1) {
54 if (deg
> 270 and deg
<= phase
and phase
> 270)
56 if (deg
<= 270 and deg
> phase
and phase
< 270)
58 if ((deg
== start
and phase
== start
)
59 or (deg
== 270. and phase
> 270.))
63 if (self
->type
== 2) {
65 if (deg
> phase
or phase
== start
)
69 if (self
->type
== 3) {
70 for (unsigned j
= 0; j
< self
->ticks
.size(); j
++) {
71 float tp
= fmod((start
+ range01(self
->ticks
[j
]) * 360.) - phase
+ 360, 360);
72 if (tp
> 360 - tickw
or tp
< tickw
) {
76 if (deg
> phase
and deg
> last
+ tickw
and last
< phase
)
87 calf_knob_expose (GtkWidget
*widget
, GdkEventExpose
*event
)
89 g_assert(CALF_IS_KNOB(widget
));
90 CalfKnob
*self
= CALF_KNOB(widget
);
91 CalfKnobClass
*cls
= CALF_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget
));
92 GdkPixbuf
*pixbuf
= cls
->knob_image
[self
->size
- 1];
93 gint iw
= gdk_pixbuf_get_width(pixbuf
);
94 gint ih
= gdk_pixbuf_get_height(pixbuf
);
96 float widths
[6] = {0, 2.5, 3.5, 3.5, 4.2, 5.5};
97 float margins
[6] = {0, 2.2, 3.5, 3.8, 4.2, 4.5};
98 float pins_m
[6] = {0, 6, 10, 10, 11, 13};
99 float pins_s
[6] = {0, 3, 4, 4, 4, 4};
101 GtkAdjustment
*adj
= gtk_range_get_adjustment(GTK_RANGE(widget
));
102 cairo_t
*ctx
= gdk_cairo_create(GDK_DRAWABLE(widget
->window
));
105 get_fg_color(widget
, NULL
, &r
, &g
, &b
);
107 double ox
= widget
->allocation
.x
+ (widget
->allocation
.width
- iw
) / 2;
108 double oy
= widget
->allocation
.y
+ (widget
->allocation
.height
- ih
) / 2;
110 float rad
= size
/ 2;
111 double xc
= ox
+ rad
;
112 double yc
= oy
+ rad
;
125 double lwidth
= widths
[self
->size
];
126 double lmarg
= margins
[self
->size
];
127 double perim
= (rad
- lmarg
) * 2 * M_PI
;
128 double tickw
= 2. / perim
* 360.;
129 double tickw2
= tickw
/ 2.;
131 const unsigned int debug
= 0;
133 cairo_rectangle(ctx
, ox
, oy
, size
+ size
/ 2, size
+ size
/ 2);
137 gdk_draw_pixbuf(GDK_DRAWABLE(widget
->window
), widget
->style
->fg_gc
[0], pixbuf
,
138 0, 0, ox
, oy
, iw
, ih
, GDK_RGB_DITHER_NORMAL
, 0, 0);
140 switch (self
->type
) {
172 phase
= (adj
->value
- adj
->lower
) * base
/ (adj
->upper
- adj
->lower
) + start
;
175 float x1
= ox
+ rad
+ (rad
- pins_m
[self
->size
]) * cos(phase
* (M_PI
/ 180.));
176 float y1
= oy
+ rad
+ (rad
- pins_m
[self
->size
]) * sin(phase
* (M_PI
/ 180.));
177 float x2
= ox
+ rad
+ (rad
- pins_s
[self
->size
] - pins_m
[self
->size
]) * cos(phase
* (M_PI
/ 180.));
178 float y2
= oy
+ rad
+ (rad
- pins_s
[self
->size
] - pins_m
[self
->size
]) * sin(phase
* (M_PI
/ 180.));
179 cairo_move_to(ctx
, x1
, y1
);
180 cairo_line_to(ctx
, x2
, y2
);
181 cairo_set_source_rgba(ctx
, r
, g
, b
, 0.99);
182 cairo_set_line_width(ctx
, lwidth
/ 2.);
185 cairo_set_line_width(ctx
, lwidth
);
187 // draw ticks and rings
188 unsigned int evsize
= 4;
189 double events
[evsize
] = { start
, zero
, end
, phase
};
192 std::sort(events
, events
+ evsize
);
194 printf("start %.2f end %.2f last %.2f deg %.2f tick %d ticks %d phase %.2f base %.2f nend %.2f\n", start
, end
, last
, deg
, tick
, int(self
->ticks
.size()), phase
, base
, nend
);
195 for (unsigned int i
= 0; i
< self
->ticks
.size(); i
++) {
196 printf("tick %d %.2f\n", i
, self
->ticks
[i
]);
200 if (debug
) printf("tick %d deg %.2f last %.2f end %.2f\n", tick
, deg
, last
, end
);
201 if (self
->ticks
.size() and deg
== start
+ range01(self
->ticks
[tick
]) * base
) {
202 // seems we want to draw a tick on this angle.
203 // so we have to fill the void between the last set angle
204 // and the point directly before the tick first.
205 // (draw from last known angle to tickw2 + tickw before actual deg)
206 if (last
< deg
- tickw
- tickw2
) {
207 opac
= calf_knob_get_color(self
, (deg
- tickw
- tickw2
), phase
, start
, last
, tickw
+ tickw2
);
208 cairo_set_source_rgba(ctx
, r
, g
, b
, opac
);
209 cairo_arc(ctx
, xc
, yc
, rad
- lmarg
, last
* (M_PI
/ 180.), std::max(last
, std::min(nend
, (deg
- tickw
- tickw2
))) * (M_PI
/ 180.));
211 if (debug
) printf("fill from %.2f to %.2f @ %.2f\n", last
, (deg
- tickw
- tickw2
), opac
);
213 // draw the tick itself
214 opac
= calf_knob_get_color(self
, deg
, phase
, start
, end
, tickw
+ tickw2
);
215 cairo_set_source_rgba(ctx
, r
, g
, b
, opac
);
216 cairo_arc(ctx
, xc
, yc
, rad
- lmarg
, (deg
- tickw2
) * (M_PI
/ 180.), (deg
+ tickw2
) * (M_PI
/ 180.));
218 if (debug
) printf("tick from %.2f to %.2f @ %.2f\n", (deg
- tickw2
), (deg
+ tickw2
), opac
);
219 // set last known angle to deg plus tickw + tickw2
220 last
= deg
+ tickw
+ tickw2
;
223 // remember the next ticks void end
224 if (tick
< self
->ticks
.size())
225 nend
= range01(self
->ticks
[tick
]) * base
+ start
- tickw
- tickw2
;
229 // seems we want to fill a gap between the last event and
230 // the actual one, while the actual one isn't a tick (but a
231 // knobs position or a center)
233 opac
= calf_knob_get_color(self
, deg
, phase
, start
, last
, tickw
+ tickw2
);
234 cairo_set_source_rgba(ctx
, r
, g
, b
, opac
);
235 cairo_arc(ctx
, xc
, yc
, rad
- lmarg
, last
* (M_PI
/ 180.), std::min(nend
, std::max(last
, deg
)) * (M_PI
/ 180.));
237 if (debug
) printf("void from %.2f to %.2f @ %.2f\n", last
, std::min(nend
, std::max(last
, deg
)), opac
);
243 // set deg to next event
244 for (unsigned int i
= 0; i
< evsize
; i
++) {
245 if (debug
> 1) printf("checking %.2f (start %.2f zero %.2f phase %.2f end %.2f)\n", events
[i
], start
, zero
, phase
, end
);
246 if (events
[i
] > deg
) {
248 if (debug
> 1) printf("taken.\n");
252 if (tick
< self
->ticks
.size()) {
253 deg
= std::min(deg
, start
+ range01(self
->ticks
[tick
]) * base
);
254 if (debug
> 1) printf("checking tick %d %.2f\n", tick
, start
+ range01(self
->ticks
[tick
]) * base
);
256 //deg = std::max(last, deg);
257 if (debug
> 1) printf("finally! deg %.2f\n", deg
);
259 if (debug
) printf("\n");
265 calf_knob_size_request (GtkWidget
*widget
,
266 GtkRequisition
*requisition
)
268 g_assert(CALF_IS_KNOB(widget
));
270 CalfKnob
*self
= CALF_KNOB(widget
);
272 CalfKnobClass
* cls
= CALF_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget
));
273 requisition
->width
= gdk_pixbuf_get_width(cls
->knob_image
[self
->size
- 1]);
274 requisition
->height
= gdk_pixbuf_get_height(cls
->knob_image
[self
->size
- 1]);
277 static gboolean
calf_knob_enter (GtkWidget
*widget
, GdkEventCrossing
* ev
)
279 if (gtk_widget_get_state(widget
) == GTK_STATE_NORMAL
) {
280 gtk_widget_set_state(widget
, GTK_STATE_PRELIGHT
);
281 gtk_widget_queue_draw(widget
);
286 static gboolean
calf_knob_leave (GtkWidget
*widget
, GdkEventCrossing
*ev
)
288 if (gtk_widget_get_state(widget
) == GTK_STATE_PRELIGHT
) {
289 gtk_widget_set_state(widget
, GTK_STATE_NORMAL
);
290 gtk_widget_queue_draw(widget
);
296 calf_knob_incr (GtkWidget
*widget
, int dir_down
)
298 g_assert(CALF_IS_KNOB(widget
));
299 CalfKnob
*self
= CALF_KNOB(widget
);
300 GtkAdjustment
*adj
= gtk_range_get_adjustment(GTK_RANGE(widget
));
302 int oldstep
= (int)(0.5f
+ (adj
->value
- adj
->lower
) / adj
->step_increment
);
304 int nsteps
= (int)(0.5f
+ (adj
->upper
- adj
->lower
) / adj
->step_increment
); // less 1 actually
309 if (self
->type
== 3 && step
>= nsteps
)
311 if (self
->type
== 3 && step
< 0)
312 step
= nsteps
- (nsteps
- step
) % nsteps
;
314 // trying to reduce error cumulation here, by counting from lowest or from highest
315 float value
= adj
->lower
+ step
* double(adj
->upper
- adj
->lower
) / nsteps
;
316 gtk_range_set_value(GTK_RANGE(widget
), value
);
317 // printf("step %d:%d nsteps %d value %f:%f\n", oldstep, step, nsteps, oldvalue, value);
321 calf_knob_key_press (GtkWidget
*widget
, GdkEventKey
*event
)
323 g_assert(CALF_IS_KNOB(widget
));
324 CalfKnob
*self
= CALF_KNOB(widget
);
325 GtkAdjustment
*adj
= gtk_range_get_adjustment(GTK_RANGE(widget
));
326 gtk_widget_set_state(widget
, GTK_STATE_ACTIVE
);
327 gtk_widget_queue_draw(widget
);
328 switch(event
->keyval
)
331 gtk_range_set_value(GTK_RANGE(widget
), adj
->lower
);
335 gtk_range_set_value(GTK_RANGE(widget
), adj
->upper
);
339 calf_knob_incr(widget
, 0);
343 calf_knob_incr(widget
, 1);
348 self
->start_value
= gtk_range_get_value(GTK_RANGE(widget
));
349 self
->start_y
= self
->last_y
;
357 calf_knob_key_release (GtkWidget
*widget
, GdkEventKey
*event
)
359 g_assert(CALF_IS_KNOB(widget
));
360 CalfKnob
*self
= CALF_KNOB(widget
);
362 if(event
->keyval
== GDK_Shift_L
|| event
->keyval
== GDK_Shift_R
)
364 self
->start_value
= gtk_range_get_value(GTK_RANGE(widget
));
365 self
->start_y
= self
->last_y
;
368 gtk_widget_set_state(widget
, GTK_STATE_NORMAL
);
369 gtk_widget_queue_draw(widget
);
374 calf_knob_button_press (GtkWidget
*widget
, GdkEventButton
*event
)
376 g_assert(CALF_IS_KNOB(widget
));
377 CalfKnob
*self
= CALF_KNOB(widget
);
379 if (event
->type
== GDK_2BUTTON_PRESS
) {
380 gtk_range_set_value(GTK_RANGE(widget
), self
->default_value
);
383 // CalfKnob *lg = CALF_KNOB(widget);
384 gtk_widget_grab_focus(widget
);
385 gtk_grab_add(widget
);
386 self
->start_x
= event
->x
;
387 self
->last_y
= self
->start_y
= event
->y
;
388 self
->start_value
= gtk_range_get_value(GTK_RANGE(widget
));
389 gtk_widget_set_state(widget
, GTK_STATE_ACTIVE
);
390 gtk_widget_queue_draw(widget
);
395 calf_knob_button_release (GtkWidget
*widget
, GdkEventButton
*event
)
397 g_assert(CALF_IS_KNOB(widget
));
399 if (GTK_WIDGET_HAS_GRAB(widget
))
400 gtk_grab_remove(widget
);
401 gtk_widget_set_state(widget
, GTK_STATE_NORMAL
);
402 gtk_widget_queue_draw(widget
);
406 static inline float endless(float value
)
409 return fmod(value
, 1.f
);
411 return fmod(1.f
- fmod(1.f
- value
, 1.f
), 1.f
);
414 static inline float deadzone(GtkWidget
*widget
, float value
, float incr
)
423 float nv
= ov
+ incr
;
433 calf_knob_pointer_motion (GtkWidget
*widget
, GdkEventMotion
*event
)
435 g_assert(CALF_IS_KNOB(widget
));
436 CalfKnob
*self
= CALF_KNOB(widget
);
438 float scale
= (event
->state
& GDK_SHIFT_MASK
) ? 2500 : 250;
439 gboolean moved
= FALSE
;
441 if (GTK_WIDGET_HAS_GRAB(widget
))
445 gtk_range_set_value(GTK_RANGE(widget
), endless(self
->start_value
- (event
->y
- self
->start_y
) / scale
));
450 gtk_range_set_value(GTK_RANGE(widget
), deadzone(GTK_WIDGET(widget
), self
->start_value
, -(event
->y
- self
->start_y
) / scale
));
454 gtk_range_set_value(GTK_RANGE(widget
), self
->start_value
- (event
->y
- self
->start_y
) / scale
);
458 self
->last_y
= event
->y
;
463 calf_knob_scroll (GtkWidget
*widget
, GdkEventScroll
*event
)
465 calf_knob_incr(widget
, event
->direction
);
470 calf_knob_class_init (CalfKnobClass
*klass
)
472 // GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
473 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS(klass
);
474 widget_class
->expose_event
= calf_knob_expose
;
475 widget_class
->size_request
= calf_knob_size_request
;
476 widget_class
->enter_notify_event
= calf_knob_enter
;
477 widget_class
->leave_notify_event
= calf_knob_leave
;
478 widget_class
->button_press_event
= calf_knob_button_press
;
479 widget_class
->button_release_event
= calf_knob_button_release
;
480 widget_class
->motion_notify_event
= calf_knob_pointer_motion
;
481 widget_class
->key_press_event
= calf_knob_key_press
;
482 widget_class
->key_release_event
= calf_knob_key_release
;
483 widget_class
->scroll_event
= calf_knob_scroll
;
484 GError
*error
= NULL
;
485 klass
->knob_image
[0] = gdk_pixbuf_new_from_file(PKGLIBDIR
"/knob1.png", &error
);
486 klass
->knob_image
[1] = gdk_pixbuf_new_from_file(PKGLIBDIR
"/knob2.png", &error
);
487 klass
->knob_image
[2] = gdk_pixbuf_new_from_file(PKGLIBDIR
"/knob3.png", &error
);
488 klass
->knob_image
[3] = gdk_pixbuf_new_from_file(PKGLIBDIR
"/knob4.png", &error
);
489 klass
->knob_image
[4] = gdk_pixbuf_new_from_file(PKGLIBDIR
"/knob5.png", &error
);
490 g_assert(klass
->knob_image
!= NULL
);
494 calf_knob_init (CalfKnob
*self
)
496 GtkWidget
*widget
= GTK_WIDGET(self
);
497 GTK_WIDGET_SET_FLAGS (GTK_WIDGET(self
), GTK_CAN_FOCUS
);
498 widget
->requisition
.width
= 40;
499 widget
->requisition
.height
= 40;
505 GtkAdjustment
*adj
= (GtkAdjustment
*)gtk_adjustment_new(0, 0, 1, 0.01, 0.5, 0);
506 return calf_knob_new_with_adjustment(adj
);
509 static gboolean
calf_knob_value_changed(gpointer obj
)
511 GtkWidget
*widget
= (GtkWidget
*)obj
;
512 gtk_widget_queue_draw(widget
);
516 GtkWidget
*calf_knob_new_with_adjustment(GtkAdjustment
*_adjustment
)
518 GtkWidget
*widget
= GTK_WIDGET( g_object_new (CALF_TYPE_KNOB
, NULL
));
520 gtk_range_set_adjustment(GTK_RANGE(widget
), _adjustment
);
521 g_signal_connect(GTK_OBJECT(widget
), "value-changed", G_CALLBACK(calf_knob_value_changed
), widget
);
527 calf_knob_get_type (void)
529 static GType type
= 0;
532 static const GTypeInfo type_info
= {
533 sizeof(CalfKnobClass
),
534 NULL
, /* base_init */
535 NULL
, /* base_finalize */
536 (GClassInitFunc
)calf_knob_class_init
,
537 NULL
, /* class_finalize */
538 NULL
, /* class_data */
541 (GInstanceInitFunc
)calf_knob_init
544 for (int i
= 0; ; i
++) {
545 char *name
= g_strdup_printf("CalfKnob%u%d",
546 ((unsigned int)(intptr_t)calf_knob_class_init
) >> 16, i
);
547 if (g_type_from_name(name
)) {
551 type
= g_type_register_static(GTK_TYPE_RANGE
,