1 /*
2 
3     This widget provides knobs
4 
5     (c) Fraser Stuart 2009
6 
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11 
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with this program; if not, write to the Free Software
19     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 */
22 
23 #include "stdlib.h"
24 #include "math.h"
25 #include "string.h"
26 #include "widgets.h"
27 #include "knob.h"
28 #include "knob-img_small.xpm"
29 #include "knob-img_medium.xpm"
30 #include "knob-img_large.xpm"
31 
32 
33 static void 	inv_knob_class_init(InvKnobClass *klass);
34 static void 	inv_knob_init(InvKnob *knob);
35 static void 	inv_knob_size_request(GtkWidget *widget, GtkRequisition *requisition);
36 static void 	inv_knob_size_allocate(GtkWidget *widget, GtkAllocation *allocation);
37 static void 	inv_knob_realize(GtkWidget *widget);
38 static gboolean inv_knob_expose(GtkWidget *widget,GdkEventExpose *event);
39 static void 	inv_knob_paint(GtkWidget *widget, gint mode);
40 static void	inv_knob_destroy(GtkObject *object);
41 static gboolean	inv_knob_button_press_event (GtkWidget *widget, GdkEventButton *event);
42 static gboolean	inv_knob_motion_notify_event(GtkWidget *widget, GdkEventMotion *event);
43 static gboolean inv_knob_button_release_event (GtkWidget *widget, GdkEventButton *event);
44 
45 static void	inv_knob_label(gint mode, char *label, char *units, gint human, float value);
46 static void	inv_knob_label_pan(char *label, float value, float min, float max);
47 static float	inv_knob_label_set_dp(float value);
48 static float    inv_marking_to_value(float mark, gint curve, float min, float max);
49 static float	inv_value_to_angle(float value, gint curve, float min, float max);
50 static float	inv_value_from_motion(float x_delta, float y_delta, float current, gint curve, float min, float max);
51 
52 
53 GtkType
inv_knob_get_type(void)54 inv_knob_get_type(void)
55 {
56 	static GType inv_knob_type = 0;
57 	char *name;
58 	int i;
59 
60 
61 	if (!inv_knob_type)
62 	{
63 		static const GTypeInfo type_info = {
64 			sizeof(InvKnobClass),
65 			NULL, /* base_init */
66 			NULL, /* base_finalize */
67 			(GClassInitFunc)inv_knob_class_init,
68 			NULL, /* class_finalize */
69 			NULL, /* class_data */
70 			sizeof(InvKnob),
71 			0,    /* n_preallocs */
72 			(GInstanceInitFunc)inv_knob_init
73 		};
74 		for (i = 0; ; i++) {
75 
76 			name = g_strdup_printf("InvKnob-%p-%d", inv_knob_class_init, i);
77 			if (g_type_from_name(name)) {
78 				free(name);
79 				continue;
80 			}
81 			inv_knob_type = g_type_register_static(GTK_TYPE_WIDGET,name,&type_info,(GTypeFlags)0);
82 			free(name);
83 			break;
84 		}
85 	}
86 	return inv_knob_type;
87 }
88 
89 void
inv_knob_set_bypass(InvKnob * knob,gint num)90 inv_knob_set_bypass(InvKnob *knob, gint num)
91 {
92 	knob->bypass = num;
93 }
94 
95 void
inv_knob_set_size(InvKnob * knob,gint num)96 inv_knob_set_size(InvKnob *knob, gint num)
97 {
98 	knob->size = num;
99 }
100 
101 void
inv_knob_set_curve(InvKnob * knob,gint num)102 inv_knob_set_curve(InvKnob *knob, gint num)
103 {
104 	knob->curve = num;
105 }
106 
107 void
inv_knob_set_markings(InvKnob * knob,gint num)108 inv_knob_set_markings(InvKnob *knob, gint num)
109 {
110 	knob->markings = num;
111 }
112 
113 void
inv_knob_set_custom(InvKnob * knob,gint pos,char * label)114 inv_knob_set_custom(InvKnob *knob, gint pos, char *label)
115 {
116 	switch(pos) {
117 		case 0:
118 			strncpy(knob->clow, label, 9);
119 			break;
120 		case 1:
121 			strncpy(knob->cmid, label, 9);
122 			break;
123 		case 2:
124 			strncpy(knob->chigh, label, 9);
125 			break;
126 	}
127 }
128 
129 void
inv_knob_set_highlight(InvKnob * knob,gint num)130 inv_knob_set_highlight(InvKnob *knob, gint num)
131 {
132 	knob->highlight = num;
133 }
134 
135 void
inv_knob_set_human(InvKnob * knob)136 inv_knob_set_human(InvKnob *knob)
137 {
138 	knob->human=1;
139 }
140 
141 
142 void
inv_knob_set_units(InvKnob * knob,char * units)143 inv_knob_set_units(InvKnob *knob, char *units)
144 {
145 	strncpy(knob->units, units, 4);
146 }
147 
148 void
inv_knob_set_min(InvKnob * knob,float num)149 inv_knob_set_min(InvKnob *knob, float num)
150 {
151 	knob->min = num;
152 }
153 
154 void
inv_knob_set_max(InvKnob * knob,float num)155 inv_knob_set_max(InvKnob *knob, float num)
156 {
157 	knob->max = num;
158 }
159 
160 void
inv_knob_set_value(InvKnob * knob,float num)161 inv_knob_set_value(InvKnob *knob, float num)
162 {
163 	if(num < knob->min)
164 		knob->value = knob->min;
165 	else if(num > knob->max)
166 		knob->value = knob->min;
167 	else
168 		knob->value = num;
169 	if(knob->value != knob->lastvalue) {
170 		if(GTK_WIDGET_REALIZED(knob))
171 			inv_knob_paint(GTK_WIDGET(knob),INV_KNOB_DRAW_DATA);
172 	}
173 }
174 
175 void
inv_knob_set_tooltip(InvKnob * knob,gchar * tip)176 inv_knob_set_tooltip(InvKnob *knob, gchar *tip)
177 {
178 	gtk_widget_set_tooltip_markup(GTK_WIDGET(knob),tip);
179 }
180 
181 float
inv_knob_get_value(InvKnob * knob)182 inv_knob_get_value(InvKnob *knob)
183 {
184 	return knob->value;
185 }
186 
inv_knob_new()187 GtkWidget * inv_knob_new()
188 {
189 	return GTK_WIDGET(gtk_type_new(inv_knob_get_type()));
190 }
191 
192 
193 static void
inv_knob_class_init(InvKnobClass * klass)194 inv_knob_class_init(InvKnobClass *klass)
195 {
196 	GtkWidgetClass *widget_class;
197 	GtkObjectClass *object_class;
198 
199 
200 	widget_class = (GtkWidgetClass *) klass;
201 	object_class = (GtkObjectClass *) klass;
202 
203 	widget_class->realize = inv_knob_realize;
204 	widget_class->size_request = inv_knob_size_request;
205 	widget_class->size_allocate = inv_knob_size_allocate;
206 	widget_class->expose_event = inv_knob_expose;
207 
208     	widget_class->button_press_event = inv_knob_button_press_event;
209     	widget_class->motion_notify_event = inv_knob_motion_notify_event;
210     	widget_class->button_release_event = inv_knob_button_release_event;
211 
212 	object_class->destroy = inv_knob_destroy;
213 }
214 
215 
216 static void
inv_knob_init(InvKnob * knob)217 inv_knob_init(InvKnob *knob)
218 {
219 	knob->bypass    = INV_PLUGIN_ACTIVE;
220 	knob->size      = INV_KNOB_SIZE_MEDIUM;
221 	knob->curve     = INV_KNOB_CURVE_LINEAR;
222 	knob->markings  = INV_KNOB_MARKINGS_5;
223 	knob->highlight = INV_KNOB_HIGHLIGHT_L;
224 	strcpy(knob->units,"");
225 	strcpy(knob->clow,"");
226 	strcpy(knob->cmid,"");
227 	strcpy(knob->chigh,"");
228 	knob->human 	= 0;
229 	knob->min       = 0.0;
230 	knob->max       = 1.0;
231 	knob->value     = 0.5;
232 	knob->lastvalue = 0.5;
233 	knob->click_x	=0;
234 	knob->click_y	=0;
235 
236      	knob->img_small=gdk_pixbuf_new_from_xpm_data((const char **)knob_img_small_xpm);
237      	knob->img_med=gdk_pixbuf_new_from_xpm_data((const char **)knob_img_medium_xpm);
238      	knob->img_large=gdk_pixbuf_new_from_xpm_data((const char **)knob_img_large_xpm);
239 
240 	knob->font_size=0;
241 
242     	GTK_WIDGET_SET_FLAGS (GTK_WIDGET(knob), GTK_CAN_FOCUS);
243 }
244 
245 static void
inv_knob_size_request(GtkWidget * widget,GtkRequisition * requisition)246 inv_knob_size_request(GtkWidget *widget,
247     GtkRequisition *requisition)
248 {
249 	g_return_if_fail(widget != NULL);
250 	g_return_if_fail(INV_IS_KNOB(widget));
251 	g_return_if_fail(requisition != NULL);
252 
253 	requisition->width  = INV_KNOB (widget)->size+4;
254 	switch(INV_KNOB (widget)->size) {
255 		case INV_KNOB_SIZE_SMALL:
256 			requisition->height = INV_KNOB (widget)->size+50;
257 			break;
258 		case INV_KNOB_SIZE_MEDIUM:
259 			requisition->height = INV_KNOB (widget)->size+56;
260 			break;
261 		case INV_KNOB_SIZE_LARGE:
262 		default:
263 			requisition->height = INV_KNOB (widget)->size+62;
264 			break;
265 	}
266 
267 }
268 
269 
270 static void
inv_knob_size_allocate(GtkWidget * widget,GtkAllocation * allocation)271 inv_knob_size_allocate(GtkWidget *widget,
272     GtkAllocation *allocation)
273 {
274 	g_return_if_fail(widget != NULL);
275 	g_return_if_fail(INV_IS_KNOB(widget));
276 	g_return_if_fail(allocation != NULL);
277 
278 	widget->allocation = *allocation;
279 
280 	if (GTK_WIDGET_REALIZED(widget)) {
281 		gdk_window_move_resize(
282 		   widget->window,
283 		   allocation->x, allocation->y,
284 		   allocation->width, allocation->height
285 		);
286 	}
287 }
288 
289 
290 static void
inv_knob_realize(GtkWidget * widget)291 inv_knob_realize(GtkWidget *widget)
292 {
293 	GdkWindowAttr attributes;
294 	guint attributes_mask;
295 
296 	g_return_if_fail(widget != NULL);
297 	g_return_if_fail(INV_IS_KNOB(widget));
298 
299 	GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
300 
301 	attributes.window_type = GDK_WINDOW_CHILD;
302 	attributes.x = widget->allocation.x;
303 	attributes.y = widget->allocation.y;
304 	attributes.width = INV_KNOB (widget)->size + 4;
305 	switch(INV_KNOB (widget)->size) {
306 		case INV_KNOB_SIZE_SMALL:
307 			attributes.height = INV_KNOB (widget)->size + 50;
308 			break;
309 		case INV_KNOB_SIZE_MEDIUM:
310 			attributes.height = INV_KNOB (widget)->size + 56;
311 			break;
312 		case INV_KNOB_SIZE_LARGE:
313 		default:
314 			attributes.height = INV_KNOB (widget)->size + 62;
315 			break;
316 	}
317 
318 
319 	attributes.wclass = GDK_INPUT_OUTPUT;
320 	attributes.event_mask = gtk_widget_get_events(widget) |
321 				GDK_EXPOSURE_MASK |
322 				GDK_BUTTON_PRESS_MASK |
323 				GDK_BUTTON_RELEASE_MASK |
324 				GDK_BUTTON_MOTION_MASK;
325 
326 	attributes_mask = GDK_WA_X | GDK_WA_Y;
327 
328 	widget->window = gdk_window_new(
329 	  gtk_widget_get_parent_window (widget),
330 	  & attributes, attributes_mask
331 	);
332 
333 	gdk_window_set_user_data(widget->window, widget);
334 
335 	widget->style = gtk_style_attach(widget->style, widget->window);
336 	gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
337 }
338 
339 
340 static gboolean
inv_knob_expose(GtkWidget * widget,GdkEventExpose * event)341 inv_knob_expose(GtkWidget *widget, GdkEventExpose *event)
342 {
343 	g_return_val_if_fail(widget != NULL, FALSE);
344 	g_return_val_if_fail(INV_IS_KNOB(widget), FALSE);
345 	g_return_val_if_fail(event != NULL, FALSE);
346 
347 	inv_knob_paint(widget,INV_KNOB_DRAW_ALL);
348 
349 	return FALSE;
350 }
351 
352 
353 static void
inv_knob_paint(GtkWidget * widget,gint mode)354 inv_knob_paint(GtkWidget *widget, gint mode)
355 {
356 	cairo_t		*cr;
357 	gint  		bypass;
358 	gint  		size;
359 	gint  		curve;
360 	gint  		markings;
361 	gint  		highlight;
362 	gint  		human;
363 	char		*units;
364 	char		*clow;
365 	char		*cmid;
366 	char		*chigh;
367 	float		min;
368 	float		max;
369 	float 		value,lastvalue;
370 	GdkPixbuf 	*img;
371 	GtkStateType	state;
372 	GtkStyle	*style;
373 	cairo_pattern_t *pat;
374 
375 	gint i;
376 	float xc,yc,r,ll,ls,tb,angle;
377 	gint fontheight;
378 	char label[20];
379 	cairo_text_extents_t extents;
380 
381 	cr = gdk_cairo_create(widget->window);
382 
383 	state = GTK_WIDGET_STATE(widget);
384 	style = gtk_widget_get_style(widget);
385 
386 	bypass = INV_KNOB(widget)->bypass;
387 	size = INV_KNOB(widget)->size;
388 	curve = INV_KNOB(widget)->curve;
389 	markings = INV_KNOB(widget)->markings;
390 	highlight = INV_KNOB(widget)->highlight;
391 	human = INV_KNOB(widget)->human;
392 	units = INV_KNOB(widget)->units;
393 	clow = INV_KNOB(widget)->clow;
394 	cmid = INV_KNOB(widget)->cmid;
395 	chigh = INV_KNOB(widget)->chigh;
396 	min = INV_KNOB(widget)->min;
397 	max = INV_KNOB(widget)->max;
398 	value = INV_KNOB(widget)->value;
399 	lastvalue = INV_KNOB(widget)->lastvalue;
400 
401 	xc=(size/2)+2;
402 	r=size/2;
403 	switch(size) {
404 		case INV_KNOB_SIZE_SMALL:
405 			yc=(size/2)+19;
406 			fontheight=5;
407 			ls=3;
408 			ll=7;
409 			tb=11;
410 			img = INV_KNOB(widget)->img_small;
411 			break;
412 		case INV_KNOB_SIZE_MEDIUM:
413 			yc=(size/2)+22;
414 			fontheight=6;
415 			ls=5;
416 			ll=9;
417 			tb=12;
418 			img = INV_KNOB(widget)->img_med;
419 			break;
420 		case INV_KNOB_SIZE_LARGE:
421 		default:
422 			yc=(size/2)+25;
423 			fontheight=7;
424 			ls=7;
425 			ll=11;
426 			tb=13;
427 			img = INV_KNOB(widget)->img_large;
428 			break;
429 	}
430 
431 	if(INV_KNOB(widget)->font_size==0) {
432 		INV_KNOB(widget)->font_size=inv_choose_font_size(cr,"sans-serif",CAIRO_FONT_SLANT_NORMAL,CAIRO_FONT_WEIGHT_NORMAL,99.0,(double)fontheight+0.1,"0");
433 	}
434 
435 	/* sanity check - ardour 2.7 doesn't initialise control values properly */
436 
437 	if(value < min)
438 		value=min;
439 	else if (value > max)
440 		value=max;
441 
442 	if(mode==INV_KNOB_DRAW_ALL) {
443 
444 		gdk_cairo_set_source_color(cr,&style->bg[GTK_STATE_NORMAL]);
445 		cairo_paint(cr);
446 		cairo_new_path(cr);
447 
448 		cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
449 
450 		if(inv_choose_light_dark(&style->bg[GTK_STATE_NORMAL],&style->light[GTK_STATE_NORMAL],&style->dark[GTK_STATE_NORMAL])==1) {
451 			gdk_cairo_set_source_color(cr,&style->light[GTK_STATE_NORMAL]);
452 		} else {
453 			gdk_cairo_set_source_color(cr,&style->dark[GTK_STATE_NORMAL]);
454 		}
455 
456 		if(markings==INV_KNOB_MARKINGS_3 || markings==INV_KNOB_MARKINGS_4 || markings==INV_KNOB_MARKINGS_5 || markings==INV_KNOB_MARKINGS_CUST12) {
457 			for(i=0;i<=12;i++)
458 			{
459 				cairo_move_to(cr,xc+(r-6)*sin((INV_PI/3)+(i*INV_PI/9)),yc+(r-6)*cos((INV_PI/3)+(i*INV_PI/9)));
460 				if(i==0 || i==12) {
461 					/* bottom L & R */
462 					cairo_line_to(cr,xc+r*sin((INV_PI/3)+(i*INV_PI/9)),yc+r*cos((INV_PI/3)+(i*INV_PI/9)));
463 					cairo_line_to(cr,xc+r*sin((INV_PI/3)+(i*INV_PI/9)),yc+r-2);
464 					cairo_set_line_width(cr,2);
465 					cairo_stroke(cr);
466 				} else if ((i==3 || i==9) && markings==INV_KNOB_MARKINGS_5) {
467 					/*top L & R for M5 */
468 					cairo_line_to(cr,xc+r*sin((INV_PI/3)+(i*INV_PI/9)),yc+r*cos((INV_PI/3)+(i*INV_PI/9)));
469 					cairo_line_to(cr,xc+r*sin((INV_PI/3)+(i*INV_PI/9)),yc-r+2);
470 					cairo_set_line_width(cr,2);
471 					cairo_stroke(cr);
472 				} else if ((i==4 || i==8) && markings==INV_KNOB_MARKINGS_4) {
473 					/*top L & R for M4 */
474 					cairo_line_to(cr,xc+(r+ls)*sin((INV_PI/3)+(i*INV_PI/9)),yc+(r+ls)*cos((INV_PI/3)+(i*INV_PI/9)));
475 					cairo_line_to(cr,xc+(r+ls)*sin((INV_PI/3)+(i*INV_PI/9)),yc+(r+ls)*cos((INV_PI/3)+(i*INV_PI/9))-(ls+1));
476 					cairo_set_line_width(cr,2);
477 					cairo_stroke(cr);
478 				} else if (i==6 && markings==INV_KNOB_MARKINGS_5) {
479 					/*top M5 */
480 					cairo_line_to(cr,xc+(r+ll)*sin((INV_PI/3)+(i*INV_PI/9)),yc+(r+ll)*cos((INV_PI/3)+(i*INV_PI/9)));
481 					cairo_set_line_width(cr,2);
482 					cairo_stroke(cr);
483 				} else if (i==6 && (markings==INV_KNOB_MARKINGS_3 || markings==INV_KNOB_MARKINGS_CUST12)){
484 					/*top M3 */
485 					cairo_line_to(cr,xc+(r+ls)*sin((INV_PI/3)+(i*INV_PI/9)),yc+(r+ls)*cos((INV_PI/3)+(i*INV_PI/9)));
486 					cairo_set_line_width(cr,2);
487 					cairo_stroke(cr);
488 				} else {
489 					/* all others */
490 					cairo_line_to(cr,xc+(r-2)*sin((INV_PI/3)+(i*INV_PI/9)),yc+(r-2)*cos((INV_PI/3)+(i*INV_PI/9)));
491 					cairo_set_line_width(cr,1.3);
492 					cairo_stroke(cr);
493 				}
494 			}
495 		} else { /* pan, cust10 & 10 */
496 			for(i=0;i<=10;i++)
497 			{
498 				cairo_move_to(cr,xc+(r-6)*sin((INV_PI/3)+(4*i*INV_PI/30)),yc+(r-6)*cos((INV_PI/3)+(4*i*INV_PI/30)));
499 				if(i==0 || i==10) {
500 					/* bottom L & R */
501 					cairo_line_to(cr,xc+r*sin((INV_PI/3)+(4*i*INV_PI/30)),yc+r*cos((INV_PI/3)+(4*i*INV_PI/30)));
502 					cairo_line_to(cr,xc+r*sin((INV_PI/3)+(4*i*INV_PI/30)),yc+r-2);
503 					cairo_set_line_width(cr,2);
504 					cairo_stroke(cr);
505 				} else if (i==5){
506 					/*top */
507 					cairo_line_to(cr,xc+(r+ls)*sin((INV_PI/3)+(4*i*INV_PI/30)),yc+(r+ls)*cos((INV_PI/3)+(4*i*INV_PI/30)));
508 					cairo_set_line_width(cr,2);
509 					cairo_stroke(cr);
510 				} else {
511 					/* all others */
512 					cairo_line_to(cr,xc+(r-2)*sin((INV_PI/3)+(4*i*INV_PI/30)),yc+(r-2)*cos((INV_PI/3)+(4*i*INV_PI/30)));
513 					cairo_set_line_width(cr,1.3);
514 					cairo_stroke(cr);
515 				}
516 			}
517 		}
518 
519 		cairo_select_font_face(cr,"sans-serif",CAIRO_FONT_SLANT_NORMAL,CAIRO_FONT_WEIGHT_NORMAL);
520 		cairo_set_font_size(cr,INV_KNOB(widget)->font_size);
521 		if(bypass==INV_PLUGIN_BYPASS) {
522 			gdk_cairo_set_source_color(cr,&style->fg[GTK_STATE_INSENSITIVE]);
523 		} else {
524 			gdk_cairo_set_source_color(cr,&style->fg[state]);
525 		}
526 
527 		/* bottom left */
528 		switch(markings)
529 		{
530 			case INV_KNOB_MARKINGS_PAN:
531 				switch(size)
532 				{
533 					case INV_KNOB_SIZE_MEDIUM:
534 					case INV_KNOB_SIZE_LARGE:
535 						strcpy(label,"Left");
536 						break;
537 					case INV_KNOB_SIZE_SMALL:
538 					default:
539 						strcpy(label,"L");
540 						break;
541 				}
542 				break;
543 
544 			case INV_KNOB_MARKINGS_CUST10:
545 			case INV_KNOB_MARKINGS_CUST12:
546 				strcpy(label,clow);
547 				break;
548 
549 			case INV_KNOB_MARKINGS_3:
550 			case INV_KNOB_MARKINGS_4:
551 			case INV_KNOB_MARKINGS_5:
552 			case INV_KNOB_MARKINGS_10:
553 				inv_knob_label(0,label, units,human, min);
554 				break;
555 		}
556 
557 		cairo_move_to(cr,1,yc+r+8);
558 		cairo_show_text(cr,label);
559 
560 		/* bottom right */
561 		switch(markings)
562 		{
563 			case INV_KNOB_MARKINGS_PAN:
564 				switch(size)
565 				{
566 					case INV_KNOB_SIZE_MEDIUM:
567 					case INV_KNOB_SIZE_LARGE:
568 						strcpy(label,"Right");
569 						break;
570 					case INV_KNOB_SIZE_SMALL:
571 					default:
572 						strcpy(label,"R");
573 						break;
574 				}
575 				break;
576 
577 			case INV_KNOB_MARKINGS_CUST10:
578 			case INV_KNOB_MARKINGS_CUST12:
579 				strcpy(label,chigh);
580 				break;
581 
582 			case INV_KNOB_MARKINGS_3:
583 			case INV_KNOB_MARKINGS_4:
584 			case INV_KNOB_MARKINGS_5:
585 			case INV_KNOB_MARKINGS_10:
586 				inv_knob_label(0,label, units, human, max);
587 				break;
588 		}
589 
590 		cairo_text_extents (cr,label,&extents);
591 		cairo_move_to(cr,size+1-extents.width,yc+r+8);
592 		cairo_show_text(cr,label);
593 
594 		/* top */
595 		if(markings != INV_KNOB_MARKINGS_4) {
596 			switch(markings)
597 			{
598 				case INV_KNOB_MARKINGS_PAN:
599 					strcpy(label,"Centre");
600 					break;
601 
602 				case INV_KNOB_MARKINGS_CUST10:
603 				case INV_KNOB_MARKINGS_CUST12:
604 					strcpy(label,cmid);
605 					break;
606 
607 				case INV_KNOB_MARKINGS_3:
608 				case INV_KNOB_MARKINGS_5:
609 				case INV_KNOB_MARKINGS_10:
610 					inv_knob_label(0,label, units, human, inv_marking_to_value(1.0/2.0, curve, min, max));
611 					break;
612 
613 			}
614 			cairo_text_extents (cr,label,&extents);
615 			switch(markings)
616 			{
617 				case INV_KNOB_MARKINGS_PAN:
618 				case INV_KNOB_MARKINGS_3:
619 				case INV_KNOB_MARKINGS_10:
620 				case INV_KNOB_MARKINGS_CUST10:
621 				case INV_KNOB_MARKINGS_CUST12:
622 					cairo_move_to(cr,xc-(extents.width/2)-1,(2*fontheight)+1);
623 					break;
624 				case INV_KNOB_MARKINGS_5:
625 					cairo_move_to(cr,xc-(extents.width/2)-1,fontheight+3);
626 					break;
627 			}
628 			cairo_show_text(cr,label);
629 		}
630 
631 		/* M5 top left */
632 		if(markings==INV_KNOB_MARKINGS_5) {
633 			inv_knob_label(0,label, units,human, inv_marking_to_value(1.0/4.0, curve, min, max));
634 			cairo_move_to(cr,1,yc-r-1);
635 			cairo_show_text(cr,label);
636 		}
637 
638 		/* M5 top right */
639 		if(markings==INV_KNOB_MARKINGS_5) {
640 			inv_knob_label(0,label, units,human, inv_marking_to_value(3.0/4.0, curve, min, max));
641 			cairo_text_extents (cr,label,&extents);
642 			cairo_move_to(cr,size+1-extents.width,yc-r-1);
643 			cairo_show_text(cr,label);
644 		}
645 
646 		/* M4 top left */
647 		if(markings==INV_KNOB_MARKINGS_4) {
648 			inv_knob_label(0,label, units,human, inv_marking_to_value(1.0/3.0, curve, min, max));
649 			cairo_move_to(cr,1,yc-r-(ls+1));
650 			cairo_show_text(cr,label);
651 		}
652 
653 		/* M4 top right */
654 		if(markings==INV_KNOB_MARKINGS_4) {
655 			inv_knob_label(0,label, units,human, inv_marking_to_value(2.0/3.0, curve, min, max));
656 			cairo_text_extents (cr,label,&extents);
657 			cairo_move_to(cr,size+1-extents.width,yc-r-(ls+1));
658 			cairo_show_text(cr,label);
659 		}
660 		cairo_new_path(cr);
661 
662 		cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
663 		cairo_set_antialias (cr,CAIRO_ANTIALIAS_NONE);
664 		cairo_set_line_width(cr,1);
665 
666 		gdk_cairo_set_source_color(cr,&style->dark[GTK_STATE_NORMAL]);
667 		cairo_move_to(cr, 3, yc+r+ll+tb+10);
668 		cairo_line_to(cr, 3, yc+r+ll+8);
669 		cairo_line_to(cr, 2*r+1, yc+r+ll+8);
670 		cairo_stroke(cr);
671 
672 		gdk_cairo_set_source_color(cr,&style->light[GTK_STATE_NORMAL]);
673 		cairo_move_to(cr, 3, yc+r+ll+tb+10);
674 		cairo_line_to(cr, 2*r+1, yc+r+ll+tb+10);
675 		cairo_line_to(cr, 2*r+1, yc+r+ll+8);
676 		cairo_stroke(cr);
677 
678 		cairo_set_antialias (cr,CAIRO_ANTIALIAS_DEFAULT);
679 		cairo_new_path(cr);
680 
681 	}
682 
683 	if(value!=lastvalue || mode==INV_KNOB_DRAW_ALL)
684 	{
685 		if(bypass==INV_PLUGIN_BYPASS) {
686 			gdk_cairo_set_source_color(cr,&style->base[GTK_STATE_INSENSITIVE]);
687 		} else {
688 			gdk_cairo_set_source_color(cr,&style->base[state]);
689 		}
690 		cairo_rectangle(cr, 4, yc+r+ll+9, 2*r-4, tb);
691 		cairo_fill(cr);
692 
693 		cairo_set_font_size(cr,INV_KNOB(widget)->font_size);
694 		if(bypass==INV_PLUGIN_BYPASS) {
695 			gdk_cairo_set_source_color(cr,&style->text[GTK_STATE_INSENSITIVE]);
696 		} else {
697 			gdk_cairo_set_source_color(cr,&style->text[state]);
698 		}
699 
700 		switch(markings) {
701 			case INV_KNOB_MARKINGS_3:
702 			case INV_KNOB_MARKINGS_4:
703 			case INV_KNOB_MARKINGS_5:
704 			case INV_KNOB_MARKINGS_10:
705 			case INV_KNOB_MARKINGS_CUST10:
706 			case INV_KNOB_MARKINGS_CUST12:
707 				inv_knob_label(1,label, units, human, value);
708 				break;
709 			case INV_KNOB_MARKINGS_PAN:
710 				inv_knob_label_pan(label, value, min, max);
711 				break;
712 		}
713 		cairo_text_extents (cr,label,&extents);
714 		cairo_move_to(cr,xc-(extents.width/2)-1,yc+r+ll+11-extents.y_bearing);
715 		cairo_show_text(cr,label);
716 
717 		cairo_new_path(cr);
718 
719 
720 		cairo_set_line_width(cr,1);
721 		gdk_cairo_set_source_color(cr,&style->bg[GTK_STATE_NORMAL]);
722 		cairo_arc(cr,xc,yc,r-7.5,0,2*INV_PI);
723 		cairo_stroke(cr);
724 
725 		cairo_save(cr);
726 
727 		angle=inv_value_to_angle(value,curve,min,max);
728 		cairo_translate(cr,xc,yc);
729 		cairo_rotate(cr,angle+0.03);
730 		cairo_arc(cr,0,0,r-9,0,2*INV_PI);
731 		cairo_clip(cr);
732 		gdk_cairo_set_source_pixbuf(cr,img, -(r-9), -(r-9));
733 		cairo_paint(cr);
734 
735 		cairo_restore(cr);
736 
737 		pat = cairo_pattern_create_linear (0.0, 0.0,  xc*2, yc*2);
738 		cairo_pattern_add_color_stop_rgba (pat, 0.0, 0.00, 0.00, 1.00, 1);
739 		cairo_pattern_add_color_stop_rgba (pat, 0.32, 0.91, 0.89, 0.83, 1);
740 		cairo_pattern_add_color_stop_rgba (pat, 0.5, 0.43, 0.32, 0.26, 1);
741 		cairo_pattern_add_color_stop_rgba (pat, 0.68, 0.10, 0.05, 0.04, 1);
742 		cairo_pattern_add_color_stop_rgba (pat, 1.0, 0.00, 0.00, 1.00, 1);
743 		cairo_set_source (cr, pat);
744 		cairo_set_line_width(cr,2.0);
745 		cairo_arc(cr,xc,yc,r-8.5,0,2*INV_PI);
746 
747 		cairo_stroke(cr);
748 
749 		INV_KNOB(widget)->lastvalue=value;
750 	}
751 	cairo_destroy(cr);
752 
753 }
754 
755 
756 static void
inv_knob_destroy(GtkObject * object)757 inv_knob_destroy(GtkObject *object)
758 {
759 	InvKnob *knob;
760 	InvKnobClass *klass;
761 
762 	g_return_if_fail(object != NULL);
763 	g_return_if_fail(INV_IS_KNOB(object));
764 
765 	knob = INV_KNOB(object);
766 
767 	klass = gtk_type_class(gtk_widget_get_type());
768 
769 	if (GTK_OBJECT_CLASS(klass)->destroy) {
770 		(* GTK_OBJECT_CLASS(klass)->destroy) (object);
771 	}
772 }
773 
774 static gboolean
inv_knob_button_press_event(GtkWidget * widget,GdkEventButton * event)775 inv_knob_button_press_event (GtkWidget *widget, GdkEventButton *event)
776 {
777 	g_assert(INV_IS_KNOB(widget));
778 	g_object_set(G_OBJECT(widget),"has-tooltip",FALSE,NULL);
779 	gtk_widget_set_state(widget,GTK_STATE_ACTIVE);
780     	gtk_widget_grab_focus(widget);
781 
782 	INV_KNOB(widget)->click_x=event->x;
783 	INV_KNOB(widget)->click_y=event->y;
784 
785 	inv_knob_paint(widget,INV_KNOB_DRAW_ALL);
786 
787 	return TRUE;
788 }
789 
790 static gboolean
inv_knob_motion_notify_event(GtkWidget * widget,GdkEventMotion * event)791 inv_knob_motion_notify_event(GtkWidget *widget, GdkEventMotion *event)
792 {
793 	g_assert(INV_IS_KNOB(widget));
794 
795 	if((GTK_WIDGET (widget)->state)==GTK_STATE_ACTIVE) {
796 		INV_KNOB(widget)->value = inv_value_from_motion(INV_KNOB(widget)->click_x-event->x,
797 								INV_KNOB(widget)->click_y-event->y,
798 								INV_KNOB(widget)->value,
799 								INV_KNOB(widget)->curve,
800 								INV_KNOB(widget)->min,
801 								INV_KNOB(widget)->max );
802 		INV_KNOB(widget)->click_y = event->y;
803 		inv_knob_paint(widget,INV_KNOB_DRAW_DATA);
804 		return FALSE; //let the after signal run
805 	} else {
806 		return TRUE;
807 	}
808 }
809 
810 static gboolean
inv_knob_button_release_event(GtkWidget * widget,GdkEventButton * event)811 inv_knob_button_release_event (GtkWidget *widget, GdkEventButton *event)
812 {
813 	g_assert(INV_IS_KNOB(widget));
814 	gtk_widget_set_state(widget,GTK_STATE_NORMAL);
815 	g_object_set(G_OBJECT(widget),"has-tooltip",TRUE,NULL);
816 	inv_knob_paint(widget,INV_KNOB_DRAW_ALL);
817 
818 	return TRUE;
819 }
820 
821 static void
inv_knob_label(gint mode,char * label,char * units,gint human,float value)822 inv_knob_label(gint mode, char *label, char *units, gint human, float value)
823 {
824 	float rounded;
825 	if(mode==0) {
826 		if(human==1) {
827 			if(fabs(value)<0.001) {
828 				sprintf(label,"%0.0fµ%s",value*1000000,units);
829 			} else if(fabs(value)<1) {
830 				sprintf(label,"%0.0fm%s",value*1000,units);
831 			} else if(fabs(value<1000)) {
832 				sprintf(label,"%0.0f%s",value,units);
833 			} else if(fabs(value<1000000)) {
834 				sprintf(label,"%0.0fk%s",value/1000,units);
835 			} else {
836 				sprintf(label,"%0.0fM%s",value/1000000,units);
837 			}
838 		} else {
839 			sprintf(label,"%0.0f%s",value,units);
840 		}
841 	} else {
842 		if(human==1) {
843 			if(fabs(value)<0.001) {
844 				rounded=inv_knob_label_set_dp(value*1000000);
845 				sprintf(label,"%0.3g µ%s",rounded,units);
846 			} else if(fabs(value)<1) {
847 				rounded=inv_knob_label_set_dp(value*1000);
848 				sprintf(label,"%0.3g m%s",rounded,units);
849 			} else if(fabs(value<1000)) {
850 				rounded=inv_knob_label_set_dp(value);
851 				sprintf(label,"%0.3g %s",rounded,units);
852 			} else if(fabs(value<1000000)) {
853 				rounded=inv_knob_label_set_dp(value/1000);
854 				sprintf(label,"%0.3g k%s",rounded,units);
855 			} else {
856 				rounded=inv_knob_label_set_dp(value/1000000);
857 				sprintf(label,"%0.3g M%s",rounded,units);
858 			}
859 		} else {
860 			rounded=inv_knob_label_set_dp(value);
861 			sprintf(label,"%0.3g %s",rounded,units);
862 		}
863 	}
864 }
865 
866 static void
inv_knob_label_pan(char * label,float value,float min,float max)867 inv_knob_label_pan(char *label, float value, float min, float max)
868 {
869 	float center;
870 	gint pan;
871 
872 	center = (max+min)/2;
873 
874 	if(value < center) /* left */
875 	{
876 		pan=-100 * (value/(center-min));
877 		if(pan==0) {
878 			sprintf(label,"Centre");
879 		} else {
880 			sprintf(label,"%i%% L",pan);
881 		}
882 	} else { 	/* right */
883 		pan=100 * (value/(max-center));
884 		if(pan==0) {
885 			sprintf(label,"Centre");
886 		} else {
887 			sprintf(label,"%i%% R",pan);
888 		}
889 	}
890 }
891 
892 static float
inv_knob_label_set_dp(float value)893 inv_knob_label_set_dp(float value)
894 {
895 	float exponent,newval;
896 
897 	exponent= value==0 ? 0 : log10(fabs(value));
898 
899 	if(exponent<1) {
900 		newval=floor(value*100)/100;
901 	} else if(exponent<2) {
902 		newval=floor(value*10)/10;
903 	} else  {
904 		newval=floor(value);
905 	}
906 	return newval;
907 }
908 
909 static float
inv_marking_to_value(float mark,gint curve,float min,float max)910 inv_marking_to_value(float mark, gint curve, float min, float max)
911 {
912 	float value;
913 
914 	switch(curve)
915 	{
916 		case INV_KNOB_CURVE_LOG:
917 			value=pow(10,log10(min)+(mark*(log10(max)-log10(min))));
918 			break;
919 		case INV_KNOB_CURVE_QUAD:
920 			value= mark < 0.5 ?
921 				((min+max)/2) - (pow((2*mark)-1,2) * ((max-min)/2)):
922 				((min+max)/2) + (pow((2*mark)-1,2) * ((max-min)/2));
923 			break;
924 		case INV_KNOB_CURVE_LINEAR:
925 		default:
926 			value = min + (max-min) * mark;
927 			break;
928 	}
929 	return value;
930 }
931 
932 static float
inv_value_to_angle(float value,gint curve,float min,float max)933 inv_value_to_angle(float value, gint curve, float min, float max)
934 {
935 	float angle;
936 
937 	switch(curve)
938 	{
939 		case INV_KNOB_CURVE_LOG:
940 			angle=(4.0*INV_PI*(log10(value)-log10(min)))/(3*(log10(max)-log10(min))) ;
941 			break;
942 		case INV_KNOB_CURVE_QUAD:
943 			angle= value < (max+min)/2 ?
944 				4.0*INV_PI*(1-pow(((min+max)-(2*value))/(max-min),0.5))/6 :
945 				4.0*INV_PI*(pow(((2*value)-(min+max))/(max-min),0.5)+1)/6 ;
946 			break;
947 		case INV_KNOB_CURVE_LINEAR:
948 		default:
949 			angle = (4.0*INV_PI*(value-min))/(3*(max-min)) ;
950 			break;
951 	}
952 	return angle;
953 }
954 
955 
956 static float
inv_value_from_motion(float x_delta,float y_delta,float current,gint curve,float min,float max)957 inv_value_from_motion(float x_delta, float y_delta, float current, gint curve, float min, float max)
958 {
959 	float sens,value,pos;
960 
961 	sens=1/(75*(1+fabs(x_delta/10)));
962 
963 	switch(curve)
964 	{
965 		case INV_KNOB_CURVE_LOG:
966 			value = pow(10,log10(current) + (y_delta * sens * (log10(max)-log10(min))));
967 			break;
968 		case INV_KNOB_CURVE_QUAD:
969 			pos= current < (max+min)/2 ?
970 				(1-pow(((min+max)-(2*current))/(max-min),0.5))/2 :
971 				(pow(((2*current)-(min+max))/(max-min),0.5)+1)/2 ;
972 			value= pos + (y_delta * sens) < 0.5 ?
973 				((min+max)/2) - (pow((2*(pos + (y_delta * sens)))-1,2) * ((max-min)/2)):
974 				((min+max)/2) + (pow((2*(pos + (y_delta * sens)))-1,2) * ((max-min)/2));
975 			break;
976 		case INV_KNOB_CURVE_LINEAR:
977 		default:
978 			value = current + (y_delta * sens * (max-min));
979 			break;
980 	}
981 
982 	if(value < min) value = min;
983 	if(value > max) value = max;
984 
985 	return value;
986 }
987 
988 
989 
990