Transient Designer: image and text for manual, small change to the UI
[calf.git] / src / ctl_tube.cpp
blob2154eb28b0135e16a19fed571c82585794961974
1 /* Calf DSP Library
2 * Tube custom control.
3 * Copyright (C) 2010-2012 Markus Schmidt and others
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301 USA
21 #include "config.h"
22 #include <calf/ctl_tube.h>
23 #include <cairo/cairo.h>
24 #if !defined(__APPLE__)
25 #include <malloc.h>
26 #endif
27 #include <math.h>
28 #include <stdint.h>
29 #include <stdlib.h>
30 #include <gdk/gdk.h>
31 #include <sys/time.h>
33 static gboolean
34 calf_tube_expose (GtkWidget *widget, GdkEventExpose *event)
36 g_assert(CALF_IS_TUBE(widget));
38 CalfTube *self = CALF_TUBE(widget);
39 GdkWindow *window = widget->window;
40 GtkStyle *style = gtk_widget_get_style(widget);
41 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
43 int ox = 4, oy = 4, inner = 1, pad;
44 int sx = widget->allocation.width - (ox * 2), sy = widget->allocation.height - (oy * 2);
46 if( self->cache_surface == NULL ) {
47 // looks like its either first call or the widget has been resized.
48 // create the cache_surface.
49 cairo_surface_t *window_surface = cairo_get_target( c );
50 self->cache_surface = cairo_surface_create_similar( window_surface,
51 CAIRO_CONTENT_COLOR,
52 widget->allocation.width,
53 widget->allocation.height );
55 // And render the meterstuff again.
56 cairo_t *cache_cr = cairo_create( self->cache_surface );
57 // theme background for reduced width and round borders
58 // if(widget->style->bg_pixmap[0] == NULL) {
59 gdk_cairo_set_source_color(cache_cr,&style->bg[GTK_STATE_NORMAL]);
60 // } else {
61 // gdk_cairo_set_source_pixbuf(cache_cr, GDK_PIXBUF(widget->style->bg_pixmap[0]), widget->allocation.x, widget->allocation.y + 20);
62 // }
63 cairo_paint(cache_cr);
65 // outer (black)
66 pad = 0;
67 cairo_rectangle(cache_cr, pad, pad, sx + ox * 2 - pad * 2, sy + oy * 2 - pad * 2);
68 cairo_set_source_rgb(cache_cr, 0, 0, 0);
69 cairo_fill(cache_cr);
71 // inner (bevel)
72 pad = 1;
73 cairo_rectangle(cache_cr, pad, pad, sx + ox * 2 - pad * 2, sy + oy * 2 - pad * 2);
74 cairo_pattern_t *pat2 = cairo_pattern_create_linear (0, 0, 0, sy + oy * 2 - pad * 2);
75 cairo_pattern_add_color_stop_rgba (pat2, 0, 0.23, 0.23, 0.23, 1);
76 cairo_pattern_add_color_stop_rgba (pat2, 0.5, 0, 0, 0, 1);
77 cairo_set_source (cache_cr, pat2);
78 cairo_fill(cache_cr);
79 cairo_pattern_destroy(pat2);
81 cairo_rectangle(cache_cr, ox, oy, sx, sy);
82 cairo_set_source_rgb (cache_cr, 0, 0, 0);
83 cairo_fill(cache_cr);
85 cairo_surface_t *image;
86 switch(self->direction) {
87 case 1:
88 // vertical
89 switch(self->size) {
90 default:
91 case 1:
92 image = cairo_image_surface_create_from_png (PKGLIBDIR "tubeV1.png");
93 break;
94 case 2:
95 image = cairo_image_surface_create_from_png (PKGLIBDIR "tubeV2.png");
96 break;
98 break;
99 default:
100 case 2:
101 // horizontal
102 switch(self->size) {
103 default:
104 case 1:
105 image = cairo_image_surface_create_from_png (PKGLIBDIR "tubeH1.png");
106 break;
107 case 2:
108 image = cairo_image_surface_create_from_png (PKGLIBDIR "tubeH2.png");
109 break;
111 break;
113 cairo_set_source_surface (cache_cr, image, widget->allocation.width / 2 - sx / 2 + inner, widget->allocation.height / 2 - sy / 2 + inner);
114 cairo_paint (cache_cr);
115 cairo_surface_destroy (image);
116 cairo_destroy( cache_cr );
119 cairo_set_source_surface( c, self->cache_surface, 0,0 );
120 cairo_paint( c );
122 // get microseconds
123 timeval tv;
124 gettimeofday(&tv, 0);
125 long time = tv.tv_sec * 1000 * 1000 + tv.tv_usec;
127 // limit to 1.f
128 float value_orig = self->value > 1.f ? 1.f : self->value;
129 value_orig = value_orig < 0.f ? 0.f : value_orig;
130 float value = 0.f;
132 float s = ((float)(time - self->last_falltime) / 1000000.0);
133 float m = self->last_falloff * s * 2.5;
134 self->last_falloff -= m;
135 // new max value?
136 if(value_orig > self->last_falloff) {
137 self->last_falloff = value_orig;
139 value = self->last_falloff;
140 self->last_falltime = time;
141 self->falling = self->last_falloff > 0.000001;
142 cairo_pattern_t *pat;
143 // draw upper light
144 switch(self->direction) {
145 case 1:
146 // vertical
147 cairo_arc(c, ox + sx * 0.5, oy + sy * 0.2, sx, 0, 2 * M_PI);
148 pat = cairo_pattern_create_radial (ox + sx * 0.5, oy + sy * 0.2, 3, ox + sx * 0.5, oy + sy * 0.2, sx);
149 break;
150 default:
151 case 2:
152 // horizontal
153 cairo_arc(c, ox + sx * 0.8, oy + sy * 0.5, sy, 0, 2 * M_PI);
154 pat = cairo_pattern_create_radial (ox + sx * 0.8, oy + sy * 0.5, 3, ox + sx * 0.8, oy + sy * 0.5, sy);
155 break;
157 cairo_pattern_add_color_stop_rgba (pat, 0, 1, 1, 1, value);
158 cairo_pattern_add_color_stop_rgba (pat, 0.3, 1, 0.8, 0.3, value * 0.4);
159 cairo_pattern_add_color_stop_rgba (pat, 0.31, 0.9, 0.5, 0.1, value * 0.5);
160 cairo_pattern_add_color_stop_rgba (pat, 1, 0.0, 0.2, 0.7, 0);
161 cairo_set_source (c, pat);
162 cairo_fill(c);
163 // draw lower light
164 switch(self->direction) {
165 case 1:
166 // vertical
167 cairo_arc(c, ox + sx * 0.5, oy + sy * 0.75, sx / 2, 0, 2 * M_PI);
168 pat = cairo_pattern_create_radial (ox + sx * 0.5, oy + sy * 0.75, 2, ox + sx * 0.5, oy + sy * 0.75, sx / 2);
169 break;
170 default:
171 case 2:
172 // horizontal
173 cairo_arc(c, ox + sx * 0.25, oy + sy * 0.5, sy / 2, 0, 2 * M_PI);
174 pat = cairo_pattern_create_radial (ox + sx * 0.25, oy + sy * 0.5, 2, ox + sx * 0.25, oy + sy * 0.5, sy / 2);
175 break;
177 cairo_pattern_add_color_stop_rgba (pat, 0, 1, 1, 1, value);
178 cairo_pattern_add_color_stop_rgba (pat, 0.3, 1, 0.8, 0.3, value * 0.4);
179 cairo_pattern_add_color_stop_rgba (pat, 0.31, 0.9, 0.5, 0.1, value * 0.5);
180 cairo_pattern_add_color_stop_rgba (pat, 1, 0.0, 0.2, 0.7, 0);
181 cairo_set_source (c, pat);
182 cairo_fill(c);
183 cairo_destroy(c);
184 return TRUE;
187 static void
188 calf_tube_size_request (GtkWidget *widget,
189 GtkRequisition *requisition)
191 g_assert(CALF_IS_TUBE(widget));
193 CalfTube *self = CALF_TUBE(widget);
194 switch(self->direction) {
195 case 1:
196 switch(self->size) {
197 case 1:
198 widget->requisition.width = 82;
199 widget->requisition.height = 130;
200 break;
201 default:
202 case 2:
203 widget->requisition.width = 130;
204 widget->requisition.height = 210;
205 break;
207 break;
208 default:
209 case 2:
210 switch(self->size) {
211 case 1:
212 widget->requisition.width = 130;
213 widget->requisition.height = 82;
214 break;
215 default:
216 case 2:
217 widget->requisition.width = 210;
218 widget->requisition.height = 130;
219 break;
221 break;
225 static void
226 calf_tube_size_allocate (GtkWidget *widget,
227 GtkAllocation *allocation)
229 g_assert(CALF_IS_TUBE(widget));
230 CalfTube *tube = CALF_TUBE(widget);
232 GtkWidgetClass *parent_class = (GtkWidgetClass *) g_type_class_peek_parent( CALF_TUBE_GET_CLASS( tube ) );
234 parent_class->size_allocate( widget, allocation );
236 if( tube->cache_surface )
237 cairo_surface_destroy( tube->cache_surface );
238 tube->cache_surface = NULL;
241 static void
242 calf_tube_class_init (CalfTubeClass *klass)
244 // GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
245 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
246 widget_class->expose_event = calf_tube_expose;
247 widget_class->size_request = calf_tube_size_request;
248 widget_class->size_allocate = calf_tube_size_allocate;
251 static void
252 calf_tube_init (CalfTube *self)
254 GtkWidget *widget = GTK_WIDGET(self);
255 GTK_WIDGET_SET_FLAGS (GTK_WIDGET(self), GTK_CAN_FOCUS);
256 switch(self->direction) {
257 case 1:
258 switch(self->size) {
259 case 1:
260 widget->requisition.width = 82;
261 widget->requisition.height = 130;
262 break;
263 default:
264 case 2:
265 widget->requisition.width = 130;
266 widget->requisition.height = 210;
267 break;
269 break;
270 default:
271 case 2:
272 switch(self->size) {
273 case 1:
274 widget->requisition.width = 130;
275 widget->requisition.height = 82;
276 break;
277 default:
278 case 2:
279 widget->requisition.width = 210;
280 widget->requisition.height = 130;
281 break;
283 break;
285 self->falling = false;
286 self->cache_surface = NULL;
289 GtkWidget *
290 calf_tube_new()
292 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_TUBE, NULL ));
293 return widget;
296 extern void calf_tube_set_value(CalfTube *tube, float value)
298 if (value != tube->value or tube->falling)
300 tube->value = value;
301 gtk_widget_queue_draw(GTK_WIDGET(tube));
305 GType
306 calf_tube_get_type (void)
308 static GType type = 0;
309 if (!type) {
311 static const GTypeInfo type_info = {
312 sizeof(CalfTubeClass),
313 NULL, /* base_init */
314 NULL, /* base_finalize */
315 (GClassInitFunc)calf_tube_class_init,
316 NULL, /* class_finalize */
317 NULL, /* class_data */
318 sizeof(CalfTube),
319 0, /* n_preallocs */
320 (GInstanceInitFunc)calf_tube_init
323 for (int i = 0; ; i++) {
324 char *name = g_strdup_printf("CalfTube%u%d",
325 ((unsigned int)(intptr_t)calf_tube_class_init) >> 16, i);
326 if (g_type_from_name(name)) {
327 free(name);
328 continue;
330 type = g_type_register_static( GTK_TYPE_DRAWING_AREA,
331 name,
332 &type_info,
333 (GTypeFlags)0);
334 free(name);
335 break;
338 return type;