Merge branch 'master' of https://github.com/calf-studio-gear/calf
[calf.git] / src / ctl_led.cpp
blob73939e1dc3bc1a8ee04bc0cf6da46761f5303f5b
1 /* Calf DSP Library
2 * Light emitting diode-like control.
4 * Copyright (C) 2008 Krzysztof Foltman
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.
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.
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
21 #include <calf/ctl_led.h>
22 #include <math.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <calf/drawingutils.h>
27 GtkWidget *
28 calf_led_new()
30 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_LED, NULL ));
31 return widget;
34 static gboolean
35 calf_led_expose (GtkWidget *widget, GdkEventExpose *event)
37 g_assert(CALF_IS_LED(widget));
39 CalfLed *self = CALF_LED(widget);
40 GdkWindow *window = widget->window;
41 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
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;
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 );
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);
72 cairo_destroy( cache_cr );
75 cairo_set_source_surface( c, self->cache_surface, x, y );
76 cairo_paint( c );
78 ox += x;
79 oy += y;
81 cairo_pattern_t *pt = cairo_pattern_create_radial(xc, yc, 0, xc, yc, sx > sy ? sx/2 : sy/2);
83 float value = self->led_value;
85 if(self->led_mode >= 4 && self->led_mode <= 5 && value > 1.f) {
86 value = 1.f;
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);
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);
146 break;
149 cairo_rectangle(c, ox + 1, oy + 1, sx - 2, sy - 2);
150 cairo_set_source (c, pt);
151 cairo_fill_preserve(c);
153 pt = cairo_pattern_create_linear (ox, oy, ox, oy + sy);
154 cairo_pattern_add_color_stop_rgba (pt, 0, 1, 1, 1, 0.4);
155 cairo_pattern_add_color_stop_rgba (pt, 0.4, 1, 1, 1, 0.1);
156 cairo_pattern_add_color_stop_rgba (pt, 0.401, 0, 0, 0, 0.0);
157 cairo_pattern_add_color_stop_rgba (pt, 1, 0, 0, 0, 0.2);
158 cairo_set_source (c, pt);
159 cairo_fill(c);
160 cairo_pattern_destroy(pt);
162 cairo_destroy(c);
164 return TRUE;
167 static void
168 calf_led_size_request (GtkWidget *widget,
169 GtkRequisition *requisition)
171 g_assert(CALF_IS_LED(widget));
172 CalfLed *self = CALF_LED(widget);
173 requisition->width = self->size ? 24 : 19;
174 requisition->height = self->size ? 18 : 14;
177 static void
178 calf_led_size_allocate (GtkWidget *widget,
179 GtkAllocation *allocation)
181 g_assert(CALF_IS_LED(widget));
182 CalfLed *led = CALF_LED(widget);
184 GtkWidgetClass *parent_class = (GtkWidgetClass *) g_type_class_peek_parent( CALF_LED_GET_CLASS( led ) );
185 parent_class->size_allocate( widget, allocation );
187 if( led->cache_surface )
188 cairo_surface_destroy( led->cache_surface );
189 led->cache_surface = NULL;
192 static gboolean
193 calf_led_button_press (GtkWidget *widget, GdkEventButton *event)
195 return TRUE;
198 static void
199 calf_led_class_init (CalfLedClass *klass)
201 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
202 widget_class->expose_event = calf_led_expose;
203 widget_class->size_request = calf_led_size_request;
204 widget_class->size_allocate = calf_led_size_allocate;
205 widget_class->button_press_event = calf_led_button_press;
206 gtk_widget_class_install_style_property(
207 widget_class, g_param_spec_float("border-radius", "Border Radius", "Generate round edges",
208 0, 24, 4, GParamFlags(G_PARAM_READWRITE)));
209 gtk_widget_class_install_style_property(
210 widget_class, g_param_spec_float("bevel", "Bevel", "Bevel the object",
211 -2, 2, 0.2, GParamFlags(G_PARAM_READWRITE)));
214 static void
215 calf_led_init (CalfLed *self)
217 GtkWidget *widget = GTK_WIDGET(self);
218 // GtkWidget *widget = GTK_WIDGET(self);
219 // GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
220 self->led_mode = 0;
221 self->size = 0;
222 self->led_value = 0.f;
223 self->cache_surface = NULL;
224 widget->requisition.width = self->size ? 24 : 19;
225 widget->requisition.height = self->size ? 18 : 14;
226 gtk_widget_set_has_window(widget, FALSE);
229 void calf_led_set_value(CalfLed *led, float value)
231 if (value != led->led_value)
233 float old_value = led->led_value;
234 led->led_value = value;
235 if (led->led_mode >= 2 || (old_value > 0) != (value > 0))
237 GtkWidget *widget = GTK_WIDGET (led);
238 if (GTK_WIDGET_REALIZED(widget))
239 gtk_widget_queue_draw (widget);
244 gboolean calf_led_get_value(CalfLed *led)
246 return led->led_value;
249 GType
250 calf_led_get_type (void)
252 static GType type = 0;
253 if (!type) {
254 static const GTypeInfo type_info = {
255 sizeof(CalfLedClass),
256 NULL, /* base_init */
257 NULL, /* base_finalize */
258 (GClassInitFunc)calf_led_class_init,
259 NULL, /* class_finalize */
260 NULL, /* class_data */
261 sizeof(CalfLed),
262 0, /* n_preallocs */
263 (GInstanceInitFunc)calf_led_init
266 for (int i = 0; ; i++) {
267 //const char *name = "CalfLed";
268 char *name = g_strdup_printf("CalfLed%u%d",
269 ((unsigned int)(intptr_t)calf_led_class_init) >> 16, i);
270 if (g_type_from_name(name)) {
271 free(name);
272 continue;
274 type = g_type_register_static(GTK_TYPE_DRAWING_AREA,
275 name,
276 &type_info,
277 (GTypeFlags)0);
278 free(name);
279 break;
282 return type;