1 /* gtkknob.c - libwhy GtkKnob knob widget
2  *
3  * Copyright (C) 2008, 2010 Sean Bolton
4  *
5  * Parts of this code come from GTK+, both the library source and
6  * the example programs.  Other bits come from gAlan 0.2.0,
7  * copyright (C) 1999 Tony Garnock-Jones.
8  *
9  * gtkknob-cairo_or_pixmap_selectable.c
10  *
11  * - This version requires GTK+ 2.0 or later.
12  * - This version can use fast but ugly server-side pixmaps to draw
13  *   the knobs.
14  * - On GTK+ 2.8 or later, this can also use cairo to draw nice but
15  *   relatively slower anti-aliased knobs.
16  * - On GTK+ 2.8 or later, cairo is the default, but this can be
17  *   changed using the function gtk_knob_set_fast_rendering().
18  * -FIX- It would be nice to add a property "fast-rendering" that
19  * could be easily set....
20  *
21  * This program is free software; you can redistribute it and/or
22  * modify it under the terms of the GNU General Public License as
23  * published by the Free Software Foundation; either version 2 of
24  * the License, or (at your option) any later version.
25  *
26  * This program is distributed in the hope that it will be
27  * useful, but WITHOUT ANY WARRANTY; without even the implied
28  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
29  * PURPOSE.  See the GNU General Public License for more details.
30  *
31  * You should have received a copy of the GNU General Public
32  * License along with this program; if not, write to the Free
33  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
34  * Boston, MA 02110-1301 USA.
35  */
36 
37 #define _ISOC99_SOURCE
38 #define _BSD_SOURCE
39 
40 #include <stdlib.h>
41 #include <string.h>
42 #include <math.h>
43 #include <gtk/gtk.h>
44 
45 #include "gtkknob.h"
46 
47 #define KNOB_SIZE               32
48 #define HALF_KNOB_SIZE          (KNOB_SIZE / 2)
49 
50 #define UPDATE_DELAY_LENGTH    300
51 
52 
53 enum {
54     STATE_IDLE,
55     STATE_PRESSED,
56     STATE_DRAGGING
57 };
58 
59 static GtkWidgetClass *parent_class = NULL;
60 
61 #if GTK_CHECK_VERSION(2, 8, 0)
62 static int prefer_cairo = TRUE;
63 #else /* GTK_CHECK_VERSION(2, 8, 0) */
64 static int prefer_cairo = FALSE;
65 #endif /* GTK_CHECK_VERSION(2, 8, 0) */
66 
67 static struct {
68     int refcount;
69     GdkPixmap *pixmap;
70     GdkPixmap *insensitive_pixmap;
71     GdkBitmap *mask;
72     GdkGC     *mask_gc;
73     GdkGC     *red_gc;
74 #if GTK_CHECK_VERSION(2, 8, 0)
75     cairo_surface_t *knob_surface;
76     cairo_surface_t *insensitive_knob_surface;
77     cairo_pattern_t *shadow_pattern;
78 #endif
79 } common;
80 
81 G_DEFINE_TYPE (GtkKnob, gtk_knob, GTK_TYPE_WIDGET);
82 
83 static void     gtk_knob_class_init    (GtkKnobClass *klass);
84 static void     gtk_knob_init          (GtkKnob *knob);
85 static void     gtk_knob_destroy       (GtkObject *object);
86 static void     gtk_knob_finalize      (GObject   *object);
87 static void     gtk_knob_realize       (GtkWidget *widget);
88 static void     gtk_knob_size_request  (GtkWidget *widget, GtkRequisition *requisition);
89 static void     gtk_knob_size_allocate (GtkWidget *widget, GtkAllocation  *allocation);
90 static gint     gtk_knob_button_press  (GtkWidget *widget, GdkEventButton *event);
91 static gint     gtk_knob_button_release(GtkWidget *widget, GdkEventButton *event);
92 static gint     gtk_knob_motion_notify (GtkWidget *widget, GdkEventMotion *event);
93 static gint     gtk_knob_timer_callback(GtkKnob *knob);
94 static void     gtk_knob_update_mouse  (GtkKnob *knob, gint x, gint y, gboolean absolute);
95 static void     gtk_knob_adjustment_value_changed (GtkAdjustment *adjustment,
96                                                    gpointer       data);
97 static void     gtk_knob_adjustment_changed (GtkAdjustment *adjustment,
98                                              gpointer       data);
99 static gboolean gtk_knob_expose        (GtkWidget *knob, GdkEventExpose *event);
100 static void     gtk_knob_common_initialize(GtkWidget *widget);
101 static void     gtk_knob_common_finalize(void);
102 
103 
104 static void
gtk_knob_class_init(GtkKnobClass * class)105 gtk_knob_class_init (GtkKnobClass *class)
106 {
107     GObjectClass   *gobject_class = G_OBJECT_CLASS (class);
108     GtkObjectClass *object_class = (GtkObjectClass*) class;
109     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
110 
111     parent_class = g_type_class_peek_parent (class);
112 
113     gobject_class->finalize = gtk_knob_finalize;
114     object_class->destroy = gtk_knob_destroy;
115 
116     widget_class->realize = gtk_knob_realize;
117     widget_class->size_request = gtk_knob_size_request;
118     widget_class->size_allocate = gtk_knob_size_allocate;
119     widget_class->expose_event = gtk_knob_expose;
120     widget_class->button_press_event = gtk_knob_button_press;
121     widget_class->button_release_event = gtk_knob_button_release;
122     widget_class->motion_notify_event = gtk_knob_motion_notify;
123 
124     common.refcount = 0;
125     common.pixmap = NULL;
126     common.insensitive_pixmap = NULL;
127     common.mask = NULL;
128     common.mask_gc = NULL;
129     common.red_gc = NULL;
130 #if GTK_CHECK_VERSION(2, 8, 0)
131     common.knob_surface = NULL;
132     common.insensitive_knob_surface = NULL;
133     common.shadow_pattern = NULL;
134 #endif
135 }
136 
137 
138 static void
gtk_knob_init(GtkKnob * knob)139 gtk_knob_init (GtkKnob *knob)
140 {
141     knob->adjustment = NULL;
142     knob->policy = GTK_UPDATE_CONTINUOUS;
143     knob->state = STATE_IDLE;
144     knob->center_x = 0;
145     knob->center_y = 0;
146     knob->saved_x = 0;
147     knob->saved_y = 0;
148     knob->old_value = 0.0;
149     knob->timer = 0;
150 
151     common.refcount++;
152 }
153 
154 
155 GtkWidget*
gtk_knob_new(GtkAdjustment * adjustment)156 gtk_knob_new (GtkAdjustment *adjustment)
157 {
158     GtkKnob *knob = g_object_new (GTK_TYPE_KNOB, NULL);
159 
160     gtk_knob_set_adjustment(knob, adjustment);
161 
162     return GTK_WIDGET(knob);
163 }
164 
165 
166 static void
gtk_knob_finalize(GObject * object)167 gtk_knob_finalize (GObject *object)
168 {
169     /* GtkKnob *knob = GTK_KNOB (object); */
170 
171     common.refcount--;
172     if (common.refcount == 0) gtk_knob_common_finalize();
173 
174     (* G_OBJECT_CLASS (parent_class)->finalize) (object);
175 }
176 
177 
178 static void
gtk_knob_destroy(GtkObject * object)179 gtk_knob_destroy (GtkObject *object)
180 {
181     GtkKnob *knob = GTK_KNOB(object);
182 
183     if (knob->adjustment) {
184         g_signal_handlers_disconnect_by_func (knob->adjustment,
185                                               gtk_knob_adjustment_changed,
186                                               knob);
187         g_signal_handlers_disconnect_by_func (knob->adjustment,
188                                               gtk_knob_adjustment_value_changed,
189                                               knob);
190         g_object_unref(G_OBJECT(knob->adjustment));
191         knob->adjustment = NULL;
192     }
193     if (knob->timer) {
194         g_source_remove(knob->timer);
195         knob->timer = 0;
196     }
197 
198     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
199 }
200 
201 
202 void
gtk_knob_set_fast_rendering(gboolean setting)203 gtk_knob_set_fast_rendering(gboolean setting)
204 {
205 #if GTK_CHECK_VERSION(2, 8, 0)
206     prefer_cairo = (setting ? FALSE : TRUE);
207 #else
208     prefer_cairo = FALSE;
209 #endif
210 }
211 
212 
213 GtkAdjustment*
gtk_knob_get_adjustment(GtkKnob * knob)214 gtk_knob_get_adjustment (GtkKnob *knob)
215 {
216     g_return_val_if_fail (GTK_IS_KNOB (knob), NULL);
217 
218     if (!knob->adjustment)
219         gtk_knob_set_adjustment (knob, NULL);
220 
221     return knob->adjustment;
222 }
223 
224 
225 void
gtk_knob_set_adjustment(GtkKnob * knob,GtkAdjustment * adjustment)226 gtk_knob_set_adjustment (GtkKnob *knob, GtkAdjustment *adjustment)
227 {
228     g_return_if_fail (GTK_IS_KNOB (knob));
229 
230     if (!adjustment)
231         adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
232     else
233         g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
234 
235     if (knob->adjustment) {
236         g_signal_handlers_disconnect_by_func (knob->adjustment,
237                                               gtk_knob_adjustment_changed,
238                                               knob);
239         g_signal_handlers_disconnect_by_func (knob->adjustment,
240                                               gtk_knob_adjustment_value_changed,
241                                               knob);
242         g_object_unref(G_OBJECT(knob->adjustment));
243     }
244 
245     knob->adjustment = adjustment;
246     g_object_ref(G_OBJECT(knob->adjustment));
247     gtk_object_sink(GTK_OBJECT(knob->adjustment)); /* deprecated, but g_object_ref_sink() requires glib 2.10 or later */
248 
249     g_signal_connect(G_OBJECT(adjustment), "changed",
250                      G_CALLBACK(gtk_knob_adjustment_changed), (gpointer) knob);
251     g_signal_connect(G_OBJECT(adjustment), "value_changed",
252                      G_CALLBACK(gtk_knob_adjustment_value_changed), (gpointer) knob);
253 
254     gtk_knob_adjustment_changed (adjustment, knob);
255 }
256 
257 
258 void
gtk_knob_set_update_policy(GtkKnob * knob,GtkUpdateType policy)259 gtk_knob_set_update_policy(GtkKnob *knob, GtkUpdateType policy)
260 {
261     g_return_if_fail (GTK_IS_KNOB (knob));
262 
263     knob->policy = policy;
264 }
265 
266 
267 static void
gtk_knob_realize(GtkWidget * widget)268 gtk_knob_realize (GtkWidget *widget)
269 {
270     GtkKnob *knob;
271     GdkWindowAttr attributes;
272     gint attributes_mask;
273 
274     g_return_if_fail (GTK_IS_KNOB (widget));
275 
276     knob = GTK_KNOB (widget);
277     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
278 
279     attributes.window_type = GDK_WINDOW_CHILD;
280     attributes.x = widget->allocation.x;
281     attributes.y = widget->allocation.y;
282     attributes.width = widget->allocation.width;
283     attributes.height = widget->allocation.height;
284     attributes.wclass = GDK_INPUT_OUTPUT;
285     attributes.visual = gtk_widget_get_visual (widget);
286     attributes.colormap = gtk_widget_get_colormap (widget);
287     attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK |
288                                 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
289                                 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
290 
291     attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
292 
293     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
294     gdk_window_set_user_data (widget->window, knob);
295 
296     widget->style = gtk_style_attach (widget->style, widget->window);
297     gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
298 
299     gtk_knob_common_initialize(widget);
300 }
301 
302 
303 static void
gtk_knob_size_request(GtkWidget * widget,GtkRequisition * requisition)304 gtk_knob_size_request (GtkWidget *widget, GtkRequisition *requisition)
305 {
306     requisition->width = KNOB_SIZE;
307     requisition->height = KNOB_SIZE;
308 }
309 
310 
311 static void
gtk_knob_size_allocate(GtkWidget * widget,GtkAllocation * allocation)312 gtk_knob_size_allocate (GtkWidget     *widget,
313                         GtkAllocation *allocation)
314 {
315     g_return_if_fail (GTK_IS_KNOB (widget));
316     g_return_if_fail (allocation != NULL);
317 
318     widget->allocation = *allocation;
319 
320     if (GTK_WIDGET_REALIZED (widget)) {
321         gdk_window_move_resize (widget->window,
322                                 allocation->x, allocation->y,
323                                 allocation->width, allocation->height);
324     }
325 }
326 
327 
328 static gint
gtk_knob_button_press(GtkWidget * widget,GdkEventButton * event)329 gtk_knob_button_press (GtkWidget *widget, GdkEventButton *event)
330 {
331     GtkKnob *knob;
332 
333     g_return_val_if_fail(GTK_IS_KNOB(widget), FALSE);
334     g_return_val_if_fail(event != NULL, FALSE);
335 
336     knob = GTK_KNOB(widget);
337 
338     switch (knob->state) {
339       case STATE_IDLE:
340         switch (event->button) {
341           case 1:
342           case 3:
343             gtk_grab_add(widget);
344             knob->state = STATE_PRESSED;
345             knob->center_x = widget->allocation.width / 2;
346             knob->center_y = widget->allocation.height / 2;
347             knob->saved_x = event->x;
348             knob->saved_y = event->y;
349             break;
350 
351           default:
352             break;
353         }
354         break;
355 
356       default:
357         break;
358     }
359 
360     return FALSE;
361 }
362 
363 
364 static gint
gtk_knob_button_release(GtkWidget * widget,GdkEventButton * event)365 gtk_knob_button_release (GtkWidget *widget, GdkEventButton *event)
366 {
367     GtkKnob *knob;
368     gfloat value;
369 
370     g_return_val_if_fail(GTK_IS_KNOB(widget), FALSE);
371     g_return_val_if_fail(event != NULL, FALSE);
372 
373     knob = GTK_KNOB(widget);
374 
375     switch (knob->state) {
376       case STATE_PRESSED:
377         gtk_grab_remove(widget);
378         knob->state = STATE_IDLE;
379 
380         switch (event->button) {
381           case 1:
382             value = knob->adjustment->value - knob->adjustment->page_increment;
383             value = MAX(MIN(value, knob->adjustment->upper),
384                         knob->adjustment->lower);
385             if (knob->adjustment->value != value) {
386                 knob->adjustment->value = value;
387                 g_signal_emit_by_name(G_OBJECT(knob->adjustment), "value_changed");
388             }
389             break;
390 
391           case 3:
392             value = knob->adjustment->value + knob->adjustment->page_increment;
393             value = MAX(MIN(value, knob->adjustment->upper),
394                         knob->adjustment->lower);
395             if (knob->adjustment->value != value) {
396                 knob->adjustment->value = value;
397                 g_signal_emit_by_name(G_OBJECT(knob->adjustment), "value_changed");
398             }
399             break;
400 
401           default:
402             break;
403         }
404         break;
405 
406       case STATE_DRAGGING:
407         gtk_grab_remove(widget);
408         knob->state = STATE_IDLE;
409 
410         if (knob->policy != GTK_UPDATE_CONTINUOUS && knob->old_value != knob->adjustment->value)
411             g_signal_emit_by_name(G_OBJECT(knob->adjustment), "value_changed");
412 
413         break;
414 
415       default:
416         break;
417     }
418 
419     return FALSE;
420 }
421 
422 
423 static gint
gtk_knob_motion_notify(GtkWidget * widget,GdkEventMotion * event)424 gtk_knob_motion_notify (GtkWidget *widget, GdkEventMotion *event)
425 {
426     GtkKnob *knob;
427     GdkModifierType mods;
428     gint x, y;
429 
430     g_return_val_if_fail(GTK_IS_KNOB(widget), FALSE);
431     g_return_val_if_fail(event != NULL, FALSE);
432 
433     knob = GTK_KNOB(widget);
434 
435     x = event->x;
436     y = event->y;
437 
438     if (event->is_hint || (event->window != widget->window))
439         gdk_window_get_pointer(widget->window, &x, &y, &mods);
440 
441     switch (knob->state) {
442       case STATE_PRESSED:
443         knob->state = STATE_DRAGGING;
444         /* fall through */
445 
446       case STATE_DRAGGING:
447         if (mods & GDK_BUTTON1_MASK) {
448             gtk_knob_update_mouse(knob, x, y, TRUE);
449             return TRUE;
450         } else if (mods & GDK_BUTTON3_MASK) {
451             gtk_knob_update_mouse(knob, x, y, FALSE);
452             return TRUE;
453         }
454         break;
455 
456       default:
457         break;
458     }
459 
460     return FALSE;
461 }
462 
463 
464 static gint
gtk_knob_timer_callback(GtkKnob * knob)465 gtk_knob_timer_callback (GtkKnob *knob)
466 {
467     g_return_val_if_fail(GTK_IS_KNOB(knob), FALSE);
468 
469     if (knob->policy == GTK_UPDATE_DELAYED)
470         g_signal_emit_by_name(G_OBJECT(knob->adjustment), "value_changed");
471 
472     return FALSE;       /* don't keep running this timer */
473 }
474 
475 
476 static void
gtk_knob_update_mouse(GtkKnob * knob,gint x,gint y,gboolean absolute)477 gtk_knob_update_mouse(GtkKnob *knob, gint x, gint y, gboolean absolute)
478 {
479     gfloat old_value, new_value;
480     gdouble angle;
481     gint dv, dh;
482 
483     g_return_if_fail(GTK_IS_KNOB(knob));
484 
485     old_value = knob->adjustment->value;
486 
487     if (absolute) {
488 
489         angle = atan2(-y + knob->center_y, x - knob->center_x);
490         angle /= M_PI;
491         if (angle < -0.5)
492             angle += 2;
493 
494         new_value = -(2.0/3.0) * (angle - 1.25);   /* map [1.25pi, -0.25pi] onto [0, 1] */
495         new_value *= knob->adjustment->upper - knob->adjustment->lower;
496         new_value += knob->adjustment->lower;
497 
498     } else {
499 
500         dv = knob->center_y - y; /* inverted cartesian graphics coordinate system */
501         dh = x - knob->center_x;
502 
503         if (abs(dv) < HALF_KNOB_SIZE && abs(dh) < HALF_KNOB_SIZE) {
504 
505             /* translate close-in motion into fine adjustment */
506             dv = knob->saved_y - y;
507             dh = x - knob->saved_x;
508             new_value = knob->adjustment->value +
509                         (float)(dv + dh) * knob->adjustment->step_increment;
510 
511         } else if (abs(dv) > abs(dh)) {
512 
513             /* translate up-and-down motion into fine adjustment */
514             dv = knob->saved_y - y;
515             new_value = knob->adjustment->value +
516                         (float)dv * knob->adjustment->step_increment;
517 
518         } else {
519 
520             /* translate side-to-side motion into coarse adjustment */
521             dh = x - knob->saved_x;
522             new_value = knob->adjustment->value +
523                         (float)dh * (knob->adjustment->upper -
524                                      knob->adjustment->lower) / 300.0f;
525 
526         }
527         knob->saved_x = x;
528         knob->saved_y = y;
529     }
530 
531     new_value = MAX(MIN(new_value, knob->adjustment->upper),
532                     knob->adjustment->lower);
533 
534     knob->adjustment->value = new_value;
535 
536     if (knob->adjustment->value != old_value) {
537 
538         if (knob->policy == GTK_UPDATE_CONTINUOUS)
539             g_signal_emit_by_name(G_OBJECT(knob->adjustment), "value_changed");
540         else {
541             gtk_widget_queue_draw (GTK_WIDGET(knob));
542 
543             if (knob->policy == GTK_UPDATE_DELAYED) {
544                 if (knob->timer)
545                     g_source_remove(knob->timer);
546 
547                 knob->timer = g_timeout_add (UPDATE_DELAY_LENGTH,
548                                              (GSourceFunc) gtk_knob_timer_callback,
549                                              (gpointer) knob);
550           }
551         }
552     }
553 }
554 
555 
556 static void
gtk_knob_adjustment_changed(GtkAdjustment * adjustment,gpointer data)557 gtk_knob_adjustment_changed(GtkAdjustment *adjustment, gpointer data)
558 {
559     GtkKnob *knob;
560 
561     g_return_if_fail(adjustment != NULL);
562     g_return_if_fail(data != NULL);
563 
564     knob = GTK_KNOB(data);
565 
566     knob->old_value = adjustment->value;
567 
568     gtk_widget_queue_draw (GTK_WIDGET(knob));
569 }
570 
571 static void
gtk_knob_adjustment_value_changed(GtkAdjustment * adjustment,gpointer data)572 gtk_knob_adjustment_value_changed (GtkAdjustment *adjustment, gpointer data)
573 {
574     GtkKnob *knob;
575 
576     g_return_if_fail(adjustment != NULL);
577     g_return_if_fail(data != NULL);
578 
579     knob = GTK_KNOB(data);
580 
581     if (knob->old_value != adjustment->value) {
582         knob->old_value = adjustment->value;
583 
584         gtk_widget_queue_draw (GTK_WIDGET(knob));
585     }
586 }
587 
588 
589 static void
gtk_knob_paint_xlib(GtkWidget * widget,int center_x,int center_y)590 gtk_knob_paint_xlib(GtkWidget *widget, int center_x, int center_y)
591 {
592     GtkKnob *knob = GTK_KNOB(widget);
593     gfloat dx, dy;
594 
595     gdk_window_clear_area(widget->window, 0, 0, widget->allocation.width, widget->allocation.height);
596 
597     center_x -= HALF_KNOB_SIZE;
598     center_y -= HALF_KNOB_SIZE;
599     gdk_gc_set_clip_origin(common.mask_gc, center_x, center_y);
600 
601     dx = knob->adjustment->value - knob->adjustment->lower;
602     dy = knob->adjustment->upper - knob->adjustment->lower;
603 
604     if (dy != 0 && GTK_WIDGET_IS_SENSITIVE (widget)) {
605         dx = MIN(MAX(dx / dy, 0), 1);
606         dx = -1.5 * dx + 1.25;
607 
608         gdk_draw_arc (widget->window, common.red_gc, TRUE,
609                       1 + center_x , 1 + center_y,
610                       KNOB_SIZE - 4, KNOB_SIZE - 2,
611                       dx * 180.f * 64.f, (1.25f - dx) * 180.f * 64.f);
612         gdk_draw_pixmap(widget->window, common.mask_gc, common.pixmap,
613                         0, 0, center_x, center_y, KNOB_SIZE, KNOB_SIZE);
614 
615         dx *= M_PI;
616         dy = -sin(dx) * 11 + 15.5;
617         dx = cos(dx) * 11 + 15.5;
618 
619         gdk_draw_line(widget->window, widget->style->white_gc,
620                       15 + center_x, 16 + center_y,
621                       dx + center_x, dy + center_y);
622     } else {
623         gdk_draw_pixmap(widget->window, common.mask_gc, common.insensitive_pixmap,
624                         0, 0, center_x, center_y, KNOB_SIZE, KNOB_SIZE);
625     }
626 }
627 
628 
629 #if GTK_CHECK_VERSION(2, 8, 0)
630 static void
gtk_knob_paint_cairo(GtkWidget * widget,cairo_t * cr)631 gtk_knob_paint_cairo(GtkWidget *widget, cairo_t *cr)
632 {
633     GtkKnob *knob = GTK_KNOB(widget);
634     gfloat value, range;
635 
636     if (GTK_WIDGET_IS_SENSITIVE (widget)) {
637 
638         range = knob->adjustment->upper - knob->adjustment->lower;
639         if (fabs(range) < 1e-37) {
640             value = 0.0;
641         } else {
642             value = MAX(MIN(knob->adjustment->value, knob->adjustment->upper),
643                         knob->adjustment->lower);
644             value = value - knob->adjustment->lower;
645             value /= range;  /* now scaled 0.0 to 1.0 */
646         }
647 
648         /* draw the red arc */
649         cairo_set_source_rgb (cr, 1.0, 0, 0);
650         cairo_move_to (cr, HALF_KNOB_SIZE, HALF_KNOB_SIZE);
651         cairo_arc (cr, HALF_KNOB_SIZE, HALF_KNOB_SIZE, (KNOB_SIZE * 0.47),
652                    M_PI * 0.75, M_PI * 0.75 + value * (1.5 * M_PI));
653         cairo_close_path (cr);
654         cairo_fill (cr);
655 
656         /* draw the knob shadow */
657         cairo_set_source (cr, common.shadow_pattern);
658         cairo_paint (cr);
659 
660         /* paint the knob and reference points over what we have so far */
661         cairo_set_source_surface (cr, common.knob_surface, 0, 0);
662         cairo_paint (cr);
663 
664         /* add cursor to knob */
665         cairo_translate (cr, HALF_KNOB_SIZE, HALF_KNOB_SIZE);
666         cairo_rotate (cr, M_PI * 0.75 + value * (1.5 * M_PI));
667         cairo_set_line_width (cr, KNOB_SIZE / 32.0 * 1.5);
668         cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
669         cairo_move_to (cr, KNOB_SIZE * 0.06, 0);
670         cairo_line_to (cr, KNOB_SIZE * 0.30, 0);
671         cairo_stroke (cr);
672 
673     } else {  /* insensitive */
674         cairo_set_source_surface (cr, common.insensitive_knob_surface, 0, 0);
675         cairo_paint (cr);
676     }
677 }
678 #endif /* GTK_CHECK_VERSION(2, 8, 0) */
679 
680 
681 static gboolean
gtk_knob_expose(GtkWidget * widget,GdkEventExpose * event)682 gtk_knob_expose (GtkWidget *widget, GdkEventExpose *event)
683 {
684     int center_x, center_y;
685 #if GTK_CHECK_VERSION(2, 8, 0)
686     int l, t, r, b;
687     cairo_t *cr;
688 #endif /* GTK_CHECK_VERSION(2, 8, 0) */
689 
690     g_return_val_if_fail(GTK_IS_KNOB(widget), FALSE);
691     g_return_val_if_fail(event != NULL, FALSE);
692 
693     /* minimize the clip area to just what we'll actually draw (32x32 at most) */
694     center_x = widget->allocation.width / 2;
695     center_y = widget->allocation.height / 2;
696 
697     if (prefer_cairo) {
698 #if GTK_CHECK_VERSION(2, 8, 0)
699         if (common.knob_surface == NULL)
700             gtk_knob_common_initialize(widget);
701 
702         if ((center_x - HALF_KNOB_SIZE >= event->area.x + event->area.width)  ||
703             (center_x + HALF_KNOB_SIZE <= event->area.x)                      ||
704             (center_y - HALF_KNOB_SIZE >= event->area.y + event->area.height) ||
705             (center_y + HALF_KNOB_SIZE <= event->area.y)) {
706             /* clip area is empty */
707             return FALSE;
708         }
709         l = MAX(event->area.x,                      center_x - HALF_KNOB_SIZE);
710         r = MIN(event->area.x + event->area.width,  center_x + HALF_KNOB_SIZE);
711         t = MAX(event->area.y,                      center_y - HALF_KNOB_SIZE);
712         b = MIN(event->area.y + event->area.height, center_y + HALF_KNOB_SIZE);
713         cr = gdk_cairo_create (widget->window);
714         cairo_rectangle (cr, l, t, r - l, b - t);
715         cairo_clip (cr);
716         cairo_translate(cr, center_x - HALF_KNOB_SIZE, center_y - HALF_KNOB_SIZE);
717 
718         gtk_knob_paint_cairo(widget, cr);
719 
720         cairo_destroy (cr);
721 #endif /* GTK_CHECK_VERSION(2, 8, 0) */
722     } else {
723         if (common.pixmap == NULL)
724             gtk_knob_common_initialize(widget);
725 
726         gtk_knob_paint_xlib(widget, center_x, center_y);
727     }
728 
729     return FALSE;
730 }
731 
732 
733 /* forward declarations of XPM data below */
734 static char * knob_xpm[];
735 static char * insensitive_knob_xpm[];
736 
737 static void
gtk_knob_common_initialize_xlib(GtkWidget * widget)738 gtk_knob_common_initialize_xlib(GtkWidget *widget)
739 {
740     GdkColor color = { 0, 0xffff, 0, 0 };
741 
742     common.pixmap = gdk_pixmap_create_from_xpm_d(widget->window, &common.mask,
743                                                  &widget->style->bg[GTK_STATE_NORMAL],
744                                                  knob_xpm);
745     common.insensitive_pixmap = gdk_pixmap_create_from_xpm_d(widget->window, &common.mask,
746                                                  &widget->style->bg[GTK_STATE_NORMAL],
747                                                  insensitive_knob_xpm);
748 
749     common.mask_gc = gdk_gc_new(widget->window);
750     gdk_gc_copy(common.mask_gc, widget->style->bg_gc[GTK_STATE_NORMAL]);
751     gdk_gc_set_clip_mask(common.mask_gc, common.mask);
752 
753     common.red_gc = gdk_gc_new(widget->window);
754     gdk_gc_copy(common.red_gc, widget->style->bg_gc[GTK_STATE_NORMAL]);
755     gdk_colormap_alloc_color(gtk_widget_get_colormap (widget), &color, FALSE, TRUE);
756     gdk_gc_set_foreground(common.red_gc, &color);
757 }
758 
759 static void
gtk_knob_common_finalize_xlib(void)760 gtk_knob_common_finalize_xlib(void)
761 {
762     if (common.pixmap) {
763         gdk_pixmap_unref(common.pixmap);
764         common.pixmap = NULL;
765     }
766     if (common.insensitive_pixmap) {
767         gdk_pixmap_unref(common.insensitive_pixmap);
768         common.insensitive_pixmap = NULL;
769     }
770     if (common.mask) {
771         gdk_bitmap_unref(common.mask);
772         common.mask = NULL;
773     }
774     if (common.mask_gc) {
775         gdk_gc_unref(common.mask_gc);
776         common.mask_gc = NULL;
777     }
778     if (common.red_gc) {
779         gdk_gc_unref(common.red_gc);
780         common.red_gc = NULL;
781     }
782 }
783 
784 #if GTK_CHECK_VERSION(2, 8, 0)
785 
786 /* forward declarations of PNG data below */
787 static size_t        knob_c_png_length;
788 static unsigned char knob_c_png[];
789 static size_t        knob_i_png_length;
790 static unsigned char knob_i_png[];
791 
792 struct cairo_read_func_closure {
793     size_t         length;
794     size_t         offset;
795     unsigned char *data;
796 };
797 
798 static cairo_status_t
cairo_read_func(void * closure,unsigned char * data,unsigned int length)799 cairo_read_func(void *closure, unsigned char *data, unsigned int length)
800 {
801     struct cairo_read_func_closure *cl = (struct cairo_read_func_closure *)closure;
802     size_t l = MIN(length, cl->length - cl->offset);
803 
804     memcpy(data, cl->data + cl->offset, l);
805 
806     cl->offset += l;
807 
808     return CAIRO_STATUS_SUCCESS;
809 }
810 
811 static void
gtk_knob_common_initialize_cairo(void)812 gtk_knob_common_initialize_cairo(void)
813 {
814     struct cairo_read_func_closure cl;
815     cairo_pattern_t *pat;
816 
817     /* create knob surfaces */
818     cl.length = knob_c_png_length;
819     cl.offset = 0;
820     cl.data = knob_c_png;
821     common.knob_surface = cairo_image_surface_create_from_png_stream(cairo_read_func, &cl);
822     cl.length = knob_i_png_length;
823     cl.offset = 0;
824     cl.data = knob_i_png;
825     common.insensitive_knob_surface = cairo_image_surface_create_from_png_stream(cairo_read_func, &cl);
826 
827     /* create the shadow pattern */
828     pat = cairo_pattern_create_radial (KNOB_SIZE * 0.61, KNOB_SIZE * 0.61, 0.0,
829                                        KNOB_SIZE * 0.61, KNOB_SIZE * 0.61, KNOB_SIZE * 0.4);  /* 0.375 */
830     cairo_pattern_add_color_stop_rgba (pat, 0.75, 0, 0, 0, 0.5);
831     cairo_pattern_add_color_stop_rgba (pat, 0.90, 0, 0, 0, 0.1);
832     cairo_pattern_add_color_stop_rgba (pat, 1.00, 0, 0, 0, 0);
833     common.shadow_pattern = pat;
834 }
835 
836 static void
gtk_knob_common_finalize_cairo(void)837 gtk_knob_common_finalize_cairo(void)
838 {
839     if (common.knob_surface) {
840         cairo_surface_destroy (common.knob_surface);
841         common.knob_surface = NULL;
842     }
843     if (common.insensitive_knob_surface) {
844         cairo_surface_destroy (common.insensitive_knob_surface);
845         common.insensitive_knob_surface = NULL;
846     }
847     if (common.shadow_pattern) {
848         cairo_pattern_destroy (common.shadow_pattern);
849         common.shadow_pattern = NULL;
850     }
851 }
852 
853 #endif  /* GTK_CHECK_VERSION(2, 8, 0) */
854 
855 
856 static void
gtk_knob_common_initialize(GtkWidget * widget)857 gtk_knob_common_initialize(GtkWidget *widget)
858 {
859 #if GTK_CHECK_VERSION(2, 8, 0)
860     if (prefer_cairo) {
861         gtk_knob_common_initialize_cairo();
862         gtk_knob_common_finalize_xlib();
863     } else {
864         gtk_knob_common_initialize_xlib(widget);
865         gtk_knob_common_finalize_cairo();
866     }
867 #else /* GTK_CHECK_VERSION(2, 8, 0) */
868     gtk_knob_common_initialize_xlib(widget);
869 #endif /* GTK_CHECK_VERSION(2, 8, 0) */
870 }
871 
872 static void
gtk_knob_common_finalize(void)873 gtk_knob_common_finalize(void)
874 {
875     gtk_knob_common_finalize_xlib();
876 #if GTK_CHECK_VERSION(2, 8, 0)
877     gtk_knob_common_finalize_cairo();
878 #endif /* GTK_CHECK_VERSION(2, 8, 0) */
879 }
880 
881 
882 static char * knob_xpm[] = {
883 "32 32 106 2",
884 "  	c None",
885 ". 	c #FCFCFD",
886 "+ 	c #0A0A20",
887 "@ 	c #6B6B9E",
888 "# 	c #8989B3",
889 "$ 	c #66669A",
890 "% 	c #AEAECC",
891 "& 	c #AAAACA",
892 "* 	c #A6A6C7",
893 "= 	c #9C9CC0",
894 "- 	c #9494BB",
895 "; 	c #8E8EB7",
896 "> 	c #8C8CB6",
897 ", 	c #7F7FAD",
898 "' 	c #A0A0C3",
899 ") 	c #B5B5D1",
900 "! 	c #B0B0CE",
901 "~ 	c #8181AE",
902 "{ 	c #7C7CAA",
903 "] 	c #7575A5",
904 "^ 	c #595991",
905 "/ 	c #9696BC",
906 "( 	c #8383AF",
907 "_ 	c #7A7AA9",
908 ": 	c #7171A3",
909 "< 	c #7070A2",
910 "[ 	c #626298",
911 "} 	c #42427E",
912 "| 	c #9292BA",
913 "1 	c #6E6EA0",
914 "2 	c #616197",
915 "3 	c #5F5F95",
916 "4 	c #585890",
917 "5 	c #464682",
918 "6 	c #2E2E6D",
919 "7 	c #9A9ABF",
920 "8 	c #69699D",
921 "9 	c #5B5B92",
922 "0 	c #4C4C86",
923 "a 	c #363674",
924 "b 	c #222262",
925 "c 	c #5E5E94",
926 "d 	c #52528B",
927 "e 	c #3F3F7C",
928 "f 	c #282868",
929 "g 	c #A2A2C4",
930 "h 	c #43437F",
931 "i 	c #2D2D6C",
932 "j 	c #1F1F5E",
933 "k 	c #333371",
934 "l 	c #202060",
935 "m 	c #30306F",
936 "n 	c #1D1D58",
937 "o 	c #53538C",
938 "p 	c #454581",
939 "q 	c #1C1C54",
940 "r 	c #19194C",
941 "s 	c #50508A",
942 "t 	c #3B3B79",
943 "u 	c #232363",
944 "v 	c #181848",
945 "w 	c #15153F",
946 "x 	c #4A4A85",
947 "y 	c #353573",
948 "z 	c #1C1C56",
949 "A 	c #14143E",
950 "B 	c #121237",
951 "C 	c #2F2F6E",
952 "D 	c #121236",
953 "E 	c #7777A7",
954 "F 	c #292969",
955 "G 	c #161642",
956 "H 	c #0E0E2C",
957 "I 	c #13133A",
958 "J 	c #0C0C26",
959 "K 	c #393977",
960 "L 	c #0E0E2B",
961 "M 	c #4F4F89",
962 "N 	c #161644",
963 "O 	c #0A0A1F",
964 "P 	c #474783",
965 "Q 	c #41417D",
966 "R 	c #3E3E7B",
967 "S 	c #54548D",
968 "T 	c #171747",
969 "U 	c #0A0A1E",
970 "V 	c #080819",
971 "W 	c #1E1E5A",
972 "X 	c #0F0F2D",
973 "Y 	c #09091C",
974 "Z 	c #070717",
975 "` 	c #2B2B6A",
976 " .	c #343472",
977 "..	c #252565",
978 "+.	c #19194B",
979 "@.	c #0D0D29",
980 "#.	c #070715",
981 "$.	c #1A1A4F",
982 "%.	c #101032",
983 "&.	c #0D0D28",
984 "*.	c #0F0F2F",
985 "=.	c #101030",
986 "-.	c #0B0B22",
987 ";.	c #09091B",
988 ">.	c #060614",
989 ",.	c #060612",
990 "                                                                ",
991 "                              + +                               ",
992 "                              + +                               ",
993 "                                                                ",
994 "                            @ # # $                             ",
995 "                      % & * = - ; > # # ,                       ",
996 "                  ' ) ! & = ; # ~ , { { ] @ ^                   ",
997 "                % ) ) * / ( _ : < @ @ @ @ [ ^ }                 ",
998 "              & ) ) * | { 1 $ 2 2 3 3 3 3 3 4 5 6               ",
999 "            7 ) ) & | _ 8 3 3 3 3 3 3 3 3 3 9 0 a b             ",
1000 "            & % * | { $ 3 3 3 3 3 3 3 3 3 3 c d e f             ",
1001 "          ' & g | { $ 3 3 3 3 3 3 3 3 3 3 3 3 4 h i j           ",
1002 "          * * / , @ 3 3 3 3 3 3 3 3 3 3 3 3 3 9 0 k l           ",
1003 "          g ' > ] 2 3 3 3 3 3 3 3 3 3 3 3 3 3 ^ 0 m n           ",
1004 "        1 ' = ( 1 3 3 3 3 3 3 3 3 3 3 3 3 3 9 o p i q r         ",
1005 "        > = / , @ 3 3 3 3 3 3 3 3 3 3 3 3 c ^ s t u v w         ",
1006 "        # 7 ; { $ 3 3 3 3 3 3 3 3 3 3 3 3 9 4 x y z A B         ",
1007 "        3 > ( ] 2 3 3 3 3 3 3 3 3 3 3 3 3 ^ d 5 C r D B         ",
1008 "          E E @ 3 3 3 3 3 3 3 3 3 3 3 3 9 4 s e F G H           ",
1009 "          [ [ 3 c c 3 3 3 3 3 3 3 3 3 3 9 d 5 a j I J           ",
1010 "          c ^ o o 9 9 3 3 3 3 c 9 9 9 ^ d 5 K u G L +           ",
1011 "            s x x s 4 9 3 3 c 9 4 o s M x a u N H O             ",
1012 "            P Q R h M S 9 9 ^ o M P h e k b T L U V             ",
1013 "              K k a t h 0 M 0 x h K m f W G X Y Z               ",
1014 "                ` i ` i 6 k  .m ` ..n +.B @.Y #.                ",
1015 "          + +     l q r +.v $.$.v w %.&.U Z #.    + +           ",
1016 "          + +         B *.=.=.=.@.-.;.>.,.        + +           ",
1017 "                            %.J O J                             ",
1018 "                                                                ",
1019 "                                                                ",
1020 "                                                                ",
1021 "                                                                "};
1022 
1023 static char * insensitive_knob_xpm[] = {
1024 "32 32 82 1",
1025 " 	c None",
1026 ".	c #777777",
1027 "+	c #B8B8B8",
1028 "@	c #C7C7C7",
1029 "#	c #B5B5B5",
1030 "$	c #D9D9D9",
1031 "%	c #D7D7D7",
1032 "&	c #D5D5D5",
1033 "*	c #D0D0D0",
1034 "=	c #CCCCCC",
1035 "-	c #CACACA",
1036 ";	c #C8C8C8",
1037 ">	c #C2C2C2",
1038 ",	c #D2D2D2",
1039 "'	c #DCDCDC",
1040 ")	c #DADADA",
1041 "!	c #C3C3C3",
1042 "~	c #C0C0C0",
1043 "{	c #BDBDBD",
1044 "]	c #AFAFAF",
1045 "^	c #CDCDCD",
1046 "/	c #C4C4C4",
1047 "(	c #BBBBBB",
1048 "_	c #BABABA",
1049 ":	c #B3B3B3",
1050 "<	c #A3A3A3",
1051 "[	c #CBCBCB",
1052 "}	c #B9B9B9",
1053 "|	c #B2B2B2",
1054 "1	c #AEAEAE",
1055 "2	c #A5A5A5",
1056 "3	c #989898",
1057 "4	c #CFCFCF",
1058 "5	c #B7B7B7",
1059 "6	c #B0B0B0",
1060 "7	c #A8A8A8",
1061 "8	c #9C9C9C",
1062 "9	c #919191",
1063 "0	c #B1B1B1",
1064 "a	c #ABABAB",
1065 "b	c #A1A1A1",
1066 "c	c #959595",
1067 "d	c #D3D3D3",
1068 "e	c #8F8F8F",
1069 "f	c #9A9A9A",
1070 "g	c #909090",
1071 "h	c #999999",
1072 "i	c #8D8D8D",
1073 "j	c #ACACAC",
1074 "k	c #A4A4A4",
1075 "l	c #8B8B8B",
1076 "m	c #888888",
1077 "n	c #AAAAAA",
1078 "o	c #9F9F9F",
1079 "p	c #929292",
1080 "q	c #878787",
1081 "r	c #838383",
1082 "s	c #A7A7A7",
1083 "t	c #8C8C8C",
1084 "u	c #808080",
1085 "v	c #BEBEBE",
1086 "w	c #848484",
1087 "x	c #7C7C7C",
1088 "y	c #818181",
1089 "z	c #797979",
1090 "A	c #9E9E9E",
1091 "B	c #858585",
1092 "C	c #A2A2A2",
1093 "D	c #868686",
1094 "E	c #767676",
1095 "F	c #757575",
1096 "G	c #8E8E8E",
1097 "H	c #737373",
1098 "I	c #969696",
1099 "J	c #9B9B9B",
1100 "K	c #939393",
1101 "L	c #7A7A7A",
1102 "M	c #8A8A8A",
1103 "N	c #7E7E7E",
1104 "O	c #7D7D7D",
1105 "P	c #787878",
1106 "Q	c #727272",
1107 "                                ",
1108 "               ..               ",
1109 "               ..               ",
1110 "                                ",
1111 "              +@@#              ",
1112 "           $%&*=-;@@>           ",
1113 "         ,')%*-@!>~~{+]         ",
1114 "        $''&^/~(_++++:]<        ",
1115 "       %''&[~}#::|||||123       ",
1116 "      4''%[~5|||||||||6789      ",
1117 "      %$&[~#||||||||||0abc      ",
1118 "     ,%d[~#||||||||||||1<3e     ",
1119 "     &&^>+|||||||||||||67fg     ",
1120 "     d,;{:|||||||||||||]7hi     ",
1121 "    },*/}|||||||||||||6jk3lm    ",
1122 "    ;*^>+||||||||||||0]nopqr    ",
1123 "    @4-~#||||||||||||61s8tru    ",
1124 "    |;/{:||||||||||||]a2hmuu    ",
1125 "     vv+||||||||||||61nbcwx     ",
1126 "     ::|00||||||||||6a28eyz     ",
1127 "     0]jj66||||0666]a2Apwx.     ",
1128 "      nssn16||061jnns8pBx.      ",
1129 "      2Cb<nj66]jn2<bf9DxEF      ",
1130 "       Af8o<7n7s<AhcGwxEH       ",
1131 "        I3I33fJhIKimuLEH        ",
1132 "     ..  glmmqMMqrNLEHH  ..     ",
1133 "     ..    uOOOOLPFQQ    ..     ",
1134 "              Nz.z              ",
1135 "                                ",
1136 "                                ",
1137 "                                ",
1138 "                                "};
1139 
1140 #if GTK_CHECK_VERSION(2, 8, 0)
1141 
1142 static size_t        knob_c_png_length = 1082;
1143 static unsigned char knob_c_png[1082] =
1144   "\211PNG\15\12\32\12\0\0\0\15IHDR\0\0\0\40\0\0\0\40\10\6\0\0\0szz\364"
1145   "\0\0\0\1sRGB\0\256\316\34\351\0\0\0\6bKGD\0\377\0\377\0\377\240\275\247"
1146   "\223\0\0\3\342IDATX\303\355VM\213\34U\24=\357\325Gw'0\321\4\304\30w\371"
1147   "\15a\\d\231a6\1]E\30W\16\10.t'\"nD\307\215\333\254$\213\350&\270\226"
1148   "\20\2\223E>6\306e\230\14\202\211`23NwM\367t}\276z_\367=\27\325\335d\222"
1149   "\350twt!\346@AQE\335{\352\336s\356}\300\177\34~t\315\15\216\377;\330"
1150   "<\37\335\272\365\313\345$\311\227\262L\236\320\332\40\216\303\301\261"
1151   "c\235\33\27.,\256\376k\4n\336|\360q\232\346\27\215!XK\20BAJ\3\255-\234"
1152   "\363\40\"\0@U)xO\77\257\255\275\373\326\77F`}\375\2760\206:\306\20\264"
1153   "\266\310s\201<\257a\214\205\224\346\0\1\245\14\352ZCk[\\\272\364\301"
1154   "\302\13\23\270v\355\336D\345Y&\220\246\25\6\203\2u\255Q\327\32\336{\30"
1155   "c\17|#\245\206\326\2044-p\365\352gln\2O'\357\367\13$I\212<\27\220R\303"
1156   "\230\346\257\235s\7\2032\6\"\207\252R\350\367S\334\271\263\306f\266\341"
1157   "\372\372}1\276o\2\25\350v\207\30\14\12dY\5\245\14\234s\317$\37#\14\3"
1158   "\264Z\21:\235\26\226\227\277\334\236\211\300\355\333\17>MS\321Q\312\40"
1159   "\313\4\272\335\24I\222\242\337\317\40\204\234J\264A\20\240\325\212q\344"
1160   "H\7E\241N\235\77\377\325\367S\23\330\331\31|\3\0e)'eO\222\24J\231\351"
1161   "\355\305\200(\12\21\206\1\242(D\257\227\255\314\320\2\17k\11e)1\30\24"
1162   "\30\16\313\231\222\3\36\336{p\316\320\351\304\210\343\10D>\232\212\300"
1163   "\365\353\33\227\255m\354&\204\206\20\22B\250\331\26\204\307\310\232\16"
1164   "\336\3D\4\"\211s\347>\371\361\31\255<\375\240(\304\222sM\5\352ZM\304"
1165   "6+\32\201\2J\351\21\21\13k\353\327\16%\40\245=1\256\200\265\16Dn\276"
1166   "\31\3170\262\242\204\20""5\254-\261\277\257\337<\224@\303\336CJ\3\"z"
1167   "f\310\314\322\6\"\202R\32J\25""0\246\6c\366p\15(e\6y^\243\252$\2242\223"
1168   "a3\17\234s\320\332\200H\200H\0""0\371\241\4\30\303\215\275\275\14e\331"
1169   "\210o\236\3763\326LCk\233\12Z[\203\250F\247\23<\234j\24\257\256~\353"
1170   "\1L\26\3148\250\367\323$g\340\234\3039\217~\77\303\366\366.\312\3627"
1171   "(\325\307\326\326]6\225\6\232\3367k\227s\216v;\236\370\332\373\306\343"
1172   "\1779Z9\7c\14E!\220$\373\250\353\77`L\6\316\235\231z\20\2359s\372\243"
1173   "q\377\211\34\254u\10\202\0\214q\4A\0\316\331s\313>~\227\246%vv\22d\331"
1174   "\357\220\262\7kK,,DWf\332\206++\27\207E!^a\214#\212B\34=\332A\20p\204"
1175   "!\7\21M*\301\30\3cM(k\35\322\264D\2577@\226m\241\256\267`m\2010\364\335"
1176   "G\217\356\235\234y\35//\177\355\211\10a\30\"\212\"\264Z\21\332\355h\264"
1177   "n\351\200\320\2242\250*\211<\37\242\252v\241T\2\255\207pNc8\34\276\241"
1178   "Tow\256\3\311\331\263\237\373\40\10\340\234\7\347M\307\342\270\221N\223"
1179   "\2749\226)U\302\332\22R\356\301\230\34\326\26pN\242\333\375u\256\3\211"
1180   "\177\362\375\322\322\27\333\275\336\356)\"9\32""2\26\234\267F^W\260\266"
1181   "\2061\305\304\357D\22q\34\364\36\77\336|\375y\361f&\320T\342\303\357"
1182   "\372\375\235\367\244,#\306\330\23\216Qp\256\271\210$\30200\307\217\277"
1183   "zec\343\247\367\377.\336\13\35\313\27\27\337\371\241,\325i!\206'\235"
1184   "S\10C\226\267\333\361\303\315\315\273o\343%^bF\374\11\251\215\305RV\321"
1185   "\250V\0\0\0\0IEND\256B`\202";
1186 
1187 static size_t        knob_i_png_length = 819;
1188 static unsigned char knob_i_png[819] =
1189   "\211PNG\15\12\32\12\0\0\0\15IHDR\0\0\0\40\0\0\0\40\10\6\0\0\0szz\364"
1190   "\0\0\0\1sRGB\0\256\316\34\351\0\0\2\355IDATX\303\355W\273N#A\20\254\331"
1191   "]\303O8#'u@d\211\4\221\372\3\10\220\34\20\\\200D\300\27\40K\266d\210"
1192   "\20\374\7!1\21\22\"[9\363\37\354kf\272{\346\22\317j\15\346\3305Gp:Zr"
1193   "fOWUwW\267\201\1779\306\343\361\343x<~\374\312\33\21\376\367P\333\374"
1194   "\350\365\365\365\210\210\6\314\334\27\21""8\347\226\336\373\247\203\203"
1195   "\203\207o\3\220\246\351>3O\234s\273\336{Xk!\"X\1\200\367\36\0@D\246("
1196   "\212\213\343\343\343\227\277\6\40M\323{\0{!\2411\246\6\300\314\360\336"
1197   "\3039\7\0\20\21\20\21\312\262\\\214F\243\323/\3H\323\264\356rc\14\252"
1198   "\252BUU`f\20\321Z\362\20D\4\"B\236\347899\31n\15\340m\362\242(P\24\5"
1199   "\2141`f\210\10\0\324\3627CDP\226%\262,\303\331\331\331\260\363\30\256"
1200   "d\7\0Xk\353\344UUAk]K\277)9\0(\245\20\3071\222$\301l6\273\357\4\40M\323"
1201   "}k\355\36""3\303\30\203<\317k\0\326\332v\6\23EH\222\4;;;\210\343xo6\233"
1202   "\355\267\6\240\265\236\274e^\24\5\230\271\323\210\305q\\+\341\234\233"
1203   "\264\6\240\224\252G\255)y\347\31W\12\275^/\0\331m\5\340\371\371\371("
1204   "\214[\350\346\266\262\277\215\320\37\336{\210\10&\223\311Q\33\5\6\241"
1205   "\213\231\271n\266\256\341\234[3*\347\34\210h\360\366{\311\6\324\375M"
1206   "\16\267\15{\347\34\254\265\260\326\202\210\40\"\375O\1""4\3478\260\330"
1207   "\206}xCD`\255\5""3\2773\254\215%\320Z/\265\326k^\277\15\373&\201\306"
1208   ";\313O\1\224e\371T\226e-[\327\22\4\226!y\350\243\225u\77}\12\340\360"
1209   "\360\360\241i\271\333D\230\240P\377\240\300\325\325\325C\253\36p\316"
1210   "\31\21\331\15\362'IR\273\333G\214\233\322\33c\326v\207\265\26\3169\323"
1211   "\332\210D\344\"x@`\262i\353\205)a\346\272dD\204\252\252\220e\31\262,"
1212   "CUU\40\"\0\270h\15`4\32\2750\363\"$7\306\2041\252g:\324\267\371\35\255"
1213   "5\262,[[\\+\366\213\351t\372\322y\35\337\335\335=23\242(\2527[\257\327"
1214   "C\24Ek\354\233j5o\206P\377\371|>\334\372\40\271\271\271yTJ\301{\17\245"
1215   "\24\224R\210\242\10J\251\2651\13\15\27\256\245\240\330\237\222\177\10"
1216   "\40\334\372\267\267\267C\0\230N\247\367D\264'\"\365\15\20\307\361;\263"
1217   "\11\335\277j\336\305|>\77\335\364^\347\377\5\347\347\347\247\0~i\255"
1218   "M\250m\236\347k\237\306\251f\242(\372\25\222\177\313Y~yyy\244\224\32"
1219   "Xk\373\253%\263\4\360t}}\375\200\237\370\211\216\361\33\353\274\40}\314"
1220   "\227E/\0\0\0\0IEND\256B`\202";
1221 
1222 #endif /* GTK_CHECK_VERSION(2, 8, 0) */
1223 
1224