2 * Custom controls (line graph, knob).
3 * Copyright (C) 2007 Krzysztof Foltman
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
23 #include <calf/custom_ctl.h>
24 #include <gdk/gdkkeysyms.h>
25 #include <cairo/cairo.h>
31 I don't really know how to do it, or if it can be done this way.
32 struct calf_ui_type_module
38 module = g_type_module_new();
39 g_type_module_set_name(module, "calf_custom_ctl");
40 g_type_module_use(module);
42 ~calf_ui_type_module()
44 g_type_module_unuse(module);
48 static calf_ui_type_module type_module;
52 calf_line_graph_copy_cache_to_window( CalfLineGraph
*lg
, cairo_t
*c
)
55 cairo_set_source_surface( c
, lg
->cache_surface
, 0,0 );
61 calf_line_graph_copy_window_to_cache( CalfLineGraph
*lg
, cairo_t
*c
)
63 cairo_t
*cache_cr
= cairo_create( lg
->cache_surface
);
64 cairo_surface_t
*window_surface
= cairo_get_target( c
);
65 cairo_set_source_surface( cache_cr
, window_surface
, 0,0 );
66 cairo_paint( cache_cr
);
67 cairo_destroy( cache_cr
);
71 calf_line_graph_draw_grid( cairo_t
*c
, std::string
&legend
, bool vertical
, float pos
, int phase
, int sx
, int sy
)
74 cairo_text_extents_t tx
;
76 cairo_text_extents(c
, legend
.c_str(), &tx
);
79 float x
= floor(ox
+ pos
* sx
) + 0.5;
82 cairo_move_to(c
, x
, oy
);
83 cairo_line_to(c
, x
, oy
+ sy
);
86 if (phase
== 2 && !legend
.empty()) {
88 cairo_set_source_rgba(c
, 0.0, 0.0, 0.0, 0.7);
89 cairo_move_to(c
, x
- (tx
.x_bearing
+ tx
.width
/ 2.0) - 2, oy
+ sy
- 2);
90 cairo_show_text(c
, legend
.c_str());
95 float y
= floor(oy
+ sy
/ 2 - (sy
/ 2 - 1) * pos
) + 0.5;
98 cairo_move_to(c
, ox
, y
);
99 cairo_line_to(c
, ox
+ sx
, y
);
102 if (phase
== 2 && !legend
.empty()) {
103 cairo_set_source_rgba(c
, 0.0, 0.0, 0.0, 0.7);
104 cairo_move_to(c
, ox
+ sx
- 4 - tx
.width
, y
+ tx
.height
/2);
105 cairo_show_text(c
, legend
.c_str());
111 calf_line_graph_draw_graph( cairo_t
*c
, float *data
, int sx
, int sy
)
115 for (int i
= 0; i
< 2 * sx
; i
++)
117 int y
= (int)(oy
+ sy
/ 2 - (sy
/ 2 - 1) * data
[i
]);
118 //if (y < oy) y = oy;
119 //if (y >= oy + sy) y = oy + sy - 1;
121 cairo_line_to(c
, ox
+ i
* 0.5, y
);
123 cairo_move_to(c
, ox
, y
);
129 calf_line_graph_expose (GtkWidget
*widget
, GdkEventExpose
*event
)
131 g_assert(CALF_IS_LINE_GRAPH(widget
));
133 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
134 //int ox = widget->allocation.x + 1, oy = widget->allocation.y + 1;
135 int ox
= 5, oy
= 5, rad
, pad
;
136 int sx
= widget
->allocation
.width
- ox
* 2, sy
= widget
->allocation
.height
- oy
* 2;
138 cairo_t
*c
= gdk_cairo_create(GDK_DRAWABLE(widget
->window
));
140 style
= gtk_widget_get_style(widget
);
141 GdkColor sc
= { 0, 0, 0, 0 };
143 bool cache_dirty
= 0;
145 if( lg
->cache_surface
== NULL
) {
146 // looks like its either first call or the widget has been resized.
147 // create the cache_surface.
148 cairo_surface_t
*window_surface
= cairo_get_target( c
);
149 lg
->cache_surface
= cairo_surface_create_similar( window_surface
,
151 widget
->allocation
.width
,
152 widget
->allocation
.height
);
153 //cairo_set_source_surface( cache_cr, window_surface, 0,0 );
154 //cairo_paint( cache_cr );
159 cairo_select_font_face(c
, "Bitstream Vera Sans", CAIRO_FONT_SLANT_NORMAL
, CAIRO_FONT_WEIGHT_NORMAL
);
160 cairo_set_font_size(c
, 9);
161 gdk_cairo_set_source_color(c
, &sc
);
170 bool vertical
= false;
172 float *data
= new float[2 * sx
];
173 GdkColor sc2
= { 0, 0.35 * 65535, 0.4 * 65535, 0.2 * 65535 };
176 GdkColor sc3
= { 0, 0.35 * 65535, 0.4 * 65535, 0.2 * 65535 };
178 int graph_n
, grid_n
, dot_n
, grid_n_save
;
180 int cache_graph_index
, cache_dot_index
, cache_grid_index
;
181 int gen_index
= lg
->source
->get_changed_offsets( lg
->source_id
, lg
->last_generation
, cache_graph_index
, cache_dot_index
, cache_grid_index
);
183 if( cache_dirty
|| (gen_index
!= lg
->last_generation
) ) {
185 cairo_t
*cache_cr
= cairo_create( lg
->cache_surface
);
187 // if(widget->style->bg_pixmap[0] == NULL) {
188 gdk_cairo_set_source_color(cache_cr
,&style
->bg
[GTK_STATE_NORMAL
]);
190 // gdk_cairo_set_source_pixbuf(cache_cr, GDK_PIXBUF(&style->bg_pixmap[GTK_STATE_NORMAL]), widget->allocation.x, widget->allocation.y + 20);
192 cairo_paint(cache_cr
);
197 cairo_arc(cache_cr
, rad
+ pad
, rad
+ pad
, rad
, 0, 2 * M_PI
);
198 cairo_arc(cache_cr
, ox
* 2 + sx
- rad
- pad
, rad
+ pad
, rad
, 0, 2 * M_PI
);
199 cairo_arc(cache_cr
, rad
+ pad
, oy
* 2 + sy
- rad
- pad
, rad
, 0, 2 * M_PI
);
200 cairo_arc(cache_cr
, ox
* 2 + sx
- rad
- pad
, oy
* 2 + sy
- rad
- pad
, rad
, 0, 2 * M_PI
);
201 cairo_rectangle(cache_cr
, pad
, rad
+ pad
, sx
+ ox
* 2 - pad
* 2, sy
+ oy
* 2 - rad
* 2 - pad
* 2);
202 cairo_rectangle(cache_cr
, rad
+ pad
, pad
, sx
+ ox
* 2 - rad
* 2 - pad
* 2, sy
+ oy
* 2 - pad
* 2);
203 cairo_pattern_t
*pat2
= cairo_pattern_create_linear (0, 0, 0, sy
+ oy
* 2 - pad
* 2);
204 cairo_pattern_add_color_stop_rgba (pat2
, 0, 0, 0, 0, 0.3);
205 cairo_pattern_add_color_stop_rgba (pat2
, 1, 1, 1, 1, 0.6);
206 cairo_set_source (cache_cr
, pat2
);
207 cairo_fill(cache_cr
);
212 cairo_arc(cache_cr
, rad
+ pad
, rad
+ pad
, rad
, 0, 2 * M_PI
);
213 cairo_arc(cache_cr
, ox
* 2 + sx
- rad
- pad
, rad
+ pad
, rad
, 0, 2 * M_PI
);
214 cairo_arc(cache_cr
, rad
+ pad
, oy
* 2 + sy
- rad
- pad
, rad
, 0, 2 * M_PI
);
215 cairo_arc(cache_cr
, ox
* 2 + sx
- rad
- pad
, oy
* 2 + sy
- rad
- pad
, rad
, 0, 2 * M_PI
);
216 cairo_rectangle(cache_cr
, pad
, rad
+ pad
, sx
+ ox
* 2 - pad
* 2, sy
+ oy
* 2 - rad
* 2 - pad
* 2);
217 cairo_rectangle(cache_cr
, rad
+ pad
, pad
, sx
+ ox
* 2 - rad
* 2 - pad
* 2, sy
+ oy
* 2 - pad
* 2);
218 pat2
= cairo_pattern_create_linear (0, 0, 0, sy
+ oy
* 2 - pad
* 2);
219 cairo_pattern_add_color_stop_rgba (pat2
, 0, 0.23, 0.23, 0.23, 1);
220 cairo_pattern_add_color_stop_rgba (pat2
, 0.5, 0, 0, 0, 1);
221 cairo_set_source (cache_cr
, pat2
);
222 cairo_fill(cache_cr
);
223 cairo_pattern_destroy(pat2
);
225 cairo_rectangle(cache_cr
, ox
- 1, oy
- 1, sx
+ 2, sy
+ 2);
226 cairo_set_source_rgb (cache_cr
, 0, 0, 0);
227 cairo_fill(cache_cr
);
229 cairo_pattern_t
*pt
= cairo_pattern_create_linear(ox
, oy
, ox
, sy
);
230 cairo_pattern_add_color_stop_rgb(pt
, 0.0, 0.44, 0.44, 0.30);
231 cairo_pattern_add_color_stop_rgb(pt
, 0.025, 0.89, 0.99, 0.54);
232 cairo_pattern_add_color_stop_rgb(pt
, 0.4, 0.78, 0.89, 0.45);
233 cairo_pattern_add_color_stop_rgb(pt
, 0.400001,0.71, 0.82, 0.33);
234 cairo_pattern_add_color_stop_rgb(pt
, 1.0, 0.89, 1.00, 0.45);
235 cairo_set_source (cache_cr
, pt
);
236 cairo_rectangle(cache_cr
, ox
, oy
, sx
, sy
);
237 cairo_fill(cache_cr
);
239 cairo_select_font_face(cache_cr
, "Bitstream Vera Sans", CAIRO_FONT_SLANT_NORMAL
, CAIRO_FONT_WEIGHT_NORMAL
);
240 cairo_set_font_size(cache_cr
, 9);
242 cairo_impl cache_cimpl
;
243 cache_cimpl
.context
= cache_cr
;
245 lg
->source
->get_changed_offsets( lg
->source_id
, gen_index
, cache_graph_index
, cache_dot_index
, cache_grid_index
);
246 lg
->last_generation
= gen_index
;
248 cairo_set_line_width(cache_cr
, 1);
249 for(int phase
= 1; phase
<= 2; phase
++)
251 for(grid_n
= 0; legend
= std::string(), cairo_set_source_rgba(cache_cr
, 0, 0, 0, 0.6), (grid_n
<cache_grid_index
) && lg
->source
->get_gridline(lg
->source_id
, grid_n
, pos
, vertical
, legend
, &cache_cimpl
); grid_n
++)
253 calf_line_graph_draw_grid( cache_cr
, legend
, vertical
, pos
, phase
, sx
, sy
);
256 grid_n_save
= grid_n
;
258 gdk_cairo_set_source_color(cache_cr
, &sc2
);
259 cairo_set_line_join(cache_cr
, CAIRO_LINE_JOIN_MITER
);
260 cairo_set_line_width(cache_cr
, 1);
261 for(graph_n
= 0; (graph_n
<cache_graph_index
) && lg
->source
->get_graph(lg
->source_id
, graph_n
, data
, 2 * sx
, &cache_cimpl
); graph_n
++)
263 calf_line_graph_draw_graph( cache_cr
, data
, sx
, sy
);
265 gdk_cairo_set_source_color(cache_cr
, &sc3
);
266 for(dot_n
= 0; (dot_n
<cache_dot_index
) && lg
->source
->get_dot(lg
->source_id
, dot_n
, x
, y
, size
= 3, &cache_cimpl
); dot_n
++)
268 int yv
= (int)(oy
+ sy
/ 2 - (sy
/ 2 - 1) * y
);
269 cairo_arc(cache_cr
, ox
+ x
* sx
, yv
, size
, 0, 2 * M_PI
);
270 cairo_fill(cache_cr
);
273 // copy window to cache.
274 cairo_destroy( cache_cr
);
275 calf_line_graph_copy_cache_to_window( lg
, c
);
277 grid_n_save
= cache_grid_index
;
278 graph_n
= cache_graph_index
;
279 dot_n
= cache_dot_index
;
280 calf_line_graph_copy_cache_to_window( lg
, c
);
283 cairo_rectangle(c
, ox
, oy
, sx
, sy
);
286 cairo_set_line_width(c
, 1);
287 for(int phase
= 1; phase
<= 2; phase
++)
289 for(int gn
=grid_n_save
; legend
= std::string(), cairo_set_source_rgba(c
, 0, 0, 0, 0.6), lg
->source
->get_gridline(lg
->source_id
, gn
, pos
, vertical
, legend
, &cimpl
); gn
++)
291 calf_line_graph_draw_grid( c
, legend
, vertical
, pos
, phase
, sx
, sy
);
295 gdk_cairo_set_source_color(c
, &sc2
);
296 cairo_set_line_join(c
, CAIRO_LINE_JOIN_MITER
);
297 cairo_set_line_width(c
, 1);
298 for(int gn
= graph_n
; lg
->source
->get_graph(lg
->source_id
, gn
, data
, 2 * sx
, &cimpl
); gn
++)
300 calf_line_graph_draw_graph( c
, data
, sx
, sy
);
302 gdk_cairo_set_source_color(c
, &sc3
);
303 for(int gn
= dot_n
; lg
->source
->get_dot(lg
->source_id
, gn
, x
, y
, size
= 3, &cimpl
); gn
++)
305 int yv
= (int)(oy
+ sy
/ 2 - (sy
/ 2 - 1) * y
);
306 cairo_arc(c
, ox
+ x
* sx
, yv
, size
, 0, 2 * M_PI
);
314 // printf("exposed %p %dx%d %d+%d\n", widget->window, event->area.x, event->area.y, event->area.width, event->area.height);
319 void calf_line_graph_set_square(CalfLineGraph
*graph
, bool is_square
)
321 g_assert(CALF_IS_LINE_GRAPH(graph
));
322 graph
->is_square
= is_square
;
325 int calf_line_graph_update_if(CalfLineGraph
*graph
, int last_drawn_generation
)
327 g_assert(CALF_IS_LINE_GRAPH(graph
));
328 int generation
= last_drawn_generation
;
331 int subgraph
, dot
, gridline
;
332 generation
= graph
->source
->get_changed_offsets(graph
->source_id
, generation
, subgraph
, dot
, gridline
);
333 if (subgraph
== INT_MAX
&& dot
== INT_MAX
&& gridline
== INT_MAX
&& generation
== last_drawn_generation
)
335 gtk_widget_queue_draw(GTK_WIDGET(graph
));
341 calf_line_graph_size_request (GtkWidget
*widget
,
342 GtkRequisition
*requisition
)
344 g_assert(CALF_IS_LINE_GRAPH(widget
));
346 // CalfLineGraph *lg = CALF_LINE_GRAPH(widget);
350 calf_line_graph_size_allocate (GtkWidget
*widget
,
351 GtkAllocation
*allocation
)
353 g_assert(CALF_IS_LINE_GRAPH(widget
));
354 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
356 GtkWidgetClass
*parent_class
= (GtkWidgetClass
*) g_type_class_peek_parent( CALF_LINE_GRAPH_GET_CLASS( lg
) );
358 if( lg
->cache_surface
)
359 cairo_surface_destroy( lg
->cache_surface
);
360 lg
->cache_surface
= NULL
;
362 widget
->allocation
= *allocation
;
363 GtkAllocation
&a
= widget
->allocation
;
366 if (a
.width
> a
.height
)
368 a
.x
+= (a
.width
- a
.height
) / 2;
371 if (a
.width
< a
.height
)
373 a
.y
+= (a
.height
- a
.width
) / 2;
377 parent_class
->size_allocate( widget
, &a
);
381 calf_line_graph_class_init (CalfLineGraphClass
*klass
)
383 // GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
384 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS(klass
);
385 widget_class
->expose_event
= calf_line_graph_expose
;
386 widget_class
->size_request
= calf_line_graph_size_request
;
387 widget_class
->size_allocate
= calf_line_graph_size_allocate
;
391 calf_line_graph_init (CalfLineGraph
*self
)
393 GtkWidget
*widget
= GTK_WIDGET(self
);
394 widget
->requisition
.width
= 40;
395 widget
->requisition
.height
= 40;
396 self
->cache_surface
= NULL
;
397 self
->last_generation
= 0;
401 calf_line_graph_new()
403 return GTK_WIDGET( g_object_new (CALF_TYPE_LINE_GRAPH
, NULL
));
407 calf_line_graph_get_type (void)
409 static GType type
= 0;
411 static const GTypeInfo type_info
= {
412 sizeof(CalfLineGraphClass
),
413 NULL
, /* base_init */
414 NULL
, /* base_finalize */
415 (GClassInitFunc
)calf_line_graph_class_init
,
416 NULL
, /* class_finalize */
417 NULL
, /* class_data */
418 sizeof(CalfLineGraph
),
420 (GInstanceInitFunc
)calf_line_graph_init
423 GTypeInfo
*type_info_copy
= new GTypeInfo(type_info
);
425 for (int i
= 0; ; i
++) {
426 char *name
= g_strdup_printf("CalfLineGraph%u%d", ((unsigned int)(intptr_t)calf_line_graph_class_init
) >> 16, i
);
427 if (g_type_from_name(name
)) {
431 type
= g_type_register_static( GTK_TYPE_DRAWING_AREA
,
442 ///////////////////////////////////////// vu meter ///////////////////////////////////////////////
445 calf_vumeter_expose (GtkWidget
*widget
, GdkEventExpose
*event
)
447 g_assert(CALF_IS_VUMETER(widget
));
450 CalfVUMeter
*vu
= CALF_VUMETER(widget
);
453 int ox
= 4, oy
= 3, led
= 3, inner
= 1, rad
, pad
;
454 int sx
= widget
->allocation
.width
- (ox
* 2) - ((widget
->allocation
.width
- inner
* 2 - ox
* 2) % led
) - 1, sy
= widget
->allocation
.height
- (oy
* 2);
455 style
= gtk_widget_get_style(widget
);
456 cairo_t
*c
= gdk_cairo_create(GDK_DRAWABLE(widget
->window
));
458 if( vu
->cache_surface
== NULL
) {
459 // looks like its either first call or the widget has been resized.
460 // create the cache_surface.
461 cairo_surface_t
*window_surface
= cairo_get_target( c
);
462 vu
->cache_surface
= cairo_surface_create_similar( window_surface
,
464 widget
->allocation
.width
,
465 widget
->allocation
.height
);
467 // And render the meterstuff again.
469 cairo_t
*cache_cr
= cairo_create( vu
->cache_surface
);
471 // theme background for reduced width and round borders
472 // if(widget->style->bg_pixmap[0] == NULL) {
473 gdk_cairo_set_source_color(cache_cr
,&style
->bg
[GTK_STATE_NORMAL
]);
475 // gdk_cairo_set_source_pixbuf(cache_cr, GDK_PIXBUF(widget->style->bg_pixmap[0]), widget->allocation.x, widget->allocation.y + 20);
477 cairo_paint(cache_cr
);
482 cairo_arc(cache_cr
, rad
+ pad
, rad
+ pad
, rad
, 0, 2 * M_PI
);
483 cairo_arc(cache_cr
, ox
* 2 + sx
- rad
- pad
, rad
+ pad
, rad
, 0, 2 * M_PI
);
484 cairo_arc(cache_cr
, rad
+ pad
, oy
* 2 + sy
- rad
- pad
, rad
, 0, 2 * M_PI
);
485 cairo_arc(cache_cr
, ox
* 2 + sx
- rad
- pad
, oy
* 2 + sy
- rad
- pad
, rad
, 0, 2 * M_PI
);
486 cairo_rectangle(cache_cr
, pad
, rad
+ pad
, sx
+ ox
* 2 - pad
* 2, sy
+ oy
* 2 - rad
* 2 - pad
* 2);
487 cairo_rectangle(cache_cr
, rad
+ pad
, pad
, sx
+ ox
* 2 - rad
* 2 - pad
* 2, sy
+ oy
* 2 - pad
* 2);
488 cairo_pattern_t
*pat2
= cairo_pattern_create_linear (0, 0, 0, sy
+ oy
* 2 - pad
* 2);
489 cairo_pattern_add_color_stop_rgba (pat2
, 0, 0, 0, 0, 0.3);
490 cairo_pattern_add_color_stop_rgba (pat2
, 1, 1, 1, 1, 0.6);
491 cairo_set_source (cache_cr
, pat2
);
492 cairo_fill(cache_cr
);
497 cairo_arc(cache_cr
, rad
+ pad
, rad
+ pad
, rad
, 0, 2 * M_PI
);
498 cairo_arc(cache_cr
, ox
* 2 + sx
- rad
- pad
, rad
+ pad
, rad
, 0, 2 * M_PI
);
499 cairo_arc(cache_cr
, rad
+ pad
, oy
* 2 + sy
- rad
- pad
, rad
, 0, 2 * M_PI
);
500 cairo_arc(cache_cr
, ox
* 2 + sx
- rad
- pad
, oy
* 2 + sy
- rad
- pad
, rad
, 0, 2 * M_PI
);
501 cairo_rectangle(cache_cr
, pad
, rad
+ pad
, sx
+ ox
* 2 - pad
* 2, sy
+ oy
* 2 - rad
* 2 - pad
* 2);
502 cairo_rectangle(cache_cr
, rad
+ pad
, pad
, sx
+ ox
* 2 - rad
* 2 - pad
* 2, sy
+ oy
* 2 - pad
* 2);
503 pat2
= cairo_pattern_create_linear (0, 0, 0, sy
+ oy
* 2 - pad
* 2);
504 cairo_pattern_add_color_stop_rgba (pat2
, 0, 0.23, 0.23, 0.23, 1);
505 cairo_pattern_add_color_stop_rgba (pat2
, 0.5, 0, 0, 0, 1);
506 cairo_set_source (cache_cr
, pat2
);
507 //cairo_set_source_rgb(cache_cr, 0, 0, 0);
508 cairo_fill(cache_cr
);
509 cairo_pattern_destroy(pat2
);
511 cairo_rectangle(cache_cr
, ox
, oy
, sx
, sy
);
512 cairo_set_source_rgb (cache_cr
, 0, 0, 0);
513 cairo_fill(cache_cr
);
515 cairo_set_line_width(cache_cr
, 1);
517 for (int x
= ox
+ inner
; x
<= ox
+ sx
- led
; x
+= led
)
519 float ts
= (x
- ox
) * 1.0 / sx
;
520 float r
= 0.f
, g
= 0.f
, b
= 0.f
;
526 r
= ts
/ 0.75, g
= 0.5 + ts
* 0.66, b
= 1 - ts
/ 0.75;
528 r
= 1, g
= 1 - (ts
- 0.75) / 0.25, b
= 0;
529 // if (vu->value < ts || vu->value <= 0)
530 // r *= 0.5, g *= 0.5, b *= 0.5;
532 case VU_MONOCHROME_REVERSE
:
533 r
= 0, g
= 170.0 / 255.0, b
= 1;
534 // if (!(vu->value < ts) || vu->value >= 1.0)
535 // r *= 0.5, g *= 0.5, b *= 0.5;
538 r
= 0, g
= 170.0 / 255.0, b
= 1;
539 // if (vu->value < ts || vu->value <= 0)
540 // r *= 0.5, g *= 0.5, b *= 0.5;
543 GdkColor sc2
= { 0, (guint16
)(65535 * r
+ 0.2), (guint16
)(65535 * g
), (guint16
)(65535 * b
) };
544 GdkColor sc3
= { 0, (guint16
)(65535 * r
* 0.7), (guint16
)(65535 * g
* 0.7), (guint16
)(65535 * b
* 0.7) };
545 gdk_cairo_set_source_color(cache_cr
, &sc2
);
546 cairo_move_to(cache_cr
, x
+ 0.5, oy
+ 1);
547 cairo_line_to(cache_cr
, x
+ 0.5, oy
+ sy
- inner
);
548 cairo_stroke(cache_cr
);
549 gdk_cairo_set_source_color(cache_cr
, &sc3
);
550 cairo_move_to(cache_cr
, x
+ 1.5, oy
+ sy
- inner
);
551 cairo_line_to(cache_cr
, x
+ 1.5, oy
+ 1);
552 cairo_stroke(cache_cr
);
554 // create blinder pattern
555 vu
->pat
= cairo_pattern_create_linear (ox
, oy
, ox
, ox
+ sy
);
556 cairo_pattern_add_color_stop_rgba (vu
->pat
, 0, 0.5, 0.5, 0.5, 0.6);
557 cairo_pattern_add_color_stop_rgba (vu
->pat
, 0.4, 0, 0, 0, 0.6);
558 cairo_pattern_add_color_stop_rgba (vu
->pat
, 0.401, 0, 0, 0, 0.8);
559 cairo_pattern_add_color_stop_rgba (vu
->pat
, 1, 0, 0, 0, 0.6);
560 cairo_destroy( cache_cr
);
563 cairo_set_source_surface( c
, vu
->cache_surface
, 0,0 );
565 cairo_set_source( c
, vu
->pat
);
569 gettimeofday(&tv
, 0);
570 long time
= tv
.tv_sec
* 1000 * 1000 + tv
.tv_usec
;
573 float value_orig
= vu
->value
> 1.f
? 1.f
: vu
->value
;
574 value_orig
= value_orig
< 0.f
? 0.f
: value_orig
;
578 if(vu
->vumeter_falloff
> 0.f
and vu
->mode
!= VU_MONOCHROME_REVERSE
) {
580 float s
= ((float)(time
- vu
->last_falltime
) / 1000000.0);
581 float m
= vu
->last_falloff
* s
* vu
->vumeter_falloff
;
582 vu
->last_falloff
-= m
;
584 if(value_orig
> vu
->last_falloff
) {
585 vu
->last_falloff
= value_orig
;
587 value
= vu
->last_falloff
;
588 vu
->last_falltime
= time
;
589 vu
->falling
= vu
->last_falloff
> 0.000001;
592 vu
->last_falloff
= 0.f
;
593 vu
->last_falltime
= 0.f
;
597 if(vu
->vumeter_hold
> 0.0) {
599 if(time
- (long)(vu
->vumeter_hold
* 1000 * 1000) > vu
->last_hold
) {
601 vu
->last_value
= value
;
602 vu
->last_hold
= time
;
605 if( vu
->mode
== VU_MONOCHROME_REVERSE
) {
606 if(value
< vu
->last_value
) {
607 // value is above peak hold
608 vu
->last_value
= value
;
609 vu
->last_hold
= time
;
612 int hpx
= round(vu
->last_value
* (sx
- 2 * inner
));
613 hpx
= hpx
+ (1 - (hpx
+ inner
) % led
);
614 int vpx
= round((1 - value
) * (sx
- 2 * inner
));
615 vpx
= vpx
+ (1 - (vpx
+ inner
) % led
);
616 int widthA
= std::min(led
+ hpx
, (sx
- 2 * inner
));
617 int widthB
= std::min(std::max((sx
- 2 * inner
) - vpx
- led
- hpx
, 0), (sx
- 2 * inner
));
618 cairo_rectangle( c
, ox
+ inner
, oy
+ inner
, hpx
, sy
- 2 * inner
);
619 cairo_rectangle( c
, ox
+ inner
+ widthA
, oy
+ inner
, widthB
, sy
- 2 * inner
);
621 if(value
> vu
->last_value
) {
622 // value is above peak hold
623 vu
->last_value
= value
;
624 vu
->last_hold
= time
;
627 int hpx
= round((1 - vu
->last_value
) * (sx
- 2 * inner
));
628 hpx
= hpx
+ (1 - (hpx
+ inner
) % led
);
629 int vpx
= round(value
* (sx
- 2 * inner
));
630 vpx
= vpx
+ (1 - (vpx
+ inner
) % led
);
631 int width
= std::min(std::max((sx
- 2 * inner
) - vpx
- led
- hpx
, 0), (sx
- 2 * inner
));
632 cairo_rectangle( c
, ox
+ inner
+ vpx
, oy
+ inner
, width
, sy
- 2 * inner
);
633 cairo_rectangle( c
, ox
+ inner
+ (sx
- 2 * inner
) - hpx
, oy
+ inner
, hpx
, sy
- 2 * inner
);
637 if( vu
->mode
== VU_MONOCHROME_REVERSE
)
638 cairo_rectangle( c
, ox
+ inner
,oy
+ inner
, value
* (sx
- 2 * inner
), sy
- 2 * inner
);
640 cairo_rectangle( c
, ox
+ inner
+ value
* (sx
- 2 * inner
), oy
+ inner
, (sx
- 2 * inner
) * (1 - value
), sy
- 2 * inner
);
644 //gtk_paint_shadow(widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN, NULL, widget, NULL, ox - 2, oy - 2, sx + 4, sy + 4);
645 //printf("exposed %p %d+%d\n", widget->window, widget->allocation.x, widget->allocation.y);
651 calf_vumeter_size_request (GtkWidget
*widget
,
652 GtkRequisition
*requisition
)
654 g_assert(CALF_IS_VUMETER(widget
));
656 requisition
->width
= 50;
657 requisition
->height
= 18;
661 calf_vumeter_size_allocate (GtkWidget
*widget
,
662 GtkAllocation
*allocation
)
664 g_assert(CALF_IS_VUMETER(widget
));
665 CalfVUMeter
*vu
= CALF_VUMETER(widget
);
667 GtkWidgetClass
*parent_class
= (GtkWidgetClass
*) g_type_class_peek_parent( CALF_VUMETER_GET_CLASS( vu
) );
669 parent_class
->size_allocate( widget
, allocation
);
671 if( vu
->cache_surface
)
672 cairo_surface_destroy( vu
->cache_surface
);
673 vu
->cache_surface
= NULL
;
677 calf_vumeter_class_init (CalfVUMeterClass
*klass
)
679 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS(klass
);
680 widget_class
->expose_event
= calf_vumeter_expose
;
681 widget_class
->size_request
= calf_vumeter_size_request
;
682 widget_class
->size_allocate
= calf_vumeter_size_allocate
;
686 calf_vumeter_init (CalfVUMeter
*self
)
688 GtkWidget
*widget
= GTK_WIDGET(self
);
689 //GTK_WIDGET_SET_FLAGS (widget, GTK_NO_WINDOW);
690 widget
->requisition
.width
= 50;
691 widget
->requisition
.height
= 18;
692 self
->cache_surface
= NULL
;
693 self
->falling
= false;
694 self
->holding
= false;
700 return GTK_WIDGET( g_object_new (CALF_TYPE_VUMETER
, NULL
));
704 calf_vumeter_get_type (void)
706 static GType type
= 0;
708 static const GTypeInfo type_info
= {
709 sizeof(CalfVUMeterClass
),
710 NULL
, /* base_init */
711 NULL
, /* base_finalize */
712 (GClassInitFunc
)calf_vumeter_class_init
,
713 NULL
, /* class_finalize */
714 NULL
, /* class_data */
717 (GInstanceInitFunc
)calf_vumeter_init
720 GTypeInfo
*type_info_copy
= new GTypeInfo(type_info
);
722 for (int i
= 0; ; i
++) {
723 char *name
= g_strdup_printf("CalfVUMeter%u%d", ((unsigned int)(intptr_t)calf_vumeter_class_init
) >> 16, i
);
724 if (g_type_from_name(name
)) {
728 type
= g_type_register_static( GTK_TYPE_DRAWING_AREA
,
739 extern void calf_vumeter_set_value(CalfVUMeter
*meter
, float value
)
741 if (value
!= meter
->value
or meter
->holding
or meter
->falling
)
743 meter
->value
= value
;
744 gtk_widget_queue_draw(GTK_WIDGET(meter
));
748 extern float calf_vumeter_get_value(CalfVUMeter
*meter
)
753 extern void calf_vumeter_set_mode(CalfVUMeter
*meter
, CalfVUMeterMode mode
)
755 if (mode
!= meter
->mode
)
758 if(mode
== VU_MONOCHROME_REVERSE
) {
760 meter
->last_value
= 1.f
;
763 meter
->last_value
= 0.f
;
765 meter
->vumeter_falloff
= 0.f
;
766 meter
->last_falloff
= (long)0;
767 meter
->last_hold
= (long)0;
768 gtk_widget_queue_draw(GTK_WIDGET(meter
));
772 extern CalfVUMeterMode
calf_vumeter_get_mode(CalfVUMeter
*meter
)
777 extern void calf_vumeter_set_falloff(CalfVUMeter
*meter
, float value
)
779 if (value
!= meter
->vumeter_falloff
)
781 meter
->vumeter_falloff
= value
;
782 gtk_widget_queue_draw(GTK_WIDGET(meter
));
786 extern float calf_vumeter_get_falloff(CalfVUMeter
*meter
)
788 return meter
->vumeter_falloff
;
791 extern void calf_vumeter_set_hold(CalfVUMeter
*meter
, float value
)
793 if (value
!= meter
->vumeter_falloff
)
795 meter
->vumeter_hold
= value
;
796 gtk_widget_queue_draw(GTK_WIDGET(meter
));
800 extern float calf_vumeter_get_hold(CalfVUMeter
*meter
)
802 return meter
->vumeter_hold
;
805 ///////////////////////////////////////// knob ///////////////////////////////////////////////
808 calf_knob_expose (GtkWidget
*widget
, GdkEventExpose
*event
)
810 g_assert(CALF_IS_KNOB(widget
));
812 CalfKnob
*self
= CALF_KNOB(widget
);
813 GdkWindow
*window
= widget
->window
;
814 GtkAdjustment
*adj
= gtk_range_get_adjustment(GTK_RANGE(widget
));
816 // printf("adjustment = %p value = %f\n", adj, adj->value);
817 int ox
= widget
->allocation
.x
, oy
= widget
->allocation
.y
;
818 ox
+= (widget
->allocation
.width
- self
->knob_size
* 20) / 2;
819 oy
+= (widget
->allocation
.height
- self
->knob_size
* 20) / 2;
821 int phase
= (int)((adj
->value
- adj
->lower
) * 64 / (adj
->upper
- adj
->lower
));
822 // skip middle phase except for true middle value
823 if (self
->knob_type
== 1 && phase
== 32) {
824 double pt
= (adj
->value
- adj
->lower
) * 2.0 / (adj
->upper
- adj
->lower
) - 1.0;
830 // endless knob: skip 90deg highlights unless the value is really a multiple of 90deg
831 if (self
->knob_type
== 3 && !(phase
% 16)) {
834 double nom
= adj
->lower
+ phase
* (adj
->upper
- adj
->lower
) / 64.0;
835 double diff
= (adj
->value
- nom
) / (adj
->upper
- adj
->lower
);
837 phase
= (phase
+ 1) % 64;
839 phase
= (phase
+ 63) % 64;
841 gdk_draw_pixbuf(GDK_DRAWABLE(widget
->window
), widget
->style
->fg_gc
[0], CALF_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget
))->knob_image
[self
->knob_size
- 1], phase
* self
->knob_size
* 20, self
->knob_type
* self
->knob_size
* 20, ox
, oy
, self
->knob_size
* 20, self
->knob_size
* 20, GDK_RGB_DITHER_NORMAL
, 0, 0);
842 // printf("exposed %p %d+%d\n", widget->window, widget->allocation.x, widget->allocation.y);
843 if (gtk_widget_is_focus(widget
))
845 gtk_paint_focus(widget
->style
, window
, GTK_STATE_NORMAL
, NULL
, widget
, NULL
, ox
, oy
, self
->knob_size
* 20, self
->knob_size
* 20);
852 calf_knob_size_request (GtkWidget
*widget
,
853 GtkRequisition
*requisition
)
855 g_assert(CALF_IS_KNOB(widget
));
857 CalfKnob
*self
= CALF_KNOB(widget
);
859 // width/height is hardwired at 40px now
860 // is now chooseable by "size" value in XML (1-4)
861 requisition
->width
= 20 * self
->knob_size
;
862 requisition
->height
= 20 * self
->knob_size
;
866 calf_knob_incr (GtkWidget
*widget
, int dir_down
)
868 g_assert(CALF_IS_KNOB(widget
));
869 CalfKnob
*self
= CALF_KNOB(widget
);
870 GtkAdjustment
*adj
= gtk_range_get_adjustment(GTK_RANGE(widget
));
872 int oldstep
= (int)(0.5f
+ (adj
->value
- adj
->lower
) / adj
->step_increment
);
874 int nsteps
= (int)(0.5f
+ (adj
->upper
- adj
->lower
) / adj
->step_increment
); // less 1 actually
879 if (self
->knob_type
== 3 && step
>= nsteps
)
881 if (self
->knob_type
== 3 && step
< 0)
882 step
= nsteps
- (nsteps
- step
) % nsteps
;
884 // trying to reduce error cumulation here, by counting from lowest or from highest
885 float value
= adj
->lower
+ step
* double(adj
->upper
- adj
->lower
) / nsteps
;
886 gtk_range_set_value(GTK_RANGE(widget
), value
);
887 // printf("step %d:%d nsteps %d value %f:%f\n", oldstep, step, nsteps, oldvalue, value);
891 calf_knob_key_press (GtkWidget
*widget
, GdkEventKey
*event
)
893 g_assert(CALF_IS_KNOB(widget
));
894 CalfKnob
*self
= CALF_KNOB(widget
);
895 GtkAdjustment
*adj
= gtk_range_get_adjustment(GTK_RANGE(widget
));
897 switch(event
->keyval
)
900 gtk_range_set_value(GTK_RANGE(widget
), adj
->lower
);
904 gtk_range_set_value(GTK_RANGE(widget
), adj
->upper
);
908 calf_knob_incr(widget
, 0);
912 calf_knob_incr(widget
, 1);
917 self
->start_value
= gtk_range_get_value(GTK_RANGE(widget
));
918 self
->start_y
= self
->last_y
;
926 calf_knob_key_release (GtkWidget
*widget
, GdkEventKey
*event
)
928 g_assert(CALF_IS_KNOB(widget
));
929 CalfKnob
*self
= CALF_KNOB(widget
);
931 if(event
->keyval
== GDK_Shift_L
|| event
->keyval
== GDK_Shift_R
)
933 self
->start_value
= gtk_range_get_value(GTK_RANGE(widget
));
934 self
->start_y
= self
->last_y
;
942 calf_knob_button_press (GtkWidget
*widget
, GdkEventButton
*event
)
944 g_assert(CALF_IS_KNOB(widget
));
945 CalfKnob
*self
= CALF_KNOB(widget
);
947 // CalfKnob *lg = CALF_KNOB(widget);
948 gtk_widget_grab_focus(widget
);
949 gtk_grab_add(widget
);
950 self
->start_x
= event
->x
;
951 self
->start_y
= event
->y
;
952 self
->start_value
= gtk_range_get_value(GTK_RANGE(widget
));
958 calf_knob_button_release (GtkWidget
*widget
, GdkEventButton
*event
)
960 g_assert(CALF_IS_KNOB(widget
));
962 if (GTK_WIDGET_HAS_GRAB(widget
))
963 gtk_grab_remove(widget
);
967 static inline float endless(float value
)
970 return fmod(value
, 1.f
);
972 return fmod(1.f
- fmod(1.f
- value
, 1.f
), 1.f
);
975 static inline float deadzone(GtkWidget
*widget
, float value
, float incr
, float scale
)
977 CalfKnob
*self
= CALF_KNOB(widget
);
978 float dz
= 20 / scale
;
979 if(self
->last_dz
< 0.f
) {
980 self
-> last_dz
= value
+ incr
;
981 } else if (self
->last_dz
> 1.f
) {
984 self
->last_dz
+= incr
;
986 if(self
->last_dz
> 0.5 + dz
) {
987 return std::min(0.5 + (self
->last_dz
- 0.5 - dz
) * 0.5 / (0.5 - dz
), (double)1);
989 if(self
->last_dz
< 0.5 - dz
) {
990 return std::max(self
->last_dz
* 0.5 / (0.5 - dz
), (double)0);
996 calf_knob_pointer_motion (GtkWidget
*widget
, GdkEventMotion
*event
)
998 g_assert(CALF_IS_KNOB(widget
));
999 CalfKnob
*self
= CALF_KNOB(widget
);
1001 float scale
= (event
->state
& GDK_SHIFT_MASK
) ? 1000 : 100;
1002 gboolean moved
= FALSE
;
1004 if (GTK_WIDGET_HAS_GRAB(widget
))
1006 float sens
= 1 + fabs(event
->x
- self
->start_x
) / 10;
1007 if (self
->knob_type
== 3)
1009 gtk_range_set_value(GTK_RANGE(widget
), endless(gtk_range_get_value(GTK_RANGE(widget
)) - (event
->y
- self
->last_y
) / (scale
* sens
)));
1012 if (self
->knob_type
== 1)
1014 gtk_range_set_value(GTK_RANGE(widget
), deadzone(GTK_WIDGET(widget
), gtk_range_get_value(GTK_RANGE(widget
)), -(event
->y
- self
->last_y
) / (scale
* sens
), (scale
* sens
)));
1018 gtk_range_set_value(GTK_RANGE(widget
), gtk_range_get_value(GTK_RANGE(widget
)) - (event
->y
- self
->last_y
) / (scale
* sens
));
1022 self
->last_y
= event
->y
;
1023 self
->last_x
= event
->x
;
1028 calf_knob_scroll (GtkWidget
*widget
, GdkEventScroll
*event
)
1030 calf_knob_incr(widget
, event
->direction
);
1035 calf_knob_class_init (CalfKnobClass
*klass
)
1037 // GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
1038 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS(klass
);
1039 widget_class
->expose_event
= calf_knob_expose
;
1040 widget_class
->size_request
= calf_knob_size_request
;
1041 widget_class
->button_press_event
= calf_knob_button_press
;
1042 widget_class
->button_release_event
= calf_knob_button_release
;
1043 widget_class
->motion_notify_event
= calf_knob_pointer_motion
;
1044 widget_class
->key_press_event
= calf_knob_key_press
;
1045 widget_class
->key_release_event
= calf_knob_key_release
;
1046 widget_class
->scroll_event
= calf_knob_scroll
;
1047 GError
*error
= NULL
;
1048 klass
->knob_image
[0] = gdk_pixbuf_new_from_file(PKGLIBDIR
"/knob1.png", &error
);
1049 klass
->knob_image
[1] = gdk_pixbuf_new_from_file(PKGLIBDIR
"/knob2.png", &error
);
1050 klass
->knob_image
[2] = gdk_pixbuf_new_from_file(PKGLIBDIR
"/knob3.png", &error
);
1051 klass
->knob_image
[3] = gdk_pixbuf_new_from_file(PKGLIBDIR
"/knob4.png", &error
);
1052 klass
->knob_image
[4] = gdk_pixbuf_new_from_file(PKGLIBDIR
"/knob5.png", &error
);
1053 g_assert(klass
->knob_image
!= NULL
);
1057 calf_knob_init (CalfKnob
*self
)
1059 GtkWidget
*widget
= GTK_WIDGET(self
);
1060 GTK_WIDGET_SET_FLAGS (GTK_WIDGET(self
), GTK_CAN_FOCUS
);
1061 widget
->requisition
.width
= 40;
1062 widget
->requisition
.height
= 40;
1063 self
->last_dz
= -1.f
;
1069 GtkAdjustment
*adj
= (GtkAdjustment
*)gtk_adjustment_new(0, 0, 1, 0.01, 0.5, 0);
1070 return calf_knob_new_with_adjustment(adj
);
1073 static gboolean
calf_knob_value_changed(gpointer obj
)
1075 GtkWidget
*widget
= (GtkWidget
*)obj
;
1076 gtk_widget_queue_draw(widget
);
1080 GtkWidget
*calf_knob_new_with_adjustment(GtkAdjustment
*_adjustment
)
1082 GtkWidget
*widget
= GTK_WIDGET( g_object_new (CALF_TYPE_KNOB
, NULL
));
1084 gtk_range_set_adjustment(GTK_RANGE(widget
), _adjustment
);
1085 gtk_signal_connect(GTK_OBJECT(widget
), "value-changed", G_CALLBACK(calf_knob_value_changed
), widget
);
1091 calf_knob_get_type (void)
1093 static GType type
= 0;
1096 static const GTypeInfo type_info
= {
1097 sizeof(CalfKnobClass
),
1098 NULL
, /* base_init */
1099 NULL
, /* base_finalize */
1100 (GClassInitFunc
)calf_knob_class_init
,
1101 NULL
, /* class_finalize */
1102 NULL
, /* class_data */
1104 0, /* n_preallocs */
1105 (GInstanceInitFunc
)calf_knob_init
1108 for (int i
= 0; ; i
++) {
1109 char *name
= g_strdup_printf("CalfKnob%u%d",
1110 ((unsigned int)(intptr_t)calf_knob_class_init
) >> 16, i
);
1111 if (g_type_from_name(name
)) {
1115 type
= g_type_register_static(GTK_TYPE_RANGE
,
1126 ///////////////////////////////////////// toggle ///////////////////////////////////////////////
1129 calf_toggle_expose (GtkWidget
*widget
, GdkEventExpose
*event
)
1131 g_assert(CALF_IS_TOGGLE(widget
));
1133 CalfToggle
*self
= CALF_TOGGLE(widget
);
1134 GdkWindow
*window
= widget
->window
;
1136 int ox
= widget
->allocation
.x
+ widget
->allocation
.width
/ 2 - self
->size
* 15;
1137 int oy
= widget
->allocation
.y
+ widget
->allocation
.height
/ 2 - self
->size
* 10;
1138 int width
= self
->size
* 30, height
= self
->size
* 20;
1140 gdk_draw_pixbuf(GDK_DRAWABLE(widget
->window
), widget
->style
->fg_gc
[0], CALF_TOGGLE_CLASS(GTK_OBJECT_GET_CLASS(widget
))->toggle_image
[self
->size
- 1], 0, height
* gtk_range_get_value(GTK_RANGE(widget
)), ox
, oy
, width
, height
, GDK_RGB_DITHER_NORMAL
, 0, 0);
1141 if (gtk_widget_is_focus(widget
))
1143 gtk_paint_focus(widget
->style
, window
, GTK_STATE_NORMAL
, NULL
, widget
, NULL
, ox
, oy
, width
, height
);
1150 calf_toggle_size_request (GtkWidget
*widget
,
1151 GtkRequisition
*requisition
)
1153 g_assert(CALF_IS_TOGGLE(widget
));
1155 CalfToggle
*self
= CALF_TOGGLE(widget
);
1157 requisition
->width
= 30 * self
->size
;
1158 requisition
->height
= 20 * self
->size
;
1162 calf_toggle_button_press (GtkWidget
*widget
, GdkEventButton
*event
)
1164 g_assert(CALF_IS_TOGGLE(widget
));
1165 GtkAdjustment
*adj
= gtk_range_get_adjustment(GTK_RANGE(widget
));
1166 if (gtk_range_get_value(GTK_RANGE(widget
)) == adj
->lower
)
1168 gtk_range_set_value(GTK_RANGE(widget
), adj
->upper
);
1170 gtk_range_set_value(GTK_RANGE(widget
), adj
->lower
);
1176 calf_toggle_key_press (GtkWidget
*widget
, GdkEventKey
*event
)
1178 switch(event
->keyval
)
1183 return calf_toggle_button_press(widget
, NULL
);
1189 calf_toggle_class_init (CalfToggleClass
*klass
)
1191 // GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
1192 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS(klass
);
1193 widget_class
->expose_event
= calf_toggle_expose
;
1194 widget_class
->size_request
= calf_toggle_size_request
;
1195 widget_class
->button_press_event
= calf_toggle_button_press
;
1196 widget_class
->key_press_event
= calf_toggle_key_press
;
1197 GError
*error
= NULL
;
1198 klass
->toggle_image
[0] = gdk_pixbuf_new_from_file(PKGLIBDIR
"/toggle1.png", &error
);
1199 klass
->toggle_image
[1] = gdk_pixbuf_new_from_file(PKGLIBDIR
"/toggle2.png", &error
);
1200 g_assert(klass
->toggle_image
!= NULL
);
1204 calf_toggle_init (CalfToggle
*self
)
1206 GtkWidget
*widget
= GTK_WIDGET(self
);
1207 GTK_WIDGET_SET_FLAGS (GTK_WIDGET(self
), GTK_CAN_FOCUS
);
1208 widget
->requisition
.width
= 30;
1209 widget
->requisition
.height
= 20;
1216 GtkAdjustment
*adj
= (GtkAdjustment
*)gtk_adjustment_new(0, 0, 1, 1, 0, 0);
1217 return calf_toggle_new_with_adjustment(adj
);
1220 static gboolean
calf_toggle_value_changed(gpointer obj
)
1222 GtkWidget
*widget
= (GtkWidget
*)obj
;
1223 gtk_widget_queue_draw(widget
);
1227 GtkWidget
*calf_toggle_new_with_adjustment(GtkAdjustment
*_adjustment
)
1229 GtkWidget
*widget
= GTK_WIDGET( g_object_new (CALF_TYPE_TOGGLE
, NULL
));
1231 gtk_range_set_adjustment(GTK_RANGE(widget
), _adjustment
);
1232 gtk_signal_connect(GTK_OBJECT(widget
), "value-changed", G_CALLBACK(calf_toggle_value_changed
), widget
);
1238 calf_toggle_get_type (void)
1240 static GType type
= 0;
1243 static const GTypeInfo type_info
= {
1244 sizeof(CalfToggleClass
),
1245 NULL
, /* base_init */
1246 NULL
, /* base_finalize */
1247 (GClassInitFunc
)calf_toggle_class_init
,
1248 NULL
, /* class_finalize */
1249 NULL
, /* class_data */
1251 0, /* n_preallocs */
1252 (GInstanceInitFunc
)calf_toggle_init
1255 for (int i
= 0; ; i
++) {
1256 char *name
= g_strdup_printf("CalfToggle%u%d",
1257 ((unsigned int)(intptr_t)calf_toggle_class_init
) >> 16, i
);
1258 if (g_type_from_name(name
)) {
1262 type
= g_type_register_static( GTK_TYPE_RANGE
,
1273 ///////////////////////////////////////// tube ///////////////////////////////////////////////
1276 calf_tube_expose (GtkWidget
*widget
, GdkEventExpose
*event
)
1278 g_assert(CALF_IS_TUBE(widget
));
1280 CalfTube
*self
= CALF_TUBE(widget
);
1281 GdkWindow
*window
= widget
->window
;
1282 GtkStyle
*style
= gtk_widget_get_style(widget
);
1283 cairo_t
*c
= gdk_cairo_create(GDK_DRAWABLE(window
));
1285 int ox
= 4, oy
= 4, inner
= 1, rad
, pad
;
1286 int sx
= widget
->allocation
.width
- (ox
* 2), sy
= widget
->allocation
.height
- (oy
* 2);
1288 if( self
->cache_surface
== NULL
) {
1289 // looks like its either first call or the widget has been resized.
1290 // create the cache_surface.
1291 cairo_surface_t
*window_surface
= cairo_get_target( c
);
1292 self
->cache_surface
= cairo_surface_create_similar( window_surface
,
1293 CAIRO_CONTENT_COLOR
,
1294 widget
->allocation
.width
,
1295 widget
->allocation
.height
);
1297 // And render the meterstuff again.
1298 cairo_t
*cache_cr
= cairo_create( self
->cache_surface
);
1299 // theme background for reduced width and round borders
1300 // if(widget->style->bg_pixmap[0] == NULL) {
1301 gdk_cairo_set_source_color(cache_cr
,&style
->bg
[GTK_STATE_NORMAL
]);
1303 // gdk_cairo_set_source_pixbuf(cache_cr, GDK_PIXBUF(widget->style->bg_pixmap[0]), widget->allocation.x, widget->allocation.y + 20);
1305 cairo_paint(cache_cr
);
1310 cairo_arc(cache_cr
, rad
+ pad
, rad
+ pad
, rad
, 0, 2 * M_PI
);
1311 cairo_arc(cache_cr
, ox
* 2 + sx
- rad
- pad
, rad
+ pad
, rad
, 0, 2 * M_PI
);
1312 cairo_arc(cache_cr
, rad
+ pad
, oy
* 2 + sy
- rad
- pad
, rad
, 0, 2 * M_PI
);
1313 cairo_arc(cache_cr
, ox
* 2 + sx
- rad
- pad
, oy
* 2 + sy
- rad
- pad
, rad
, 0, 2 * M_PI
);
1314 cairo_rectangle(cache_cr
, pad
, rad
+ pad
, sx
+ ox
* 2 - pad
* 2, sy
+ oy
* 2 - rad
* 2 - pad
* 2);
1315 cairo_rectangle(cache_cr
, rad
+ pad
, pad
, sx
+ ox
* 2 - rad
* 2 - pad
* 2, sy
+ oy
* 2 - pad
* 2);
1316 cairo_pattern_t
*pat2
= cairo_pattern_create_linear (0, 0, 0, sy
+ oy
* 2 - pad
* 2);
1317 cairo_pattern_add_color_stop_rgba (pat2
, 0, 0, 0, 0, 0.3);
1318 cairo_pattern_add_color_stop_rgba (pat2
, 1, 1, 1, 1, 0.6);
1319 cairo_set_source (cache_cr
, pat2
);
1320 cairo_fill(cache_cr
);
1325 cairo_arc(cache_cr
, rad
+ pad
, rad
+ pad
, rad
, 0, 2 * M_PI
);
1326 cairo_arc(cache_cr
, ox
* 2 + sx
- rad
- pad
, rad
+ pad
, rad
, 0, 2 * M_PI
);
1327 cairo_arc(cache_cr
, rad
+ pad
, oy
* 2 + sy
- rad
- pad
, rad
, 0, 2 * M_PI
);
1328 cairo_arc(cache_cr
, ox
* 2 + sx
- rad
- pad
, oy
* 2 + sy
- rad
- pad
, rad
, 0, 2 * M_PI
);
1329 cairo_rectangle(cache_cr
, pad
, rad
+ pad
, sx
+ ox
* 2 - pad
* 2, sy
+ oy
* 2 - rad
* 2 - pad
* 2);
1330 cairo_rectangle(cache_cr
, rad
+ pad
, pad
, sx
+ ox
* 2 - rad
* 2 - pad
* 2, sy
+ oy
* 2 - pad
* 2);
1331 pat2
= cairo_pattern_create_linear (0, 0, 0, sy
+ oy
* 2 - pad
* 2);
1332 cairo_pattern_add_color_stop_rgba (pat2
, 0, 0.23, 0.23, 0.23, 1);
1333 cairo_pattern_add_color_stop_rgba (pat2
, 0.5, 0, 0, 0, 1);
1334 cairo_set_source (cache_cr
, pat2
);
1335 //cairo_set_source_rgb(cache_cr, 0, 0, 0);
1336 cairo_fill(cache_cr
);
1337 cairo_pattern_destroy(pat2
);
1339 cairo_rectangle(cache_cr
, ox
, oy
, sx
, sy
);
1340 cairo_set_source_rgb (cache_cr
, 0, 0, 0);
1341 cairo_fill(cache_cr
);
1343 cairo_surface_t
*image
;
1344 switch(self
->direction
) {
1347 switch(self
->size
) {
1350 image
= cairo_image_surface_create_from_png (PKGLIBDIR
"tubeV1.png");
1353 image
= cairo_image_surface_create_from_png (PKGLIBDIR
"tubeV2.png");
1360 switch(self
->size
) {
1363 image
= cairo_image_surface_create_from_png (PKGLIBDIR
"tubeH1.png");
1366 image
= cairo_image_surface_create_from_png (PKGLIBDIR
"tubeH2.png");
1371 cairo_set_source_surface (cache_cr
, image
, widget
->allocation
.width
/ 2 - sx
/ 2 + inner
, widget
->allocation
.height
/ 2 - sy
/ 2 + inner
);
1372 cairo_paint (cache_cr
);
1373 cairo_surface_destroy (image
);
1374 cairo_destroy( cache_cr
);
1377 cairo_set_source_surface( c
, self
->cache_surface
, 0,0 );
1382 gettimeofday(&tv
, 0);
1383 long time
= tv
.tv_sec
* 1000 * 1000 + tv
.tv_usec
;
1386 float value_orig
= self
->value
> 1.f
? 1.f
: self
->value
;
1387 value_orig
= value_orig
< 0.f
? 0.f
: value_orig
;
1390 float s
= ((float)(time
- self
->last_falltime
) / 1000000.0);
1391 float m
= self
->last_falloff
* s
* 2.5;
1392 self
->last_falloff
-= m
;
1394 if(value_orig
> self
->last_falloff
) {
1395 self
->last_falloff
= value_orig
;
1397 value
= self
->last_falloff
;
1398 self
->last_falltime
= time
;
1399 self
->falling
= self
->last_falloff
> 0.000001;
1400 cairo_pattern_t
*pat
;
1402 switch(self
->direction
) {
1405 cairo_arc(c
, ox
+ sx
* 0.5, oy
+ sy
* 0.2, sx
, 0, 2 * M_PI
);
1406 pat
= cairo_pattern_create_radial (ox
+ sx
* 0.5, oy
+ sy
* 0.2, 3, ox
+ sx
* 0.5, oy
+ sy
* 0.2, sx
);
1411 cairo_arc(c
, ox
+ sx
* 0.8, oy
+ sy
* 0.5, sy
, 0, 2 * M_PI
);
1412 pat
= cairo_pattern_create_radial (ox
+ sx
* 0.8, oy
+ sy
* 0.5, 3, ox
+ sx
* 0.8, oy
+ sy
* 0.5, sy
);
1415 cairo_pattern_add_color_stop_rgba (pat
, 0, 1, 1, 1, value
);
1416 cairo_pattern_add_color_stop_rgba (pat
, 0.3, 1, 0.8, 0.3, value
* 0.4);
1417 cairo_pattern_add_color_stop_rgba (pat
, 0.31, 0.9, 0.5, 0.1, value
* 0.5);
1418 cairo_pattern_add_color_stop_rgba (pat
, 1, 0.0, 0.2, 0.7, 0);
1419 cairo_set_source (c
, pat
);
1422 switch(self
->direction
) {
1425 cairo_arc(c
, ox
+ sx
* 0.5, oy
+ sy
* 0.75, sx
/ 2, 0, 2 * M_PI
);
1426 pat
= cairo_pattern_create_radial (ox
+ sx
* 0.5, oy
+ sy
* 0.75, 2, ox
+ sx
* 0.5, oy
+ sy
* 0.75, sx
/ 2);
1431 cairo_arc(c
, ox
+ sx
* 0.25, oy
+ sy
* 0.5, sy
/ 2, 0, 2 * M_PI
);
1432 pat
= cairo_pattern_create_radial (ox
+ sx
* 0.25, oy
+ sy
* 0.5, 2, ox
+ sx
* 0.25, oy
+ sy
* 0.5, sy
/ 2);
1435 cairo_pattern_add_color_stop_rgba (pat
, 0, 1, 1, 1, value
);
1436 cairo_pattern_add_color_stop_rgba (pat
, 0.3, 1, 0.8, 0.3, value
* 0.4);
1437 cairo_pattern_add_color_stop_rgba (pat
, 0.31, 0.9, 0.5, 0.1, value
* 0.5);
1438 cairo_pattern_add_color_stop_rgba (pat
, 1, 0.0, 0.2, 0.7, 0);
1439 cairo_set_source (c
, pat
);
1446 calf_tube_size_request (GtkWidget
*widget
,
1447 GtkRequisition
*requisition
)
1449 g_assert(CALF_IS_TUBE(widget
));
1451 CalfTube
*self
= CALF_TUBE(widget
);
1452 switch(self
->direction
) {
1454 switch(self
->size
) {
1456 widget
->requisition
.width
= 82;
1457 widget
->requisition
.height
= 130;
1461 widget
->requisition
.width
= 130;
1462 widget
->requisition
.height
= 210;
1468 switch(self
->size
) {
1470 widget
->requisition
.width
= 130;
1471 widget
->requisition
.height
= 82;
1475 widget
->requisition
.width
= 210;
1476 widget
->requisition
.height
= 130;
1484 calf_tube_size_allocate (GtkWidget
*widget
,
1485 GtkAllocation
*allocation
)
1487 g_assert(CALF_IS_TUBE(widget
));
1488 CalfTube
*tube
= CALF_TUBE(widget
);
1490 GtkWidgetClass
*parent_class
= (GtkWidgetClass
*) g_type_class_peek_parent( CALF_TUBE_GET_CLASS( tube
) );
1492 parent_class
->size_allocate( widget
, allocation
);
1494 if( tube
->cache_surface
)
1495 cairo_surface_destroy( tube
->cache_surface
);
1496 tube
->cache_surface
= NULL
;
1500 calf_tube_class_init (CalfTubeClass
*klass
)
1502 // GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
1503 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS(klass
);
1504 widget_class
->expose_event
= calf_tube_expose
;
1505 widget_class
->size_request
= calf_tube_size_request
;
1506 widget_class
->size_allocate
= calf_tube_size_allocate
;
1510 calf_tube_init (CalfTube
*self
)
1512 GtkWidget
*widget
= GTK_WIDGET(self
);
1513 GTK_WIDGET_SET_FLAGS (GTK_WIDGET(self
), GTK_CAN_FOCUS
);
1514 switch(self
->direction
) {
1516 switch(self
->size
) {
1518 widget
->requisition
.width
= 82;
1519 widget
->requisition
.height
= 130;
1523 widget
->requisition
.width
= 130;
1524 widget
->requisition
.height
= 210;
1530 switch(self
->size
) {
1532 widget
->requisition
.width
= 130;
1533 widget
->requisition
.height
= 82;
1537 widget
->requisition
.width
= 210;
1538 widget
->requisition
.height
= 130;
1543 self
->falling
= false;
1544 self
->cache_surface
= NULL
;
1550 GtkWidget
*widget
= GTK_WIDGET( g_object_new (CALF_TYPE_TUBE
, NULL
));
1554 extern void calf_tube_set_value(CalfTube
*tube
, float value
)
1556 if (value
!= tube
->value
or tube
->falling
)
1558 tube
->value
= value
;
1559 gtk_widget_queue_draw(GTK_WIDGET(tube
));
1564 calf_tube_get_type (void)
1566 static GType type
= 0;
1569 static const GTypeInfo type_info
= {
1570 sizeof(CalfTubeClass
),
1571 NULL
, /* base_init */
1572 NULL
, /* base_finalize */
1573 (GClassInitFunc
)calf_tube_class_init
,
1574 NULL
, /* class_finalize */
1575 NULL
, /* class_data */
1577 0, /* n_preallocs */
1578 (GInstanceInitFunc
)calf_tube_init
1581 for (int i
= 0; ; i
++) {
1582 char *name
= g_strdup_printf("CalfTube%u%d",
1583 ((unsigned int)(intptr_t)calf_tube_class_init
) >> 16, i
);
1584 if (g_type_from_name(name
)) {
1588 type
= g_type_register_static( GTK_TYPE_DRAWING_AREA
,