Manual screenshots; minor changes
[calf.git] / src / ctl_tube.cpp
blob3d37745c5fa110ed9883445e8cc0265ff705efa6
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 #include <malloc.h>
25 #include <math.h>
26 #include <stdint.h>
27 #include <stdlib.h>
28 #include <gdk/gdk.h>
29 #include <sys/time.h>
31 static gboolean
32 calf_tube_expose (GtkWidget *widget, GdkEventExpose *event)
34 g_assert(CALF_IS_TUBE(widget));
36 CalfTube *self = CALF_TUBE(widget);
37 GdkWindow *window = widget->window;
38 GtkStyle *style = gtk_widget_get_style(widget);
39 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
41 int ox = 4, oy = 4, inner = 1, pad;
42 int sx = widget->allocation.width - (ox * 2), sy = widget->allocation.height - (oy * 2);
44 if( self->cache_surface == NULL ) {
45 // looks like its either first call or the widget has been resized.
46 // create the cache_surface.
47 cairo_surface_t *window_surface = cairo_get_target( c );
48 self->cache_surface = cairo_surface_create_similar( window_surface,
49 CAIRO_CONTENT_COLOR,
50 widget->allocation.width,
51 widget->allocation.height );
53 // And render the meterstuff again.
54 cairo_t *cache_cr = cairo_create( self->cache_surface );
55 // theme background for reduced width and round borders
56 // if(widget->style->bg_pixmap[0] == NULL) {
57 gdk_cairo_set_source_color(cache_cr,&style->bg[GTK_STATE_NORMAL]);
58 // } else {
59 // gdk_cairo_set_source_pixbuf(cache_cr, GDK_PIXBUF(widget->style->bg_pixmap[0]), widget->allocation.x, widget->allocation.y + 20);
60 // }
61 cairo_paint(cache_cr);
63 // outer (black)
64 pad = 0;
65 cairo_rectangle(cache_cr, pad, pad, sx + ox * 2 - pad * 2, sy + oy * 2 - pad * 2);
66 cairo_set_source_rgb(cache_cr, 0, 0, 0);
67 cairo_fill(cache_cr);
69 // inner (bevel)
70 pad = 1;
71 cairo_rectangle(cache_cr, pad, pad, sx + ox * 2 - pad * 2, sy + oy * 2 - pad * 2);
72 cairo_pattern_t *pat2 = cairo_pattern_create_linear (0, 0, 0, sy + oy * 2 - pad * 2);
73 cairo_pattern_add_color_stop_rgba (pat2, 0, 0.23, 0.23, 0.23, 1);
74 cairo_pattern_add_color_stop_rgba (pat2, 0.5, 0, 0, 0, 1);
75 cairo_set_source (cache_cr, pat2);
76 cairo_fill(cache_cr);
77 cairo_pattern_destroy(pat2);
79 cairo_rectangle(cache_cr, ox, oy, sx, sy);
80 cairo_set_source_rgb (cache_cr, 0, 0, 0);
81 cairo_fill(cache_cr);
83 cairo_surface_t *image;
84 switch(self->direction) {
85 case 1:
86 // vertical
87 switch(self->size) {
88 default:
89 case 1:
90 image = cairo_image_surface_create_from_png (PKGLIBDIR "tubeV1.png");
91 break;
92 case 2:
93 image = cairo_image_surface_create_from_png (PKGLIBDIR "tubeV2.png");
94 break;
96 break;
97 default:
98 case 2:
99 // horizontal
100 switch(self->size) {
101 default:
102 case 1:
103 image = cairo_image_surface_create_from_png (PKGLIBDIR "tubeH1.png");
104 break;
105 case 2:
106 image = cairo_image_surface_create_from_png (PKGLIBDIR "tubeH2.png");
107 break;
109 break;
111 cairo_set_source_surface (cache_cr, image, widget->allocation.width / 2 - sx / 2 + inner, widget->allocation.height / 2 - sy / 2 + inner);
112 cairo_paint (cache_cr);
113 cairo_surface_destroy (image);
114 cairo_destroy( cache_cr );
117 cairo_set_source_surface( c, self->cache_surface, 0,0 );
118 cairo_paint( c );
120 // get microseconds
121 timeval tv;
122 gettimeofday(&tv, 0);
123 long time = tv.tv_sec * 1000 * 1000 + tv.tv_usec;
125 // limit to 1.f
126 float value_orig = self->value > 1.f ? 1.f : self->value;
127 value_orig = value_orig < 0.f ? 0.f : value_orig;
128 float value = 0.f;
130 float s = ((float)(time - self->last_falltime) / 1000000.0);
131 float m = self->last_falloff * s * 2.5;
132 self->last_falloff -= m;
133 // new max value?
134 if(value_orig > self->last_falloff) {
135 self->last_falloff = value_orig;
137 value = self->last_falloff;
138 self->last_falltime = time;
139 self->falling = self->last_falloff > 0.000001;
140 cairo_pattern_t *pat;
141 // draw upper light
142 switch(self->direction) {
143 case 1:
144 // vertical
145 cairo_arc(c, ox + sx * 0.5, oy + sy * 0.2, sx, 0, 2 * M_PI);
146 pat = cairo_pattern_create_radial (ox + sx * 0.5, oy + sy * 0.2, 3, ox + sx * 0.5, oy + sy * 0.2, sx);
147 break;
148 default:
149 case 2:
150 // horizontal
151 cairo_arc(c, ox + sx * 0.8, oy + sy * 0.5, sy, 0, 2 * M_PI);
152 pat = cairo_pattern_create_radial (ox + sx * 0.8, oy + sy * 0.5, 3, ox + sx * 0.8, oy + sy * 0.5, sy);
153 break;
155 cairo_pattern_add_color_stop_rgba (pat, 0, 1, 1, 1, value);
156 cairo_pattern_add_color_stop_rgba (pat, 0.3, 1, 0.8, 0.3, value * 0.4);
157 cairo_pattern_add_color_stop_rgba (pat, 0.31, 0.9, 0.5, 0.1, value * 0.5);
158 cairo_pattern_add_color_stop_rgba (pat, 1, 0.0, 0.2, 0.7, 0);
159 cairo_set_source (c, pat);
160 cairo_fill(c);
161 // draw lower light
162 switch(self->direction) {
163 case 1:
164 // vertical
165 cairo_arc(c, ox + sx * 0.5, oy + sy * 0.75, sx / 2, 0, 2 * M_PI);
166 pat = cairo_pattern_create_radial (ox + sx * 0.5, oy + sy * 0.75, 2, ox + sx * 0.5, oy + sy * 0.75, sx / 2);
167 break;
168 default:
169 case 2:
170 // horizontal
171 cairo_arc(c, ox + sx * 0.25, oy + sy * 0.5, sy / 2, 0, 2 * M_PI);
172 pat = cairo_pattern_create_radial (ox + sx * 0.25, oy + sy * 0.5, 2, ox + sx * 0.25, oy + sy * 0.5, sy / 2);
173 break;
175 cairo_pattern_add_color_stop_rgba (pat, 0, 1, 1, 1, value);
176 cairo_pattern_add_color_stop_rgba (pat, 0.3, 1, 0.8, 0.3, value * 0.4);
177 cairo_pattern_add_color_stop_rgba (pat, 0.31, 0.9, 0.5, 0.1, value * 0.5);
178 cairo_pattern_add_color_stop_rgba (pat, 1, 0.0, 0.2, 0.7, 0);
179 cairo_set_source (c, pat);
180 cairo_fill(c);
181 cairo_destroy(c);
182 return TRUE;
185 static void
186 calf_tube_size_request (GtkWidget *widget,
187 GtkRequisition *requisition)
189 g_assert(CALF_IS_TUBE(widget));
191 CalfTube *self = CALF_TUBE(widget);
192 switch(self->direction) {
193 case 1:
194 switch(self->size) {
195 case 1:
196 widget->requisition.width = 82;
197 widget->requisition.height = 130;
198 break;
199 default:
200 case 2:
201 widget->requisition.width = 130;
202 widget->requisition.height = 210;
203 break;
205 break;
206 default:
207 case 2:
208 switch(self->size) {
209 case 1:
210 widget->requisition.width = 130;
211 widget->requisition.height = 82;
212 break;
213 default:
214 case 2:
215 widget->requisition.width = 210;
216 widget->requisition.height = 130;
217 break;
219 break;
223 static void
224 calf_tube_size_allocate (GtkWidget *widget,
225 GtkAllocation *allocation)
227 g_assert(CALF_IS_TUBE(widget));
228 CalfTube *tube = CALF_TUBE(widget);
230 GtkWidgetClass *parent_class = (GtkWidgetClass *) g_type_class_peek_parent( CALF_TUBE_GET_CLASS( tube ) );
232 parent_class->size_allocate( widget, allocation );
234 if( tube->cache_surface )
235 cairo_surface_destroy( tube->cache_surface );
236 tube->cache_surface = NULL;
239 static void
240 calf_tube_class_init (CalfTubeClass *klass)
242 // GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
243 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
244 widget_class->expose_event = calf_tube_expose;
245 widget_class->size_request = calf_tube_size_request;
246 widget_class->size_allocate = calf_tube_size_allocate;
249 static void
250 calf_tube_init (CalfTube *self)
252 GtkWidget *widget = GTK_WIDGET(self);
253 GTK_WIDGET_SET_FLAGS (GTK_WIDGET(self), GTK_CAN_FOCUS);
254 switch(self->direction) {
255 case 1:
256 switch(self->size) {
257 case 1:
258 widget->requisition.width = 82;
259 widget->requisition.height = 130;
260 break;
261 default:
262 case 2:
263 widget->requisition.width = 130;
264 widget->requisition.height = 210;
265 break;
267 break;
268 default:
269 case 2:
270 switch(self->size) {
271 case 1:
272 widget->requisition.width = 130;
273 widget->requisition.height = 82;
274 break;
275 default:
276 case 2:
277 widget->requisition.width = 210;
278 widget->requisition.height = 130;
279 break;
281 break;
283 self->falling = false;
284 self->cache_surface = NULL;
287 GtkWidget *
288 calf_tube_new()
290 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_TUBE, NULL ));
291 return widget;
294 extern void calf_tube_set_value(CalfTube *tube, float value)
296 if (value != tube->value or tube->falling)
298 tube->value = value;
299 gtk_widget_queue_draw(GTK_WIDGET(tube));
303 GType
304 calf_tube_get_type (void)
306 static GType type = 0;
307 if (!type) {
309 static const GTypeInfo type_info = {
310 sizeof(CalfTubeClass),
311 NULL, /* base_init */
312 NULL, /* base_finalize */
313 (GClassInitFunc)calf_tube_class_init,
314 NULL, /* class_finalize */
315 NULL, /* class_data */
316 sizeof(CalfTube),
317 0, /* n_preallocs */
318 (GInstanceInitFunc)calf_tube_init
321 for (int i = 0; ; i++) {
322 char *name = g_strdup_printf("CalfTube%u%d",
323 ((unsigned int)(intptr_t)calf_tube_class_init) >> 16, i);
324 if (g_type_from_name(name)) {
325 free(name);
326 continue;
328 type = g_type_register_static( GTK_TYPE_DRAWING_AREA,
329 name,
330 &type_info,
331 (GTypeFlags)0);
332 free(name);
333 break;
336 return type;