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