2 * Custom controls (line graph, knob).
3 * Copyright (C) 2007-2010 Krzysztof Foltman, Torben Hohn, Markus Schmidt
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA
23 #include <calf/drawingutils.h>
24 #include <calf/ctl_linegraph.h>
25 #include <gdk/gdkkeysyms.h>
30 #include <calf/giface.h>
34 #define RGBAtoINT(r, g, b, a) ((uint32_t)(r * 255) << 24) + ((uint32_t)(g * 255) << 16) + ((uint32_t)(b * 255) << 8) + (uint32_t)(a * 255)
35 #define INTtoR(color) (float)((color & 0xff000000) >> 24) / 255.f
36 #define INTtoG(color) (float)((color & 0x00ff0000) >> 16) / 255.f
37 #define INTtoB(color) (float)((color & 0x0000ff00) >> 8) / 255.f
38 #define INTtoA(color) (float)((color & 0x000000ff) >> 0) / 255.f
40 using namespace calf_plugins
;
43 calf_line_graph_draw_grid( CalfLineGraph
* lg
, cairo_t
*ctx
, std::string
&legend
, bool vertical
, float pos
)
52 cairo_text_extents_t tx
;
54 if (!legend
.empty()) {
55 cairo_text_extents(ctx
, legend
.c_str(), &tx
);
56 size
= vertical
? tx
.height
: tx
.width
;
64 x
= floor(ox
+ pos
* sx
) + 0.5;
65 cairo_move_to(ctx
, x
, oy
);
66 cairo_line_to(ctx
, x
, oy
+ sy
- size
);
68 if (!legend
.empty()) {
69 cairo_set_source_rgba(ctx
, 0.0, 0.0, 0.0, 0.5);
70 cairo_move_to(ctx
, x
- (tx
.x_bearing
+ tx
.width
/ 2.0), oy
+ sy
- 2);
71 cairo_show_text(ctx
, legend
.c_str());
76 y
= floor(oy
+ sy
/ 2 - (sy
/ 2 - 1) * pos
) + 0.5;
77 cairo_move_to(ctx
, ox
, y
);
78 cairo_line_to(ctx
, ox
+ sx
- size
, y
);
81 if (!legend
.empty()) {
82 cairo_set_source_rgba(ctx
, 0.0, 0.0, 0.0, 0.5);
83 cairo_move_to(ctx
, ox
+ sx
- 4 - tx
.width
, y
+ tx
.height
/2 - 2);
84 cairo_show_text(ctx
, legend
.c_str());
87 if (lg
->debug
> 2) printf("* grid vert: %d, x: %d, y: %d, label: %s\n", vertical
? 1 : 0, (int)x
, (int)y
, legend
.c_str());
91 calf_line_graph_draw_graph( CalfLineGraph
* lg
, cairo_t
*ctx
, float *data
, int mode
= 0 )
93 if (lg
->debug
) printf("(draw graph)\n");
104 for (int i
= 0; i
< sx
; i
++) {
105 y
= (oy
+ sy
/ 2 - (sy
/ 2 - 1) * data
[i
]);
106 if (lg
->debug
> 2) printf("* graph x: %d, y: %.5f, data: %.5f\n", i
, y
, data
[i
]);
111 // we want to draw a line
112 if (i
and (data
[i
] < INFINITY
or i
== sx
- 1)) {
113 cairo_line_to(ctx
, ox
+ i
, y
);
114 } else if (i
and startdraw
>= 0) {
117 cairo_move_to(ctx
, ox
, y
);
124 if (i
and ((data
[i
] < INFINITY
) or i
== sx
- 1)) {
125 cairo_rectangle(ctx
, ox
+ _lastx
, (int)y
, i
- _lastx
, sy
- (int)y
+ oy
);
128 startdraw
= ox
+ _lastx
;
134 // this one is drawing little boxes at the values position
135 if (i
and ((data
[i
] < INFINITY
) or i
== sx
- 1)) {
136 cairo_rectangle(ctx
, ox
+ _lastx
, (int)y
- 1, i
- _lastx
, 2);
139 startdraw
= ox
+ _lastx
;
145 // this one is drawing bars centered on the x axis
146 if (i
and ((data
[i
] < INFINITY
) or i
== sx
- 1)) {
147 cairo_rectangle(ctx
, ox
+ _lastx
, oy
+ sy
/ 2, i
- _lastx
, -1 * data
[i
] * (sy
/ 2));
150 startdraw
= ox
+ _lastx
;
156 // this one is drawing bars centered on the x axis with 1
158 if (i
and ((data
[i
] < INFINITY
) or i
== sx
- 1)) {
159 cairo_rectangle(ctx
, ox
+ _lastx
,oy
+ sy
/ 2 - sy
* lg
->offset
/ 2, i
- _lastx
, -1 * data
[i
] * (sy
/ 2) + sy
* lg
->offset
/ 2);
162 startdraw
= ox
+ _lastx
;
170 cairo_line_to(ctx
, sx
+ 2 * ox
, sy
+ 2 * oy
);
171 cairo_line_to(ctx
, 0, sy
+ 2 * oy
);
172 cairo_close_path(ctx
);
183 calf_line_graph_draw_moving(CalfLineGraph
* lg
, cairo_t
*ctx
, float *data
, int direction
, int offset
, int color
)
185 if (lg
->debug
) printf("(draw moving)\n");
195 int sm
= (direction
== LG_MOVING_UP
|| direction
== LG_MOVING_DOWN
? sx
: sy
);
196 int om
= (direction
== LG_MOVING_UP
|| direction
== LG_MOVING_DOWN
? ox
: oy
);
197 for (int i
= 0; i
< sm
; i
++) {
198 if (lg
->debug
> 2) printf("* moving i: %d, dir: %d, offset: %d, data: %.5f\n", i
, direction
, offset
, data
[i
]);
199 if (i
and ((data
[i
] < INFINITY
) or i
>= sm
)) {
200 cairo_set_source_rgba(ctx
, INTtoR(color
), INTtoG(color
), INTtoB(color
), (data
[i
] + 1) / 1.4 * INTtoA(color
));
204 cairo_rectangle(ctx
, ox
+ sx
- 1 - offset
, oy
+ _last
, 1, i
- _last
);
206 case LG_MOVING_RIGHT
:
207 cairo_rectangle(ctx
, ox
+ offset
, oy
+ _last
, 1, i
- _last
);
210 cairo_rectangle(ctx
, ox
+ _last
, oy
+ sy
- 1 - offset
, i
- _last
, 1);
213 cairo_rectangle(ctx
, ox
+ _last
, oy
+ offset
, i
- _last
, 1);
220 startdraw
= om
+ _last
;
228 void calf_line_graph_draw_label(cairo_t
*cache_cr
, std::string label
, int x
, int y
)
232 int n
= int(std::count(label
.begin(), label
.end(), '\n')) + 1;
233 cairo_set_source_rgba(cache_cr
, 0, 0, 0, 0.5);
234 std::string::size_type lpos
= label
.find_first_not_of("\n", 0);
235 std::string::size_type pos
= label
.find_first_of("\n", lpos
);
237 while (std::string::npos
!= pos
|| std::string::npos
!= lpos
) {
238 const char *str
= label
.substr(lpos
, pos
- lpos
).c_str();
240 cairo_text_extents_t tx
;
241 cairo_text_extents(cache_cr
, str
, &tx
);
243 p
= y
- 3 - (n
/ 2) * (tx
.height
+ 4);
245 cairo_move_to(cache_cr
, x
- 8 - tx
.width
, p
);
247 cairo_show_text(cache_cr
, str
);
248 lpos
= label
.find_first_not_of("\n", pos
);
249 pos
= label
.find_first_of("\n", lpos
);
253 void calf_line_graph_draw_crosshairs(CalfLineGraph
* lg
, cairo_t
* cache_cr
, bool gradient
, int gradient_rad
, float alpha
, int mask
, bool circle
, int x
, int y
, std::string label
)
255 if (lg
->debug
) printf("(draw crosshairs)\n");
266 cairo_pattern_t
*pat
;
268 if(mask
> 0 and circle
) {
269 cairo_move_to(cache_cr
, _x
, _y
);
270 cairo_arc (cache_cr
, _x
, _y
, mask
, 0, 2 * M_PI
);
271 cairo_set_source_rgba(cache_cr
, 0, 0, 0, alpha
);
272 cairo_fill(cache_cr
);
274 cairo_move_to(cache_cr
, _x
, _y
);
275 cairo_arc (cache_cr
, _x
, _y
, HANDLE_WIDTH
/ 2, 0, 2 * M_PI
);
276 cairo_set_source_rgba(cache_cr
, 0, 0, 0, 0.2);
277 cairo_fill(cache_cr
);
280 if(gradient
and gradient_rad
> 0) {
281 // draw the crosshairs with a steady gradient around
282 pat
= cairo_pattern_create_radial(_x
, _y
, 1, _x
, _y
, gradient_rad
* 2);
283 cairo_pattern_add_color_stop_rgba(pat
, 0, 0, 0, 0, alpha
);
284 cairo_pattern_add_color_stop_rgba(pat
, 0, 0, 0, 0, 0);
286 cairo_rectangle(cache_cr
, _x
, _y
- gradient_rad
, 1, gradient_rad
- mask
);
288 cairo_rectangle(cache_cr
, _x
+ mask
, _y
, gradient_rad
- mask
, 1);
290 cairo_rectangle(cache_cr
, _x
, _y
+ mask
, 1, gradient_rad
- mask
);
292 cairo_rectangle(cache_cr
, _x
- gradient_rad
, _y
, gradient_rad
- mask
, 1);
294 cairo_set_source(cache_cr
, pat
);
295 cairo_fill(cache_cr
);
296 } else if(gradient
) {
297 // draw the crosshairs with a gradient to the frame
299 cairo_rectangle(cache_cr
, _x
, oy
, 1, y
- mask
);
300 pat
= cairo_pattern_create_linear(_x
, oy
, _x
, _y
);
301 cairo_pattern_add_color_stop_rgba(pat
, 0, 0, 0, 0, 0);
302 cairo_pattern_add_color_stop_rgba(pat
, 1, 0, 0, 0, alpha
);
303 cairo_set_source(cache_cr
, pat
);
304 cairo_fill(cache_cr
);
306 cairo_rectangle(cache_cr
, _x
+ mask
, _y
, sx
- x
- mask
, 1);
307 pat
= cairo_pattern_create_linear(_x
, oy
, sx
, oy
);
308 cairo_pattern_add_color_stop_rgba(pat
, 0, 0, 0, 0, alpha
);
309 cairo_pattern_add_color_stop_rgba(pat
, 1, 0, 0, 0, 0);
310 cairo_set_source(cache_cr
, pat
);
311 cairo_fill(cache_cr
);
313 cairo_rectangle(cache_cr
, _x
, _y
+ mask
, 1, sy
- y
- mask
);
314 pat
= cairo_pattern_create_linear(_x
, _y
, _x
, oy
+ sy
);
315 cairo_pattern_add_color_stop_rgba(pat
, 0, 0, 0, 0, alpha
);
316 cairo_pattern_add_color_stop_rgba(pat
, 1, 0, 0, 0, 0);
317 cairo_set_source(cache_cr
, pat
);
318 cairo_fill(cache_cr
);
320 cairo_rectangle(cache_cr
, ox
, _y
, x
- mask
, 1);
321 pat
= cairo_pattern_create_linear(ox
, oy
, _x
, oy
);
322 cairo_pattern_add_color_stop_rgba(pat
, 0, 0, 0, 0, 0);
323 cairo_pattern_add_color_stop_rgba(pat
, 1, 0, 0, 0, alpha
);
324 cairo_set_source(cache_cr
, pat
);
325 cairo_fill(cache_cr
);
327 // draw normal crosshairs
329 cairo_move_to(cache_cr
, _x
+ 0.5, oy
+ 0.5);
330 cairo_line_to(cache_cr
, _x
+ 0.5, _y
- mask
+ 0.5);
332 cairo_move_to(cache_cr
, _x
+ mask
+ 0.5, _y
+ 0.5);
333 cairo_line_to(cache_cr
, ox
+ sx
+ 0.5, _y
+ 0.5);
335 cairo_move_to(cache_cr
, _x
+ 0.5, _y
+ mask
+ 0.5);
336 cairo_line_to(cache_cr
, _x
+ 0.5, oy
+ sy
+ 0.5);
338 cairo_move_to(cache_cr
, ox
+ 0.5, _y
+ 0.5);
339 cairo_line_to(cache_cr
, _x
- mask
+ 0.5, _y
+ 0.5);
341 cairo_set_source_rgba(cache_cr
, 0, 0, 0, alpha
);
342 cairo_stroke(cache_cr
);
344 calf_line_graph_draw_label(cache_cr
, label
, x
, y
);
347 void calf_line_graph_draw_freqhandles(CalfLineGraph
* lg
, cairo_t
* c
)
350 if (lg
->debug
) printf("(draw handles)\n");
357 if (lg
->freqhandles
> 0) {
358 cairo_set_source_rgba(c
, 0.0, 0.0, 0.0, 1.0);
359 cairo_set_line_width(c
, 1.0);
361 for (int i
= 0; i
< lg
->freqhandles
; i
++) {
362 FreqHandle
*handle
= &lg
->freq_handles
[i
];
363 if(!handle
->is_active())
366 if (handle
->value_x
> 0.0 && handle
->value_x
< 1.0) {
367 int val_x
= round(handle
->value_x
* sx
);
368 int val_y
= (handle
->dimensions
>= 2) ? round(handle
->value_y
* sy
) : 0;
372 // choose colors between dragged and normal state
373 if (lg
->handle_hovered
== i
) {
376 cairo_set_source_rgba(c
, 0, 0, 0, 0.7);
380 //cairo_set_source_rgb(c, 0.44, 0.5, 0.21);
381 cairo_set_source_rgba(c
, 0, 0, 0, 0.5);
383 if (handle
->dimensions
>= 2) {
384 cairo_move_to(c
, val_x
+ 8, val_y
);
386 cairo_move_to(c
, val_x
+ 11, oy
+ 15);
389 if (handle
->dimensions
== 1) {
390 // draw the main line
391 cairo_move_to(c
, ox
+ val_x
+ 0.5, oy
);
392 cairo_line_to(c
, ox
+ val_x
+ 0.5, oy
+ sy
);
394 // draw some one-dimensional bling-bling
395 cairo_pattern_t
*pat
;
396 switch(handle
->style
) {
399 // bell filters, default
400 pat
= cairo_pattern_create_linear(ox
, oy
, ox
, sy
);
401 cairo_pattern_add_color_stop_rgba(pat
, 0.f
, 0, 0, 0, 0);
402 cairo_pattern_add_color_stop_rgba(pat
, 0.5, 0, 0, 0, pat_alpha
);
403 cairo_pattern_add_color_stop_rgba(pat
, 1.f
, 0, 0, 0, 0);
404 cairo_rectangle(c
, ox
+ val_x
- 7, oy
, 6, sy
);
405 cairo_rectangle(c
, ox
+ val_x
+ 2, oy
, 6, sy
);
409 pat
= cairo_pattern_create_linear(ox
, oy
, ox
+ val_x
, oy
);
410 cairo_pattern_add_color_stop_rgba(pat
, 0.f
, 0, 0, 0, 0);
411 cairo_pattern_add_color_stop_rgba(pat
, 1.f
, 0, 0, 0, pat_alpha
);
412 cairo_rectangle(c
, ox
, oy
, val_x
- 1, sy
);
416 pat
= cairo_pattern_create_linear(ox
, oy
, ox
, sy
);
417 cairo_pattern_add_color_stop_rgba(pat
, 0.f
, 0, 0, 0, 0);
418 cairo_pattern_add_color_stop_rgba(pat
, 0.5, 0, 0, 0, pat_alpha
* 1.5);
419 cairo_pattern_add_color_stop_rgba(pat
, 1.f
, 0, 0, 0, 0);
420 cairo_rectangle(c
, ox
, oy
, val_x
- 1, sy
);
424 pat
= cairo_pattern_create_linear(ox
, oy
, ox
, sy
);
425 cairo_pattern_add_color_stop_rgba(pat
, 0.f
, 0, 0, 0, 0);
426 cairo_pattern_add_color_stop_rgba(pat
, 0.5, 0, 0, 0, pat_alpha
* 1.5);
427 cairo_pattern_add_color_stop_rgba(pat
, 1.f
, 0, 0, 0, 0);
428 cairo_rectangle(c
, ox
+ val_x
+ 2, oy
, sx
- val_x
- 2, sy
);
432 pat
= cairo_pattern_create_linear(ox
+ val_x
, oy
, ox
+ sx
, oy
);
433 cairo_pattern_add_color_stop_rgba(pat
, 0.f
, 0, 0, 0, pat_alpha
);
434 cairo_pattern_add_color_stop_rgba(pat
, 1.f
, 0, 0, 0, 0);
435 cairo_rectangle(c
, ox
+ val_x
+ 2, oy
, sx
- val_x
- 1, sy
);
438 cairo_set_source(c
, pat
);
440 cairo_pattern_destroy(pat
);
441 float freq
= exp((handle
->value_x
) * log(1000)) * 20.0;
443 if (handle
->label
&& strlen(handle
->label
))
444 sprintf(label
, "%.2f Hz\n%s", freq
, handle
->label
);
446 sprintf(label
, "%.2f Hz", freq
);
447 calf_line_graph_draw_label(c
, label
, val_x
, oy
+ 15);
449 float freq
= exp((handle
->value_x
) * log(1000)) * 20.0;
451 if (handle
->label
&& strlen(handle
->label
))
452 sprintf(label
, "%.2f Hz\n%.2f dB\n%s", freq
, dsp::amp2dB(handle
->value_y
), handle
->label
);
454 sprintf(label
, "%.2f Hz\n%.2f dB", freq
, dsp::amp2dB(handle
->value_y
));
455 int mask
= 30 - log10(1 + handle
->value_z
* 9) * 30 + HANDLE_WIDTH
/ 2.f
;
456 calf_line_graph_draw_crosshairs(lg
, c
, grad
, -1, pat_alpha
, mask
, true, val_x
, val_y
, std::string(label
));
464 calf_line_graph_destroy_surfaces (GtkWidget
*widget
)
466 g_assert(CALF_IS_LINE_GRAPH(widget
));
467 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
469 if (lg
->debug
) printf("{destroy surfaces}\n");
471 // destroy all surfaces - and don't tell anybody about it - hehe
472 if( lg
->background_surface
)
473 cairo_surface_destroy( lg
->background_surface
);
474 if( lg
->grid_surface
)
475 cairo_surface_destroy( lg
->grid_surface
);
476 if( lg
->cache_surface
)
477 cairo_surface_destroy( lg
->cache_surface
);
478 if( lg
->moving_surface
[0] )
479 cairo_surface_destroy( lg
->moving_surface
[0] );
480 if( lg
->moving_surface
[1] )
481 cairo_surface_destroy( lg
->moving_surface
[1] );
482 if( lg
->handles_surface
)
483 cairo_surface_destroy( lg
->handles_surface
);
484 if( lg
->realtime_surface
)
485 cairo_surface_destroy( lg
->realtime_surface
);
488 calf_line_graph_create_surfaces (GtkWidget
*widget
)
490 g_assert(CALF_IS_LINE_GRAPH(widget
));
491 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
493 if (lg
->debug
) printf("{create surfaces}\n");
495 int width
= widget
->allocation
.width
;
496 int height
= widget
->allocation
.height
;
498 // the size of the "real" drawing area
499 lg
->size_x
= width
- lg
->pad_x
* 2;
500 lg
->size_y
= height
- lg
->pad_y
* 2;
502 calf_line_graph_destroy_surfaces(widget
);
503 // create the background surface.
504 // background holds the graphics of the frame and the yellowish
505 // background light for faster redrawing of static stuff
506 lg
->background_surface
= cairo_image_surface_create(
507 CAIRO_FORMAT_ARGB32
, width
, height
);
509 // create the grid surface.
510 // this one is used as a cache for the grid on the background in the
511 // cache phase. If a graph or dot in cache phase needs to be redrawn
512 // we don't need to redraw the whole grid.
513 lg
->grid_surface
= cairo_image_surface_create(
514 CAIRO_FORMAT_ARGB32
, width
, height
);
516 // create the cache surface.
517 // cache holds a copy of the background with a static part of
518 // the grid and some static curves and dots.
519 lg
->cache_surface
= cairo_image_surface_create(
520 CAIRO_FORMAT_ARGB32
, width
, height
);
522 // create the moving surface.
523 // moving is used as a cache for any slowly moving graphics like
524 // spectralizer or waveforms
525 lg
->moving_surface
[0] = cairo_image_surface_create(
526 CAIRO_FORMAT_ARGB32
, width
, height
);
528 // create the moving temp surface.
529 // moving is used as a cache for any slowly moving graphics like
530 // spectralizer or waveforms
531 lg
->moving_surface
[1] = cairo_image_surface_create(
532 CAIRO_FORMAT_ARGB32
, width
, height
);
534 // create the handles surface.
535 // this one contains the handles graphics to avoid redrawing
537 lg
->handles_surface
= cairo_image_surface_create(
538 CAIRO_FORMAT_ARGB32
, width
, height
);
540 // create the realtime surface.
541 // realtime is used to cache the realtime graphics for drawing the
542 // crosshairs on top if nothing else changed
543 lg
->realtime_surface
= cairo_image_surface_create(
544 CAIRO_FORMAT_ARGB32
, width
, height
);
546 lg
->force_cache
= true;
550 *calf_line_graph_switch_context(CalfLineGraph
* lg
, cairo_t
*ctx
, cairo_impl
*cimpl
)
552 if (lg
->debug
) printf("{switch context}\n");
554 cimpl
->context
= ctx
;
555 cairo_select_font_face(ctx
, "Sans",
556 CAIRO_FONT_SLANT_NORMAL
, CAIRO_FONT_WEIGHT_NORMAL
);
557 cairo_set_font_size(ctx
, 9);
558 cairo_set_line_join(ctx
, CAIRO_LINE_JOIN_MITER
);
559 cairo_rectangle(ctx
, lg
->pad_x
, lg
->pad_y
, lg
->size_x
, lg
->size_y
);
565 calf_line_graph_copy_surface(cairo_t
*ctx
, cairo_surface_t
*source
, float fade
= 1.f
)
567 // copy a surface to a cairo context
569 cairo_set_source_surface(ctx
, source
, 0, 0);
571 cairo_paint_with_alpha(ctx
, fade
* 0.35 + 0.05);
579 calf_line_graph_clear_surface(cairo_t
*ctx
)
581 // clears a surface to transparent
583 cairo_set_operator(ctx
, CAIRO_OPERATOR_CLEAR
);
588 void calf_line_graph_expose_request (GtkWidget
*widget
, bool force
)
590 // someone thinks we should redraw the line graph. let's see what
591 // the plugin thinks about. To do that a bitmask is sent to the
592 // plugin which can be changed. If the plugin returns true or if
593 // the request is in response of something like dragged handles, an
594 // exposition of the widget is requested from GTK
596 g_assert(CALF_IS_LINE_GRAPH(widget
));
597 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
599 // quit if no source available
600 if (!lg
->source
) return;
602 if (lg
->debug
> 1) printf("\n\n### expose request %d ###\n", lg
->generation
);
604 // let a bitmask be switched by the plugin to determine the layers
605 // we want to draw. We set all cache layers to true if force_cache
606 // is set otherwise default is to draw nothing. The return value
607 // tells us whether the plugin wants to draw at all or not.
609 //if (lg->force_cache || lg->recreate_surfaces)
610 //lg->layers |= LG_CACHE_GRID | LG_CACHE_GRAPH | LG_CACHE_DOT | LG_CACHE_MOVING;
612 //if (lg->debug > 1) {
613 //printf("bitmask ");
614 //dsp::print_bits(sizeof(lg->layers), &lg->layers);
618 // if plugin returns true (something has obviously changed) or if
619 // the requestor forces a redraw, request an exposition of the widget
621 if (lg
->source
->get_layers(lg
->source_id
, lg
->generation
, lg
->layers
) or force
)
622 gtk_widget_queue_draw(widget
);
626 calf_line_graph_expose (GtkWidget
*widget
, GdkEventExpose
*event
)
628 g_assert(CALF_IS_LINE_GRAPH(widget
));
629 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
631 // quit if no source available
632 if (!lg
->source
) return FALSE
;
634 if (lg
->debug
) printf("\n\n####### exposing %d #######\n", lg
->generation
);
636 // cairo context of the window
637 cairo_t
*c
= gdk_cairo_create(GDK_DRAWABLE(widget
->window
));
639 // recreate surfaces if someone needs it (init of the widget,
640 // resizing the window..)
641 if (lg
->recreate_surfaces
) {
642 if (lg
->debug
) printf("recreation...\n");
643 calf_line_graph_create_surfaces(widget
);
645 // all surfaces were recreated, so background is empty.
646 // draw the yellowish lighting on the background surface
647 cairo_t
*bg
= cairo_create(lg
->background_surface
);
648 if (lg
->debug
) printf("(draw background)\n");
649 display_background(widget
, bg
, 0, 0, lg
->size_x
, lg
->size_y
, lg
->pad_x
, lg
->pad_y
);
653 // the cache, grid and realtime surface wrapped in a cairo context
654 cairo_t
*grid_c
= cairo_create( lg
->grid_surface
);
655 cairo_t
*cache_c
= cairo_create( lg
->cache_surface
);
656 cairo_t
*realtime_c
= cairo_create( lg
->realtime_surface
);
658 if (lg
->recreate_surfaces
) {
659 // and copy it to the grid surface in case no grid is drawn
660 if (lg
->debug
) printf("copy bg->grid\n");
661 calf_line_graph_copy_surface(grid_c
, lg
->background_surface
);
663 // and copy it to the cache surface in case no cache is drawn
664 if (lg
->debug
) printf("copy bg->cache\n");
665 calf_line_graph_copy_surface(cache_c
, lg
->background_surface
);
667 // and copy it to the realtime surface in case no realtime is drawn
668 if (lg
->debug
) printf("copy bg->realtime\n");
669 calf_line_graph_copy_surface(realtime_c
, lg
->background_surface
);
671 if (lg
->recreate_surfaces
or lg
->force_redraw
) {
672 // reset generation value and request a new expose event
674 lg
->source
->get_layers(lg
->source_id
, lg
->generation
, lg
->layers
);
682 if (lg
->debug
) printf("width: %d height: %d x: %d y: %d\n", sx
, sy
, ox
, oy
);
686 dsp::print_bits(sizeof(lg
->layers
), &lg
->layers
);
690 // context used for the actual surface we want to draw on. It is
691 // switched over the drawing process via calf_line_graph_switch_context
693 cairo_t
*_ctx
= NULL
;
695 // the contexts for both moving curve caches
696 cairo_t
*moving_c
[2];
697 moving_c
[0] = cairo_create( lg
->moving_surface
[0] );
698 moving_c
[1] = cairo_create( lg
->moving_surface
[1] );
700 // the line widths to switch to between cycles
701 float grid_width
= 1.0;
702 float graph_width
= 1.5;
703 float dot_width
= 0.0;
705 // more vars we have to initialize, mainly stuff we use in callback
707 float *data
= new float[2 * std::max(lg
->size_x
, lg
->size_y
)];
709 bool vertical
= false;
710 std::string legend
= "";
715 // a cairo wrapper to hand over contexts to the plugin for setting
716 // line colors, widths aso
723 // some state variables used to determine what has happened
724 bool realtime_drawn
= false;
725 bool cache_drawn
= false;
726 bool grid_drawn
= false;
730 // check if we can skip the whole drawing stuff and go on with
731 // copying everything we drawed before
737 // 
743 or lg
->layers
& LG_CACHE_GRID
744 or lg
->layers
& LG_CACHE_GRAPH
745 or lg
->layers
& LG_CACHE_DOT
) {
746 if (lg
->debug
) printf("\n->cache\n");
748 // someone needs a redraw of the cache so start with the cache
752 // set the right context to work with
755 // and switch to grid surface in case we want to draw on it
756 if (lg
->debug
) printf("switch to grid\n");
757 ctx
= calf_line_graph_switch_context(lg
, grid_c
, &cimpl
);
759 if (lg
->debug
) printf("\n->realtime\n");
761 // no cache drawing neccessary, so skip the first drawing phase
764 // set the right context to work with
767 // and switch to the realtime surface
768 if (lg
->debug
) printf("switch to realtime\n");
769 ctx
= calf_line_graph_switch_context(lg
, realtime_c
, &cimpl
);
774 for (int phase
= drawing_phase
; phase
< 2; phase
++) {
775 // draw elements on the realtime and/or the cache surface
777 if (lg
->debug
) printf("\n### drawing phase %d\n", phase
);
779 ///////////////////////////////////////////////////////////////
781 ///////////////////////////////////////////////////////////////
783 if ((lg
->layers
& LG_CACHE_GRID
and !phase
) || (lg
->layers
& LG_REALTIME_GRID
and phase
)) {
784 // The plugin can set "vertical" to 1
785 // to force drawing of vertical lines instead of horizontal ones
786 // (which is the default)
787 // size and color of the grid (which can be set by the plugin
788 // via the context) are reset for every line.
790 // we're in cache phase and it seems we really want to
791 // draw new grid lines. so "clear" the grid surface
792 // with a pure background
793 if (lg
->debug
) printf("copy bg->grid\n");
794 calf_line_graph_copy_surface(ctx
, lg
->background_surface
);
798 legend
= std::string(),
799 cairo_set_source_rgba(ctx
, 0.15, 0.2, 0.0, 0.66),
800 cairo_set_line_width(ctx
, grid_width
),
801 lg
->source
->get_gridline(lg
->source_id
, a
, phase
, pos
, vertical
, legend
, &cimpl
);
804 if (!a
and lg
->debug
) printf("(draw grid)\n");
805 calf_line_graph_draw_grid( lg
, ctx
, legend
, vertical
, pos
);
809 // we're in cache phase so we have to switch back to
810 // the cache surface after drawing the grid on its surface
811 if (lg
->debug
) printf("switch to cache\n");
812 ctx
= calf_line_graph_switch_context(lg
, _ctx
, &cimpl
);
813 // if a grid was drawn copy it to cache
815 if (lg
->debug
) printf("copy grid->cache\n");
816 calf_line_graph_copy_surface(ctx
, lg
->grid_surface
);
821 ///////////////////////////////////////////////////////////////
823 ///////////////////////////////////////////////////////////////
825 if ((lg
->layers
& LG_CACHE_GRAPH
and !phase
) || (lg
->layers
& LG_REALTIME_GRAPH
and phase
)) {
826 // Cycle through all graphs and hand over the amount of horizontal
827 // pixels. The plugin is expected to set all corresponding vertical
828 // values in an array.
829 // size and color of the graph (which can be set by the plugin
830 // via the context) are reset for every graph.
833 // we are drawing the first graph in cache phase, so
834 // prepare the cache surface with the grid surface
835 if (lg
->debug
) printf("copy grid->cache\n");
836 calf_line_graph_copy_surface(ctx
, lg
->grid_surface
);
838 } else if (!realtime_drawn
) {
839 // we're in realtime phase and the realtime surface wasn't
840 // reset to cache by now (because there was no cache
841 // phase and no realtime grid was drawn)
842 // so "clear" the realtime surface with the cache
843 if (lg
->debug
) printf("copy cache->realtime\n");
844 calf_line_graph_copy_surface(ctx
, lg
->cache_surface
, lg
->force_cache
? 1 : lg
->fade
);
845 realtime_drawn
= true;
850 cairo_set_source_rgba(ctx
, 0.15, 0.2, 0.0, 0.8),
851 cairo_set_line_width(ctx
, graph_width
),
852 lg
->source
->get_graph(lg
->source_id
, a
, phase
, data
, lg
->size_x
, &cimpl
, &lg
->mode
);
855 if (lg
->debug
) printf("graph %d\n", a
);
856 calf_line_graph_draw_graph( lg
, ctx
, data
, lg
->mode
);
860 ///////////////////////////////////////////////////////////////
862 ///////////////////////////////////////////////////////////////
864 if ((lg
->layers
& LG_CACHE_MOVING
and !phase
) || (lg
->layers
& LG_REALTIME_MOVING
and phase
)) {
865 // we have a moving curve. switch to moving surface and
866 // clear it before we start to draw
867 if (lg
->debug
) printf("switch to moving %d\n", lg
->movesurf
);
868 ctx
= calf_line_graph_switch_context(lg
, moving_c
[lg
->movesurf
], &cimpl
);
869 calf_line_graph_clear_surface(ctx
);
871 if (!phase
and !cache_drawn
) {
872 // we are drawing the first moving in cache phase and
873 // no cache has been created by now, so
874 // prepare the cache surface with the grid surface
875 if (lg
->debug
) printf("copy grid->cache\n");
876 calf_line_graph_copy_surface(cache_c
, lg
->grid_surface
);
878 } else if (phase
and !realtime_drawn
) {
879 // we're in realtime phase and the realtime surface wasn't
880 // reset to cache by now (because there was no cache
881 // phase and no realtime grid was drawn)
882 // so "clear" the realtime surface with the cache
883 if (lg
->debug
) printf("copy cache->realtime\n");
884 calf_line_graph_copy_surface(realtime_c
, lg
->cache_surface
);
885 realtime_drawn
= true;
894 color
= RGBAtoINT(0.35, 0.4, 0.2, 1),
895 lg
->source
->get_moving(lg
->source_id
, a
, direction
, data
, lg
->size_x
, lg
->size_y
, offset
, color
);
898 if (lg
->debug
) printf("moving %d\n", a
);
899 calf_line_graph_draw_moving(lg
, ctx
, data
, direction
, offset
, color
);
903 // set moving distances according to direction
912 case LG_MOVING_RIGHT
:
925 // copy the old moving surface to the right position on the
927 if (lg
->debug
) printf("copy cached moving->moving\n");
928 cairo_set_source_surface(ctx
, lg
->moving_surface
[(int)!lg
->movesurf
], x
, y
);
931 // switch back to the actual context
932 if (lg
->debug
) printf("switch to realtime/cache\n");
933 ctx
= calf_line_graph_switch_context(lg
, _ctx
, &cimpl
);
935 if (lg
->debug
) printf("copy moving->realtime/cache\n");
936 calf_line_graph_copy_surface(ctx
, lg
->moving_surface
[lg
->movesurf
], 1);
938 // toggle the moving cache
939 lg
->movesurf
= (int)!lg
->movesurf
;
942 ///////////////////////////////////////////////////////////////
944 ///////////////////////////////////////////////////////////////
946 if ((lg
->layers
& LG_CACHE_DOT
and !phase
) || (lg
->layers
& LG_REALTIME_DOT
and phase
)) {
947 // Cycle through all dots. The plugin is expected to set the x
948 // and y value of the dot.
949 // color of the dot (which can be set by the plugin
950 // via the context) is reset for every graph.
952 if (!cache_drawn
and !phase
) {
953 // we are drawing dots in cache phase while
954 // the cache wasn't renewed (no graph was drawn), so
955 // prepare the cache surface with the grid surface
956 if (lg
->debug
) printf("copy grid->cache\n");
957 calf_line_graph_copy_surface(ctx
, lg
->grid_surface
);
960 if (!realtime_drawn
and phase
) {
961 // we're in realtime phase and the realtime surface wasn't
962 // reset to cache by now (because there was no cache
963 // phase and no realtime grid or graph was drawn)
964 // so "clear" the realtime surface with the cache
965 if (lg
->debug
) printf("copy cache->realtime\n");
966 calf_line_graph_copy_surface(ctx
, lg
->cache_surface
, lg
->force_cache
? 1 : lg
->fade
);
967 realtime_drawn
= true;
970 cairo_set_source_rgba(ctx
, 0.15, 0.2, 0.0, 1),
971 cairo_set_line_width(ctx
, dot_width
),
972 lg
->source
->get_dot(lg
->source_id
, a
, phase
, x
, y
, size
= 3, &cimpl
);
975 if (lg
->debug
) printf("dot %d\n", a
);
976 float yv
= oy
+ sy
/ 2 - (sy
/ 2 - 1) * y
;
977 cairo_arc(ctx
, ox
+ x
* sx
, yv
, size
, 0, 2 * M_PI
);
983 // if we have a second cycle for drawing on the realtime
984 // after the cache was renewed it's time to copy the
985 // cache to the realtime and switch the target surface
986 if (lg
->debug
) printf("switch to realtime\n");
987 ctx
= calf_line_graph_switch_context(lg
, realtime_c
, &cimpl
);
991 // copy the cache to the realtime if it was changed
992 if (lg
->debug
) printf("copy cache->realtime\n");
993 calf_line_graph_copy_surface(ctx
, lg
->cache_surface
, lg
->force_cache
? 1 : lg
->fade
);
994 realtime_drawn
= true;
995 } else if (grid_drawn
) {
996 // copy the grid to the realtime if it was changed
997 if (lg
->debug
) printf("copy grid->realtime\n");
998 calf_line_graph_copy_surface(ctx
, lg
->grid_surface
, lg
->force_cache
? 1 : lg
->fade
);
999 realtime_drawn
= true;
1002 // check if we can skip the whole realtime phase
1003 if (!(lg
->layers
& LG_REALTIME_GRID
)
1004 and !(lg
->layers
& LG_REALTIME_GRAPH
)
1005 and !(lg
->layers
& LG_REALTIME_DOT
)) {
1009 } // one or two cycles for drawing cached and non-cached elements
1012 // îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚
1017 if (lg
->debug
) printf("\n### finalize\n");
1019 // whatever happened - we need to copy the realtime surface to the
1021 //if (lg->debug) printf("switch to window\n");
1022 //ctx = calf_line_graph_switch_context(lg, c, &cimpl);
1023 if (lg
->debug
) printf("copy realtime->window\n");
1024 calf_line_graph_copy_surface(c
, lg
->realtime_surface
);
1026 // if someone changed the handles via drag'n'drop or externally we
1027 // need a redraw of the handles surface
1028 if (lg
->freqhandles
and (lg
->handle_redraw
or lg
->force_redraw
)) {
1029 cairo_t
*hs
= cairo_create(lg
->handles_surface
);
1030 calf_line_graph_clear_surface(hs
);
1031 calf_line_graph_draw_freqhandles(lg
, hs
);
1035 // if we're using frequency handles we need to copy them to the
1037 if (lg
->freqhandles
) {
1038 if (lg
->debug
) printf("copy handles->window\n");
1039 calf_line_graph_copy_surface(c
, lg
->handles_surface
);
1042 // and draw the crosshairs on top if neccessary
1043 if (lg
->use_crosshairs
&& lg
->crosshairs_active
&& lg
->mouse_x
> 0
1044 && lg
->mouse_y
> 0 && lg
->handle_grabbed
< 0) {
1046 s
= lg
->source
->get_crosshair_label((int)(lg
->mouse_x
- ox
), (int)(lg
->mouse_y
- oy
), sx
, sy
, &cimpl
);
1047 cairo_set_line_width(c
, 1),
1048 calf_line_graph_draw_crosshairs(lg
, c
, false, 0, 0.5, 5, false, lg
->mouse_x
- ox
, lg
->mouse_y
- oy
, s
);
1051 lg
->force_cache
= false;
1052 lg
->force_redraw
= false;
1053 lg
->handle_redraw
= 0;
1054 lg
->recreate_surfaces
= 0;
1057 // destroy all temporarily created cairo contexts
1059 cairo_destroy(realtime_c
);
1060 cairo_destroy(grid_c
);
1061 cairo_destroy(cache_c
);
1062 cairo_destroy(moving_c
[0]);
1063 cairo_destroy(moving_c
[1]);
1065 lg
->generation
+= 1;
1071 calf_line_graph_get_handle_at(CalfLineGraph
*lg
, double x
, double y
)
1073 int sx
= lg
->size_x
;
1074 int sy
= lg
->size_y
;
1081 // loop on all handles
1082 for (int i
= 0; i
< lg
->freqhandles
; i
++) {
1083 FreqHandle
*handle
= &lg
->freq_handles
[i
];
1084 if (!handle
->is_active())
1087 if (handle
->dimensions
== 1) {
1088 // if user clicked inside a vertical band with width HANDLE_WIDTH handle is considered grabbed
1089 if (lg
->mouse_x
<= ox
+ round(handle
->value_x
* sx
+ HANDLE_WIDTH
/ 2.0) + 0.5 &&
1090 lg
->mouse_x
>= ox
+ round(handle
->value_x
* sx
- HANDLE_WIDTH
/ 2.0) - 0.5 ) {
1093 } else if (handle
->dimensions
>= 2) {
1094 double dx
= lg
->mouse_x
- round(ox
+ handle
->value_x
* sx
);
1095 double dy
= lg
->mouse_y
- round(oy
+ handle
->value_y
* sy
);
1097 // if mouse clicked inside circle of HANDLE_WIDTH
1098 if (sqrt(dx
* dx
+ dy
* dy
) <= HANDLE_WIDTH
/ 2.0)
1106 calf_line_graph_pointer_motion (GtkWidget
*widget
, GdkEventMotion
*event
)
1108 g_assert(CALF_IS_LINE_GRAPH(widget
));
1109 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1111 int sx
= lg
->size_x
;
1112 int sy
= lg
->size_y
;
1119 lg
->mouse_x
= event
->x
;
1120 lg
->mouse_y
= event
->y
;
1122 if (lg
->handle_grabbed
>= 0) {
1123 FreqHandle
*handle
= &lg
->freq_handles
[lg
->handle_grabbed
];
1125 float new_x_value
= float(event
->x
- ox
) / float(sx
);
1126 float new_y_value
= float(event
->y
- oy
) / float(sy
);
1128 if (new_x_value
< handle
->left_bound
) {
1129 new_x_value
= handle
->left_bound
;
1130 } else if (new_x_value
> handle
->right_bound
) {
1131 new_x_value
= handle
->right_bound
;
1134 // restrict y range by top and bottom
1135 if (handle
->dimensions
>= 2) {
1136 if(new_y_value
< 0.0) new_y_value
= 0.0;
1137 if(new_y_value
> 1.0) new_y_value
= 1.0;
1140 if (new_x_value
!= handle
->value_x
||
1141 new_y_value
!= handle
->value_y
) {
1142 handle
->value_x
= new_x_value
;
1143 handle
->value_y
= new_y_value
;
1145 g_signal_emit_by_name(widget
, "freqhandle-changed", handle
);
1147 lg
->handle_redraw
= 1;
1148 calf_line_graph_expose_request(widget
, true);
1152 gdk_event_request_motions(event
);
1155 int handle_hovered
= calf_line_graph_get_handle_at(lg
, event
->x
, event
->y
);
1156 if (handle_hovered
!= lg
->handle_hovered
) {
1157 if (lg
->handle_grabbed
>= 0 ||
1158 handle_hovered
!= -1) {
1159 gdk_window_set_cursor(widget
->window
, lg
->hand_cursor
);
1160 lg
->handle_hovered
= handle_hovered
;
1162 gdk_window_set_cursor(widget
->window
, lg
->arrow_cursor
);
1163 lg
->handle_hovered
= -1;
1165 lg
->handle_redraw
= 1;
1166 calf_line_graph_expose_request(widget
, true);
1168 if(lg
->crosshairs_active
and lg
->use_crosshairs
) {
1169 calf_line_graph_expose_request(widget
, true);
1175 calf_line_graph_button_press (GtkWidget
*widget
, GdkEventButton
*event
)
1177 g_assert(CALF_IS_LINE_GRAPH(widget
));
1178 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1179 bool inside_handle
= false;
1181 int i
= calf_line_graph_get_handle_at(lg
, lg
->mouse_x
, lg
->mouse_y
);
1184 FreqHandle
*handle
= &lg
->freq_handles
[i
];
1186 if (handle
->dimensions
== 1) {
1187 // if user clicked inside a vertical band with width HANDLE_WIDTH handle is considered grabbed
1188 lg
->handle_grabbed
= i
;
1189 inside_handle
= true;
1191 if (lg
->enforce_handle_order
) {
1192 // look for previous one dimensional handle to find left_bound
1193 for (int j
= i
- 1; j
>= 0; j
--) {
1194 FreqHandle
*prevhandle
= &lg
->freq_handles
[j
];
1195 if(prevhandle
->is_active() && prevhandle
->dimensions
== 1) {
1196 handle
->left_bound
= prevhandle
->value_x
+ lg
->min_handle_distance
;
1201 // look for next one dimensional handle to find right_bound
1202 for (int j
= i
+ 1; j
< lg
->freqhandles
; j
++) {
1203 FreqHandle
*nexthandle
= &lg
->freq_handles
[j
];
1204 if(nexthandle
->is_active() && nexthandle
->dimensions
== 1) {
1205 handle
->right_bound
= nexthandle
->value_x
- lg
->min_handle_distance
;
1210 } else if (handle
->dimensions
>= 2) {
1211 lg
->handle_grabbed
= i
;
1212 inside_handle
= true;
1216 if (inside_handle
&& event
->type
== GDK_2BUTTON_PRESS
) {
1217 FreqHandle
&handle
= lg
->freq_handles
[lg
->handle_grabbed
];
1218 handle
.value_x
= handle
.default_value_x
;
1219 handle
.value_y
= handle
.default_value_y
;
1220 g_signal_emit_by_name(widget
, "freqhandle-changed", &handle
);
1223 if(!inside_handle
) {
1224 lg
->crosshairs_active
= !lg
->crosshairs_active
;
1227 calf_line_graph_expose_request(widget
, true);
1228 gtk_widget_grab_focus(widget
);
1229 gtk_grab_add(widget
);
1235 calf_line_graph_button_release (GtkWidget
*widget
, GdkEventButton
*event
)
1237 g_assert(CALF_IS_LINE_GRAPH(widget
));
1238 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1240 lg
->handle_grabbed
= -1;
1242 if (GTK_WIDGET_HAS_GRAB(widget
))
1243 gtk_grab_remove(widget
);
1245 calf_line_graph_expose_request(widget
, true);
1250 calf_line_graph_scroll (GtkWidget
*widget
, GdkEventScroll
*event
)
1252 g_assert(CALF_IS_LINE_GRAPH(widget
));
1253 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1255 int i
= calf_line_graph_get_handle_at(lg
, lg
->mouse_x
, lg
->mouse_y
);
1258 FreqHandle
*handle
= &lg
->freq_handles
[i
];
1259 if (handle
->dimensions
== 3) {
1260 if (event
->direction
== GDK_SCROLL_UP
) {
1261 handle
->value_z
+= 0.05;
1262 if(handle
->value_z
> 1.0) {
1263 handle
->value_z
= 1.0;
1265 g_signal_emit_by_name(widget
, "freqhandle-changed", handle
);
1266 } else if (event
->direction
== GDK_SCROLL_DOWN
) {
1267 handle
->value_z
-= 0.05;
1268 if(handle
->value_z
< 0.0) {
1269 handle
->value_z
= 0.0;
1271 g_signal_emit_by_name(widget
, "freqhandle-changed", handle
);
1273 lg
->handle_redraw
= 1;
1280 calf_line_graph_enter (GtkWidget
*widget
, GdkEventCrossing
*event
)
1282 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1284 if (lg
->debug
) printf("[enter]\n");
1289 calf_line_graph_leave (GtkWidget
*widget
, GdkEventCrossing
*event
)
1292 g_assert(CALF_IS_LINE_GRAPH(widget
));
1293 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1295 if (lg
->debug
) printf("[leave]\n");
1296 if (lg
->mouse_x
>= 0 or lg
->mouse_y
>= 0)
1297 calf_line_graph_expose_request(widget
, true);
1305 void calf_line_graph_set_square(CalfLineGraph
*graph
, bool is_square
)
1307 g_assert(CALF_IS_LINE_GRAPH(graph
));
1308 graph
->is_square
= is_square
;
1312 calf_line_graph_size_request (GtkWidget
*widget
,
1313 GtkRequisition
*requisition
)
1315 g_assert(CALF_IS_LINE_GRAPH(widget
));
1317 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1319 if (lg
->debug
) printf("size request\n");
1323 calf_line_graph_size_allocate (GtkWidget
*widget
,
1324 GtkAllocation
*allocation
)
1326 g_assert(CALF_IS_LINE_GRAPH(widget
));
1327 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1329 if (lg
->debug
) printf("size allocation\n");
1331 GtkWidgetClass
*parent_class
= (GtkWidgetClass
*) g_type_class_peek_parent( CALF_LINE_GRAPH_GET_CLASS( lg
) );
1333 // remember the allocation
1334 widget
->allocation
= *allocation
;
1336 // reset the allocation if a square widget is requested
1337 GtkAllocation
&a
= widget
->allocation
;
1340 if (a
.width
> a
.height
)
1342 a
.x
+= (a
.width
- a
.height
) / 2;
1345 if (a
.width
< a
.height
)
1347 a
.y
+= (a
.height
- a
.width
) / 2;
1352 lg
->size_x
= a
.width
- lg
->pad_x
* 2;
1353 lg
->size_y
= a
.height
- lg
->pad_y
* 2;
1355 lg
->recreate_surfaces
= 1;
1356 parent_class
->size_allocate( widget
, &a
);
1361 calf_line_graph_class_init (CalfLineGraphClass
*klass
)
1363 // GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
1364 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS(klass
);
1365 widget_class
->expose_event
= calf_line_graph_expose
;
1366 widget_class
->size_request
= calf_line_graph_size_request
;
1367 widget_class
->size_allocate
= calf_line_graph_size_allocate
;
1368 widget_class
->button_press_event
= calf_line_graph_button_press
;
1369 widget_class
->button_release_event
= calf_line_graph_button_release
;
1370 widget_class
->motion_notify_event
= calf_line_graph_pointer_motion
;
1371 widget_class
->scroll_event
= calf_line_graph_scroll
;
1372 widget_class
->enter_notify_event
= calf_line_graph_enter
;
1373 widget_class
->leave_notify_event
= calf_line_graph_leave
;
1375 g_signal_new("freqhandle-changed",
1376 G_TYPE_OBJECT
, G_SIGNAL_RUN_FIRST
,
1378 g_cclosure_marshal_VOID__POINTER
,
1379 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
1383 calf_line_graph_unrealize (GtkWidget
*widget
, CalfLineGraph
*lg
)
1385 if (lg
->debug
) printf("unrealize\n");
1386 calf_line_graph_destroy_surfaces(widget
);
1390 calf_line_graph_init (CalfLineGraph
*lg
)
1392 GtkWidget
*widget
= GTK_WIDGET(lg
);
1394 if (lg
->debug
) printf("lg init\n");
1396 GTK_WIDGET_SET_FLAGS (widget
, GTK_CAN_FOCUS
| GTK_SENSITIVE
| GTK_PARENT_SENSITIVE
);
1397 gtk_widget_add_events(widget
, GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
| GDK_POINTER_MOTION_MASK
| GDK_POINTER_MOTION_HINT_MASK
| GDK_ENTER_NOTIFY_MASK
| GDK_LEAVE_NOTIFY_MASK
);
1399 widget
->requisition
.width
= 40;
1400 widget
->requisition
.height
= 40;
1401 lg
->force_cache
= true;
1402 lg
->force_redraw
= false;
1404 lg
->param_zoom
= -1;
1406 lg
->param_offset
= -1;
1407 lg
->recreate_surfaces
= 1;
1411 lg
->arrow_cursor
= gdk_cursor_new(GDK_LEFT_PTR
);
1412 lg
->hand_cursor
= gdk_cursor_new(GDK_FLEUR
);
1413 lg
->layers
= LG_CACHE_GRID
| LG_CACHE_GRAPH
1414 | LG_CACHE_DOT
| LG_CACHE_MOVING
1415 | LG_REALTIME_GRID
| LG_REALTIME_GRAPH
1416 | LG_REALTIME_DOT
| LG_REALTIME_MOVING
;
1418 g_signal_connect(GTK_OBJECT(widget
), "unrealize", G_CALLBACK(calf_line_graph_unrealize
), (gpointer
)lg
);
1420 for(int i
= 0; i
< FREQ_HANDLES
; i
++) {
1421 FreqHandle
*handle
= &lg
->freq_handles
[i
];
1422 handle
->active
= false;
1423 handle
->param_active_no
= -1;
1424 handle
->param_x_no
= -1;
1425 handle
->param_y_no
= -1;
1426 handle
->value_x
= -1.0;
1427 handle
->value_y
= -1.0;
1428 handle
->param_x_no
= -1;
1429 handle
->label
= NULL
;
1430 handle
->left_bound
= 0.0 + lg
->min_handle_distance
;
1431 handle
->right_bound
= 1.0 - lg
->min_handle_distance
;
1434 lg
->handle_grabbed
= -1;
1435 lg
->handle_hovered
= -1;
1436 lg
->handle_redraw
= 1;
1437 lg
->min_handle_distance
= 0.025;
1439 lg
->background_surface
= NULL
;
1440 lg
->grid_surface
= NULL
;
1441 lg
->cache_surface
= NULL
;
1442 lg
->moving_surface
[0] = NULL
;
1443 lg
->moving_surface
[1] = NULL
;
1444 lg
->handles_surface
= NULL
;
1445 lg
->realtime_surface
= NULL
;
1449 calf_line_graph_new()
1451 return GTK_WIDGET( g_object_new (CALF_TYPE_LINE_GRAPH
, NULL
));
1455 calf_line_graph_get_type (void)
1457 static GType type
= 0;
1459 static const GTypeInfo type_info
= {
1460 sizeof(CalfLineGraphClass
),
1461 NULL
, /* base_init */
1462 NULL
, /* base_finalize */
1463 (GClassInitFunc
)calf_line_graph_class_init
,
1464 NULL
, /* class_finalize */
1465 NULL
, /* class_data */
1466 sizeof(CalfLineGraph
),
1467 0, /* n_preallocs */
1468 (GInstanceInitFunc
)calf_line_graph_init
1471 GTypeInfo
*type_info_copy
= new GTypeInfo(type_info
);
1473 for (int i
= 0; ; i
++) {
1474 char *name
= g_strdup_printf("CalfLineGraph%u%d", ((unsigned int)(intptr_t)calf_line_graph_class_init
) >> 16, i
);
1475 if (g_type_from_name(name
)) {
1479 type
= g_type_register_static( GTK_TYPE_DRAWING_AREA
,