1 /* Calf DSP Library
2  * Light emitting diode-like control.
3  *
4  * Copyright (C) 2008 Krzysztof Foltman
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General
17  * Public License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA  02110-1301  USA
20  */
21 #include <calf/ctl_led.h>
22 #include <math.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <calf/drawingutils.h>
26 
27 GtkWidget *
calf_led_new()28 calf_led_new()
29 {
30     GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_LED, NULL ));
31     return widget;
32 }
33 
34 static gboolean
calf_led_expose(GtkWidget * widget,GdkEventExpose * event)35 calf_led_expose (GtkWidget *widget, GdkEventExpose *event)
36 {
37     g_assert(CALF_IS_LED(widget));
38 
39     CalfLed *self = CALF_LED(widget);
40     GdkWindow *window = widget->window;
41     cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
42 
43     int width = widget->allocation.width;
44     int height = widget->allocation.height;
45     int x  = widget->allocation.x;
46     int y  = widget->allocation.y;
47     int ox = widget->style->xthickness;
48     int oy = widget->style->ythickness;
49     int sx = width - ox * 2;
50     int sy = height - oy * 2;
51     int xc = x + width / 2;
52     int yc = y + height / 2;
53     float r, g, b;
54 
55     if( self->cache_surface == NULL ) {
56         // looks like its either first call or the widget has been resized.
57         // create the cache_surface.
58         self->cache_surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, width, height );
59         cairo_t *cache_cr = cairo_create( self->cache_surface );
60 
61         float radius, bevel;
62         get_bg_color(widget, NULL, &r, &g, &b);
63         gtk_widget_style_get(widget, "border-radius", &radius, "bevel",  &bevel, NULL);
64         create_rectangle(cache_cr, 0, 0, width, height, radius);
65         cairo_set_source_rgb(cache_cr, r, g, b);
66         cairo_fill(cache_cr);
67         draw_bevel(cache_cr, 0, 0, width, height, radius, bevel);
68         cairo_rectangle(cache_cr, ox, oy, sx, sy);
69         cairo_set_source_rgb (cache_cr, 0, 0, 0);
70         cairo_fill(cache_cr);
71 
72         cairo_destroy( cache_cr );
73     }
74 
75     cairo_set_source_surface( c, self->cache_surface, x, y );
76     cairo_paint( c );
77 
78     ox += x;
79     oy += y;
80 
81     cairo_pattern_t *pt = cairo_pattern_create_radial(xc, yc, 0, xc, yc, sx > sy ? sx/2 : sy/2);
82 
83     float value = self->led_value;
84 
85     if(self->led_mode >= 4 && self->led_mode <= 5 && value > 1.f) {
86         value = 1.f;
87     }
88     switch (self->led_mode) {
89         default:
90         case 0:
91             // blue-on/off
92             cairo_pattern_add_color_stop_rgb(pt, 0.0, value > 0.f ? 0.2 : 0.0, value > 0.f ? 1.0 : 0.25, value > 0.f ? 1.0 : 0.35);
93             cairo_pattern_add_color_stop_rgb(pt, 0.5, value > 0.f ? 0.1 : 0.0, value > 0.f ? 0.6 : 0.15, value > 0.f ? 0.75 : 0.2);
94             cairo_pattern_add_color_stop_rgb(pt, 1.0, 0.0,                     value > 0.f ? 0.3 : 0.1,  value > 0.f ? 0.5 : 0.1);
95             break;
96         case 1:
97             // red-on/off
98             cairo_pattern_add_color_stop_rgb(pt, 0.0, value > 0.f ? 1.0 : 0.35, value > 0.f ? 0.5 : 0.0, value > 0.f ? 0.2 : 0.0);
99             cairo_pattern_add_color_stop_rgb(pt, 0.5, value > 0.f ? 0.80 : 0.2, value > 0.f ? 0.2 : 0.0, value > 0.f ? 0.1 : 0.0);
100             cairo_pattern_add_color_stop_rgb(pt, 1.0, value > 0.f ? 0.65 : 0.1, value > 0.f ? 0.1 : 0.0, 0.0);
101             break;
102         case 2:
103         case 4:
104             // blue-dynamic (limited)
105             cairo_pattern_add_color_stop_rgb(pt, 0.0, value * 0.2, value * 0.75 + 0.25, value * 0.65 + 0.35);
106             cairo_pattern_add_color_stop_rgb(pt, 0.5, value * 0.1, value * 0.45 + 0.15, value * 0.55 + 0.2);
107             cairo_pattern_add_color_stop_rgb(pt, 1.0, 0.0,         value * 0.2 + 0.1,   value * 0.4 + 0.1);
108             break;
109         case 3:
110         case 5:
111             // red-dynamic (limited)
112             cairo_pattern_add_color_stop_rgb(pt, 0.0, value * 0.65 + 0.35, value * 0.5, value * 0.2);
113             cairo_pattern_add_color_stop_rgb(pt, 0.5, value * 0.6 + 0.2,   value * 0.2, value * 0.1);
114             cairo_pattern_add_color_stop_rgb(pt, 1.0, value * 0.66 + 0.1,  value * 0.1, 0.0);
115             break;
116         case 6:
117             // blue-dynamic with red peak at >= 1.f
118             if(value < 1.0) {
119                 cairo_pattern_add_color_stop_rgb(pt, 0.0, value * 0.2, value * 0.75 + 0.25, value * 0.65 + 0.35);
120                 cairo_pattern_add_color_stop_rgb(pt, 0.5, value * 0.1, value * 0.45 + 0.15, value * 0.55 + 0.2);
121                 cairo_pattern_add_color_stop_rgb(pt, 1.0, 0.0,         value * 0.2 + 0.1,   value * 0.4 + 0.1);
122             } else {
123                 cairo_pattern_add_color_stop_rgb(pt, 0.0, 1.0,  0.5, 0.2);
124                 cairo_pattern_add_color_stop_rgb(pt, 0.5, 0.80, 0.2, 0.1);
125                 cairo_pattern_add_color_stop_rgb(pt, 1.0, 0.66, 0.1, 0.0);
126             }
127             break;
128         case 7:
129             // off @ 0.0, blue < 1.0, red @ 1.0
130             if(value < 1.f and value > 0.f) {
131                 // blue
132                 cairo_pattern_add_color_stop_rgb(pt, 0.0, 0.2, 1.0, 1.0);
133                 cairo_pattern_add_color_stop_rgb(pt, 0.5, 0.1, 0.6, 0.75);
134                 cairo_pattern_add_color_stop_rgb(pt, 1.0, 0.0, 0.3, 0.5);
135             } else if(value == 0.f) {
136                 // off
137                 cairo_pattern_add_color_stop_rgb(pt, 0.0, 0.0, 0.25, 0.35);
138                 cairo_pattern_add_color_stop_rgb(pt, 0.5, 0.0, 0.15, 0.2);
139                 cairo_pattern_add_color_stop_rgb(pt, 1.0, 0.0, 0.1,  0.1);
140             } else {
141                 // red
142                 cairo_pattern_add_color_stop_rgb(pt, 0.0, 1.0,  0.5, 0.2);
143                 cairo_pattern_add_color_stop_rgb(pt, 0.5, 0.80, 0.2, 0.1);
144                 cairo_pattern_add_color_stop_rgb(pt, 1.0, 0.66, 0.1, 0.0);
145             }
146             break;
147     }
148 
149     cairo_rectangle(c, ox + 1, oy + 1, sx - 2, sy - 2);
150     cairo_set_source (c, pt);
151     cairo_fill_preserve(c);
152 
153     float glass;
154     gtk_widget_style_get(widget, "glass",  &glass, NULL);
155 
156     if (glass > 0.f) {
157         pt = cairo_pattern_create_linear (ox, oy, ox, oy + sy);
158         cairo_pattern_add_color_stop_rgba (pt, 0,     1, 1, 1, 0.4 * glass);
159         cairo_pattern_add_color_stop_rgba (pt, 0.4,   1, 1, 1, 0.1 * glass);
160         cairo_pattern_add_color_stop_rgba (pt, 0.401, 0, 0, 0, 0.0);
161         cairo_pattern_add_color_stop_rgba (pt, 1,     0, 0, 0, 0.2 * glass);
162         cairo_set_source (c, pt);
163         cairo_fill(c);
164         cairo_pattern_destroy(pt);
165     }
166 
167     cairo_destroy(c);
168 
169     return TRUE;
170 }
171 
172 static void
calf_led_size_request(GtkWidget * widget,GtkRequisition * requisition)173 calf_led_size_request (GtkWidget *widget,
174                            GtkRequisition *requisition)
175 {
176     g_assert(CALF_IS_LED(widget));
177     CalfLed *self = CALF_LED(widget);
178     requisition->width = self->size ? 24 : 19;
179     requisition->height = self->size ? 18 : 14;
180 }
181 
182 static void
calf_led_size_allocate(GtkWidget * widget,GtkAllocation * allocation)183 calf_led_size_allocate (GtkWidget *widget,
184                            GtkAllocation *allocation)
185 {
186     g_assert(CALF_IS_LED(widget));
187     CalfLed *led = CALF_LED(widget);
188 
189     GtkWidgetClass *parent_class = (GtkWidgetClass *) g_type_class_peek_parent( CALF_LED_GET_CLASS( led ) );
190     parent_class->size_allocate( widget, allocation );
191 
192     if( led->cache_surface )
193         cairo_surface_destroy( led->cache_surface );
194     led->cache_surface = NULL;
195 }
196 
197 static gboolean
calf_led_button_press(GtkWidget * widget,GdkEventButton * event)198 calf_led_button_press (GtkWidget *widget, GdkEventButton *event)
199 {
200     return TRUE;
201 }
202 
203 static void
calf_led_class_init(CalfLedClass * klass)204 calf_led_class_init (CalfLedClass *klass)
205 {
206     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
207     widget_class->expose_event = calf_led_expose;
208     widget_class->size_request = calf_led_size_request;
209     widget_class->size_allocate = calf_led_size_allocate;
210     widget_class->button_press_event = calf_led_button_press;
211     gtk_widget_class_install_style_property(
212         widget_class, g_param_spec_float("border-radius", "Border Radius", "Generate round edges",
213         0, 24, 4, GParamFlags(G_PARAM_READWRITE)));
214     gtk_widget_class_install_style_property(
215         widget_class, g_param_spec_float("bevel", "Bevel", "Bevel the object",
216         -2, 2, 0.2, GParamFlags(G_PARAM_READWRITE)));
217     gtk_widget_class_install_style_property(
218         widget_class, g_param_spec_float("glass", "Glass", "Glass effect on top",
219         0, 1, 1, GParamFlags(G_PARAM_READWRITE)));
220 }
221 
222 static void
calf_led_init(CalfLed * self)223 calf_led_init (CalfLed *self)
224 {
225     GtkWidget *widget = GTK_WIDGET(self);
226     // GtkWidget *widget = GTK_WIDGET(self);
227     // GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
228     self->led_mode = 0;
229     self->size = 0;
230     self->led_value = 0.f;
231     self->cache_surface = NULL;
232     widget->requisition.width = self->size ? 24 : 19;
233     widget->requisition.height = self->size ? 18 : 14;
234     gtk_widget_set_has_window(widget, FALSE);
235 }
236 
calf_led_set_value(CalfLed * led,float value)237 void calf_led_set_value(CalfLed *led, float value)
238 {
239     if (value != led->led_value)
240     {
241         float old_value = led->led_value;
242         led->led_value = value;
243         if (led->led_mode >= 2 || (old_value > 0) != (value > 0))
244         {
245             GtkWidget *widget = GTK_WIDGET (led);
246             if (GTK_WIDGET_REALIZED(widget))
247                 gtk_widget_queue_draw (widget);
248         }
249     }
250 }
251 
calf_led_get_value(CalfLed * led)252 gboolean calf_led_get_value(CalfLed *led)
253 {
254     return led->led_value;
255 }
256 
257 GType
calf_led_get_type(void)258 calf_led_get_type (void)
259 {
260     static GType type = 0;
261     if (!type) {
262         static const GTypeInfo type_info = {
263             sizeof(CalfLedClass),
264             NULL, /* base_init */
265             NULL, /* base_finalize */
266             (GClassInitFunc)calf_led_class_init,
267             NULL, /* class_finalize */
268             NULL, /* class_data */
269             sizeof(CalfLed),
270             0,    /* n_preallocs */
271             (GInstanceInitFunc)calf_led_init
272         };
273 
274         for (int i = 0; ; i++) {
275             const char *name = "CalfLed";
276             //char *name = g_strdup_printf("CalfLed%u%d",
277                 //((unsigned int)(intptr_t)calf_led_class_init) >> 16, i);
278             if (g_type_from_name(name)) {
279                 //free(name);
280                 continue;
281             }
282             type = g_type_register_static(GTK_TYPE_DRAWING_AREA,
283                                           name,
284                                           &type_info,
285                                           (GTypeFlags)0);
286             //free(name);
287             break;
288         }
289     }
290     return type;
291 }
292