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>
33 #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)
34 #define INTtoR(color) (float)((color & 0xff000000) >> 24) / 255.f
35 #define INTtoG(color) (float)((color & 0x00ff0000) >> 16) / 255.f
36 #define INTtoB(color) (float)((color & 0x0000ff00) >> 8) / 255.f
37 #define INTtoA(color) (float)((color & 0x000000ff) >> 0) / 255.f
39 using namespace calf_plugins
;
42 calf_line_graph_draw_grid( CalfLineGraph
* lg
, cairo_t
*ctx
, std::string
&legend
, bool vertical
, float pos
)
51 cairo_text_extents_t tx
;
53 if (!legend
.empty()) {
54 cairo_text_extents(ctx
, legend
.c_str(), &tx
);
55 size
= vertical
? tx
.height
: tx
.width
;
63 x
= floor(ox
+ pos
* sx
) + 0.5;
64 cairo_move_to(ctx
, x
, oy
);
65 cairo_line_to(ctx
, x
, oy
+ sy
- size
);
67 if (!legend
.empty()) {
68 cairo_set_source_rgba(ctx
, 0.0, 0.0, 0.0, 0.5);
69 cairo_move_to(ctx
, x
- (tx
.x_bearing
+ tx
.width
/ 2.0), oy
+ sy
- 2);
70 cairo_show_text(ctx
, legend
.c_str());
75 y
= floor(oy
+ sy
/ 2 - (sy
/ 2 - 1) * pos
) + 0.5;
76 cairo_move_to(ctx
, ox
, y
);
77 cairo_line_to(ctx
, ox
+ sx
- size
, y
);
80 if (!legend
.empty()) {
81 cairo_set_source_rgba(ctx
, 0.0, 0.0, 0.0, 0.5);
82 cairo_move_to(ctx
, ox
+ sx
- 4 - tx
.width
, y
+ tx
.height
/2 - 2);
83 cairo_show_text(ctx
, legend
.c_str());
86 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());
90 calf_line_graph_draw_graph( CalfLineGraph
* lg
, cairo_t
*ctx
, float *data
, int mode
= 0 )
92 if (lg
->debug
) printf("(draw graph)\n");
103 for (int i
= 0; i
< sx
; i
++) {
104 y
= (oy
+ sy
/ 2 - (sy
/ 2 - 1) * data
[i
]);
105 if (lg
->debug
> 2) printf("* graph x: %d, y: %.5f, data: %.5f\n", i
, y
, data
[i
]);
110 // we want to draw a line
111 if (i
and (data
[i
] < INFINITY
or i
== sx
- 1)) {
112 cairo_line_to(ctx
, ox
+ i
, y
);
113 } else if (i
and startdraw
>= 0) {
116 cairo_move_to(ctx
, ox
, y
);
123 if (i
and ((data
[i
] < INFINITY
) or i
== sx
- 1)) {
124 cairo_rectangle(ctx
, ox
+ _lastx
, (int)y
, i
- _lastx
, sy
- (int)y
+ oy
);
127 startdraw
= ox
+ _lastx
;
133 // this one is drawing little boxes at the values position
134 if (i
and ((data
[i
] < INFINITY
) or i
== sx
- 1)) {
135 cairo_rectangle(ctx
, ox
+ _lastx
, (int)y
- 1, i
- _lastx
, 2);
138 startdraw
= ox
+ _lastx
;
144 // this one is drawing bars centered on the x axis
145 if (i
and ((data
[i
] < INFINITY
) or i
== sx
- 1)) {
146 cairo_rectangle(ctx
, ox
+ _lastx
, oy
+ sy
/ 2, i
- _lastx
, -1 * data
[i
] * (sy
/ 2));
149 startdraw
= ox
+ _lastx
;
155 // this one is drawing bars centered on the x axis with 1
157 if (i
and ((data
[i
] < INFINITY
) or i
== sx
- 1)) {
158 cairo_rectangle(ctx
, ox
+ _lastx
,oy
+ sy
/ 2 - sy
* lg
->offset
/ 2, i
- _lastx
, -1 * data
[i
] * (sy
/ 2) + sy
* lg
->offset
/ 2);
161 startdraw
= ox
+ _lastx
;
169 cairo_line_to(ctx
, sx
+ 2 * ox
, sy
+ 2 * oy
);
170 cairo_line_to(ctx
, 0, sy
+ 2 * oy
);
171 cairo_close_path(ctx
);
182 calf_line_graph_draw_moving(CalfLineGraph
* lg
, cairo_t
*ctx
, float *data
, int direction
, int offset
, int color
)
184 if (lg
->debug
) printf("(draw moving)\n");
194 int sm
= (direction
== LG_MOVING_UP
|| direction
== LG_MOVING_DOWN
? sx
: sy
);
195 int om
= (direction
== LG_MOVING_UP
|| direction
== LG_MOVING_DOWN
? ox
: oy
);
196 for (int i
= 0; i
< sm
; i
++) {
197 if (lg
->debug
> 2) printf("* moving i: %d, dir: %d, offset: %d, data: %.5f\n", i
, direction
, offset
, data
[i
]);
198 if (i
and ((data
[i
] < INFINITY
) or i
>= sm
)) {
199 cairo_set_source_rgba(ctx
, INTtoR(color
), INTtoG(color
), INTtoB(color
), (data
[i
] + 1) / 1.4 * INTtoA(color
));
203 cairo_rectangle(ctx
, ox
+ sx
- 1 - offset
, oy
+ _last
, 1, i
- _last
);
205 case LG_MOVING_RIGHT
:
206 cairo_rectangle(ctx
, ox
+ offset
, oy
+ _last
, 1, i
- _last
);
209 cairo_rectangle(ctx
, ox
+ _last
, oy
+ sy
- 1 - offset
, i
- _last
, 1);
212 cairo_rectangle(ctx
, ox
+ _last
, oy
+ offset
, i
- _last
, 1);
219 startdraw
= om
+ _last
;
227 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
)
229 if (lg
->debug
) printf("(draw crosshairs)\n");
240 cairo_pattern_t
*pat
;
242 if(mask
> 0 and circle
) {
243 //// draw a circle in the center of the crosshair leaving out
246 //cairo_move_to(cache_cr, _x + 1, _y);
247 //cairo_arc (cache_cr, _x + 1, _y, mask, 1.5 * M_PI, 2 * M_PI);
248 //cairo_close_path(cache_cr);
250 //cairo_move_to(cache_cr, _x + 1, _y + 1);
251 //cairo_arc (cache_cr, _x + 1, _y + 1, mask, 0, 0.5 * M_PI);
252 //cairo_close_path(cache_cr);
254 //cairo_move_to(cache_cr, _x, _y + 1);
255 //cairo_arc (cache_cr, _x, _y + 1, mask, 0.5 * M_PI, M_PI);
256 //cairo_close_path(cache_cr);
258 //cairo_move_to(cache_cr, _x, _y);
259 //cairo_arc (cache_cr, _x, _y, mask, M_PI, 1.5 * M_PI);
260 //cairo_close_path(cache_cr);
261 cairo_move_to(cache_cr
, _x
, _y
);
262 cairo_arc (cache_cr
, _x
, _y
, mask
, 0, 2 * M_PI
);
263 cairo_set_source_rgba(cache_cr
, 0, 0, 0, alpha
);
264 cairo_fill(cache_cr
);
266 cairo_move_to(cache_cr
, _x
, _y
);
267 cairo_arc (cache_cr
, _x
, _y
, HANDLE_WIDTH
/ 2, 0, 2 * M_PI
);
268 cairo_set_source_rgba(cache_cr
, 0, 0, 0, 0.2);
269 cairo_fill(cache_cr
);
272 if(gradient
and gradient_rad
> 0) {
273 // draw the crosshairs with a steady gradient around
274 pat
= cairo_pattern_create_radial(_x
, _y
, 1, _x
, _y
, gradient_rad
* 2);
275 cairo_pattern_add_color_stop_rgba(pat
, 0, 0, 0, 0, alpha
);
276 cairo_pattern_add_color_stop_rgba(pat
, 0, 0, 0, 0, 0);
278 cairo_rectangle(cache_cr
, _x
, _y
- gradient_rad
, 1, gradient_rad
- mask
);
280 cairo_rectangle(cache_cr
, _x
+ mask
, _y
, gradient_rad
- mask
, 1);
282 cairo_rectangle(cache_cr
, _x
, _y
+ mask
, 1, gradient_rad
- mask
);
284 cairo_rectangle(cache_cr
, _x
- gradient_rad
, _y
, gradient_rad
- mask
, 1);
286 cairo_set_source(cache_cr
, pat
);
287 cairo_fill(cache_cr
);
288 } else if(gradient
) {
289 // draw the crosshairs with a gradient to the frame
291 cairo_rectangle(cache_cr
, _x
, oy
, 1, y
- mask
);
292 pat
= cairo_pattern_create_linear(_x
, oy
, _x
, _y
);
293 cairo_pattern_add_color_stop_rgba(pat
, 0, 0, 0, 0, 0);
294 cairo_pattern_add_color_stop_rgba(pat
, 1, 0, 0, 0, alpha
);
295 cairo_set_source(cache_cr
, pat
);
296 cairo_fill(cache_cr
);
298 cairo_rectangle(cache_cr
, _x
+ mask
, _y
, sx
- x
- mask
, 1);
299 pat
= cairo_pattern_create_linear(_x
, oy
, sx
, oy
);
300 cairo_pattern_add_color_stop_rgba(pat
, 0, 0, 0, 0, alpha
);
301 cairo_pattern_add_color_stop_rgba(pat
, 1, 0, 0, 0, 0);
302 cairo_set_source(cache_cr
, pat
);
303 cairo_fill(cache_cr
);
305 cairo_rectangle(cache_cr
, _x
, _y
+ mask
, 1, sy
- y
- mask
);
306 pat
= cairo_pattern_create_linear(_x
, _y
, _x
, oy
+ sy
);
307 cairo_pattern_add_color_stop_rgba(pat
, 0, 0, 0, 0, alpha
);
308 cairo_pattern_add_color_stop_rgba(pat
, 1, 0, 0, 0, 0);
309 cairo_set_source(cache_cr
, pat
);
310 cairo_fill(cache_cr
);
312 cairo_rectangle(cache_cr
, ox
, _y
, x
- mask
, 1);
313 pat
= cairo_pattern_create_linear(ox
, oy
, _x
, oy
);
314 cairo_pattern_add_color_stop_rgba(pat
, 0, 0, 0, 0, 0);
315 cairo_pattern_add_color_stop_rgba(pat
, 1, 0, 0, 0, alpha
);
316 cairo_set_source(cache_cr
, pat
);
317 cairo_fill(cache_cr
);
319 // draw normal crosshairs
321 cairo_move_to(cache_cr
, _x
+ 0.5, oy
+ 0.5);
322 cairo_line_to(cache_cr
, _x
+ 0.5, _y
- mask
+ 0.5);
324 cairo_move_to(cache_cr
, _x
+ mask
+ 0.5, _y
+ 0.5);
325 cairo_line_to(cache_cr
, ox
+ sx
+ 0.5, _y
+ 0.5);
327 cairo_move_to(cache_cr
, _x
+ 0.5, _y
+ mask
+ 0.5);
328 cairo_line_to(cache_cr
, _x
+ 0.5, oy
+ sy
+ 0.5);
330 cairo_move_to(cache_cr
, ox
+ 0.5, _y
+ 0.5);
331 cairo_line_to(cache_cr
, _x
- mask
+ 0.5, _y
+ 0.5);
333 cairo_set_source_rgba(cache_cr
, 0, 0, 0, alpha
);
334 cairo_stroke(cache_cr
);
338 cairo_set_source_rgba(cache_cr
, 0, 0, 0, 0.5);
339 cairo_move_to(cache_cr
, lg
->mouse_x
+ 3, lg
->mouse_y
- 3);
340 cairo_show_text(cache_cr
, label
.c_str());
341 cairo_fill(cache_cr
);
346 void calf_line_graph_draw_freqhandles(CalfLineGraph
* lg
, cairo_t
* c
)
349 if (lg
->debug
) printf("(draw handles)\n");
356 if (lg
->freqhandles
> 0) {
357 cairo_set_source_rgba(c
, 0.0, 0.0, 0.0, 1.0);
358 cairo_set_line_width(c
, 1.0);
360 for (int i
= 0; i
< lg
->freqhandles
; i
++) {
361 FreqHandle
*handle
= &lg
->freq_handles
[i
];
362 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;
371 // choose colors between dragged and normal state
372 if (lg
->handle_hovered
== i
) {
375 cairo_set_source_rgba(c
, 0, 0, 0, 0.7);
379 //cairo_set_source_rgb(c, 0.44, 0.5, 0.21);
380 cairo_set_source_rgba(c
, 0, 0, 0, 0.5);
382 if (handle
->dimensions
>= 2) {
383 cairo_move_to(c
, val_x
+ 8, val_y
);
385 cairo_move_to(c
, val_x
+ 11, oy
+ 15);
387 // draw the freq label
388 float freq
= exp((handle
->value_x
) * log(1000)) * 20.0;
389 std::stringstream ss
;
390 ss
<< round(freq
) << " Hz";
391 cairo_show_text(c
, ss
.str().c_str());
393 // draw the label on top
394 if (handle
->label
&& handle
->label
[0]) {
396 cairo_select_font_face(c
, "Sans",
397 CAIRO_FONT_SLANT_NORMAL
, CAIRO_FONT_WEIGHT_NORMAL
);
398 cairo_set_font_size(c
, 9);
399 cairo_text_extents_t te
;
401 cairo_text_extents(c
, handle
->label
, &te
);
402 if (handle
->dimensions
>= 2) {
403 cairo_move_to(c
, val_x
- te
.width
, val_y
);
405 cairo_move_to(c
, val_x
- 3 - te
.width
, oy
+ 15);
407 cairo_show_text(c
, handle
->label
);
411 if (handle
->dimensions
== 1) {
412 // draw the main line
413 cairo_move_to(c
, ox
+ val_x
+ 0.5, oy
);
414 cairo_line_to(c
, ox
+ val_x
+ 0.5, oy
+ sy
);
416 // draw some one-dimensional bling-bling
417 cairo_pattern_t
*pat
;
418 switch(handle
->style
) {
421 // bell filters, default
422 pat
= cairo_pattern_create_linear(ox
, oy
, ox
, sy
);
423 cairo_pattern_add_color_stop_rgba(pat
, 0.f
, 0, 0, 0, 0);
424 cairo_pattern_add_color_stop_rgba(pat
, 0.5, 0, 0, 0, pat_alpha
);
425 cairo_pattern_add_color_stop_rgba(pat
, 1.f
, 0, 0, 0, 0);
426 cairo_rectangle(c
, ox
+ val_x
- 7, oy
, 6, sy
);
427 cairo_rectangle(c
, ox
+ val_x
+ 2, oy
, 6, sy
);
431 pat
= cairo_pattern_create_linear(ox
, oy
, ox
+ val_x
, oy
);
432 cairo_pattern_add_color_stop_rgba(pat
, 0.f
, 0, 0, 0, 0);
433 cairo_pattern_add_color_stop_rgba(pat
, 1.f
, 0, 0, 0, pat_alpha
);
434 cairo_rectangle(c
, ox
, oy
, val_x
- 1, sy
);
438 pat
= cairo_pattern_create_linear(ox
, oy
, ox
, sy
);
439 cairo_pattern_add_color_stop_rgba(pat
, 0.f
, 0, 0, 0, 0);
440 cairo_pattern_add_color_stop_rgba(pat
, 0.5, 0, 0, 0, pat_alpha
* 1.5);
441 cairo_pattern_add_color_stop_rgba(pat
, 1.f
, 0, 0, 0, 0);
442 cairo_rectangle(c
, ox
, oy
, val_x
- 1, sy
);
446 pat
= cairo_pattern_create_linear(ox
, oy
, ox
, sy
);
447 cairo_pattern_add_color_stop_rgba(pat
, 0.f
, 0, 0, 0, 0);
448 cairo_pattern_add_color_stop_rgba(pat
, 0.5, 0, 0, 0, pat_alpha
* 1.5);
449 cairo_pattern_add_color_stop_rgba(pat
, 1.f
, 0, 0, 0, 0);
450 cairo_rectangle(c
, ox
+ val_x
+ 2, oy
, sx
- val_x
- 2, sy
);
454 pat
= cairo_pattern_create_linear(ox
+ val_x
, oy
, ox
+ sx
, oy
);
455 cairo_pattern_add_color_stop_rgba(pat
, 0.f
, 0, 0, 0, pat_alpha
);
456 cairo_pattern_add_color_stop_rgba(pat
, 1.f
, 0, 0, 0, 0);
457 cairo_rectangle(c
, ox
+ val_x
+ 2, oy
, sx
- val_x
- 1, sy
);
460 cairo_set_source(c
, pat
);
462 cairo_pattern_destroy(pat
);
464 int mask
= 30 - log10(1 + handle
->value_z
* 9) * 30 + HANDLE_WIDTH
/ 2.f
;
465 // (CalfLineGraph* lg, cairo_t* c, bool gradient, int gradient_rad, float alpha, int mask, bool circle, int x, int y, std::string label, int ox, int oy, int sx, int sy)
467 calf_line_graph_draw_crosshairs(lg
, c
, grad
, -1, pat_alpha
, mask
, true, val_x
, val_y
, s
);
476 calf_line_graph_destroy_surfaces (GtkWidget
*widget
)
478 g_assert(CALF_IS_LINE_GRAPH(widget
));
479 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
481 if (lg
->debug
) printf("{destroy surfaces}\n");
483 // destroy all surfaces - and don't tell anybody about it - hehe
484 if( lg
->background_surface
)
485 cairo_surface_destroy( lg
->background_surface
);
486 if( lg
->grid_surface
)
487 cairo_surface_destroy( lg
->grid_surface
);
488 if( lg
->cache_surface
)
489 cairo_surface_destroy( lg
->cache_surface
);
490 if( lg
->moving_surface
[0] )
491 cairo_surface_destroy( lg
->moving_surface
[0] );
492 if( lg
->moving_surface
[1] )
493 cairo_surface_destroy( lg
->moving_surface
[1] );
494 if( lg
->handles_surface
)
495 cairo_surface_destroy( lg
->handles_surface
);
496 if( lg
->realtime_surface
)
497 cairo_surface_destroy( lg
->realtime_surface
);
500 calf_line_graph_create_surfaces (GtkWidget
*widget
)
502 g_assert(CALF_IS_LINE_GRAPH(widget
));
503 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
505 if (lg
->debug
) printf("{create surfaces}\n");
507 int width
= widget
->allocation
.width
;
508 int height
= widget
->allocation
.height
;
510 // the size of the "real" drawing area
511 lg
->size_x
= width
- lg
->pad_x
* 2;
512 lg
->size_y
= height
- lg
->pad_y
* 2;
514 calf_line_graph_destroy_surfaces(widget
);
515 // create the background surface.
516 // background holds the graphics of the frame and the yellowish
517 // background light for faster redrawing of static stuff
518 lg
->background_surface
= cairo_image_surface_create(
519 CAIRO_FORMAT_ARGB32
, width
, height
);
521 // create the grid surface.
522 // this one is used as a cache for the grid on the background in the
523 // cache phase. If a graph or dot in cache phase needs to be redrawn
524 // we don't need to redraw the whole grid.
525 lg
->grid_surface
= cairo_image_surface_create(
526 CAIRO_FORMAT_ARGB32
, width
, height
);
528 // create the cache surface.
529 // cache holds a copy of the background with a static part of
530 // the grid and some static curves and dots.
531 lg
->cache_surface
= cairo_image_surface_create(
532 CAIRO_FORMAT_ARGB32
, width
, height
);
534 // create the moving surface.
535 // moving is used as a cache for any slowly moving graphics like
536 // spectralizer or waveforms
537 lg
->moving_surface
[0] = cairo_image_surface_create(
538 CAIRO_FORMAT_ARGB32
, width
, height
);
540 // create the moving temp surface.
541 // moving is used as a cache for any slowly moving graphics like
542 // spectralizer or waveforms
543 lg
->moving_surface
[1] = cairo_image_surface_create(
544 CAIRO_FORMAT_ARGB32
, width
, height
);
546 // create the handles surface.
547 // this one contains the handles graphics to avoid redrawing
549 lg
->handles_surface
= cairo_image_surface_create(
550 CAIRO_FORMAT_ARGB32
, width
, height
);
552 // create the realtime surface.
553 // realtime is used to cache the realtime graphics for drawing the
554 // crosshairs on top if nothing else changed
555 lg
->realtime_surface
= cairo_image_surface_create(
556 CAIRO_FORMAT_ARGB32
, width
, height
);
558 lg
->force_cache
= true;
562 *calf_line_graph_switch_context(CalfLineGraph
* lg
, cairo_t
*ctx
, cairo_impl
*cimpl
)
564 if (lg
->debug
) printf("{switch context}\n");
566 cimpl
->context
= ctx
;
567 cairo_select_font_face(ctx
, "Sans",
568 CAIRO_FONT_SLANT_NORMAL
, CAIRO_FONT_WEIGHT_NORMAL
);
569 cairo_set_font_size(ctx
, 9);
570 cairo_set_line_join(ctx
, CAIRO_LINE_JOIN_MITER
);
571 cairo_rectangle(ctx
, lg
->pad_x
, lg
->pad_y
, lg
->size_x
, lg
->size_y
);
577 calf_line_graph_copy_surface(cairo_t
*ctx
, cairo_surface_t
*source
, float fade
= 1.f
)
579 // copy a surface to a cairo context
581 cairo_set_source_surface(ctx
, source
, 0, 0);
583 cairo_paint_with_alpha(ctx
, fade
* 0.35 + 0.05);
591 calf_line_graph_clear_surface(cairo_t
*ctx
)
593 // clears a surface to transparent
595 cairo_set_operator(ctx
, CAIRO_OPERATOR_CLEAR
);
600 void calf_line_graph_expose_request (GtkWidget
*widget
, bool force
)
602 // someone thinks we should redraw the line graph. let's see what
603 // the plugin thinks about. To do that a bitmask is sent to the
604 // plugin which can be changed. If the plugin returns true or if
605 // the request is in response of something like dragged handles, an
606 // exposition of the widget is requested from GTK
608 g_assert(CALF_IS_LINE_GRAPH(widget
));
609 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
611 // quit if no source available
612 if (!lg
->source
) return;
614 if (lg
->debug
> 1) printf("\n\n### expose request %d ###\n", lg
->generation
);
616 // let a bitmask be switched by the plugin to determine the layers
617 // we want to draw. We set all cache layers to true if force_cache
618 // is set otherwise default is to draw nothing. The return value
619 // tells us whether the plugin wants to draw at all or not.
621 //if (lg->force_cache || lg->recreate_surfaces)
622 //lg->layers |= LG_CACHE_GRID | LG_CACHE_GRAPH | LG_CACHE_DOT | LG_CACHE_MOVING;
624 //if (lg->debug > 1) {
625 //printf("bitmask ");
626 //dsp::print_bits(sizeof(lg->layers), &lg->layers);
630 // if plugin returns true (something has obviously changed) or if
631 // the requestor forces a redraw, request an exposition of the widget
633 if (lg
->source
->get_layers(lg
->source_id
, lg
->generation
, lg
->layers
) or force
)
634 gtk_widget_queue_draw(widget
);
638 calf_line_graph_expose (GtkWidget
*widget
, GdkEventExpose
*event
)
640 g_assert(CALF_IS_LINE_GRAPH(widget
));
641 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
643 // quit if no source available
644 if (!lg
->source
) return FALSE
;
646 if (lg
->debug
) printf("\n\n####### exposing %d #######\n", lg
->generation
);
648 // cairo context of the window
649 cairo_t
*c
= gdk_cairo_create(GDK_DRAWABLE(widget
->window
));
651 // recreate surfaces if someone needs it (init of the widget,
652 // resizing the window..)
653 if (lg
->recreate_surfaces
) {
654 if (lg
->debug
) printf("recreation...\n");
655 calf_line_graph_create_surfaces(widget
);
657 // all surfaces were recreated, so background is empty.
658 // draw the yellowish lighting on the background surface
659 cairo_t
*bg
= cairo_create(lg
->background_surface
);
660 if (lg
->debug
) printf("(draw background)\n");
661 display_background(widget
, bg
, 0, 0, lg
->size_x
, lg
->size_y
, lg
->pad_x
, lg
->pad_y
);
665 // the cache, grid and realtime surface wrapped in a cairo context
666 cairo_t
*grid_c
= cairo_create( lg
->grid_surface
);
667 cairo_t
*cache_c
= cairo_create( lg
->cache_surface
);
668 cairo_t
*realtime_c
= cairo_create( lg
->realtime_surface
);
670 if (lg
->recreate_surfaces
) {
671 // and copy it to the grid surface in case no grid is drawn
672 if (lg
->debug
) printf("copy bg->grid\n");
673 calf_line_graph_copy_surface(grid_c
, lg
->background_surface
);
675 // and copy it to the cache surface in case no cache is drawn
676 if (lg
->debug
) printf("copy bg->cache\n");
677 calf_line_graph_copy_surface(cache_c
, lg
->background_surface
);
679 // and copy it to the realtime surface in case no realtime is drawn
680 if (lg
->debug
) printf("copy bg->realtime\n");
681 calf_line_graph_copy_surface(realtime_c
, lg
->background_surface
);
683 if (lg
->recreate_surfaces
or lg
->force_redraw
) {
684 // reset generation value and request a new expose event
686 lg
->source
->get_layers(lg
->source_id
, lg
->generation
, lg
->layers
);
694 if (lg
->debug
) printf("width: %d height: %d x: %d y: %d\n", sx
, sy
, ox
, oy
);
698 dsp::print_bits(sizeof(lg
->layers
), &lg
->layers
);
702 // context used for the actual surface we want to draw on. It is
703 // switched over the drawing process via calf_line_graph_switch_context
705 cairo_t
*_ctx
= NULL
;
707 // the contexts for both moving curve caches
708 cairo_t
*moving_c
[2];
709 moving_c
[0] = cairo_create( lg
->moving_surface
[0] );
710 moving_c
[1] = cairo_create( lg
->moving_surface
[1] );
712 // the line widths to switch to between cycles
713 float grid_width
= 1.0;
714 float graph_width
= 1.5;
715 float dot_width
= 0.0;
717 // more vars we have to initialize, mainly stuff we use in callback
719 float *data
= new float[2 * std::max(lg
->size_x
, lg
->size_y
)];
721 bool vertical
= false;
722 std::string legend
= "";
727 // a cairo wrapper to hand over contexts to the plugin for setting
728 // line colors, widths aso
735 // some state variables used to determine what has happened
736 bool realtime_drawn
= false;
737 bool cache_drawn
= false;
738 bool grid_drawn
= false;
742 // check if we can skip the whole drawing stuff and go on with
743 // copying everything we drawed before
749 // 
755 or lg
->layers
& LG_CACHE_GRID
756 or lg
->layers
& LG_CACHE_GRAPH
757 or lg
->layers
& LG_CACHE_DOT
) {
758 if (lg
->debug
) printf("\n->cache\n");
760 // someone needs a redraw of the cache so start with the cache
764 // set the right context to work with
767 // and switch to grid surface in case we want to draw on it
768 if (lg
->debug
) printf("switch to grid\n");
769 ctx
= calf_line_graph_switch_context(lg
, grid_c
, &cimpl
);
771 if (lg
->debug
) printf("\n->realtime\n");
773 // no cache drawing neccessary, so skip the first drawing phase
776 // set the right context to work with
779 // and switch to the realtime surface
780 if (lg
->debug
) printf("switch to realtime\n");
781 ctx
= calf_line_graph_switch_context(lg
, realtime_c
, &cimpl
);
786 for (int phase
= drawing_phase
; phase
< 2; phase
++) {
787 // draw elements on the realtime and/or the cache surface
789 if (lg
->debug
) printf("\n### drawing phase %d\n", phase
);
791 ///////////////////////////////////////////////////////////////
793 ///////////////////////////////////////////////////////////////
795 if ((lg
->layers
& LG_CACHE_GRID
and !phase
) || (lg
->layers
& LG_REALTIME_GRID
and phase
)) {
796 // The plugin can set "vertical" to 1
797 // to force drawing of vertical lines instead of horizontal ones
798 // (which is the default)
799 // size and color of the grid (which can be set by the plugin
800 // via the context) are reset for every line.
802 // we're in cache phase and it seems we really want to
803 // draw new grid lines. so "clear" the grid surface
804 // with a pure background
805 if (lg
->debug
) printf("copy bg->grid\n");
806 calf_line_graph_copy_surface(ctx
, lg
->background_surface
);
810 legend
= std::string(),
811 cairo_set_source_rgba(ctx
, 0.15, 0.2, 0.0, 0.66),
812 cairo_set_line_width(ctx
, grid_width
),
813 lg
->source
->get_gridline(lg
->source_id
, a
, phase
, pos
, vertical
, legend
, &cimpl
);
816 if (!a
and lg
->debug
) printf("(draw grid)\n");
817 calf_line_graph_draw_grid( lg
, ctx
, legend
, vertical
, pos
);
821 // we're in cache phase so we have to switch back to
822 // the cache surface after drawing the grid on its surface
823 if (lg
->debug
) printf("switch to cache\n");
824 ctx
= calf_line_graph_switch_context(lg
, _ctx
, &cimpl
);
825 // if a grid was drawn copy it to cache
827 if (lg
->debug
) printf("copy grid->cache\n");
828 calf_line_graph_copy_surface(ctx
, lg
->grid_surface
);
833 ///////////////////////////////////////////////////////////////
835 ///////////////////////////////////////////////////////////////
837 if ((lg
->layers
& LG_CACHE_GRAPH
and !phase
) || (lg
->layers
& LG_REALTIME_GRAPH
and phase
)) {
838 // Cycle through all graphs and hand over the amount of horizontal
839 // pixels. The plugin is expected to set all corresponding vertical
840 // values in an array.
841 // size and color of the graph (which can be set by the plugin
842 // via the context) are reset for every graph.
845 // we are drawing the first graph in cache phase, so
846 // prepare the cache surface with the grid surface
847 if (lg
->debug
) printf("copy grid->cache\n");
848 calf_line_graph_copy_surface(ctx
, lg
->grid_surface
);
850 } else if (!realtime_drawn
) {
851 // we're in realtime phase and the realtime surface wasn't
852 // reset to cache by now (because there was no cache
853 // phase and no realtime grid was drawn)
854 // so "clear" the realtime surface with the cache
855 if (lg
->debug
) printf("copy cache->realtime\n");
856 calf_line_graph_copy_surface(ctx
, lg
->cache_surface
, lg
->force_cache
? 1 : lg
->fade
);
857 realtime_drawn
= true;
862 cairo_set_source_rgba(ctx
, 0.15, 0.2, 0.0, 0.8),
863 cairo_set_line_width(ctx
, graph_width
),
864 lg
->source
->get_graph(lg
->source_id
, a
, phase
, data
, lg
->size_x
, &cimpl
, &lg
->mode
);
867 if (lg
->debug
) printf("graph %d\n", a
);
868 calf_line_graph_draw_graph( lg
, ctx
, data
, lg
->mode
);
872 ///////////////////////////////////////////////////////////////
874 ///////////////////////////////////////////////////////////////
876 if ((lg
->layers
& LG_CACHE_MOVING
and !phase
) || (lg
->layers
& LG_REALTIME_MOVING
and phase
)) {
877 // we have a moving curve. switch to moving surface and
878 // clear it before we start to draw
879 if (lg
->debug
) printf("switch to moving %d\n", lg
->movesurf
);
880 ctx
= calf_line_graph_switch_context(lg
, moving_c
[lg
->movesurf
], &cimpl
);
881 calf_line_graph_clear_surface(ctx
);
883 if (!phase
and !cache_drawn
) {
884 // we are drawing the first moving in cache phase and
885 // no cache has been created by now, so
886 // prepare the cache surface with the grid surface
887 if (lg
->debug
) printf("copy grid->cache\n");
888 calf_line_graph_copy_surface(cache_c
, lg
->grid_surface
);
890 } else if (phase
and !realtime_drawn
) {
891 // we're in realtime phase and the realtime surface wasn't
892 // reset to cache by now (because there was no cache
893 // phase and no realtime grid was drawn)
894 // so "clear" the realtime surface with the cache
895 if (lg
->debug
) printf("copy cache->realtime\n");
896 calf_line_graph_copy_surface(realtime_c
, lg
->cache_surface
);
897 realtime_drawn
= true;
906 color
= RGBAtoINT(0.35, 0.4, 0.2, 1),
907 lg
->source
->get_moving(lg
->source_id
, a
, direction
, data
, lg
->size_x
, lg
->size_y
, offset
, color
);
910 if (lg
->debug
) printf("moving %d\n", a
);
911 calf_line_graph_draw_moving(lg
, ctx
, data
, direction
, offset
, color
);
915 // set moving distances according to direction
924 case LG_MOVING_RIGHT
:
937 // copy the old moving surface to the right position on the
939 if (lg
->debug
) printf("copy cached moving->moving\n");
940 cairo_set_source_surface(ctx
, lg
->moving_surface
[(int)!lg
->movesurf
], x
, y
);
943 // switch back to the actual context
944 if (lg
->debug
) printf("switch to realtime/cache\n");
945 ctx
= calf_line_graph_switch_context(lg
, _ctx
, &cimpl
);
947 if (lg
->debug
) printf("copy moving->realtime/cache\n");
948 calf_line_graph_copy_surface(ctx
, lg
->moving_surface
[lg
->movesurf
], 1);
950 // toggle the moving cache
951 lg
->movesurf
= (int)!lg
->movesurf
;
954 ///////////////////////////////////////////////////////////////
956 ///////////////////////////////////////////////////////////////
958 if ((lg
->layers
& LG_CACHE_DOT
and !phase
) || (lg
->layers
& LG_REALTIME_DOT
and phase
)) {
959 // Cycle through all dots. The plugin is expected to set the x
960 // and y value of the dot.
961 // color of the dot (which can be set by the plugin
962 // via the context) is reset for every graph.
964 if (!cache_drawn
and !phase
) {
965 // we are drawing dots in cache phase while
966 // the cache wasn't renewed (no graph was drawn), so
967 // prepare the cache surface with the grid surface
968 if (lg
->debug
) printf("copy grid->cache\n");
969 calf_line_graph_copy_surface(ctx
, lg
->grid_surface
);
972 if (!realtime_drawn
and phase
) {
973 // we're in realtime phase and the realtime surface wasn't
974 // reset to cache by now (because there was no cache
975 // phase and no realtime grid or graph was drawn)
976 // so "clear" the realtime surface with the cache
977 if (lg
->debug
) printf("copy cache->realtime\n");
978 calf_line_graph_copy_surface(ctx
, lg
->cache_surface
, lg
->force_cache
? 1 : lg
->fade
);
979 realtime_drawn
= true;
982 cairo_set_source_rgba(ctx
, 0.15, 0.2, 0.0, 1),
983 cairo_set_line_width(ctx
, dot_width
),
984 lg
->source
->get_dot(lg
->source_id
, a
, phase
, x
, y
, size
= 3, &cimpl
);
987 if (lg
->debug
) printf("dot %d\n", a
);
988 float yv
= oy
+ sy
/ 2 - (sy
/ 2 - 1) * y
;
989 cairo_arc(ctx
, ox
+ x
* sx
, yv
, size
, 0, 2 * M_PI
);
995 // if we have a second cycle for drawing on the realtime
996 // after the cache was renewed it's time to copy the
997 // cache to the realtime and switch the target surface
998 if (lg
->debug
) printf("switch to realtime\n");
999 ctx
= calf_line_graph_switch_context(lg
, realtime_c
, &cimpl
);
1003 // copy the cache to the realtime if it was changed
1004 if (lg
->debug
) printf("copy cache->realtime\n");
1005 calf_line_graph_copy_surface(ctx
, lg
->cache_surface
, lg
->force_cache
? 1 : lg
->fade
);
1006 realtime_drawn
= true;
1007 } else if (grid_drawn
) {
1008 // copy the grid to the realtime if it was changed
1009 if (lg
->debug
) printf("copy grid->realtime\n");
1010 calf_line_graph_copy_surface(ctx
, lg
->grid_surface
, lg
->force_cache
? 1 : lg
->fade
);
1011 realtime_drawn
= true;
1014 // check if we can skip the whole realtime phase
1015 if (!(lg
->layers
& LG_REALTIME_GRID
)
1016 and !(lg
->layers
& LG_REALTIME_GRAPH
)
1017 and !(lg
->layers
& LG_REALTIME_DOT
)) {
1021 } // one or two cycles for drawing cached and non-cached elements
1024 // îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚
1029 if (lg
->debug
) printf("\n### finalize\n");
1031 // whatever happened - we need to copy the realtime surface to the
1033 //if (lg->debug) printf("switch to window\n");
1034 //ctx = calf_line_graph_switch_context(lg, c, &cimpl);
1035 if (lg
->debug
) printf("copy realtime->window\n");
1036 calf_line_graph_copy_surface(c
, lg
->realtime_surface
);
1038 // if someone changed the handles via drag'n'drop or externally we
1039 // need a redraw of the handles surface
1040 if (lg
->freqhandles
and (lg
->handle_redraw
or lg
->force_redraw
)) {
1041 cairo_t
*hs
= cairo_create(lg
->handles_surface
);
1042 calf_line_graph_clear_surface(hs
);
1043 calf_line_graph_draw_freqhandles(lg
, hs
);
1047 // if we're using frequency handles we need to copy them to the
1049 if (lg
->freqhandles
) {
1050 if (lg
->debug
) printf("copy handles->window\n");
1051 calf_line_graph_copy_surface(c
, lg
->handles_surface
);
1054 // and draw the crosshairs on top if neccessary
1055 if (lg
->use_crosshairs
&& lg
->crosshairs_active
&& lg
->mouse_x
> 0
1056 && lg
->mouse_y
> 0 && lg
->handle_grabbed
< 0) {
1058 s
= lg
->source
->get_crosshair_label((int)(lg
->mouse_x
- ox
), (int)(lg
->mouse_y
- oy
), sx
, sy
, &cimpl
);
1059 cairo_set_line_width(c
, 1),
1060 calf_line_graph_draw_crosshairs(lg
, c
, false, 0, 0.5, 5, false, lg
->mouse_x
- ox
, lg
->mouse_y
- oy
, s
);
1063 lg
->force_cache
= false;
1064 lg
->force_redraw
= false;
1065 lg
->handle_redraw
= 0;
1066 lg
->recreate_surfaces
= 0;
1069 // destroy all temporarily created cairo contexts
1071 cairo_destroy(realtime_c
);
1072 cairo_destroy(grid_c
);
1073 cairo_destroy(cache_c
);
1074 cairo_destroy(moving_c
[0]);
1075 cairo_destroy(moving_c
[1]);
1077 lg
->generation
+= 1;
1083 calf_line_graph_get_handle_at(CalfLineGraph
*lg
, double x
, double y
)
1085 int sx
= lg
->size_x
;
1086 int sy
= lg
->size_y
;
1093 // loop on all handles
1094 for (int i
= 0; i
< lg
->freqhandles
; i
++) {
1095 FreqHandle
*handle
= &lg
->freq_handles
[i
];
1096 if (!handle
->is_active())
1099 if (handle
->dimensions
== 1) {
1100 // if user clicked inside a vertical band with width HANDLE_WIDTH handle is considered grabbed
1101 if (lg
->mouse_x
<= ox
+ round(handle
->value_x
* sx
+ HANDLE_WIDTH
/ 2.0) + 0.5 &&
1102 lg
->mouse_x
>= ox
+ round(handle
->value_x
* sx
- HANDLE_WIDTH
/ 2.0) - 0.5 ) {
1105 } else if (handle
->dimensions
>= 2) {
1106 double dx
= lg
->mouse_x
- round(ox
+ handle
->value_x
* sx
);
1107 double dy
= lg
->mouse_y
- round(oy
+ handle
->value_y
* sy
);
1109 // if mouse clicked inside circle of HANDLE_WIDTH
1110 if (sqrt(dx
* dx
+ dy
* dy
) <= HANDLE_WIDTH
/ 2.0)
1118 calf_line_graph_pointer_motion (GtkWidget
*widget
, GdkEventMotion
*event
)
1120 g_assert(CALF_IS_LINE_GRAPH(widget
));
1121 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1123 int sx
= lg
->size_x
;
1124 int sy
= lg
->size_y
;
1131 lg
->mouse_x
= event
->x
;
1132 lg
->mouse_y
= event
->y
;
1134 if (lg
->handle_grabbed
>= 0) {
1135 FreqHandle
*handle
= &lg
->freq_handles
[lg
->handle_grabbed
];
1137 float new_x_value
= float(event
->x
- ox
) / float(sx
);
1138 float new_y_value
= float(event
->y
- oy
) / float(sy
);
1140 if (new_x_value
< handle
->left_bound
) {
1141 new_x_value
= handle
->left_bound
;
1142 } else if (new_x_value
> handle
->right_bound
) {
1143 new_x_value
= handle
->right_bound
;
1146 // restrict y range by top and bottom
1147 if (handle
->dimensions
>= 2) {
1148 if(new_y_value
< 0.0) new_y_value
= 0.0;
1149 if(new_y_value
> 1.0) new_y_value
= 1.0;
1152 if (new_x_value
!= handle
->value_x
||
1153 new_y_value
!= handle
->value_y
) {
1154 handle
->value_x
= new_x_value
;
1155 handle
->value_y
= new_y_value
;
1157 g_signal_emit_by_name(widget
, "freqhandle-changed", handle
);
1159 lg
->handle_redraw
= 1;
1160 calf_line_graph_expose_request(widget
, true);
1164 gdk_event_request_motions(event
);
1167 int handle_hovered
= calf_line_graph_get_handle_at(lg
, event
->x
, event
->y
);
1168 if (handle_hovered
!= lg
->handle_hovered
) {
1169 if (lg
->handle_grabbed
>= 0 ||
1170 handle_hovered
!= -1) {
1171 gdk_window_set_cursor(widget
->window
, lg
->hand_cursor
);
1172 lg
->handle_hovered
= handle_hovered
;
1174 gdk_window_set_cursor(widget
->window
, lg
->arrow_cursor
);
1175 lg
->handle_hovered
= -1;
1177 lg
->handle_redraw
= 1;
1178 calf_line_graph_expose_request(widget
, true);
1180 if(lg
->crosshairs_active
and lg
->use_crosshairs
) {
1181 calf_line_graph_expose_request(widget
, true);
1187 calf_line_graph_button_press (GtkWidget
*widget
, GdkEventButton
*event
)
1189 g_assert(CALF_IS_LINE_GRAPH(widget
));
1190 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1191 bool inside_handle
= false;
1193 int i
= calf_line_graph_get_handle_at(lg
, lg
->mouse_x
, lg
->mouse_y
);
1196 FreqHandle
*handle
= &lg
->freq_handles
[i
];
1198 if (handle
->dimensions
== 1) {
1199 // if user clicked inside a vertical band with width HANDLE_WIDTH handle is considered grabbed
1200 lg
->handle_grabbed
= i
;
1201 inside_handle
= true;
1203 if (lg
->enforce_handle_order
) {
1204 // look for previous one dimensional handle to find left_bound
1205 for (int j
= i
- 1; j
>= 0; j
--) {
1206 FreqHandle
*prevhandle
= &lg
->freq_handles
[j
];
1207 if(prevhandle
->is_active() && prevhandle
->dimensions
== 1) {
1208 handle
->left_bound
= prevhandle
->value_x
+ lg
->min_handle_distance
;
1213 // look for next one dimensional handle to find right_bound
1214 for (int j
= i
+ 1; j
< lg
->freqhandles
; j
++) {
1215 FreqHandle
*nexthandle
= &lg
->freq_handles
[j
];
1216 if(nexthandle
->is_active() && nexthandle
->dimensions
== 1) {
1217 handle
->right_bound
= nexthandle
->value_x
- lg
->min_handle_distance
;
1222 } else if (handle
->dimensions
>= 2) {
1223 lg
->handle_grabbed
= i
;
1224 inside_handle
= true;
1228 if (inside_handle
&& event
->type
== GDK_2BUTTON_PRESS
) {
1229 FreqHandle
&handle
= lg
->freq_handles
[lg
->handle_grabbed
];
1230 handle
.value_x
= handle
.default_value_x
;
1231 handle
.value_y
= handle
.default_value_y
;
1232 g_signal_emit_by_name(widget
, "freqhandle-changed", &handle
);
1235 if(!inside_handle
) {
1236 lg
->crosshairs_active
= !lg
->crosshairs_active
;
1239 calf_line_graph_expose_request(widget
, true);
1240 gtk_widget_grab_focus(widget
);
1241 gtk_grab_add(widget
);
1247 calf_line_graph_button_release (GtkWidget
*widget
, GdkEventButton
*event
)
1249 g_assert(CALF_IS_LINE_GRAPH(widget
));
1250 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1252 lg
->handle_grabbed
= -1;
1254 if (GTK_WIDGET_HAS_GRAB(widget
))
1255 gtk_grab_remove(widget
);
1257 calf_line_graph_expose_request(widget
, true);
1262 calf_line_graph_scroll (GtkWidget
*widget
, GdkEventScroll
*event
)
1264 g_assert(CALF_IS_LINE_GRAPH(widget
));
1265 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1267 int i
= calf_line_graph_get_handle_at(lg
, lg
->mouse_x
, lg
->mouse_y
);
1270 FreqHandle
*handle
= &lg
->freq_handles
[i
];
1271 if (handle
->dimensions
== 3) {
1272 if (event
->direction
== GDK_SCROLL_UP
) {
1273 handle
->value_z
+= 0.05;
1274 if(handle
->value_z
> 1.0) {
1275 handle
->value_z
= 1.0;
1277 g_signal_emit_by_name(widget
, "freqhandle-changed", handle
);
1278 } else if (event
->direction
== GDK_SCROLL_DOWN
) {
1279 handle
->value_z
-= 0.05;
1280 if(handle
->value_z
< 0.0) {
1281 handle
->value_z
= 0.0;
1283 g_signal_emit_by_name(widget
, "freqhandle-changed", handle
);
1285 lg
->handle_redraw
= 1;
1292 calf_line_graph_enter (GtkWidget
*widget
, GdkEventCrossing
*event
)
1294 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1296 if (lg
->debug
) printf("[enter]\n");
1301 calf_line_graph_leave (GtkWidget
*widget
, GdkEventCrossing
*event
)
1304 g_assert(CALF_IS_LINE_GRAPH(widget
));
1305 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1307 if (lg
->debug
) printf("[leave]\n");
1308 if (lg
->mouse_x
>= 0 or lg
->mouse_y
>= 0)
1309 calf_line_graph_expose_request(widget
, true);
1317 void calf_line_graph_set_square(CalfLineGraph
*graph
, bool is_square
)
1319 g_assert(CALF_IS_LINE_GRAPH(graph
));
1320 graph
->is_square
= is_square
;
1324 calf_line_graph_size_request (GtkWidget
*widget
,
1325 GtkRequisition
*requisition
)
1327 g_assert(CALF_IS_LINE_GRAPH(widget
));
1329 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1331 if (lg
->debug
) printf("size request\n");
1335 calf_line_graph_size_allocate (GtkWidget
*widget
,
1336 GtkAllocation
*allocation
)
1338 g_assert(CALF_IS_LINE_GRAPH(widget
));
1339 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1341 if (lg
->debug
) printf("size allocation\n");
1343 GtkWidgetClass
*parent_class
= (GtkWidgetClass
*) g_type_class_peek_parent( CALF_LINE_GRAPH_GET_CLASS( lg
) );
1345 // remember the allocation
1346 widget
->allocation
= *allocation
;
1348 // reset the allocation if a square widget is requested
1349 GtkAllocation
&a
= widget
->allocation
;
1352 if (a
.width
> a
.height
)
1354 a
.x
+= (a
.width
- a
.height
) / 2;
1357 if (a
.width
< a
.height
)
1359 a
.y
+= (a
.height
- a
.width
) / 2;
1364 lg
->size_x
= a
.width
- lg
->pad_x
* 2;
1365 lg
->size_y
= a
.height
- lg
->pad_y
* 2;
1367 lg
->recreate_surfaces
= 1;
1368 parent_class
->size_allocate( widget
, &a
);
1373 calf_line_graph_class_init (CalfLineGraphClass
*klass
)
1375 // GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
1376 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS(klass
);
1377 widget_class
->expose_event
= calf_line_graph_expose
;
1378 widget_class
->size_request
= calf_line_graph_size_request
;
1379 widget_class
->size_allocate
= calf_line_graph_size_allocate
;
1380 widget_class
->button_press_event
= calf_line_graph_button_press
;
1381 widget_class
->button_release_event
= calf_line_graph_button_release
;
1382 widget_class
->motion_notify_event
= calf_line_graph_pointer_motion
;
1383 widget_class
->scroll_event
= calf_line_graph_scroll
;
1384 widget_class
->enter_notify_event
= calf_line_graph_enter
;
1385 widget_class
->leave_notify_event
= calf_line_graph_leave
;
1387 g_signal_new("freqhandle-changed",
1388 G_TYPE_OBJECT
, G_SIGNAL_RUN_FIRST
,
1390 g_cclosure_marshal_VOID__POINTER
,
1391 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
1395 calf_line_graph_unrealize (GtkWidget
*widget
, CalfLineGraph
*lg
)
1397 if (lg
->debug
) printf("unrealize\n");
1398 calf_line_graph_destroy_surfaces(widget
);
1402 calf_line_graph_init (CalfLineGraph
*lg
)
1404 GtkWidget
*widget
= GTK_WIDGET(lg
);
1406 if (lg
->debug
) printf("lg init\n");
1408 GTK_WIDGET_SET_FLAGS (widget
, GTK_CAN_FOCUS
| GTK_SENSITIVE
| GTK_PARENT_SENSITIVE
);
1409 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
);
1411 widget
->requisition
.width
= 40;
1412 widget
->requisition
.height
= 40;
1413 lg
->force_cache
= true;
1414 lg
->force_redraw
= false;
1416 lg
->param_zoom
= -1;
1418 lg
->param_offset
= -1;
1419 lg
->recreate_surfaces
= 1;
1423 lg
->arrow_cursor
= gdk_cursor_new(GDK_RIGHT_PTR
);
1424 lg
->hand_cursor
= gdk_cursor_new(GDK_FLEUR
);
1425 lg
->layers
= LG_CACHE_GRID
| LG_CACHE_GRAPH
1426 | LG_CACHE_DOT
| LG_CACHE_MOVING
1427 | LG_REALTIME_GRID
| LG_REALTIME_GRAPH
1428 | LG_REALTIME_DOT
| LG_REALTIME_MOVING
;
1430 g_signal_connect(GTK_OBJECT(widget
), "unrealize", G_CALLBACK(calf_line_graph_unrealize
), (gpointer
)lg
);
1432 for(int i
= 0; i
< FREQ_HANDLES
; i
++) {
1433 FreqHandle
*handle
= &lg
->freq_handles
[i
];
1434 handle
->active
= false;
1435 handle
->param_active_no
= -1;
1436 handle
->param_x_no
= -1;
1437 handle
->param_y_no
= -1;
1438 handle
->value_x
= -1.0;
1439 handle
->value_y
= -1.0;
1440 handle
->param_x_no
= -1;
1441 handle
->label
= NULL
;
1442 handle
->left_bound
= 0.0 + lg
->min_handle_distance
;
1443 handle
->right_bound
= 1.0 - lg
->min_handle_distance
;
1446 lg
->handle_grabbed
= -1;
1447 lg
->handle_hovered
= -1;
1448 lg
->handle_redraw
= 1;
1449 lg
->min_handle_distance
= 0.025;
1451 lg
->background_surface
= NULL
;
1452 lg
->grid_surface
= NULL
;
1453 lg
->cache_surface
= NULL
;
1454 lg
->moving_surface
[0] = NULL
;
1455 lg
->moving_surface
[1] = NULL
;
1456 lg
->handles_surface
= NULL
;
1457 lg
->realtime_surface
= NULL
;
1461 calf_line_graph_new()
1463 return GTK_WIDGET( g_object_new (CALF_TYPE_LINE_GRAPH
, NULL
));
1467 calf_line_graph_get_type (void)
1469 static GType type
= 0;
1471 static const GTypeInfo type_info
= {
1472 sizeof(CalfLineGraphClass
),
1473 NULL
, /* base_init */
1474 NULL
, /* base_finalize */
1475 (GClassInitFunc
)calf_line_graph_class_init
,
1476 NULL
, /* class_finalize */
1477 NULL
, /* class_data */
1478 sizeof(CalfLineGraph
),
1479 0, /* n_preallocs */
1480 (GInstanceInitFunc
)calf_line_graph_init
1483 GTypeInfo
*type_info_copy
= new GTypeInfo(type_info
);
1485 for (int i
= 0; ; i
++) {
1486 char *name
= g_strdup_printf("CalfLineGraph%u%d", ((unsigned int)(intptr_t)calf_line_graph_class_init
) >> 16, i
);
1487 if (g_type_from_name(name
)) {
1491 type
= g_type_register_static( GTK_TYPE_DRAWING_AREA
,