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>
30 I don't really know how to do it, or if it can be done this way.
31 struct calf_ui_type_module
37 module = g_type_module_new();
38 g_type_module_set_name(module, "calf_custom_ctl");
39 g_type_module_use(module);
41 ~calf_ui_type_module()
43 g_type_module_unuse(module);
47 static calf_ui_type_module type_module;
51 calf_line_graph_copy_cache_to_window( CalfLineGraph
*lg
, cairo_t
*c
)
54 cairo_set_source_surface( c
, lg
->cache_surface
, 0,0 );
60 calf_line_graph_copy_window_to_cache( CalfLineGraph
*lg
, cairo_t
*c
)
62 cairo_t
*cache_cr
= cairo_create( lg
->cache_surface
);
63 cairo_surface_t
*window_surface
= cairo_get_target( c
);
64 cairo_set_source_surface( cache_cr
, window_surface
, 0,0 );
65 cairo_paint( cache_cr
);
66 cairo_destroy( cache_cr
);
70 calf_line_graph_draw_grid( cairo_t
*c
, std::string
&legend
, bool vertical
, float pos
, int phase
, int sx
, int sy
)
73 cairo_text_extents_t tx
;
75 cairo_text_extents(c
, legend
.c_str(), &tx
);
78 float x
= floor(ox
+ pos
* sx
) + 0.5;
81 cairo_move_to(c
, x
, oy
);
82 cairo_line_to(c
, x
, oy
+ sy
);
85 if (phase
== 2 && !legend
.empty()) {
87 cairo_set_source_rgba(c
, 0.0, 0.0, 0.0, 0.7);
88 cairo_move_to(c
, x
- (tx
.x_bearing
+ tx
.width
/ 2.0) - 2, oy
+ sy
- 2);
89 cairo_show_text(c
, legend
.c_str());
94 float y
= floor(oy
+ sy
/ 2 - (sy
/ 2 - 1) * pos
) + 0.5;
97 cairo_move_to(c
, ox
, y
);
98 cairo_line_to(c
, ox
+ sx
, y
);
101 if (phase
== 2 && !legend
.empty()) {
102 cairo_set_source_rgba(c
, 0.0, 0.0, 0.0, 0.7);
103 cairo_move_to(c
, ox
+ sx
- 4 - tx
.width
, y
+ tx
.height
/2);
104 cairo_show_text(c
, legend
.c_str());
110 calf_line_graph_draw_graph( cairo_t
*c
, float *data
, int sx
, int sy
)
114 for (int i
= 0; i
< 2 * sx
; i
++)
116 int y
= (int)(oy
+ sy
/ 2 - (sy
/ 2 - 1) * data
[i
]);
117 //if (y < oy) y = oy;
118 //if (y >= oy + sy) y = oy + sy - 1;
120 cairo_line_to(c
, ox
+ i
* 0.5, y
);
122 cairo_move_to(c
, ox
, y
);
128 calf_line_graph_expose (GtkWidget
*widget
, GdkEventExpose
*event
)
130 g_assert(CALF_IS_LINE_GRAPH(widget
));
132 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
133 //int ox = widget->allocation.x + 1, oy = widget->allocation.y + 1;
135 int sx
= widget
->allocation
.width
- 2, sy
= widget
->allocation
.height
- 2;
137 cairo_t
*c
= gdk_cairo_create(GDK_DRAWABLE(widget
->window
));
139 style
= gtk_widget_get_style(widget
);
140 GdkColor sc
= { 0, 0, 0, 0 };
142 bool cache_dirty
= 0;
144 if( lg
->cache_surface
== NULL
) {
145 // looks like its either first call or the widget has been resized.
146 // create the cache_surface.
147 cairo_surface_t
*window_surface
= cairo_get_target( c
);
148 lg
->cache_surface
= cairo_surface_create_similar( window_surface
,
150 widget
->allocation
.width
,
151 widget
->allocation
.height
);
152 //cairo_set_source_surface( cache_cr, window_surface, 0,0 );
153 //cairo_paint( cache_cr );
158 cairo_select_font_face(c
, "Bitstream Vera Sans", CAIRO_FONT_SLANT_NORMAL
, CAIRO_FONT_WEIGHT_NORMAL
);
159 cairo_set_font_size(c
, 9);
160 gdk_cairo_set_source_color(c
, &sc
);
161 cairo_rectangle(c
, ox
, oy
, sx
, sy
);
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
);
186 cairo_select_font_face(cache_cr
, "Bitstream Vera Sans", CAIRO_FONT_SLANT_NORMAL
, CAIRO_FONT_WEIGHT_NORMAL
);
187 cairo_set_font_size(cache_cr
, 9);
189 cairo_pattern_t
*pt
= cairo_pattern_create_linear(ox
, oy
, ox
, sy
);
190 cairo_pattern_add_color_stop_rgb(pt
, 0.0, 0.69, 0.79, 0.35);
191 cairo_pattern_add_color_stop_rgb(pt
, 0.025, 0.84, 0.94, 0.49);
192 cairo_pattern_add_color_stop_rgb(pt
, 0.5, 0.78, 0.89, 0.45);
193 cairo_pattern_add_color_stop_rgb(pt
, 0.500001,0.76, 0.87, 0.38);
194 cairo_pattern_add_color_stop_rgb(pt
, 1.0, 0.89, 1.00, 0.45);
195 //gdk_cairo_set_source_color(cache_cr, &sc);
196 cairo_set_source (cache_cr
, pt
);
197 cairo_rectangle(cache_cr
, ox
, oy
, sx
, sy
);
198 cairo_clip_preserve(cache_cr
);
199 cairo_fill_preserve(cache_cr
);
201 // cairo_set_source_rgba(cache_cr, 0, 0, 0, 0.5);
202 // cairo_set_line_width(cache_cr, 1);
203 // cairo_stroke(cache_cr);
205 cairo_impl cache_cimpl
;
206 cache_cimpl
.context
= cache_cr
;
208 lg
->source
->get_changed_offsets( lg
->source_id
, gen_index
, cache_graph_index
, cache_dot_index
, cache_grid_index
);
209 lg
->last_generation
= gen_index
;
211 cairo_set_line_width(cache_cr
, 1);
212 for(int phase
= 1; phase
<= 2; phase
++)
214 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
++)
216 calf_line_graph_draw_grid( cache_cr
, legend
, vertical
, pos
, phase
, sx
, sy
);
219 grid_n_save
= grid_n
;
221 gdk_cairo_set_source_color(cache_cr
, &sc2
);
222 cairo_set_line_join(cache_cr
, CAIRO_LINE_JOIN_MITER
);
223 cairo_set_line_width(cache_cr
, 1);
224 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
++)
226 calf_line_graph_draw_graph( cache_cr
, data
, sx
, sy
);
228 gdk_cairo_set_source_color(cache_cr
, &sc3
);
229 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
++)
231 int yv
= (int)(oy
+ sy
/ 2 - (sy
/ 2 - 1) * y
);
232 cairo_arc(cache_cr
, ox
+ x
* sx
, yv
, size
, 0, 2 * M_PI
);
233 cairo_fill(cache_cr
);
236 // copy window to cache.
237 cairo_destroy( cache_cr
);
238 calf_line_graph_copy_cache_to_window( lg
, c
);
240 grid_n_save
= cache_grid_index
;
241 graph_n
= cache_graph_index
;
242 dot_n
= cache_dot_index
;
243 calf_line_graph_copy_cache_to_window( lg
, c
);
247 cairo_set_line_width(c
, 1);
248 for(int phase
= 1; phase
<= 2; phase
++)
250 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
++)
252 calf_line_graph_draw_grid( c
, legend
, vertical
, pos
, phase
, sx
, sy
);
256 gdk_cairo_set_source_color(c
, &sc2
);
257 cairo_set_line_join(c
, CAIRO_LINE_JOIN_MITER
);
258 cairo_set_line_width(c
, 1);
259 for(int gn
= graph_n
; lg
->source
->get_graph(lg
->source_id
, gn
, data
, 2 * sx
, &cimpl
); gn
++)
261 calf_line_graph_draw_graph( c
, data
, sx
, sy
);
263 gdk_cairo_set_source_color(c
, &sc3
);
264 for(int gn
= dot_n
; lg
->source
->get_dot(lg
->source_id
, gn
, x
, y
, size
= 3, &cimpl
); gn
++)
266 int yv
= (int)(oy
+ sy
/ 2 - (sy
/ 2 - 1) * y
);
267 cairo_arc(c
, ox
+ x
* sx
, yv
, size
, 0, 2 * M_PI
);
275 gtk_paint_shadow(widget
->style
, widget
->window
, GTK_STATE_NORMAL
, GTK_SHADOW_IN
, NULL
, widget
, NULL
, ox
- 2, oy
- 2, sx
+ 4, sy
+ 4);
276 // printf("exposed %p %dx%d %d+%d\n", widget->window, event->area.x, event->area.y, event->area.width, event->area.height);
281 void calf_line_graph_set_square(CalfLineGraph
*graph
, bool is_square
)
283 g_assert(CALF_IS_LINE_GRAPH(graph
));
284 graph
->is_square
= is_square
;
287 int calf_line_graph_update_if(CalfLineGraph
*graph
, int last_drawn_generation
)
289 g_assert(CALF_IS_LINE_GRAPH(graph
));
290 int generation
= last_drawn_generation
;
293 int subgraph
, dot
, gridline
;
294 generation
= graph
->source
->get_changed_offsets(graph
->source_id
, generation
, subgraph
, dot
, gridline
);
295 if (subgraph
== INT_MAX
&& dot
== INT_MAX
&& gridline
== INT_MAX
&& generation
== last_drawn_generation
)
297 gtk_widget_queue_draw(GTK_WIDGET(graph
));
303 calf_line_graph_size_request (GtkWidget
*widget
,
304 GtkRequisition
*requisition
)
306 g_assert(CALF_IS_LINE_GRAPH(widget
));
308 // CalfLineGraph *lg = CALF_LINE_GRAPH(widget);
312 calf_line_graph_size_allocate (GtkWidget
*widget
,
313 GtkAllocation
*allocation
)
315 g_assert(CALF_IS_LINE_GRAPH(widget
));
316 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
318 GtkWidgetClass
*parent_class
= (GtkWidgetClass
*) g_type_class_peek_parent( CALF_LINE_GRAPH_GET_CLASS( lg
) );
320 if( lg
->cache_surface
)
321 cairo_surface_destroy( lg
->cache_surface
);
322 lg
->cache_surface
= NULL
;
324 widget
->allocation
= *allocation
;
325 GtkAllocation
&a
= widget
->allocation
;
328 if (a
.width
> a
.height
)
330 a
.x
+= (a
.width
- a
.height
) / 2;
333 if (a
.width
< a
.height
)
335 a
.y
+= (a
.height
- a
.width
) / 2;
339 parent_class
->size_allocate( widget
, &a
);
343 calf_line_graph_class_init (CalfLineGraphClass
*klass
)
345 // GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
346 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS(klass
);
347 widget_class
->expose_event
= calf_line_graph_expose
;
348 widget_class
->size_request
= calf_line_graph_size_request
;
349 widget_class
->size_allocate
= calf_line_graph_size_allocate
;
353 calf_line_graph_init (CalfLineGraph
*self
)
355 GtkWidget
*widget
= GTK_WIDGET(self
);
356 widget
->requisition
.width
= 40;
357 widget
->requisition
.height
= 40;
358 self
->cache_surface
= NULL
;
359 self
->last_generation
= 0;
363 calf_line_graph_new()
365 return GTK_WIDGET( g_object_new (CALF_TYPE_LINE_GRAPH
, NULL
));
369 calf_line_graph_get_type (void)
371 static GType type
= 0;
373 static const GTypeInfo type_info
= {
374 sizeof(CalfLineGraphClass
),
375 NULL
, /* base_init */
376 NULL
, /* base_finalize */
377 (GClassInitFunc
)calf_line_graph_class_init
,
378 NULL
, /* class_finalize */
379 NULL
, /* class_data */
380 sizeof(CalfLineGraph
),
382 (GInstanceInitFunc
)calf_line_graph_init
385 GTypeInfo
*type_info_copy
= new GTypeInfo(type_info
);
387 for (int i
= 0; ; i
++) {
388 char *name
= g_strdup_printf("CalfLineGraph%u%d", ((unsigned int)(intptr_t)calf_line_graph_class_init
) >> 16, i
);
389 if (g_type_from_name(name
)) {
393 type
= g_type_register_static( GTK_TYPE_DRAWING_AREA
,
404 ///////////////////////////////////////// vu meter ///////////////////////////////////////////////
407 calf_vumeter_expose (GtkWidget
*widget
, GdkEventExpose
*event
)
409 g_assert(CALF_IS_VUMETER(widget
));
412 CalfVUMeter
*vu
= CALF_VUMETER(widget
);
414 //int ox = widget->allocation.x + 1, oy = widget->allocation.y + 1;
416 // if only 1 px border:
417 // int sx = widget->allocation.width - 1 - ((widget->allocation.width - 2) % 3), sy = widget->allocation.height - 2;
418 int sx
= widget
->allocation
.width
- 4 - ((widget
->allocation
.width
- 2) % 3), sy
= widget
->allocation
.height
- 4;
419 style
= gtk_widget_get_style(widget
);
420 cairo_t
*c
= gdk_cairo_create(GDK_DRAWABLE(widget
->window
));
422 if( vu
->cache_surface
== NULL
) {
423 // looks like its either first call or the widget has been resized.
424 // create the cache_surface.
425 cairo_surface_t
*window_surface
= cairo_get_target( c
);
426 vu
->cache_surface
= cairo_surface_create_similar( window_surface
,
428 widget
->allocation
.width
,
429 widget
->allocation
.height
);
431 // And render the meterstuff again.
433 cairo_t
*cache_cr
= cairo_create( vu
->cache_surface
);
434 GdkColor sc
= { 0, 0, 0, 0 };
435 gdk_cairo_set_source_color(cache_cr
,&style
->bg
[GTK_STATE_NORMAL
]);
436 cairo_paint(cache_cr
);
437 gdk_cairo_set_source_color(cache_cr
, &sc
);
438 cairo_rectangle(cache_cr
, ox
, oy
, sx
, sy
);
439 cairo_fill(cache_cr
);
440 cairo_set_line_width(cache_cr
, 1);
442 for (int x
= ox
+ 1; x
<= ox
+ sx
- 3; x
+= 3)
444 float ts
= (x
- ox
) * 1.0 / sx
;
445 float r
= 0.f
, g
= 0.f
, b
= 0.f
;
451 r
= ts
/ 0.75, g
= 0.5 + ts
* 0.66, b
= 1 - ts
/ 0.75;
453 r
= 1, g
= 1 - (ts
- 0.75) / 0.25, b
= 0;
454 // if (vu->value < ts || vu->value <= 0)
455 // r *= 0.5, g *= 0.5, b *= 0.5;
457 case VU_MONOCHROME_REVERSE
:
458 r
= 0, g
= 170.0 / 255.0, b
= 1;
459 // if (!(vu->value < ts) || vu->value >= 1.0)
460 // r *= 0.5, g *= 0.5, b *= 0.5;
463 r
= 0, g
= 170.0 / 255.0, b
= 1;
464 // if (vu->value < ts || vu->value <= 0)
465 // r *= 0.5, g *= 0.5, b *= 0.5;
468 GdkColor sc2
= { 0, (guint16
)(65535 * r
+ 0.2), (guint16
)(65535 * g
), (guint16
)(65535 * b
) };
469 GdkColor sc3
= { 0, (guint16
)(65535 * r
* 0.7), (guint16
)(65535 * g
* 0.7), (guint16
)(65535 * b
* 0.7) };
470 gdk_cairo_set_source_color(cache_cr
, &sc2
);
471 cairo_move_to(cache_cr
, x
+ 0.5, oy
+ 1);
472 cairo_line_to(cache_cr
, x
+ 0.5, oy
+ sy
- 1);
473 cairo_stroke(cache_cr
);
474 gdk_cairo_set_source_color(cache_cr
, &sc3
);
475 cairo_move_to(cache_cr
, x
+ 1.5, oy
+ sy
- 1);
476 cairo_line_to(cache_cr
, x
+ 1.5, oy
+ 1);
477 cairo_stroke(cache_cr
);
479 cairo_destroy( cache_cr
);
482 cairo_set_source_surface( c
, vu
->cache_surface
, 0,0 );
484 cairo_set_source_rgba( c
, 0,0,0, 0.6 );
486 float value
= vu
->value
> 1.f
? 1.f
: vu
->value
;
487 if( vu
->mode
== VU_MONOCHROME_REVERSE
)
488 cairo_rectangle( c
, ox
+ 1,oy
+ 1, value
* (sx
- 2), sy
- 2);
490 cairo_rectangle( c
, ox
+ 1 + value
* (sx
- 2), oy
+ 1, (sx
- 2) * (1 - value
), sy
- 2 );
496 gtk_paint_shadow(widget
->style
, widget
->window
, GTK_STATE_NORMAL
, GTK_SHADOW_IN
, NULL
, widget
, NULL
, ox
- 2, oy
- 2, sx
+ 4, sy
+ 4);
497 //printf("exposed %p %d+%d\n", widget->window, widget->allocation.x, widget->allocation.y);
503 calf_vumeter_size_request (GtkWidget
*widget
,
504 GtkRequisition
*requisition
)
506 g_assert(CALF_IS_VUMETER(widget
));
508 requisition
->width
= 50;
509 requisition
->height
= 16;
513 calf_vumeter_size_allocate (GtkWidget
*widget
,
514 GtkAllocation
*allocation
)
516 g_assert(CALF_IS_VUMETER(widget
));
517 CalfVUMeter
*vu
= CALF_VUMETER(widget
);
519 GtkWidgetClass
*parent_class
= (GtkWidgetClass
*) g_type_class_peek_parent( CALF_VUMETER_GET_CLASS( vu
) );
521 parent_class
->size_allocate( widget
, allocation
);
523 if( vu
->cache_surface
)
524 cairo_surface_destroy( vu
->cache_surface
);
525 vu
->cache_surface
= NULL
;
529 calf_vumeter_class_init (CalfVUMeterClass
*klass
)
531 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS(klass
);
532 widget_class
->expose_event
= calf_vumeter_expose
;
533 widget_class
->size_request
= calf_vumeter_size_request
;
534 widget_class
->size_allocate
= calf_vumeter_size_allocate
;
538 calf_vumeter_init (CalfVUMeter
*self
)
540 GtkWidget
*widget
= GTK_WIDGET(self
);
541 //GTK_WIDGET_SET_FLAGS (widget, GTK_NO_WINDOW);
542 widget
->requisition
.width
= 50;
543 widget
->requisition
.height
= 16;
545 self
->cache_surface
= NULL
;
551 return GTK_WIDGET( g_object_new (CALF_TYPE_VUMETER
, NULL
));
555 calf_vumeter_get_type (void)
557 static GType type
= 0;
559 static const GTypeInfo type_info
= {
560 sizeof(CalfVUMeterClass
),
561 NULL
, /* base_init */
562 NULL
, /* base_finalize */
563 (GClassInitFunc
)calf_vumeter_class_init
,
564 NULL
, /* class_finalize */
565 NULL
, /* class_data */
568 (GInstanceInitFunc
)calf_vumeter_init
571 GTypeInfo
*type_info_copy
= new GTypeInfo(type_info
);
573 for (int i
= 0; ; i
++) {
574 char *name
= g_strdup_printf("CalfVUMeter%u%d", ((unsigned int)(intptr_t)calf_vumeter_class_init
) >> 16, i
);
575 if (g_type_from_name(name
)) {
579 type
= g_type_register_static( GTK_TYPE_DRAWING_AREA
,
590 extern void calf_vumeter_set_value(CalfVUMeter
*meter
, float value
)
592 if (value
!= meter
->value
)
594 meter
->value
= value
;
595 gtk_widget_queue_draw(GTK_WIDGET(meter
));
599 extern float calf_vumeter_get_value(CalfVUMeter
*meter
)
604 extern void calf_vumeter_set_mode(CalfVUMeter
*meter
, CalfVUMeterMode mode
)
606 if (mode
!= meter
->mode
)
609 gtk_widget_queue_draw(GTK_WIDGET(meter
));
613 extern CalfVUMeterMode
calf_vumeter_get_mode(CalfVUMeter
*meter
)
618 ///////////////////////////////////////// knob ///////////////////////////////////////////////
621 calf_knob_expose (GtkWidget
*widget
, GdkEventExpose
*event
)
623 g_assert(CALF_IS_KNOB(widget
));
625 CalfKnob
*self
= CALF_KNOB(widget
);
626 GdkWindow
*window
= widget
->window
;
627 GtkAdjustment
*adj
= gtk_range_get_adjustment(GTK_RANGE(widget
));
629 // printf("adjustment = %p value = %f\n", adj, adj->value);
630 int ox
= widget
->allocation
.x
, oy
= widget
->allocation
.y
;
632 ox
+= (widget
->allocation
.width
- self
->knob_size
* 20) / 2;
633 oy
+= (widget
->allocation
.height
- self
->knob_size
* 20) / 2;
635 int phase
= (int)((adj
->value
- adj
->lower
) * 64 / (adj
->upper
- adj
->lower
));
636 // skip middle phase except for true middle value
637 if (self
->knob_type
== 1 && phase
== 32) {
638 double pt
= (adj
->value
- adj
->lower
) * 2.0 / (adj
->upper
- adj
->lower
) - 1.0;
644 // endless knob: skip 90deg highlights unless the value is really a multiple of 90deg
645 if (self
->knob_type
== 3 && !(phase
% 16)) {
648 double nom
= adj
->lower
+ phase
* (adj
->upper
- adj
->lower
) / 64.0;
649 double diff
= (adj
->value
- nom
) / (adj
->upper
- adj
->lower
);
651 phase
= (phase
+ 1) % 64;
653 phase
= (phase
+ 63) % 64;
655 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);
656 // printf("exposed %p %d+%d\n", widget->window, widget->allocation.x, widget->allocation.y);
657 if (gtk_widget_is_focus(widget
))
659 gtk_paint_focus(widget
->style
, window
, GTK_STATE_NORMAL
, NULL
, widget
, NULL
, ox
, oy
, self
->knob_size
* 20, self
->knob_size
* 20);
666 calf_knob_size_request (GtkWidget
*widget
,
667 GtkRequisition
*requisition
)
669 g_assert(CALF_IS_KNOB(widget
));
671 CalfKnob
*self
= CALF_KNOB(widget
);
673 // width/height is hardwired at 40px now
674 // is now chooseable by "size" value in XML (1-4)
675 requisition
->width
= 20 * self
->knob_size
;
676 requisition
->height
= 20 * self
->knob_size
;
680 calf_knob_incr (GtkWidget
*widget
, int dir_down
)
682 g_assert(CALF_IS_KNOB(widget
));
683 CalfKnob
*self
= CALF_KNOB(widget
);
684 GtkAdjustment
*adj
= gtk_range_get_adjustment(GTK_RANGE(widget
));
686 int oldstep
= (int)(0.5f
+ (adj
->value
- adj
->lower
) / adj
->step_increment
);
688 int nsteps
= (int)(0.5f
+ (adj
->upper
- adj
->lower
) / adj
->step_increment
); // less 1 actually
693 if (self
->knob_type
== 3 && step
>= nsteps
)
695 if (self
->knob_type
== 3 && step
< 0)
696 step
= nsteps
- (nsteps
- step
) % nsteps
;
698 // trying to reduce error cumulation here, by counting from lowest or from highest
699 float value
= adj
->lower
+ step
* double(adj
->upper
- adj
->lower
) / nsteps
;
700 gtk_range_set_value(GTK_RANGE(widget
), value
);
701 // printf("step %d:%d nsteps %d value %f:%f\n", oldstep, step, nsteps, oldvalue, value);
705 calf_knob_key_press (GtkWidget
*widget
, GdkEventKey
*event
)
707 g_assert(CALF_IS_KNOB(widget
));
708 CalfKnob
*self
= CALF_KNOB(widget
);
709 GtkAdjustment
*adj
= gtk_range_get_adjustment(GTK_RANGE(widget
));
711 switch(event
->keyval
)
714 gtk_range_set_value(GTK_RANGE(widget
), adj
->lower
);
718 gtk_range_set_value(GTK_RANGE(widget
), adj
->upper
);
722 calf_knob_incr(widget
, 0);
726 calf_knob_incr(widget
, 1);
731 self
->start_value
= gtk_range_get_value(GTK_RANGE(widget
));
732 self
->start_y
= self
->last_y
;
740 calf_knob_key_release (GtkWidget
*widget
, GdkEventKey
*event
)
742 g_assert(CALF_IS_KNOB(widget
));
743 CalfKnob
*self
= CALF_KNOB(widget
);
745 if(event
->keyval
== GDK_Shift_L
|| event
->keyval
== GDK_Shift_R
)
747 self
->start_value
= gtk_range_get_value(GTK_RANGE(widget
));
748 self
->start_y
= self
->last_y
;
756 calf_knob_button_press (GtkWidget
*widget
, GdkEventButton
*event
)
758 g_assert(CALF_IS_KNOB(widget
));
759 CalfKnob
*self
= CALF_KNOB(widget
);
761 // CalfKnob *lg = CALF_KNOB(widget);
762 gtk_widget_grab_focus(widget
);
763 gtk_grab_add(widget
);
764 self
->start_x
= event
->x
;
765 self
->start_y
= event
->y
;
766 self
->start_value
= gtk_range_get_value(GTK_RANGE(widget
));
772 calf_knob_button_release (GtkWidget
*widget
, GdkEventButton
*event
)
774 g_assert(CALF_IS_KNOB(widget
));
776 if (GTK_WIDGET_HAS_GRAB(widget
))
777 gtk_grab_remove(widget
);
781 static inline float endless(float value
)
784 return fmod(value
, 1.f
);
786 return fmod(1.f
- fmod(1.f
- value
, 1.f
), 1.f
);
789 static inline float deadzone(float value
, float incr
, float scale
)
791 float dzw
= 10 / scale
;
799 if (value
>= (0.5 - dzw
) && value
<= (0.5 + dzw
))
807 calf_knob_pointer_motion (GtkWidget
*widget
, GdkEventMotion
*event
)
809 g_assert(CALF_IS_KNOB(widget
));
810 CalfKnob
*self
= CALF_KNOB(widget
);
812 float scale
= (event
->state
& GDK_SHIFT_MASK
) ? 10 : 1;
813 gboolean moved
= FALSE
;
815 if (GTK_WIDGET_HAS_GRAB(widget
))
817 float sens
=1/(75*(1+fabs((self
->start_x
- event
->x
)/10)));
818 if (self
->knob_type
== 3)
820 gtk_range_set_value(GTK_RANGE(widget
), endless(gtk_range_get_value(GTK_RANGE(widget
)) - (event
->y
- self
->last_y
) / scale
* sens
));
823 // if (self->knob_type == 1)
825 // gtk_range_set_value(GTK_RANGE(widget), deadzone(gtk_range_get_value(GTK_RANGE(widget)), -(event->y - self->last_y) / scale * sens, scale * sens));
829 gtk_range_set_value(GTK_RANGE(widget
), gtk_range_get_value(GTK_RANGE(widget
)) - (event
->y
- self
->last_y
) / scale
* sens
);
833 self
->last_y
= event
->y
;
834 self
->last_x
= event
->x
;
839 calf_knob_scroll (GtkWidget
*widget
, GdkEventScroll
*event
)
841 calf_knob_incr(widget
, event
->direction
);
846 calf_knob_class_init (CalfKnobClass
*klass
)
848 // GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
849 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS(klass
);
850 widget_class
->expose_event
= calf_knob_expose
;
851 widget_class
->size_request
= calf_knob_size_request
;
852 widget_class
->button_press_event
= calf_knob_button_press
;
853 widget_class
->button_release_event
= calf_knob_button_release
;
854 widget_class
->motion_notify_event
= calf_knob_pointer_motion
;
855 widget_class
->key_press_event
= calf_knob_key_press
;
856 widget_class
->key_release_event
= calf_knob_key_release
;
857 widget_class
->scroll_event
= calf_knob_scroll
;
858 GError
*error
= NULL
;
859 klass
->knob_image
[0] = gdk_pixbuf_new_from_file(PKGLIBDIR
"/knob1.png", &error
);
860 klass
->knob_image
[1] = gdk_pixbuf_new_from_file(PKGLIBDIR
"/knob2.png", &error
);
861 klass
->knob_image
[2] = gdk_pixbuf_new_from_file(PKGLIBDIR
"/knob3.png", &error
);
862 klass
->knob_image
[3] = gdk_pixbuf_new_from_file(PKGLIBDIR
"/knob4.png", &error
);
863 g_assert(klass
->knob_image
!= NULL
);
867 calf_knob_init (CalfKnob
*self
)
869 GtkWidget
*widget
= GTK_WIDGET(self
);
870 GTK_WIDGET_SET_FLAGS (GTK_WIDGET(self
), GTK_CAN_FOCUS
);
871 widget
->requisition
.width
= 40;
872 widget
->requisition
.height
= 40;
878 GtkAdjustment
*adj
= (GtkAdjustment
*)gtk_adjustment_new(0, 0, 1, 0.01, 0.5, 0);
879 return calf_knob_new_with_adjustment(adj
);
882 static gboolean
calf_knob_value_changed(gpointer obj
)
884 GtkWidget
*widget
= (GtkWidget
*)obj
;
885 gtk_widget_queue_draw(widget
);
889 GtkWidget
*calf_knob_new_with_adjustment(GtkAdjustment
*_adjustment
)
891 GtkWidget
*widget
= GTK_WIDGET( g_object_new (CALF_TYPE_KNOB
, NULL
));
893 gtk_range_set_adjustment(GTK_RANGE(widget
), _adjustment
);
894 gtk_signal_connect(GTK_OBJECT(widget
), "value-changed", G_CALLBACK(calf_knob_value_changed
), widget
);
900 calf_knob_get_type (void)
902 static GType type
= 0;
905 static const GTypeInfo type_info
= {
906 sizeof(CalfKnobClass
),
907 NULL
, /* base_init */
908 NULL
, /* base_finalize */
909 (GClassInitFunc
)calf_knob_class_init
,
910 NULL
, /* class_finalize */
911 NULL
, /* class_data */
914 (GInstanceInitFunc
)calf_knob_init
917 for (int i
= 0; ; i
++) {
918 char *name
= g_strdup_printf("CalfKnob%u%d",
919 ((unsigned int)(intptr_t)calf_knob_class_init
) >> 16, i
);
920 if (g_type_from_name(name
)) {
924 type
= g_type_register_static(GTK_TYPE_RANGE
,
935 ///////////////////////////////////////// toggle ///////////////////////////////////////////////
938 calf_toggle_expose (GtkWidget
*widget
, GdkEventExpose
*event
)
940 g_assert(CALF_IS_TOGGLE(widget
));
942 CalfToggle
*self
= CALF_TOGGLE(widget
);
943 GdkWindow
*window
= widget
->window
;
945 int ox
= widget
->allocation
.x
, oy
= widget
->allocation
.y
;
946 int width
= self
->size
* 30, height
= self
->size
* 20;
948 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);
949 if (gtk_widget_is_focus(widget
))
951 gtk_paint_focus(widget
->style
, window
, GTK_STATE_NORMAL
, NULL
, widget
, NULL
, ox
, oy
, width
, height
);
958 calf_toggle_size_request (GtkWidget
*widget
,
959 GtkRequisition
*requisition
)
961 g_assert(CALF_IS_TOGGLE(widget
));
963 CalfToggle
*self
= CALF_TOGGLE(widget
);
965 requisition
->width
= 30 * self
->size
;
966 requisition
->height
= 20 * self
->size
;
970 calf_toggle_button_press (GtkWidget
*widget
, GdkEventButton
*event
)
972 g_assert(CALF_IS_TOGGLE(widget
));
973 GtkAdjustment
*adj
= gtk_range_get_adjustment(GTK_RANGE(widget
));
974 if (gtk_range_get_value(GTK_RANGE(widget
)) == adj
->lower
)
976 gtk_range_set_value(GTK_RANGE(widget
), adj
->upper
);
978 gtk_range_set_value(GTK_RANGE(widget
), adj
->lower
);
984 calf_toggle_key_press (GtkWidget
*widget
, GdkEventKey
*event
)
986 switch(event
->keyval
)
991 return calf_toggle_button_press(widget
, NULL
);
997 calf_toggle_class_init (CalfToggleClass
*klass
)
999 // GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
1000 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS(klass
);
1001 widget_class
->expose_event
= calf_toggle_expose
;
1002 widget_class
->size_request
= calf_toggle_size_request
;
1003 widget_class
->button_press_event
= calf_toggle_button_press
;
1004 widget_class
->key_press_event
= calf_toggle_key_press
;
1005 GError
*error
= NULL
;
1006 klass
->toggle_image
[0] = gdk_pixbuf_new_from_file(PKGLIBDIR
"/toggle1.png", &error
);
1007 klass
->toggle_image
[1] = gdk_pixbuf_new_from_file(PKGLIBDIR
"/toggle2.png", &error
);
1008 g_assert(klass
->toggle_image
!= NULL
);
1012 calf_toggle_init (CalfToggle
*self
)
1014 GtkWidget
*widget
= GTK_WIDGET(self
);
1015 GTK_WIDGET_SET_FLAGS (GTK_WIDGET(self
), GTK_CAN_FOCUS
);
1016 widget
->requisition
.width
= 30;
1017 widget
->requisition
.height
= 20;
1024 GtkAdjustment
*adj
= (GtkAdjustment
*)gtk_adjustment_new(0, 0, 1, 1, 0, 0);
1025 return calf_toggle_new_with_adjustment(adj
);
1028 static gboolean
calf_toggle_value_changed(gpointer obj
)
1030 GtkWidget
*widget
= (GtkWidget
*)obj
;
1031 gtk_widget_queue_draw(widget
);
1035 GtkWidget
*calf_toggle_new_with_adjustment(GtkAdjustment
*_adjustment
)
1037 GtkWidget
*widget
= GTK_WIDGET( g_object_new (CALF_TYPE_TOGGLE
, NULL
));
1039 gtk_range_set_adjustment(GTK_RANGE(widget
), _adjustment
);
1040 gtk_signal_connect(GTK_OBJECT(widget
), "value-changed", G_CALLBACK(calf_toggle_value_changed
), widget
);
1046 calf_toggle_get_type (void)
1048 static GType type
= 0;
1051 static const GTypeInfo type_info
= {
1052 sizeof(CalfToggleClass
),
1053 NULL
, /* base_init */
1054 NULL
, /* base_finalize */
1055 (GClassInitFunc
)calf_toggle_class_init
,
1056 NULL
, /* class_finalize */
1057 NULL
, /* class_data */
1059 0, /* n_preallocs */
1060 (GInstanceInitFunc
)calf_toggle_init
1063 for (int i
= 0; ; i
++) {
1064 char *name
= g_strdup_printf("CalfToggle%u%d",
1065 ((unsigned int)(intptr_t)calf_toggle_class_init
) >> 16, i
);
1066 if (g_type_from_name(name
)) {
1070 type
= g_type_register_static( GTK_TYPE_RANGE
,