1 /* giwknob.c  -  GiwKnob widget's source    Version 0.1
2   Copyright (C) 2006  Alexandre Pereira Bueno, Eduardo Parente Ribeiro
3 
4     This library is free software; you can redistribute it and/or
5     modify it under the terms of the GNU Lesser General Public
6     License as published by the Free Software Foundation; either
7     version 2.1 of the License, or (at your option) any later version.
8 
9     This library is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12     Lesser General Public License for more details.
13 
14     You should have received a copy of the GNU Lesser General Public
15     License along with this library; if not, write to the Free Software
16     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17 
18   Maintainers
19   Alexandre Pereira Bueno - alpebu@yahoo.com.br
20   James Scott Jr <skoona@users.sourceforge.net>
21 */
22 
23 // additional code G. Finch (salsaman@gmail.com) 2010 - 2016
24 
25 
26 #include <math.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #include "main.h"
32 
33 #include "giw/giwknob.h"
34 
35 
36 #define KNOB_DEFAULT_SIZE 150
37 
38 /* Forward declarations */
39 static void giw_knob_class_init(GiwKnobClass    *klass);
40 static void giw_knob_init(GiwKnob         *knob);
41 static void giw_knob_realize(GtkWidget        *widget);
42 static void giw_knob_size_request(GtkWidget      *widget,
43                                   GtkRequisition *requisition);
44 #if GTK_CHECK_VERSION(3, 0, 0)
45 static void giw_knob_dispose(GObject *object);
46 static void giw_knob_get_preferred_width(GtkWidget *widget,
47     gint      *minimal_width,
48     gint      *natural_width);
49 static void giw_knob_get_preferred_height(GtkWidget *widget,
50     gint      *minimal_height,
51     gint      *natural_height);
52 static gboolean giw_knob_draw(GtkWidget *widget, cairo_t *cairo);
53 static void giw_knob_style_updated(GtkWidget *widget);
54 #else
55 static void giw_knob_destroy(GtkObject *object);
56 static gint giw_knob_expose(GtkWidget        *widget,
57                             GdkEventExpose   *event);
58 #endif
59 
60 static void giw_knob_size_allocate(GtkWidget     *widget,
61                                    GtkAllocation *allocation);
62 static gint giw_knob_button_press(GtkWidget        *widget,
63                                   GdkEventButton   *event);
64 static gint giw_knob_button_release(GtkWidget        *widget,
65                                     GdkEventButton   *event);
66 static gint giw_knob_motion_notify(GtkWidget        *widget,
67                                    GdkEventMotion   *event);
68 static void giw_knob_style_set(GtkWidget      *widget,
69                                GtkStyle       *previous_style);
70 // Changes the value, by mouse position
71 void knob_update_mouse(GiwKnob *knob, gint x, gint y);
72 
73 // Updates the false pointer's angle
74 void knob_update_false_mouse(GiwKnob *knob, gint x, gint y);
75 
76 // Calculate the value, using the angle
77 gdouble knob_calculate_value_with_angle(GiwKnob         *knob,
78                                         gdouble angle);
79 // Calculate the angle, using the value
80 gdouble knob_calculate_angle_with_value(GiwKnob         *knob,
81                                         gdouble value);
82 // Calculate all sizes
83 static void knob_calculate_sizes(GiwKnob         *knob);
84 // To make the changes needed when someno changes the lower ans upper fields of the adjustment
85 static void giw_knob_adjustment_changed(GtkAdjustment    *adjustment,
86                                         gpointer          data);
87 // To make the changes needed when someno changes the value of the adjustment
88 static void giw_knob_adjustment_value_changed(GtkAdjustment    *adjustment,
89     gpointer          data);
90 // A not public knob_set_angle function, for internal using, it only sets the angle, nothing more
91 void knob_set_angle(GiwKnob *knob,
92                     gdouble angle);
93 // A not public knob_set_value function, for internal using, it only sets the value, nothing more
94 void knob_set_value(GiwKnob *knob,
95                     gdouble value);
96 // A function that creates the layout of legends and calculates it's sizes
97 void knob_build_legends(GiwKnob *knob);
98 
99 // A function that frees the layout of legends
100 void knob_free_legends(GiwKnob *knob);
101 
102 // A function that creates the layout of the title
103 void knob_build_title(GiwKnob *knob);
104 
105 // A function that calculates width and height of the legend's the layout
106 void knob_calculate_legends_sizes(GiwKnob *knob);
107 
108 // A function that calculates width and height of the title's the layout
109 void knob_calculate_title_sizes(GiwKnob *knob);
110 
111 #if GTK_CHECK_VERSION(3, 0, 0)
G_DEFINE_TYPE(GiwKnob,giw_knob,GTK_TYPE_WIDGET)112 G_DEFINE_TYPE(GiwKnob, giw_knob, GTK_TYPE_WIDGET)
113 #define parent_class giw_knob_parent_class
114 
115 #else
116 static GtkWidgetClass *parent_class = NULL;
117 
118 
119 /*********************
120   Widget's Functions
121 *********************/
122 GType
123 giw_knob_get_type() {
124   static GType knob_type = 0;
125 
126   if (!knob_type) {
127     static const GtkTypeInfo knob_info = {
128       "GiwKnob",
129       sizeof(GiwKnob),
130       sizeof(GiwKnobClass),
131       (GtkClassInitFunc) giw_knob_class_init,
132       (GtkObjectInitFunc) giw_knob_init,
133       /*(GtkArgSetFunc)*/ NULL,
134       /*(GtkArgGetFunc)*/ NULL,
135       (GtkClassInitFunc) NULL,
136     };
137 
138     knob_type = gtk_type_unique(gtk_widget_get_type(), &knob_info);
139   }
140 
141   return knob_type;
142 }
143 
144 #endif
145 
146 static void
147 giw_knob_class_init(GiwKnobClass *xclass) {
148 #if GTK_CHECK_VERSION(3, 0, 0)
149   GObjectClass *object_class = G_OBJECT_CLASS(xclass);
150 #else
151   GtkObjectClass *object_class = (GtkObjectClass *) xclass;
152 #endif
153   GtkWidgetClass *widget_class;
154 
155   widget_class = (GtkWidgetClass *) xclass;
156 
157 #if GTK_CHECK_VERSION(3, 0, 0)
158   object_class->dispose = giw_knob_dispose;
159 #else
160   parent_class = (GtkWidgetClass *)gtk_type_class(gtk_widget_get_type());
161   object_class->destroy = giw_knob_destroy;
162 #endif
163 
164   widget_class->realize = giw_knob_realize;
165 
166 #if GTK_CHECK_VERSION(3, 0, 0)
167   widget_class->get_preferred_width = giw_knob_get_preferred_width;
168   widget_class->get_preferred_height = giw_knob_get_preferred_height;
169   widget_class->draw = giw_knob_draw;
170   widget_class->style_updated = giw_knob_style_updated;
171 #else
172   widget_class->expose_event = giw_knob_expose;
173   widget_class->size_request = giw_knob_size_request;
174 #endif
175   widget_class->size_allocate = giw_knob_size_allocate;
176   widget_class->button_press_event = giw_knob_button_press;
177   widget_class->button_release_event = giw_knob_button_release;
178   widget_class->motion_notify_event = giw_knob_motion_notify;
179   widget_class->style_set = giw_knob_style_set;
180 }
181 
182 static void
giw_knob_init(GiwKnob * knob)183 giw_knob_init(GiwKnob *knob) {
184   g_return_if_fail(knob != NULL);
185   g_return_if_fail(GIW_IS_KNOB(knob));
186 
187   knob->button = 0;
188   knob->mouse_policy = GIW_KNOB_MOUSE_AUTOMATICALLY;
189   knob->major_ticks = 9;
190   knob->minor_ticks = 3;
191   knob->major_ticks_size = 5;
192   knob->minor_ticks_size = 3;
193   knob->legends_digits = 3;
194   knob->title = NULL;
195   knob->wrap = FALSE;
196 
197 #if GTK_CHECK_VERSION(2,18,0)
198   gtk_widget_set_has_window(GTK_WIDGET(knob), TRUE);
199 #endif
200 
201 }
202 
203 GtkWidget *
giw_knob_new(GtkAdjustment * adjustment)204 giw_knob_new(GtkAdjustment *adjustment) {
205   GiwKnob *knob;
206 
207   g_return_val_if_fail(adjustment != NULL, NULL);
208 
209 #if GTK_CHECK_VERSION(3, 0, 0)
210   knob = (GiwKnob *)g_object_new(GIW_TYPE_KNOB, NULL);
211 #else
212   knob = (GiwKnob *)gtk_type_new(giw_knob_get_type());
213 #endif
214   giw_knob_set_adjustment(knob, adjustment);
215 
216   // Without this, in the first draw, the pointer wouldn't be in the right value
217   knob_set_angle(knob, knob_calculate_angle_with_value(knob, gtk_adjustment_get_value(adjustment)));
218 
219   return GTK_WIDGET(knob);
220 }
221 
222 GtkWidget *
giw_knob_new_with_adjustment(gdouble value,gdouble lower,gdouble upper)223 giw_knob_new_with_adjustment(gdouble value,
224                              gdouble lower,
225                              gdouble upper) {
226   GiwKnob *knob;
227 
228 #if GTK_CHECK_VERSION(3, 0, 0)
229   knob = (GiwKnob *)g_object_new(GIW_TYPE_KNOB, NULL);
230 #else
231   knob = (GiwKnob *)gtk_type_new(giw_knob_get_type());
232 #endif
233   giw_knob_set_adjustment(knob, (GtkAdjustment *) gtk_adjustment_new(value, lower, upper, 1.0, 1.0, 1.0));
234 
235   // Without this, in the first draw, the pointer wouldn't be in the right value
236   knob_set_angle(knob, knob_calculate_angle_with_value(knob, gtk_adjustment_get_value(knob->adjustment)));
237 
238   return GTK_WIDGET(knob);
239 }
240 
241 #if GTK_CHECK_VERSION(3, 0, 0)
giw_knob_dispose(GObject * object)242 static void giw_knob_dispose(GObject *object) {
243 #else
244 static void giw_knob_destroy(GtkObject *object) {
245 #endif
246   GiwKnob *knob;
247   gint loop;
248 
249   g_return_if_fail(object != NULL);
250   g_return_if_fail(GIW_IS_KNOB(object));
251 
252   knob = GIW_KNOB(object);
253 
254   if (knob->adjustment)
255     g_object_unref(G_OBJECT(knob->adjustment));
256   knob->adjustment = NULL;
257 
258   if (knob->legends) {
259     for (loop = 0; loop < knob->major_ticks; loop++) {
260       g_object_unref(G_OBJECT(knob->legends[loop]));
261     }
262     g_free(knob->legends);
263     knob->legends = NULL;
264   }
265   if (knob->title_str)
266     g_free(knob->title_str);
267   if (knob->title)
268     g_object_unref(G_OBJECT(knob->title));
269 
270 #if GTK_CHECK_VERSION(3, 0, 0)
271   G_OBJECT_CLASS(giw_knob_parent_class)->dispose(object);
272 #else
273   if (LIVES_GUI_OBJECT_CLASS(parent_class)->destroy)
274     (* LIVES_GUI_OBJECT_CLASS(parent_class)->destroy)(object);
275 #endif
276 }
277 
278 static void
279 giw_knob_realize(GtkWidget *widget) {
280   GiwKnob *knob;
281   GdkWindowAttr attributes;
282   gint attributes_mask;
283 
284 #if GTK_CHECK_VERSION(3, 0, 0)
285   GtkStyleContext *stylecon;
286 #endif
287 
288   g_return_if_fail(widget != NULL);
289   g_return_if_fail(GIW_IS_KNOB(widget));
290 
291 #if GTK_CHECK_VERSION(2,20,0)
292   gtk_widget_set_realized(widget, TRUE);
293 #else
294   GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
295 #endif
296 
297   knob = GIW_KNOB(widget);
298 
299   attributes.x = lives_widget_get_allocation_x(widget);
300   attributes.y = lives_widget_get_allocation_y(widget);
301   attributes.width = lives_widget_get_allocation_width(widget);
302   attributes.height = lives_widget_get_allocation_height(widget);
303   attributes.wclass = GDK_INPUT_OUTPUT;
304   attributes.window_type = GDK_WINDOW_CHILD;
305   attributes.event_mask = gtk_widget_get_events(widget) |
306                           GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
307                           GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
308                           GDK_POINTER_MOTION_HINT_MASK;
309 
310   attributes_mask = GDK_WA_X | GDK_WA_Y;
311 
312 
313 #if !GTK_CHECK_VERSION(3, 0, 0)
314   attributes_mask |= GDK_WA_COLORMAP | GDK_WA_VISUAL;
315   attributes.visual = gtk_widget_get_visual(widget);
316   attributes.colormap = gtk_widget_get_colormap(widget);
317 #endif
318 
319 #if GTK_CHECK_VERSION(2,18,0)
320   gtk_widget_set_window(widget, gdk_window_new(lives_widget_get_xwindow(lives_widget_get_parent(widget)), &attributes,
321                         attributes_mask));
322 #else
323   widget->window = gdk_window_new(widget->parent->window, &attributes, attributes_mask);
324 #endif
325 
326 #if GTK_CHECK_VERSION(3, 0, 0)
327   stylecon = gtk_style_context_new();
328   gtk_style_context_set_path(stylecon, gtk_widget_get_path(widget));
329   gtk_style_context_set_state(stylecon, GTK_STATE_FLAG_ACTIVE);
330   G_GNUC_BEGIN_IGNORE_DEPRECATIONS
331   gtk_style_context_set_background(stylecon, lives_widget_get_xwindow(lives_widget_get_parent(widget)));
332   G_GNUC_END_IGNORE_DEPRECATIONS
333 #else
334   widget->style = gtk_style_attach(widget->style, lives_widget_get_xwindow(widget));
335   gtk_style_set_background(widget->style, lives_widget_get_xwindow(widget), GTK_STATE_ACTIVE);
336 #endif
337 
338   gdk_window_set_user_data(lives_widget_get_xwindow(widget), widget);
339 
340   // Create the initial legends
341   knob_build_legends(knob);
342 }
343 
344 
345 
346 
347 static void
348 giw_knob_size_request(GtkWidget      *widget,
349                       GtkRequisition *requisition) {
350   requisition->width = KNOB_DEFAULT_SIZE;
351   requisition->height = KNOB_DEFAULT_SIZE;
352 }
353 
354 #if GTK_CHECK_VERSION(3, 0, 0)
355 
356 static void
357 giw_knob_get_preferred_width(GtkWidget *widget,
358                              gint      *minimal_width,
359                              gint      *natural_width) {
360   GtkRequisition requisition;
361 
362   giw_knob_size_request(widget, &requisition);
363 
364   *minimal_width = *natural_width = requisition.width / 4.;
365 }
366 
367 static void
368 giw_knob_get_preferred_height(GtkWidget *widget,
369                               gint      *minimal_height,
370                               gint      *natural_height) {
371   GtkRequisition requisition;
372 
373   giw_knob_size_request(widget, &requisition);
374 
375   *minimal_height = *natural_height = requisition.height / 4.;
376 }
377 
378 #endif
379 
380 static void
381 giw_knob_size_allocate(GtkWidget     *widget,
382                        GtkAllocation *allocation) {
383   GiwKnob *knob;
384 
385   g_return_if_fail(widget != NULL);
386   g_return_if_fail(GIW_IS_KNOB(widget));
387   g_return_if_fail(allocation != NULL);
388 
389 #if GTK_CHECK_VERSION(2,18,0)
390   gtk_widget_set_allocation(widget, allocation);
391 #else
392   widget->allocation = *allocation;
393 #endif
394 
395   knob = GIW_KNOB(widget);
396 
397   if (lives_widget_is_realized(widget)) {
398     gdk_window_move_resize(lives_widget_get_xwindow(widget),
399                            allocation->x, allocation->y,
400                            allocation->width, allocation->height);
401 
402   }
403   knob_calculate_sizes(knob);
404 }
405 
406 
407 #if GTK_CHECK_VERSION(3, 0, 0)
408 static gboolean giw_knob_draw(GtkWidget *widget, cairo_t *cairo) {
409   //GdkRGBA color;
410 #else
411 
412 static gint
413 giw_knob_expose(GtkWidget      *widget,
414                 GdkEventExpose *event) {
415 #endif
416   GiwKnob *knob;
417   gdouble s, c;
418   gint xc, yc;
419   gdouble loop1;
420   guint dx1, dy1, dx2, dy2;
421   gint counter = 0;
422   GdkRectangle rect;
423 
424   g_return_val_if_fail(widget != NULL, FALSE);
425   g_return_val_if_fail(GIW_IS_KNOB(widget), FALSE);
426 #if !GTK_CHECK_VERSION(3, 0, 0)
427   g_return_val_if_fail(event != NULL, FALSE);
428   if (event->count > 0)
429     return FALSE;
430 #endif
431 
432   knob = GIW_KNOB(widget);
433 
434   rect.x = 0;
435   rect.y = 0;
436   rect.width = lives_widget_get_allocation_width(widget);
437   rect.height = lives_widget_get_allocation_height(widget);
438 
439   // The center
440   xc = lives_widget_get_allocation_width(widget) / 2;
441   yc = lives_widget_get_allocation_height(widget) / 2;
442 
443   s = sin(knob->angle);
444   c = cos(knob->angle);
445 
446 
447 #if GTK_CHECK_VERSION(3, 0, 0)
448   gtk_render_background(gtk_widget_get_style_context(widget),
449                         cairo,
450                         0,
451                         0,
452                         rect.width,
453                         rect.height);
454 
455   cairo_set_source_rgb(cairo, 0., 0., 0.);
456 
457   cairo_arc(cairo,
458             knob->x + ((knob->size / 2)),
459             knob->y + ((knob->size / 2)),
460             knob->radius,
461             0,
462             2.*M_PI);
463 
464   cairo_fill(cairo);
465 
466   // not working
467   /*
468     gtk_style_context_get_color (gtk_widget_get_style_context (widget), gtk_widget_get_state(widget), &color);
469     cairo_set_source_rgba (cairo, color.red, color.green, color.blue, color.alpha);
470 
471     cairo_arc(cairo,
472       knob->x+((knob->size/2)),
473       knob->y+((knob->size/2)),
474       knob->radius/4.*3.,
475       0,
476       2.*M_PI);
477 
478       cairo_fill(cairo);*/
479 
480 
481   cairo_set_source_rgb(cairo, 1., 1., 1.);
482 
483   cairo_move_to(cairo,
484                 xc + c * ((float)knob->radius * 0.6),
485                 yc - s * ((float)knob->radius * 0.6));
486 
487   cairo_line_to(cairo,
488                 xc + c * knob->radius,
489                 yc - s * knob->radius);
490 
491   cairo_stroke(cairo);
492 
493   cairo_arc(cairo,
494             xc + c * ((float)knob->radius * 0.8),
495             yc - s * ((float)knob->radius * 0.8),
496             knob->radius / 2 * 0.1,
497             0,
498             2.*M_PI);
499 
500   cairo_fill(cairo);
501 
502   if ((knob->mouse_policy == GIW_KNOB_MOUSE_DELAYED) & (knob->button != 0)) {
503     s = sin(knob->false_angle);
504     c = cos(knob->false_angle);
505 
506     //lives_widget_get_fg_state_color (widget, GTK_STATE_FLAG_PRELIGHT, &color);
507 
508     cairo_move_to(cairo,
509                   xc + c * ((float)knob->radius * 0.8),
510                   yc - s * ((float)knob->radius * 0.8));
511     cairo_line_to(cairo,
512                   xc + c * knob->radius,
513                   yc - s * knob->radius);
514 
515 
516   }
517 
518   // Now, draw the ticks
519   // The major ticks (and legends)
520   if (knob->major_ticks != 0)
521     for (loop1 = (3.0 * M_PI / 2.0); loop1 >= -0.0001;
522          loop1 -= knob->d_major_ticks) { // -0.0001 (and not 0) to avoid rounding errors
523       s = sin(loop1 - M_PI / 4.0);
524       c = cos(loop1 - M_PI / 4.0);
525       dx1 = c * knob->radius;
526       dy1 = s * knob->radius;
527       dx2 = c * (knob->radius + knob->major_ticks_size);
528       dy2 = s * (knob->radius + knob->major_ticks_size);
529       cairo_move_to(cairo,
530                     xc + dx1,
531                     yc - dy1);
532 
533       cairo_line_to(cairo,
534                     xc + dx2,
535                     yc - dy2);
536 
537       cairo_stroke(cairo);
538 
539       // Drawing the legends
540       if (knob->legends_digits != 0)
541         gtk_render_layout(gtk_widget_get_style_context(widget),
542                           cairo,
543                           xc + (c * knob->legend_radius) - (knob->legend_width / 2),
544                           yc - (s * knob->legend_radius) - (knob->legend_height / 2),
545                           knob->legends[counter]);
546       counter++;
547     }
548 
549 
550   // The minor ticks
551   if (knob->minor_ticks != 0)
552     for (loop1 = (3.0 * M_PI / 2.0); loop1 >= 0.0; loop1 -= knob->d_minor_ticks) {
553       s = sin(loop1 - M_PI / 4.0);
554       c = cos(loop1 - M_PI / 4.0);
555       dx1 = c * knob->radius;
556       dy1 = s * knob->radius;
557       dx2 = c * (knob->radius + knob->minor_ticks_size);
558       dy2 = s * (knob->radius + knob->minor_ticks_size);
559       cairo_move_to(cairo,
560                     xc + dx1,
561                     yc - dy1);
562       cairo_line_to(cairo,
563                     xc + dx2,
564                     yc - dy2);
565       cairo_stroke(cairo);
566     }
567 
568   // Draw the title
569   if (knob->title_str != NULL) // font_str==NULL means no title
570     gtk_render_layout(gtk_widget_get_style_context(widget),
571                       cairo,
572                       xc - knob->title_width / 2,
573                       knob->size - knob->title_height - 5, // 5 pixels to separate from the borders
574                       knob->title);
575 
576 #else
577   // Drawing background
578   gtk_paint_flat_box(widget->style,
579                      widget->window,
580                      GTK_STATE_NORMAL,
581                      GTK_SHADOW_NONE,
582                      &rect,
583                      widget,
584                      NULL,
585                      0,
586                      0,
587                      -1,
588                      -1);
589 
590   // The arc
591   gdk_draw_arc(widget->window,
592                widget->style->black_gc,
593                TRUE,
594                knob->x + ((knob->size / 2) - knob->radius),
595                knob->y + ((knob->size / 2) - knob->radius),
596                knob->radius * 2,
597                knob->radius * 2,
598                0,
599                360 * 64);
600 
601 
602 
603   gdk_draw_line(widget->window,
604                 widget->style->white_gc,
605                 xc + c * ((float)knob->radius * 0.6),
606                 yc - s * ((float)knob->radius * 0.6),
607                 xc + c * knob->radius,
608                 yc - s * knob->radius);
609   gdk_draw_arc(widget->window,
610                widget->style->white_gc,
611                TRUE,
612                xc + c * ((float)knob->radius * 0.8) - knob->radius * 0.1,
613                yc - s * ((float)knob->radius * 0.8) - knob->radius * 0.1,
614                knob->radius * 0.2,
615                knob->radius * 0.2,
616                0,
617                360 * 64);
618 
619   // Draw the false-pointer if the delayed policy of mouse is set and a button is pressed
620   if ((knob->mouse_policy == GIW_KNOB_MOUSE_DELAYED) & (knob->button != 0)) {
621     s = sin(knob->false_angle);
622     c = cos(knob->false_angle);
623 
624     gdk_draw_line(widget->window,
625                   widget->style->fg_gc[widget->state],
626                   xc + c * ((float)knob->radius * 0.8),
627                   yc - s * ((float)knob->radius * 0.8),
628                   xc + c * knob->radius,
629                   yc - s * knob->radius);
630 
631     gdk_draw_arc(widget->window,
632                  widget->style->black_gc,
633                  FALSE,
634                  xc + c * ((float)knob->radius * 0.8) - knob->radius * 0.1,
635                  yc - s * ((float)knob->radius * 0.8) - knob->radius * 0.1,
636                  knob->radius * 0.2,
637                  knob->radius * 0.2,
638                  0,
639                  360 * 64);
640   }
641 
642   // Now, draw the ticks
643   // The major ticks (and legends)
644   if (knob->major_ticks != 0)
645     for (loop1 = (3.0 * M_PI / 2.0); loop1 >= -0.0001;
646          loop1 -= knob->d_major_ticks) { // -0.0001 (and not 0) to avoid rounding errors
647       s = sin(loop1 - M_PI / 4.0);
648       c = cos(loop1 - M_PI / 4.0);
649       dx1 = c * knob->radius;
650       dy1 = s * knob->radius;
651       dx2 = c * (knob->radius + knob->major_ticks_size);
652       dy2 = s * (knob->radius + knob->major_ticks_size);
653       gdk_draw_line(widget->window,
654                     widget->style->fg_gc[widget->state],
655                     xc + dx1,
656                     yc - dy1,
657                     xc + dx2,
658                     yc - dy2);
659       // Drawing the legends
660       if (knob->legends_digits != 0)
661         gtk_paint_layout(widget->style,
662                          widget->window,
663                          GTK_STATE_NORMAL,
664                          TRUE,
665                          &rect,
666                          widget,
667                          NULL,
668                          xc + (c * knob->legend_radius) - (knob->legend_width / 2),
669                          yc - (s * knob->legend_radius) - (knob->legend_height / 2),
670                          knob->legends[counter]);
671       counter++;
672     }
673   // The minor ticks
674   if (knob->minor_ticks != 0)
675     for (loop1 = (3.0 * M_PI / 2.0); loop1 >= 0.0; loop1 -= knob->d_minor_ticks) {
676       s = sin(loop1 - M_PI / 4.0);
677       c = cos(loop1 - M_PI / 4.0);
678       dx1 = c * knob->radius;
679       dy1 = s * knob->radius;
680       dx2 = c * (knob->radius + knob->minor_ticks_size);
681       dy2 = s * (knob->radius + knob->minor_ticks_size);
682       gdk_draw_line(widget->window,
683                     widget->style->fg_gc[widget->state],
684                     xc + dx1,
685                     yc - dy1,
686                     xc + dx2,
687                     yc - dy2);
688     }
689 
690   // Draw the title
691   if (knob->title_str != NULL) // font_str==NULL means no title
692     gtk_paint_layout(widget->style,
693                      widget->window,
694                      GTK_STATE_NORMAL,
695                      TRUE,
696                      &rect,
697                      widget,
698                      NULL,
699                      xc - knob->title_width / 2,
700                      knob->size - knob->title_height - 5, // 5 pixels to separate from the borders
701                      knob->title);
702 
703 #endif
704 
705   return FALSE;
706 }
707 
708 
709 static gint
710 giw_knob_button_press(GtkWidget      *widget,
711                       GdkEventButton *event) {
712   GiwKnob *knob;
713   gint xc, yc, dx, dy;
714 
715   g_return_val_if_fail(widget != NULL, TRUE);
716   g_return_val_if_fail(GIW_IS_KNOB(widget), TRUE);
717   g_return_val_if_fail(event != NULL, TRUE);
718 
719   knob = GIW_KNOB(widget);
720 
721   if (knob->mouse_policy == GIW_KNOB_MOUSE_DISABLED) return TRUE;
722   if (knob->button) return TRUE; // Some button is already pressed
723 
724   /* To verify if the pointer is in the knob, the distance between the pointer and the center
725     of the circle is calculated, if it's less than the radius of the circle , it's in!!*/
726 
727   xc = lives_widget_get_allocation_width(widget) / 2;
728   yc = lives_widget_get_allocation_height(widget) / 2;
729 
730   dx = abs((int)event->x - xc);
731   dy = abs((int)event->y - yc);
732 
733   if (!knob->button & (dx < knob->radius) & (dy < knob->radius))
734     knob->button = event->button;
735 
736   return FALSE;
737 }
738 
739 static gint
740 giw_knob_button_release(GtkWidget      *widget,
741                         GdkEventButton *event) {
742   GiwKnob *knob;
743   gint x, y;
744 
745   g_return_val_if_fail(widget != NULL, TRUE);
746   g_return_val_if_fail(GIW_IS_KNOB(widget), TRUE);
747   g_return_val_if_fail(event != NULL, TRUE);
748 
749   knob = GIW_KNOB(widget);
750 
751   g_return_val_if_fail(knob->adjustment != NULL, TRUE);
752 
753   if (knob->mouse_policy == GIW_KNOB_MOUSE_DISABLED) return TRUE;
754 
755   // If the policy is delayed, now that the button was released (if it is), it's time to update the value
756   if ((knob->mouse_policy == GIW_KNOB_MOUSE_DELAYED) &&
757       (knob->button == event->button)) {
758     x = event->x;
759     y = event->y;
760 
761     knob_update_mouse(knob, x, y);
762   }
763 
764 
765   if (knob->button == event->button)
766     knob->button = 0;
767 
768   return FALSE;
769 }
770 
771 static gint
772 giw_knob_motion_notify(GtkWidget      *widget,
773                        GdkEventMotion *event) {
774   GiwKnob *knob;
775   gint x, y;
776 
777   g_return_val_if_fail(widget != NULL, TRUE);
778   g_return_val_if_fail(GTK_IS_WIDGET(widget), TRUE);
779   g_return_val_if_fail(GIW_IS_KNOB(widget), TRUE);
780   g_return_val_if_fail(event != NULL, TRUE);
781 
782   knob = GIW_KNOB(widget);
783 
784   g_return_val_if_fail(knob->adjustment != NULL, TRUE);
785 
786   if (knob->mouse_policy == GIW_KNOB_MOUSE_DISABLED) return TRUE;
787 
788   // If the some button is pressed and the policy is set to update the value AUTOMATICALLY, update the knob's value
789   if ((knob->button != 0) && (knob->mouse_policy == GIW_KNOB_MOUSE_AUTOMATICALLY)) {
790     x = event->x;
791     y = event->y;
792 
793     if (event->is_hint || (event->window != lives_widget_get_xwindow(widget)))
794 #if GTK_CHECK_VERSION(3, 0, 0)
795       gdk_window_get_device_position(lives_widget_get_xwindow(widget),
796                                      gdk_event_get_device((GdkEvent *)(event)),
797                                      &x,
798                                      &y,
799                                      NULL);
800 #else
801       gdk_window_get_pointer(lives_widget_get_xwindow(widget), &x, &y, NULL);
802 #endif
803     knob_update_mouse(knob, x, y);
804   }
805 
806   // If the some button is pressed and the policy is set to update the value delayed, update the knob's false pointer's angle
807   if ((knob->button != 0) && (knob->mouse_policy == GIW_KNOB_MOUSE_DELAYED)) {
808     x = event->x;
809     y = event->y;
810 
811     if (event->is_hint || (event->window != lives_widget_get_xwindow(widget)))
812 #if GTK_CHECK_VERSION(3, 0, 0)
813       gdk_window_get_device_position(lives_widget_get_xwindow(widget),
814                                      gdk_event_get_device((GdkEvent *)(event)),
815                                      &x,
816                                      &y,
817                                      NULL);
818 #else
819       gdk_window_get_pointer(lives_widget_get_xwindow(widget), &x, &y, NULL);
820 #endif
821 
822     knob_update_false_mouse(knob, x, y);
823   }
824 
825   /*if (knob->button != 0)
826     {
827       x = event->x;
828       y = event->y;
829 
830       if (event->is_hint || (event->window != widget->window))
831         gdk_window_get_pointer (widget->window, &x, &y, NULL);
832 
833       knob_update_mouse (knob, x,y);
834     }*/
835 
836   return FALSE;
837 }
838 
839 static void
840 giw_knob_style_set(GtkWidget *widget,
841                    GtkStyle *previous_style) {
842   GiwKnob *knob;
843 
844   g_return_if_fail(widget != NULL);
845   g_return_if_fail(GIW_IS_KNOB(widget));
846 
847   knob = GIW_KNOB(widget);
848 
849   // The only thing to fo is recalculate the layout's sizes
850   knob_calculate_legends_sizes(knob);
851   knob_calculate_title_sizes(knob);
852 }
853 
854 #if GTK_CHECK_VERSION(3, 0, 0)
855 static void
856 giw_knob_style_updated(GtkWidget *widget) {
857 
858 }
859 
860 #endif
861 
862 /******************
863   Users Functions
864 ******************/
865 
866 gdouble
867 giw_knob_get_value(GiwKnob *knob) {
868   g_return_val_if_fail(knob != NULL, 0.0);
869   g_return_val_if_fail(GIW_IS_KNOB(knob), 0.0);
870 
871   return (gtk_adjustment_get_value(knob->adjustment));
872 }
873 
874 void
875 giw_knob_set_value(GiwKnob *knob,
876                    gdouble value) {
877   g_return_if_fail(knob != NULL);
878   g_return_if_fail(GIW_IS_KNOB(knob));
879 
880 
881   if (value != gtk_adjustment_get_value(knob->adjustment)) {
882     knob_set_value(knob, value);
883     g_return_if_fail(knob->adjustment != NULL);
884 #if !GTK_CHECK_VERSION(3,18,0)
885     gtk_adjustment_value_changed(knob->adjustment);
886 #endif
887   }
888 }
889 
890 void
891 giw_knob_set_adjustment(GiwKnob *knob,
892                         GtkAdjustment *adjustment) {
893   g_return_if_fail(knob != NULL);
894   g_return_if_fail(GIW_IS_KNOB(knob));
895   g_return_if_fail(adjustment != NULL);
896 
897   // Freeing the last one
898   if (knob->adjustment) {
899 #if GTK_CHECK_VERSION(3, 0, 0)
900     g_signal_handler_disconnect((gpointer)(knob->adjustment), knob->chsig);
901     g_signal_handler_disconnect((gpointer)(knob->adjustment), knob->vchsig);
902 #else
903     gtk_signal_disconnect_by_data(LIVES_GUI_OBJECT(knob->adjustment), (gpointer) knob);
904 #endif
905     g_object_unref(LIVES_GUI_OBJECT(knob->adjustment));
906   }
907 
908   knob->adjustment = adjustment;
909   g_object_ref(LIVES_GUI_OBJECT(knob->adjustment));
910 
911   knob->chsig = g_signal_connect(LIVES_GUI_OBJECT(adjustment), "changed",
912                                  (GCallback) giw_knob_adjustment_changed,
913                                  (gpointer) knob);
914   knob->vchsig = g_signal_connect(LIVES_GUI_OBJECT(adjustment), "value_changed",
915                                   (GCallback) giw_knob_adjustment_value_changed,
916                                   (gpointer) knob);
917 
918 #if !GTK_CHECK_VERSION(3,18,0)
919   gtk_adjustment_value_changed(knob->adjustment);
920   gtk_adjustment_changed(knob->adjustment);
921 #endif
922 }
923 
924 GtkAdjustment *
925 giw_knob_get_adjustment(GiwKnob *knob) {
926   g_return_val_if_fail(knob != NULL, NULL);
927   g_return_val_if_fail(GIW_IS_KNOB(knob), NULL);
928 
929   return (knob->adjustment);
930 }
931 
932 
933 void
934 giw_knob_set_wrap(GiwKnob *knob,
935                   gboolean wrap) {
936   g_return_if_fail(knob != NULL);
937   g_return_if_fail(GIW_IS_KNOB(knob));
938 
939   knob->wrap = wrap;
940 
941   knob_build_legends(knob);
942   knob_calculate_sizes(knob);
943   knob_set_angle(knob, knob_calculate_angle_with_value(knob, gtk_adjustment_get_value(knob->adjustment)));
944   gtk_widget_queue_draw(GTK_WIDGET(knob));
945 }
946 
947 
948 
949 void
950 giw_knob_set_legends_digits(GiwKnob *knob,
951                             guint digits_number) {
952   g_return_if_fail(knob != NULL);
953   g_return_if_fail(GIW_IS_KNOB(knob));
954 
955   if (digits_number != knob->legends_digits) {
956     knob_free_legends(knob);
957 
958     knob->legends_digits = digits_number;
959 
960     knob_build_legends(knob);
961     knob_calculate_sizes(knob);
962     gtk_widget_queue_draw(GTK_WIDGET(knob));
963   }
964 }
965 
966 void
967 giw_knob_set_ticks_number(GiwKnob *knob,
968                           guint major, guint minor) {
969   g_return_if_fail(knob != NULL);
970   g_return_if_fail(GIW_IS_KNOB(knob));
971 
972   if ((major != knob->major_ticks) || (minor != knob->minor_ticks)) {
973     knob_free_legends(knob);
974 
975     knob->major_ticks = major;
976 
977     if (knob->major_ticks == 0)
978       knob->minor_ticks = 0;        // It's impossible to have minor ticks without major ticks
979     else
980       knob->minor_ticks = minor;
981 
982     knob_build_legends(knob);
983     knob_calculate_sizes(knob);
984     gtk_widget_queue_draw(GTK_WIDGET(knob));
985   }
986 }
987 
988 void
989 giw_knob_set_mouse_policy(GiwKnob *knob,
990                           GiwKnobMousePolicy policy) {
991   g_return_if_fail(knob != NULL);
992   g_return_if_fail(GIW_IS_KNOB(knob));
993 
994   if (knob->button == 0) // The policy can only be change when there is no button pressed
995     knob->mouse_policy = policy;
996 }
997 
998 static void
999 giw_knob_adjustment_changed(GtkAdjustment *adjustment,
1000                             gpointer       data) {
1001   GiwKnob *knob;
1002 
1003   g_return_if_fail(adjustment != NULL);
1004   g_return_if_fail(data != NULL);
1005 
1006   knob = GIW_KNOB(data);
1007 
1008   knob_free_legends(knob);
1009   knob_build_legends(knob);
1010   knob_calculate_sizes(knob);
1011   gtk_widget_queue_draw(GTK_WIDGET(knob));
1012 }
1013 
1014 static void
1015 giw_knob_adjustment_value_changed(GtkAdjustment *adjustment,
1016                                   gpointer       data) {
1017   GiwKnob *knob;
1018 
1019   g_return_if_fail(adjustment != NULL);
1020   g_return_if_fail(data != NULL);
1021 
1022   knob = GIW_KNOB(data);
1023 
1024   knob_set_angle(knob, knob_calculate_angle_with_value(knob, gtk_adjustment_get_value(adjustment)));
1025 
1026   gtk_widget_queue_draw(GTK_WIDGET(knob));
1027 }
1028 
1029 void
1030 giw_knob_set_title(GiwKnob *knob, gchar *str) {
1031   g_return_if_fail(knob != NULL);
1032   g_return_if_fail(GIW_IS_KNOB(knob));
1033 
1034   knob->title_str = g_strdup(str); // Duplicate the string, after this, str can be freed
1035 
1036   knob_build_title(knob);
1037   knob_calculate_sizes(knob);
1038   gtk_widget_queue_draw(GTK_WIDGET(knob));
1039 }
1040 
1041 /******************
1042   Local Functions
1043 ******************/
1044 
1045 void
1046 knob_update_mouse(GiwKnob *knob, gint x, gint y) {
1047   gint xc, yc;
1048 
1049   g_return_if_fail(knob != NULL);
1050   g_return_if_fail(GIW_IS_KNOB(knob));
1051 
1052   gtk_widget_queue_draw(GTK_WIDGET(knob));
1053 
1054   xc = lives_widget_get_allocation_width(LIVES_WIDGET(knob)) / 2;
1055   yc = lives_widget_get_allocation_height(LIVES_WIDGET(knob)) / 2;
1056 
1057   // Calculating the new angle
1058   if (knob->angle != atan2(yc - y, x - xc)) {
1059     knob_set_value(knob, knob_calculate_value_with_angle(knob, atan2(yc - y, x - xc)));
1060     g_return_if_fail(knob->adjustment != NULL);
1061 #if !GTK_CHECK_VERSION(3,18,0)
1062     gtk_adjustment_value_changed(knob->adjustment);
1063 #endif
1064   }
1065 }
1066 
1067 void
1068 knob_update_false_mouse(GiwKnob *knob, gint x, gint y) {
1069   gint xc, yc;
1070 
1071   g_return_if_fail(knob != NULL);
1072   g_return_if_fail(GIW_IS_KNOB(knob));
1073 
1074   xc = lives_widget_get_allocation_width(LIVES_WIDGET(knob)) / 2;
1075   yc = lives_widget_get_allocation_height(LIVES_WIDGET(knob)) / 2;
1076 
1077   // Calculating the new angle
1078   knob->false_angle = atan2(yc - y, x - xc);
1079 
1080   // Putting the angle between 0 and 2PI, because the atan2 returns the angle between PI and -PI
1081   while (knob->false_angle < 0)
1082     knob->false_angle += (2.0 * M_PI);
1083 
1084   if (!knob->wrap) {
1085     // Taking out of the "forbidden" region
1086     if ((knob->false_angle <= (3.0 * M_PI / 2.0)) &&
1087         (knob->false_angle > (5.0 * M_PI / 4.0)))
1088       knob->false_angle = 5.0 * M_PI / 4.0;
1089     if ((knob->false_angle < (7.0 * M_PI / 4.0)) &&
1090         (knob->false_angle >= (3.0 * M_PI / 2.0)))
1091       knob->false_angle = 7.0 * M_PI / 4.0;
1092   }
1093 
1094   gtk_widget_queue_draw(GTK_WIDGET(knob));
1095 }
1096 
1097 static void
1098 knob_calculate_sizes(GiwKnob *knob) {
1099   GtkWidget *widget;
1100 
1101   g_return_if_fail(knob != NULL);
1102   g_return_if_fail(GIW_IS_KNOB(knob));
1103 
1104   widget = GTK_WIDGET(knob);
1105 
1106   // Getting the radius and size
1107   if (lives_widget_get_allocation_width(widget) < lives_widget_get_allocation_height(widget)) {
1108     knob->size = lives_widget_get_allocation_width(widget);
1109     knob->x = 0;
1110     knob->y = lives_widget_get_allocation_height(widget) / 2 - knob->size / 2;
1111   } else {
1112     knob->size = lives_widget_get_allocation_height(widget);
1113     knob->y = 0;
1114     knob->x = lives_widget_get_allocation_width(widget) / 2 - knob->size / 2;
1115   }
1116 
1117   // The distance between the radius and the widget limits is the bigger dimension of the legends plus the major_ticks_size,
1118   // so it's the half of size, less the bigger dimension of the legends less the major_ticks size (wich depends of the radius),
1119   // then, with some algebra, it results in this equation:
1120   knob->radius = 8 * ((knob->size / 2) - sqrt(knob->legend_width * knob->legend_width + knob->legend_height *
1121                       knob->legend_height)) / 9;
1122 
1123   if (!knob->wrap)
1124     knob->d_major_ticks = (3.0 * M_PI / 2.0) / (knob->major_ticks - 1);
1125   else
1126     knob->d_major_ticks = (2.0 * M_PI) / (knob->major_ticks - 1);
1127 
1128   knob->d_minor_ticks = knob->d_major_ticks / (knob->minor_ticks + 1);
1129 
1130   knob->major_ticks_size = knob->radius / 8.0;
1131   knob->minor_ticks_size = knob->radius / 16.0;
1132 
1133   // The legend will in the middle of the inside (plus the major_ticks_size) and outside circle
1134   knob->legend_radius = ((knob->radius + knob->major_ticks_size + (knob->size / 2)) / 2);
1135 }
1136 
1137 gdouble
1138 knob_calculate_value_with_angle(GiwKnob *knob, gdouble angle) {
1139   gdouble d_angle = 0.0; // How many the pointer is far from the lower angle (5PI/4)
1140 
1141   g_return_val_if_fail(knob != NULL, 0.0);
1142   g_return_val_if_fail(GIW_IS_KNOB(knob), 0.0);
1143 
1144   // Putting the angle between 0 and 2PI, because the atan2 returns the angle between PI and -PI
1145   while (angle < 0)
1146     angle = angle + (2.0 * M_PI);
1147 
1148   if (!knob->wrap) {
1149     // Taking out of the "forbidden" region
1150     if ((angle <= (3.0 * M_PI / 2.0)) && (angle  > (5.0 * M_PI / 4.0))) angle = 5.0 * M_PI / 4.0;
1151     if ((angle  < (7.0 * M_PI / 4.0)) && (angle >= (3.0 * M_PI / 2.0))) angle = 7.0 * M_PI / 4.0;
1152 
1153     // Calculating the distance (in radians) between the pointer and the lower angle
1154     if (angle <= (5.0 * M_PI / 4.0)) d_angle = (5.0 * M_PI / 4.0) - angle;
1155     if (angle >= (7.0 * M_PI / 4.0)) d_angle = (13.0 * M_PI / 4.0) - angle;
1156 
1157     return (lives_adjustment_get_lower(knob->adjustment) +
1158             fabs(lives_adjustment_get_upper(knob->adjustment) - lives_adjustment_get_lower(knob->adjustment)) * d_angle /
1159             (3.0 * M_PI / 2.0));
1160   }
1161 
1162   if (angle < 3 * M_PI / 2.) d_angle = (3.*M_PI / 2.) - angle;
1163   else d_angle = 7. / 2.*M_PI - angle;
1164   return (lives_adjustment_get_lower(knob->adjustment) +
1165           fabs(lives_adjustment_get_upper(knob->adjustment) - lives_adjustment_get_lower(knob->adjustment)) * d_angle / (2.0 * M_PI));
1166 }
1167 
1168 
1169 
1170 gdouble
1171 knob_calculate_angle_with_value(GiwKnob *knob, gdouble value) {
1172   gdouble angle;
1173 
1174   g_return_val_if_fail(knob != NULL, 0.0);
1175   g_return_val_if_fail(GIW_IS_KNOB(knob), 0.0);
1176   g_return_val_if_fail(knob->adjustment != NULL, 0.0);
1177 
1178   if (!knob->wrap) {
1179     angle = (value - lives_adjustment_get_lower(knob->adjustment)) *
1180             (3.0 * M_PI / 2.0) / fabs(lives_adjustment_get_upper(knob->adjustment) - lives_adjustment_get_lower(knob->adjustment));
1181 
1182     // Now, the angle is relative to the 3 o'clock position, and need to be changed in order to be relative to the initial angle ((5.0*M_PI/4.0)
1183     angle = (5.0 * M_PI / 4.0) - angle;
1184   } else {
1185     angle = (value - lives_adjustment_get_lower(knob->adjustment)) *
1186             (2.0 * M_PI) / fabs(lives_adjustment_get_upper(knob->adjustment) - lives_adjustment_get_lower(knob->adjustment));
1187 
1188     // Now, the angle is relative to the 3 o'clock position, and need to be changed in order to be relative to the initial angle (3*M_PI/2)
1189     angle = 3.*M_PI / 2. - angle;
1190   }
1191 
1192   return (angle);
1193 }
1194 
1195 
1196 void
1197 knob_set_angle(GiwKnob *knob,
1198                gdouble angle) {
1199 
1200   g_return_if_fail(knob != NULL);
1201   g_return_if_fail(GIW_IS_KNOB(knob));
1202 
1203   // Putting the angle between 0 and 2PI(360�)
1204   while (angle > 2.0 * M_PI)
1205     angle = angle - (2.0 * M_PI);
1206 
1207   while (angle < 0)
1208     angle = angle + (2.0 * M_PI);
1209 
1210   if (knob->angle != angle) {
1211     if (!knob->wrap) {
1212       // Taking out of the "forbidden" region
1213       if ((angle <= (3.0 * M_PI / 2.0)) && (angle > (5.0 * M_PI / 4.0))) angle = 5.0 * M_PI / 4.0;
1214       if ((angle  < (7.0 * M_PI / 4.0)) && (angle >= (3.0 * M_PI / 2.0))) angle = 7.0 * M_PI / 4.0;
1215     }
1216     knob->angle = angle;
1217   }
1218 }
1219 
1220 void
1221 knob_set_value(GiwKnob *knob,
1222                gdouble value) {
1223   g_return_if_fail(knob != NULL);
1224   g_return_if_fail(GIW_IS_KNOB(knob));
1225   g_return_if_fail(knob->adjustment != NULL);
1226 
1227   gtk_adjustment_set_value(knob->adjustment, value);
1228 }
1229 
1230 void
1231 knob_build_legends(GiwKnob *knob) {
1232   GtkWidget *widget;
1233   gint loop;
1234   gchar *str;
1235 
1236   g_return_if_fail(knob != NULL);
1237 
1238   widget = GTK_WIDGET(knob);
1239 
1240   if (knob->major_ticks == 0) // Preventing from bugs
1241     return;
1242 
1243   // Creating the legend's layouts
1244   if (knob->legends_digits != 0) {
1245     knob->legends = g_new(PangoLayout *, knob->major_ticks);
1246     str = g_new(gchar, knob->legends_digits + 1); // +1 for the '/0'
1247     for (loop = 0; loop < knob->major_ticks; loop++) {
1248       snprintf(str, knob->legends_digits + 1, "%f",
1249                lives_adjustment_get_lower(knob->adjustment) +
1250                loop * (lives_adjustment_get_upper(knob->adjustment) -
1251                        lives_adjustment_get_lower(knob->adjustment)) /
1252                (knob->major_ticks - 1)); // Creating the legends string
1253       knob->legends[loop] = gtk_widget_create_pango_layout(widget, str);
1254     }
1255     g_free(str);
1256 
1257     // Getting the size of the legends
1258     knob_calculate_legends_sizes(knob);
1259   } else { // If there are no legends (0 digits), the size is the major ticks size (5)
1260     knob->legend_width = 0;
1261     knob->legend_height = 0;
1262   }
1263 }
1264 
1265 void
1266 knob_free_legends(GiwKnob *knob) {
1267   gint loop;
1268 
1269   g_return_if_fail(knob != NULL);
1270 
1271   if (knob->legends != NULL) {
1272     for (loop = 0; loop < knob->major_ticks; loop++)
1273       if (knob->legends[loop] != NULL)
1274         g_object_unref(G_OBJECT(knob->legends[loop]));
1275     g_free(knob->legends);
1276     knob->legends = NULL;
1277   }
1278 }
1279 
1280 void
1281 knob_build_title(GiwKnob *knob) {
1282   GtkWidget *widget;
1283 
1284   g_return_if_fail(knob != NULL);
1285 
1286   widget = GTK_WIDGET(knob);
1287 
1288   if (knob->title_str == NULL) // Return if there is no title (the layout will be keeped, but not drawed)
1289     return;
1290 
1291   if (knob->title)
1292     pango_layout_set_text(knob->title, knob->title_str, strlen(knob->title_str));
1293   else // If the title hasn't been created yet..
1294     knob->title = gtk_widget_create_pango_layout(widget, knob->title_str);
1295 
1296   // Calculating new size
1297   knob_calculate_title_sizes(knob);
1298 }
1299 
1300 void
1301 knob_calculate_legends_sizes(GiwKnob *knob) {
1302   GtkWidget *widget;
1303 
1304 #if GTK_CHECK_VERSION(3,8,0)
1305   PangoFontDescription *fontdesc;
1306 #endif
1307 
1308   g_return_if_fail(knob != NULL);
1309 
1310   widget = GTK_WIDGET(knob);
1311 
1312   if (knob->legends != NULL) {
1313 
1314 #if GTK_CHECK_VERSION(3, 0, 0)
1315 #if GTK_CHECK_VERSION(3,8,0)
1316     fontdesc = pango_font_description_new();
1317     gtk_style_context_get(gtk_widget_get_style_context(widget),
1318                           gtk_widget_get_state_flags(widget),
1319                           GTK_STYLE_PROPERTY_FONT,
1320                           &fontdesc,
1321                           NULL
1322                          );
1323     pango_layout_set_font_description(knob->legends[0], fontdesc);
1324     pango_font_description_free(fontdesc);
1325 #else
1326     pango_layout_set_font_description(knob->legends[0],
1327                                       gtk_style_context_get_font(gtk_widget_get_style_context(widget), gtk_widget_get_state_flags(widget)));
1328 #endif
1329 #else
1330     pango_layout_set_font_description(knob->legends[0], widget->style->font_desc);
1331 #endif
1332     pango_layout_get_size(knob->legends[0], &(knob->legend_width), &(knob->legend_height));
1333     knob->legend_width /= PANGO_SCALE;
1334     knob->legend_height /= PANGO_SCALE;
1335   }
1336 }
1337 
1338 void
1339 knob_calculate_title_sizes(GiwKnob *knob) {
1340   GtkWidget *widget;
1341 
1342 #if GTK_CHECK_VERSION(3,8,0)
1343   PangoFontDescription *fontdesc;
1344 #endif
1345 
1346   g_return_if_fail(knob != NULL);
1347 
1348   if (knob->title == NULL) return;
1349 
1350   widget = GTK_WIDGET(knob);
1351 
1352 #if GTK_CHECK_VERSION(3, 0, 0)
1353 #if GTK_CHECK_VERSION(3,8,0)
1354   fontdesc = pango_font_description_new();
1355   gtk_style_context_get(gtk_widget_get_style_context(widget),
1356                         gtk_widget_get_state_flags(widget),
1357                         GTK_STYLE_PROPERTY_FONT,
1358                         &fontdesc,
1359                         NULL
1360                        );
1361   pango_layout_set_font_description(knob->legends[0], fontdesc);
1362   pango_font_description_free(fontdesc);
1363 #else
1364   pango_layout_set_font_description(knob->legends[0],
1365                                     gtk_style_context_get_font(gtk_widget_get_style_context(widget), gtk_widget_get_state_flags(widget)));
1366 #endif
1367 #else
1368   pango_layout_set_font_description(knob->legends[0], widget->style->font_desc);
1369 #endif
1370   pango_layout_get_size(knob->title, &(knob->title_width), &(knob->title_height));
1371   knob->title_width /= PANGO_SCALE;
1372   knob->title_height /= PANGO_SCALE;
1373 
1374   knob_calculate_sizes(knob);
1375 }
1376