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
41 using namespace calf_plugins
;
44 calf_line_graph_draw_grid( CalfLineGraph
* lg
, cairo_t
*ctx
, string
&legend
, bool vertical
, float pos
)
53 cairo_text_extents_t tx
;
55 if (!legend
.empty()) {
56 cairo_text_extents(ctx
, legend
.c_str(), &tx
);
57 size
= vertical
? tx
.height
: tx
.width
;
65 x
= floor(ox
+ pos
* sx
) + 0.5;
66 cairo_move_to(ctx
, x
, oy
);
67 cairo_line_to(ctx
, x
, oy
+ sy
- size
);
69 if (!legend
.empty()) {
70 cairo_set_source_rgba(ctx
, 0.0, 0.0, 0.0, 0.5);
71 cairo_move_to(ctx
, x
- (tx
.x_bearing
+ tx
.width
/ 2.0), oy
+ sy
- 2);
72 cairo_show_text(ctx
, legend
.c_str());
77 y
= floor(oy
+ sy
/ 2 - (sy
/ 2 - 1) * pos
) + 0.5;
78 cairo_move_to(ctx
, ox
, y
);
79 cairo_line_to(ctx
, ox
+ sx
- size
, y
);
82 if (!legend
.empty()) {
83 cairo_set_source_rgba(ctx
, 0.0, 0.0, 0.0, 0.5);
84 cairo_move_to(ctx
, ox
+ sx
- 4 - tx
.width
, y
+ tx
.height
/2 - 2);
85 cairo_show_text(ctx
, legend
.c_str());
88 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());
92 calf_line_graph_draw_graph( CalfLineGraph
* lg
, cairo_t
*ctx
, float *data
, int mode
= 0 )
94 if (lg
->debug
) printf("(draw graph)\n");
105 for (int i
= 0; i
< sx
; i
++) {
106 y
= (oy
+ sy
/ 2 - (sy
/ 2 - 1) * data
[i
]);
107 if (lg
->debug
> 2) printf("* graph x: %d, y: %.5f, data: %.5f\n", i
, y
, data
[i
]);
112 // we want to draw a line
113 if (i
and (data
[i
] < INFINITY
or i
== sx
- 1)) {
114 cairo_line_to(ctx
, ox
+ i
, y
);
115 } else if (i
and startdraw
>= 0) {
118 cairo_move_to(ctx
, ox
, y
);
125 if (i
and ((data
[i
] < INFINITY
) or i
== sx
- 1)) {
126 cairo_rectangle(ctx
, ox
+ _lastx
, (int)y
, i
- _lastx
, sy
- (int)y
+ oy
);
129 startdraw
= ox
+ _lastx
;
135 // this one is drawing little boxes at the values position
136 if (i
and ((data
[i
] < INFINITY
) or i
== sx
- 1)) {
137 cairo_rectangle(ctx
, ox
+ _lastx
, (int)y
- 1, i
- _lastx
, 2);
140 startdraw
= ox
+ _lastx
;
146 // this one is drawing bars centered on the x axis
147 if (i
and ((data
[i
] < INFINITY
) or i
== sx
- 1)) {
148 cairo_rectangle(ctx
, ox
+ _lastx
, oy
+ sy
/ 2, i
- _lastx
, -1 * data
[i
] * (sy
/ 2));
151 startdraw
= ox
+ _lastx
;
157 // this one is drawing bars centered on the x axis with 1
159 if (i
and ((data
[i
] < INFINITY
) or i
== sx
- 1)) {
160 cairo_rectangle(ctx
, ox
+ _lastx
,oy
+ sy
/ 2 - sy
* lg
->offset
/ 2, i
- _lastx
, -1 * data
[i
] * (sy
/ 2) + sy
* lg
->offset
/ 2);
163 startdraw
= ox
+ _lastx
;
171 cairo_line_to(ctx
, sx
+ 2 * ox
, sy
+ 2 * oy
);
172 cairo_line_to(ctx
, 0, sy
+ 2 * oy
);
173 cairo_close_path(ctx
);
184 calf_line_graph_draw_moving(CalfLineGraph
* lg
, cairo_t
*ctx
, float *data
, int direction
, int offset
, int color
)
186 if (lg
->debug
) printf("(draw moving)\n");
196 int sm
= (direction
== LG_MOVING_UP
|| direction
== LG_MOVING_DOWN
? sx
: sy
);
197 int om
= (direction
== LG_MOVING_UP
|| direction
== LG_MOVING_DOWN
? ox
: oy
);
198 for (int i
= 0; i
< sm
; i
++) {
199 if (lg
->debug
> 2) printf("* moving i: %d, dir: %d, offset: %d, data: %.5f\n", i
, direction
, offset
, data
[i
]);
200 if (i
and ((data
[i
] < INFINITY
) or i
>= sm
)) {
201 cairo_set_source_rgba(ctx
, INTtoR(color
), INTtoG(color
), INTtoB(color
), (data
[i
] + 1) / 1.4 * INTtoA(color
));
205 cairo_rectangle(ctx
, ox
+ sx
- 1 - offset
, oy
+ _last
, 1, i
- _last
);
207 case LG_MOVING_RIGHT
:
208 cairo_rectangle(ctx
, ox
+ offset
, oy
+ _last
, 1, i
- _last
);
211 cairo_rectangle(ctx
, ox
+ _last
, oy
+ sy
- 1 - offset
, i
- _last
, 1);
214 cairo_rectangle(ctx
, ox
+ _last
, oy
+ offset
, i
- _last
, 1);
221 startdraw
= om
+ _last
;
229 void calf_line_graph_draw_label(CalfLineGraph
* lg
, cairo_t
*cache_cr
, string label
, int x
, int y
, double bgopac
)
236 cairo_text_extents_t tx
;
237 int n
= int(std::count(label
.begin(), label
.end(), '\n')) + 1;
238 cairo_set_source_rgba(cache_cr
, 0, 0, 0, 0.5);
242 string::size_type lpos
= label
.find_first_not_of("\n", 0);
243 string::size_type pos
= label
.find_first_of("\n", lpos
);
244 while (string::npos
!= pos
|| string::npos
!= lpos
) {
245 string str
= label
.substr(lpos
, pos
- lpos
);
246 cairo_text_extents(cache_cr
, str
.c_str(), &tx
);
247 h
+= tx
.height
+ linepad
;
248 w
= std::max(w
, tx
.width
);
249 z
= tx
.height
+ linepad
;
250 lpos
= label
.find_first_not_of("\n", pos
);
251 pos
= label
.find_first_of("\n", lpos
);
253 cairo_save(cache_cr
);
255 // set bgopac to > 1 if the background should be drawn without
256 // clipping to the labels dimensions
258 cairo_rectangle(cache_cr
, x
- hmarg
- w
- 2 * bgpad
,
259 y
- 3 - int(n
/ 2) * z
- bgpad
,
262 cairo_clip(cache_cr
);
266 cairo_set_source_surface(cache_cr
, lg
->background_surface
, 0, 0);
267 cairo_paint_with_alpha(cache_cr
, bgopac
);
268 cairo_restore(cache_cr
);
270 lpos
= label
.find_first_not_of("\n", 0);
271 pos
= label
.find_first_of("\n", lpos
);
272 while (string::npos
!= pos
|| string::npos
!= lpos
) {
273 string str
= label
.substr(lpos
, pos
- lpos
);
274 cairo_text_extents(cache_cr
, str
.c_str(), &tx
);
276 p
= y
- 3 - (n
/ 2) * (tx
.height
+ linepad
);
277 p
+= tx
.height
+ linepad
;
278 cairo_move_to(cache_cr
, x
- hmarg
- tx
.width
- bgpad
, p
);
279 cairo_show_text(cache_cr
, str
.c_str());
280 lpos
= label
.find_first_not_of("\n", pos
);
281 pos
= label
.find_first_of("\n", lpos
);
285 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
, string label
, double label_bg
)
287 if (lg
->debug
) printf("(draw crosshairs)\n");
298 calf_line_graph_draw_label(lg
, cache_cr
, label
, x
- mask
, y
, label_bg
);
300 cairo_pattern_t
*pat
;
302 if(mask
> 0 and circle
) {
303 cairo_move_to(cache_cr
, _x
, _y
);
304 cairo_arc (cache_cr
, _x
, _y
, mask
, 0, 2 * M_PI
);
305 cairo_set_source_rgba(cache_cr
, 0, 0, 0, alpha
);
306 cairo_fill(cache_cr
);
308 cairo_move_to(cache_cr
, _x
, _y
);
309 cairo_arc (cache_cr
, _x
, _y
, HANDLE_WIDTH
/ 2, 0, 2 * M_PI
);
310 cairo_set_source_rgba(cache_cr
, 0, 0, 0, 0.2);
311 cairo_fill(cache_cr
);
314 if(gradient
and gradient_rad
> 0) {
315 // draw the crosshairs with a steady gradient around
316 pat
= cairo_pattern_create_radial(_x
, _y
, 1, _x
, _y
, gradient_rad
* 2);
317 cairo_pattern_add_color_stop_rgba(pat
, 0, 0, 0, 0, alpha
);
318 cairo_pattern_add_color_stop_rgba(pat
, 0, 0, 0, 0, 0);
320 cairo_rectangle(cache_cr
, _x
, _y
- gradient_rad
, 1, gradient_rad
- mask
);
322 cairo_rectangle(cache_cr
, _x
+ mask
, _y
, gradient_rad
- mask
, 1);
324 cairo_rectangle(cache_cr
, _x
, _y
+ mask
, 1, gradient_rad
- mask
);
326 cairo_rectangle(cache_cr
, _x
- gradient_rad
, _y
, gradient_rad
- mask
, 1);
328 cairo_set_source(cache_cr
, pat
);
329 cairo_fill(cache_cr
);
330 } else if(gradient
) {
331 // draw the crosshairs with a gradient to the frame
333 cairo_rectangle(cache_cr
, _x
, oy
, 1, y
- mask
);
334 pat
= cairo_pattern_create_linear(_x
, oy
, _x
, _y
);
335 cairo_pattern_add_color_stop_rgba(pat
, 0, 0, 0, 0, 0);
336 cairo_pattern_add_color_stop_rgba(pat
, 1, 0, 0, 0, alpha
);
337 cairo_set_source(cache_cr
, pat
);
338 cairo_fill(cache_cr
);
340 cairo_rectangle(cache_cr
, _x
+ mask
, _y
, sx
- x
- mask
, 1);
341 pat
= cairo_pattern_create_linear(_x
, oy
, sx
, oy
);
342 cairo_pattern_add_color_stop_rgba(pat
, 0, 0, 0, 0, alpha
);
343 cairo_pattern_add_color_stop_rgba(pat
, 1, 0, 0, 0, 0);
344 cairo_set_source(cache_cr
, pat
);
345 cairo_fill(cache_cr
);
347 cairo_rectangle(cache_cr
, _x
, _y
+ mask
, 1, sy
- y
- mask
);
348 pat
= cairo_pattern_create_linear(_x
, _y
, _x
, oy
+ sy
);
349 cairo_pattern_add_color_stop_rgba(pat
, 0, 0, 0, 0, alpha
);
350 cairo_pattern_add_color_stop_rgba(pat
, 1, 0, 0, 0, 0);
351 cairo_set_source(cache_cr
, pat
);
352 cairo_fill(cache_cr
);
354 cairo_rectangle(cache_cr
, ox
, _y
, x
- mask
, 1);
355 pat
= cairo_pattern_create_linear(ox
, oy
, _x
, oy
);
356 cairo_pattern_add_color_stop_rgba(pat
, 0, 0, 0, 0, 0);
357 cairo_pattern_add_color_stop_rgba(pat
, 1, 0, 0, 0, alpha
);
358 cairo_set_source(cache_cr
, pat
);
359 cairo_fill(cache_cr
);
361 // draw normal crosshairs
363 cairo_move_to(cache_cr
, _x
+ 0.5, oy
+ 0.5);
364 cairo_line_to(cache_cr
, _x
+ 0.5, _y
- mask
+ 0.5);
366 cairo_move_to(cache_cr
, _x
+ mask
+ 0.5, _y
+ 0.5);
367 cairo_line_to(cache_cr
, ox
+ sx
+ 0.5, _y
+ 0.5);
369 cairo_move_to(cache_cr
, _x
+ 0.5, _y
+ mask
+ 0.5);
370 cairo_line_to(cache_cr
, _x
+ 0.5, oy
+ sy
+ 0.5);
372 cairo_move_to(cache_cr
, ox
+ 0.5, _y
+ 0.5);
373 cairo_line_to(cache_cr
, _x
- mask
+ 0.5, _y
+ 0.5);
375 cairo_set_source_rgba(cache_cr
, 0, 0, 0, alpha
);
376 cairo_stroke(cache_cr
);
380 void calf_line_graph_draw_freqhandles(CalfLineGraph
* lg
, cairo_t
* c
)
383 if (lg
->debug
) printf("(draw handles)\n");
390 if (lg
->freqhandles
> 0) {
391 cairo_set_source_rgba(c
, 0.0, 0.0, 0.0, 1.0);
392 cairo_set_line_width(c
, 1.0);
394 for (int i
= 0; i
< lg
->freqhandles
; i
++) {
395 FreqHandle
*handle
= &lg
->freq_handles
[i
];
397 if(!handle
->is_active() or handle
->value_x
< 0.0 or handle
->value_x
> 1.0)
400 int val_x
= round(handle
->value_x
* sx
);
401 int val_y
= (handle
->dimensions
>= 2) ? round(handle
->value_y
* sy
) : 0;
405 float freq
= exp((handle
->value_x
) * log(1000)) * 20.0;
407 // choose colors between dragged and normal state
408 if (lg
->handle_hovered
== i
) {
411 cairo_set_source_rgba(c
, 0, 0, 0, 0.7);
415 //cairo_set_source_rgb(c, 0.44, 0.5, 0.21);
416 cairo_set_source_rgba(c
, 0, 0, 0, 0.5);
418 if (handle
->dimensions
>= 2) {
419 cairo_move_to(c
, val_x
+ 8, val_y
);
421 cairo_move_to(c
, val_x
+ 11, oy
+ 15);
424 if (handle
->dimensions
== 1) {
425 // draw the main line
426 cairo_move_to(c
, ox
+ val_x
+ 0.5, oy
);
427 cairo_line_to(c
, ox
+ val_x
+ 0.5, oy
+ sy
);
429 // draw some one-dimensional bling-bling
430 cairo_pattern_t
*pat
;
431 switch(handle
->style
) {
434 // bell filters, default
435 pat
= cairo_pattern_create_linear(ox
, oy
, ox
, sy
);
436 cairo_pattern_add_color_stop_rgba(pat
, 0.f
, 0, 0, 0, 0);
437 cairo_pattern_add_color_stop_rgba(pat
, 0.5, 0, 0, 0, pat_alpha
);
438 cairo_pattern_add_color_stop_rgba(pat
, 1.f
, 0, 0, 0, 0);
439 cairo_rectangle(c
, ox
+ val_x
- 7, oy
, 6, sy
);
440 cairo_rectangle(c
, ox
+ val_x
+ 2, oy
, 6, sy
);
444 pat
= cairo_pattern_create_linear(ox
, oy
, ox
+ val_x
, oy
);
445 cairo_pattern_add_color_stop_rgba(pat
, 0.f
, 0, 0, 0, 0);
446 cairo_pattern_add_color_stop_rgba(pat
, 1.f
, 0, 0, 0, pat_alpha
);
447 cairo_rectangle(c
, ox
, oy
, val_x
- 1, sy
);
451 pat
= cairo_pattern_create_linear(ox
, oy
, ox
, sy
);
452 cairo_pattern_add_color_stop_rgba(pat
, 0.f
, 0, 0, 0, 0);
453 cairo_pattern_add_color_stop_rgba(pat
, 0.5, 0, 0, 0, pat_alpha
* 1.5);
454 cairo_pattern_add_color_stop_rgba(pat
, 1.f
, 0, 0, 0, 0);
455 cairo_rectangle(c
, ox
, oy
, val_x
- 1, sy
);
459 pat
= cairo_pattern_create_linear(ox
, oy
, ox
, sy
);
460 cairo_pattern_add_color_stop_rgba(pat
, 0.f
, 0, 0, 0, 0);
461 cairo_pattern_add_color_stop_rgba(pat
, 0.5, 0, 0, 0, pat_alpha
* 1.5);
462 cairo_pattern_add_color_stop_rgba(pat
, 1.f
, 0, 0, 0, 0);
463 cairo_rectangle(c
, ox
+ val_x
+ 2, oy
, sx
- val_x
- 2, sy
);
467 pat
= cairo_pattern_create_linear(ox
+ val_x
, oy
, ox
+ sx
, oy
);
468 cairo_pattern_add_color_stop_rgba(pat
, 0.f
, 0, 0, 0, pat_alpha
);
469 cairo_pattern_add_color_stop_rgba(pat
, 1.f
, 0, 0, 0, 0);
470 cairo_rectangle(c
, ox
+ val_x
+ 2, oy
, sx
- val_x
- 1, sy
);
473 cairo_set_source(c
, pat
);
475 cairo_pattern_destroy(pat
);
476 if (handle
->label
&& strlen(handle
->label
))
477 sprintf(label
, "%.2f Hz\n%s", freq
, handle
->label
);
479 sprintf(label
, "%.2f Hz", freq
);
480 calf_line_graph_draw_label(lg
, c
, label
, val_x
, oy
+ 15, 0.5);
483 int mask
= 30 - log10(1 + handle
->value_z
* 9) * 30 + HANDLE_WIDTH
/ 2.f
;
484 if (lg
->handle_hovered
== i
)
485 tmp
= calf_plugins::frequency_crosshair_label(val_x
, val_y
, sx
, sy
, 1, 1, 1, 1, lg
->zoom
* 128, 0);
487 tmp
= calf_plugins::frequency_crosshair_label(val_x
, val_y
, sx
, sy
, 1, 0, 0, 0, lg
->zoom
* 128, 0);
488 if (handle
->label
&& strlen(handle
->label
))
489 sprintf(label
, "%s\n%s", handle
->label
, tmp
.c_str());
491 strcpy(label
, tmp
.c_str());
492 calf_line_graph_draw_crosshairs(lg
, c
, grad
, -1, pat_alpha
, mask
, true, val_x
, val_y
, label
, lg
->handle_hovered
== i
? 0.8 : 0.5);
499 calf_line_graph_destroy_surfaces (GtkWidget
*widget
)
501 g_assert(CALF_IS_LINE_GRAPH(widget
));
502 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
504 if (lg
->debug
) printf("{destroy surfaces}\n");
506 // destroy all surfaces - and don't tell anybody about it - hehe
507 if( lg
->background_surface
)
508 cairo_surface_destroy( lg
->background_surface
);
509 if( lg
->grid_surface
)
510 cairo_surface_destroy( lg
->grid_surface
);
511 if( lg
->cache_surface
)
512 cairo_surface_destroy( lg
->cache_surface
);
513 if( lg
->moving_surface
[0] )
514 cairo_surface_destroy( lg
->moving_surface
[0] );
515 if( lg
->moving_surface
[1] )
516 cairo_surface_destroy( lg
->moving_surface
[1] );
517 if( lg
->handles_surface
)
518 cairo_surface_destroy( lg
->handles_surface
);
519 if( lg
->realtime_surface
)
520 cairo_surface_destroy( lg
->realtime_surface
);
523 calf_line_graph_create_surfaces (GtkWidget
*widget
)
525 g_assert(CALF_IS_LINE_GRAPH(widget
));
526 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
528 if (lg
->debug
) printf("{create surfaces}\n");
530 int width
= widget
->allocation
.width
;
531 int height
= widget
->allocation
.height
;
533 // the size of the "real" drawing area
534 lg
->size_x
= width
- lg
->pad_x
* 2;
535 lg
->size_y
= height
- lg
->pad_y
* 2;
537 calf_line_graph_destroy_surfaces(widget
);
538 // create the background surface.
539 // background holds the graphics of the frame and the yellowish
540 // background light for faster redrawing of static stuff
541 lg
->background_surface
= cairo_image_surface_create(
542 CAIRO_FORMAT_ARGB32
, width
, height
);
544 // create the grid surface.
545 // this one is used as a cache for the grid on the background in the
546 // cache phase. If a graph or dot in cache phase needs to be redrawn
547 // we don't need to redraw the whole grid.
548 lg
->grid_surface
= cairo_image_surface_create(
549 CAIRO_FORMAT_ARGB32
, width
, height
);
551 // create the cache surface.
552 // cache holds a copy of the background with a static part of
553 // the grid and some static curves and dots.
554 lg
->cache_surface
= cairo_image_surface_create(
555 CAIRO_FORMAT_ARGB32
, width
, height
);
557 // create the moving surface.
558 // moving is used as a cache for any slowly moving graphics like
559 // spectralizer or waveforms
560 lg
->moving_surface
[0] = cairo_image_surface_create(
561 CAIRO_FORMAT_ARGB32
, width
, height
);
563 // create the moving temp surface.
564 // moving is used as a cache for any slowly moving graphics like
565 // spectralizer or waveforms
566 lg
->moving_surface
[1] = cairo_image_surface_create(
567 CAIRO_FORMAT_ARGB32
, width
, height
);
569 // create the handles surface.
570 // this one contains the handles graphics to avoid redrawing
572 lg
->handles_surface
= cairo_image_surface_create(
573 CAIRO_FORMAT_ARGB32
, width
, height
);
575 // create the realtime surface.
576 // realtime is used to cache the realtime graphics for drawing the
577 // crosshairs on top if nothing else changed
578 lg
->realtime_surface
= cairo_image_surface_create(
579 CAIRO_FORMAT_ARGB32
, width
, height
);
581 lg
->force_cache
= true;
585 *calf_line_graph_switch_context(CalfLineGraph
* lg
, cairo_t
*ctx
, cairo_impl
*cimpl
)
587 if (lg
->debug
) printf("{switch context}\n");
589 cimpl
->context
= ctx
;
590 cairo_select_font_face(ctx
, "Sans",
591 CAIRO_FONT_SLANT_NORMAL
, CAIRO_FONT_WEIGHT_NORMAL
);
592 cairo_set_font_size(ctx
, 9);
593 cairo_set_line_join(ctx
, CAIRO_LINE_JOIN_MITER
);
594 cairo_rectangle(ctx
, lg
->pad_x
, lg
->pad_y
, lg
->size_x
, lg
->size_y
);
600 calf_line_graph_copy_surface(cairo_t
*ctx
, cairo_surface_t
*source
, int x
= 0, int y
= 0, float fade
= 1.f
)
602 // copy a surface to a cairo context
604 cairo_set_source_surface(ctx
, source
, x
, y
);
606 cairo_paint_with_alpha(ctx
, fade
* 0.35 + 0.05);
614 calf_line_graph_clear_surface(cairo_t
*ctx
)
616 // clears a surface to transparent
618 cairo_set_operator(ctx
, CAIRO_OPERATOR_CLEAR
);
623 void calf_line_graph_expose_request (GtkWidget
*widget
, bool force
)
625 // someone thinks we should redraw the line graph. let's see what
626 // the plugin thinks about. To do that a bitmask is sent to the
627 // plugin which can be changed. If the plugin returns true or if
628 // the request is in response of something like dragged handles, an
629 // exposition of the widget is requested from GTK
631 g_assert(CALF_IS_LINE_GRAPH(widget
));
632 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
634 // quit if no source available
635 if (!lg
->source
) return;
637 if (lg
->debug
> 1) printf("\n\n### expose request %d ###\n", lg
->generation
);
639 // let a bitmask be switched by the plugin to determine the layers
640 // we want to draw. We set all cache layers to true if force_cache
641 // is set otherwise default is to draw nothing. The return value
642 // tells us whether the plugin wants to draw at all or not.
644 //if (lg->force_cache || lg->recreate_surfaces)
645 //lg->layers |= LG_CACHE_GRID | LG_CACHE_GRAPH | LG_CACHE_DOT | LG_CACHE_MOVING;
647 //if (lg->debug > 1) {
648 //printf("bitmask ");
649 //dsp::print_bits(sizeof(lg->layers), &lg->layers);
653 // if plugin returns true (something has obviously changed) or if
654 // the requestor forces a redraw, request an exposition of the widget
656 if (lg
->source
->get_layers(lg
->source_id
, lg
->generation
, lg
->layers
) or force
)
657 gtk_widget_queue_draw(widget
);
661 calf_line_graph_expose (GtkWidget
*widget
, GdkEventExpose
*event
)
663 g_assert(CALF_IS_LINE_GRAPH(widget
));
664 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
666 // quit if no source available
667 if (!lg
->source
) return FALSE
;
669 if (lg
->debug
) printf("\n\n####### exposing %d #######\n", lg
->generation
);
671 // cairo context of the window
672 cairo_t
*c
= gdk_cairo_create(GDK_DRAWABLE(widget
->window
));
675 // recreate surfaces if someone needs it (init of the widget,
676 // resizing the window..)
677 if (lg
->recreate_surfaces
) {
678 lg
->pad_x
= widget
->style
->xthickness
;
679 lg
->pad_y
= widget
->style
->ythickness
;
680 lg
->x
= widget
->allocation
.x
;
681 lg
->y
= widget
->allocation
.y
;
683 gtk_widget_style_get(widget
, "border-radius", &radius
, "bevel", &bevel
, NULL
);
685 if (lg
->debug
) printf("recreation...\n");
686 calf_line_graph_create_surfaces(widget
);
688 // all surfaces were recreated, so background is empty.
689 // draw the yellowish lighting on the background surface
690 cairo_t
*bg
= cairo_create(lg
->background_surface
);
691 if (lg
->debug
) printf("(draw background)\n");
692 display_background(widget
, bg
, 0, 0, lg
->size_x
, lg
->size_y
, lg
->pad_x
, lg
->pad_y
, radius
, bevel
);
696 // the cache, grid and realtime surface wrapped in a cairo context
697 cairo_t
*grid_c
= cairo_create( lg
->grid_surface
);
698 cairo_t
*cache_c
= cairo_create( lg
->cache_surface
);
699 cairo_t
*realtime_c
= cairo_create( lg
->realtime_surface
);
701 if (lg
->recreate_surfaces
) {
702 // and copy it to the grid surface in case no grid is drawn
703 if (lg
->debug
) printf("copy bg->grid\n");
704 calf_line_graph_copy_surface(grid_c
, lg
->background_surface
);
706 // and copy it to the cache surface in case no cache is drawn
707 if (lg
->debug
) printf("copy bg->cache\n");
708 calf_line_graph_copy_surface(cache_c
, lg
->background_surface
);
710 // and copy it to the realtime surface in case no realtime is drawn
711 if (lg
->debug
) printf("copy bg->realtime\n");
712 calf_line_graph_copy_surface(realtime_c
, lg
->background_surface
);
714 if (lg
->recreate_surfaces
or lg
->force_redraw
) {
715 // reset generation value and request a new expose event
717 lg
->source
->get_layers(lg
->source_id
, lg
->generation
, lg
->layers
);
725 if (lg
->debug
) printf("width: %d height: %d x: %d y: %d\n", sx
, sy
, ox
, oy
);
729 dsp::print_bits(sizeof(lg
->layers
), &lg
->layers
);
733 // context used for the actual surface we want to draw on. It is
734 // switched over the drawing process via calf_line_graph_switch_context
736 cairo_t
*_ctx
= NULL
;
738 // the contexts for both moving curve caches
739 cairo_t
*moving_c
[2];
740 moving_c
[0] = cairo_create( lg
->moving_surface
[0] );
741 moving_c
[1] = cairo_create( lg
->moving_surface
[1] );
743 // the line widths to switch to between cycles
744 float grid_width
= 1.0;
745 float graph_width
= 1.5;
746 float dot_width
= 0.0;
748 // more vars we have to initialize, mainly stuff we use in callback
750 float *data
= new float[2 * std::max(lg
->size_x
, lg
->size_y
)];
752 bool vertical
= false;
758 // a cairo wrapper to hand over contexts to the plugin for setting
759 // line colors, widths aso
766 // some state variables used to determine what has happened
767 bool realtime_drawn
= false;
768 bool cache_drawn
= false;
769 bool grid_drawn
= false;
773 // check if we can skip the whole drawing stuff and go on with
774 // copying everything we drawed before
780 // 
786 or lg
->layers
& LG_CACHE_GRID
787 or lg
->layers
& LG_CACHE_GRAPH
788 or lg
->layers
& LG_CACHE_DOT
) {
789 if (lg
->debug
) printf("\n->cache\n");
791 // someone needs a redraw of the cache so start with the cache
795 // set the right context to work with
798 // and switch to grid surface in case we want to draw on it
799 if (lg
->debug
) printf("switch to grid\n");
800 ctx
= calf_line_graph_switch_context(lg
, grid_c
, &cimpl
);
802 if (lg
->debug
) printf("\n->realtime\n");
804 // no cache drawing neccessary, so skip the first drawing phase
807 // set the right context to work with
810 // and switch to the realtime surface
811 if (lg
->debug
) printf("switch to realtime\n");
812 ctx
= calf_line_graph_switch_context(lg
, realtime_c
, &cimpl
);
817 for (int phase
= drawing_phase
; phase
< 2; phase
++) {
818 // draw elements on the realtime and/or the cache surface
820 if (lg
->debug
) printf("\n### drawing phase %d\n", phase
);
822 ///////////////////////////////////////////////////////////////
824 ///////////////////////////////////////////////////////////////
826 if ((lg
->layers
& LG_CACHE_GRID
and !phase
) || (lg
->layers
& LG_REALTIME_GRID
and phase
)) {
827 // The plugin can set "vertical" to 1
828 // to force drawing of vertical lines instead of horizontal ones
829 // (which is the default)
830 // size and color of the grid (which can be set by the plugin
831 // via the context) are reset for every line.
833 // we're in cache phase and it seems we really want to
834 // draw new grid lines. so "clear" the grid surface
835 // with a pure background
836 if (lg
->debug
) printf("copy bg->grid\n");
837 calf_line_graph_copy_surface(ctx
, lg
->background_surface
);
842 cairo_set_source_rgba(ctx
, 0.15, 0.2, 0.0, 0.66),
843 cairo_set_line_width(ctx
, grid_width
),
844 lg
->source
->get_gridline(lg
->source_id
, a
, phase
, pos
, vertical
, legend
, &cimpl
);
847 if (!a
and lg
->debug
) printf("(draw grid)\n");
848 calf_line_graph_draw_grid( lg
, ctx
, legend
, vertical
, pos
);
852 // we're in cache phase so we have to switch back to
853 // the cache surface after drawing the grid on its surface
854 if (lg
->debug
) printf("switch to cache\n");
855 ctx
= calf_line_graph_switch_context(lg
, _ctx
, &cimpl
);
856 // if a grid was drawn copy it to cache
858 if (lg
->debug
) printf("copy grid->cache\n");
859 calf_line_graph_copy_surface(ctx
, lg
->grid_surface
);
864 ///////////////////////////////////////////////////////////////
866 ///////////////////////////////////////////////////////////////
868 if ((lg
->layers
& LG_CACHE_GRAPH
and !phase
) || (lg
->layers
& LG_REALTIME_GRAPH
and phase
)) {
869 // Cycle through all graphs and hand over the amount of horizontal
870 // pixels. The plugin is expected to set all corresponding vertical
871 // values in an array.
872 // size and color of the graph (which can be set by the plugin
873 // via the context) are reset for every graph.
876 // we are drawing the first graph in cache phase, so
877 // prepare the cache surface with the grid surface
878 if (lg
->debug
) printf("copy grid->cache\n");
879 calf_line_graph_copy_surface(ctx
, lg
->grid_surface
);
881 } else if (!realtime_drawn
) {
882 // we're in realtime phase and the realtime surface wasn't
883 // reset to cache by now (because there was no cache
884 // phase and no realtime grid was drawn)
885 // so "clear" the realtime surface with the cache
886 if (lg
->debug
) printf("copy cache->realtime\n");
887 calf_line_graph_copy_surface(ctx
, lg
->cache_surface
, 0, 0, lg
->force_cache
? 1 : lg
->fade
);
888 realtime_drawn
= true;
893 cairo_set_source_rgba(ctx
, 0.15, 0.2, 0.0, 0.8),
894 cairo_set_line_width(ctx
, graph_width
),
895 lg
->source
->get_graph(lg
->source_id
, a
, phase
, data
, lg
->size_x
, &cimpl
, &lg
->mode
);
898 if (lg
->debug
) printf("graph %d\n", a
);
899 calf_line_graph_draw_graph( lg
, ctx
, data
, lg
->mode
);
903 ///////////////////////////////////////////////////////////////
905 ///////////////////////////////////////////////////////////////
907 if ((lg
->layers
& LG_CACHE_MOVING
and !phase
) || (lg
->layers
& LG_REALTIME_MOVING
and phase
)) {
908 // we have a moving curve. switch to moving surface and
909 // clear it before we start to draw
910 if (lg
->debug
) printf("switch to moving %d\n", lg
->movesurf
);
911 ctx
= calf_line_graph_switch_context(lg
, moving_c
[lg
->movesurf
], &cimpl
);
912 calf_line_graph_clear_surface(ctx
);
914 if (!phase
and !cache_drawn
) {
915 // we are drawing the first moving in cache phase and
916 // no cache has been created by now, so
917 // prepare the cache surface with the grid surface
918 if (lg
->debug
) printf("copy grid->cache\n");
919 calf_line_graph_copy_surface(cache_c
, lg
->grid_surface
);
921 } else if (phase
and !realtime_drawn
) {
922 // we're in realtime phase and the realtime surface wasn't
923 // reset to cache by now (because there was no cache
924 // phase and no realtime grid was drawn)
925 // so "clear" the realtime surface with the cache
926 if (lg
->debug
) printf("copy cache->realtime\n");
927 calf_line_graph_copy_surface(realtime_c
, lg
->cache_surface
);
928 realtime_drawn
= true;
937 color
= RGBAtoINT(0.35, 0.4, 0.2, 1),
938 lg
->source
->get_moving(lg
->source_id
, a
, direction
, data
, lg
->size_x
, lg
->size_y
, offset
, color
);
941 if (lg
->debug
) printf("moving %d\n", a
);
942 calf_line_graph_draw_moving(lg
, ctx
, data
, direction
, offset
, color
);
946 // set moving distances according to direction
955 case LG_MOVING_RIGHT
:
968 // copy the old moving surface to the right position on the
970 if (lg
->debug
) printf("copy cached moving->moving\n");
971 cairo_set_source_surface(ctx
, lg
->moving_surface
[(int)!lg
->movesurf
], xd
, yd
);
974 // switch back to the actual context
975 if (lg
->debug
) printf("switch to realtime/cache\n");
976 ctx
= calf_line_graph_switch_context(lg
, _ctx
, &cimpl
);
978 if (lg
->debug
) printf("copy moving->realtime/cache\n");
979 calf_line_graph_copy_surface(ctx
, lg
->moving_surface
[lg
->movesurf
]);
981 // toggle the moving cache
982 lg
->movesurf
= (int)!lg
->movesurf
;
985 ///////////////////////////////////////////////////////////////
987 ///////////////////////////////////////////////////////////////
989 if ((lg
->layers
& LG_CACHE_DOT
and !phase
) || (lg
->layers
& LG_REALTIME_DOT
and phase
)) {
990 // Cycle through all dots. The plugin is expected to set the x
991 // and y value of the dot.
992 // color of the dot (which can be set by the plugin
993 // via the context) is reset for every graph.
995 if (!cache_drawn
and !phase
) {
996 // we are drawing dots in cache phase while
997 // the cache wasn't renewed (no graph was drawn), so
998 // prepare the cache surface with the grid surface
999 if (lg
->debug
) printf("copy grid->cache\n");
1000 calf_line_graph_copy_surface(ctx
, lg
->grid_surface
);
1003 if (!realtime_drawn
and phase
) {
1004 // we're in realtime phase and the realtime surface wasn't
1005 // reset to cache by now (because there was no cache
1006 // phase and no realtime grid or graph was drawn)
1007 // so "clear" the realtime surface with the cache
1008 if (lg
->debug
) printf("copy cache->realtime\n");
1009 calf_line_graph_copy_surface(ctx
, lg
->cache_surface
, 0, 0, lg
->force_cache
? 1 : lg
->fade
);
1010 realtime_drawn
= true;
1013 cairo_set_source_rgba(ctx
, 0.15, 0.2, 0.0, 1),
1014 cairo_set_line_width(ctx
, dot_width
),
1015 lg
->source
->get_dot(lg
->source_id
, a
, phase
, x
, y
, size
= 3, &cimpl
);
1018 if (lg
->debug
) printf("dot %d\n", a
);
1019 float yv
= oy
+ sy
/ 2 - (sy
/ 2 - 1) * y
;
1020 cairo_arc(ctx
, ox
+ x
* sx
, yv
, size
, 0, 2 * M_PI
);
1026 // if we have a second cycle for drawing on the realtime
1027 // after the cache was renewed it's time to copy the
1028 // cache to the realtime and switch the target surface
1029 if (lg
->debug
) printf("switch to realtime\n");
1030 ctx
= calf_line_graph_switch_context(lg
, realtime_c
, &cimpl
);
1034 // copy the cache to the realtime if it was changed
1035 if (lg
->debug
) printf("copy cache->realtime\n");
1036 calf_line_graph_copy_surface(ctx
, lg
->cache_surface
, 0, 0, lg
->force_cache
? 1 : lg
->fade
);
1037 realtime_drawn
= true;
1038 } else if (grid_drawn
) {
1039 // copy the grid to the realtime if it was changed
1040 if (lg
->debug
) printf("copy grid->realtime\n");
1041 calf_line_graph_copy_surface(ctx
, lg
->grid_surface
, 0, 0, lg
->force_cache
? 1 : lg
->fade
);
1042 realtime_drawn
= true;
1045 // check if we can skip the whole realtime phase
1046 if (!(lg
->layers
& LG_REALTIME_GRID
)
1047 and !(lg
->layers
& LG_REALTIME_GRAPH
)
1048 and !(lg
->layers
& LG_REALTIME_DOT
)) {
1052 } // one or two cycles for drawing cached and non-cached elements
1055 // îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚
1060 if (lg
->debug
) printf("\n### finalize\n");
1062 // whatever happened - we need to copy the realtime surface to the
1064 //if (lg->debug) printf("switch to window\n");
1065 //ctx = calf_line_graph_switch_context(lg, c, &cimpl);
1066 if (lg
->debug
) printf("copy realtime->window\n");
1067 calf_line_graph_copy_surface(c
, lg
->realtime_surface
, lg
->x
, lg
->y
);
1069 // if someone changed the handles via drag'n'drop or externally we
1070 // need a redraw of the handles surface
1071 if (lg
->freqhandles
and (lg
->handle_redraw
or lg
->force_redraw
)) {
1072 cairo_t
*hs
= cairo_create(lg
->handles_surface
);
1073 calf_line_graph_clear_surface(hs
);
1074 calf_line_graph_draw_freqhandles(lg
, hs
);
1078 // if we're using frequency handles we need to copy them to the
1080 if (lg
->freqhandles
) {
1081 if (lg
->debug
) printf("copy handles->window\n");
1082 calf_line_graph_copy_surface(c
, lg
->handles_surface
, lg
->x
, lg
->y
);
1085 // and draw the crosshairs on top if neccessary
1086 if (lg
->use_crosshairs
&& lg
->crosshairs_active
&& lg
->mouse_x
> 0
1087 && lg
->mouse_y
> 0 && lg
->handle_grabbed
< 0) {
1089 s
= lg
->source
->get_crosshair_label((int)(lg
->mouse_x
- ox
), (int)(lg
->mouse_y
- oy
), sx
, sy
, 1, 1, 1, 1);
1090 cairo_set_line_width(c
, 1),
1091 calf_line_graph_draw_crosshairs(lg
, c
, false, 0, 0.5, 5, false, lg
->mouse_x
- ox
, lg
->mouse_y
- oy
, s
, 1.5);
1094 lg
->force_cache
= false;
1095 lg
->force_redraw
= false;
1096 lg
->handle_redraw
= 0;
1097 lg
->recreate_surfaces
= 0;
1100 // destroy all temporarily created cairo contexts
1102 cairo_destroy(realtime_c
);
1103 cairo_destroy(grid_c
);
1104 cairo_destroy(cache_c
);
1105 cairo_destroy(moving_c
[0]);
1106 cairo_destroy(moving_c
[1]);
1108 lg
->generation
+= 1;
1114 calf_line_graph_get_handle_at(CalfLineGraph
*lg
, double x
, double y
)
1116 int sx
= lg
->size_x
;
1117 int sy
= lg
->size_y
;
1124 // loop on all handles
1125 for (int i
= 0; i
< lg
->freqhandles
; i
++) {
1126 FreqHandle
*handle
= &lg
->freq_handles
[i
];
1127 if (!handle
->is_active())
1130 if (handle
->dimensions
== 1) {
1131 // if user clicked inside a vertical band with width HANDLE_WIDTH handle is considered grabbed
1132 if (lg
->mouse_x
<= ox
+ round(handle
->value_x
* sx
+ HANDLE_WIDTH
/ 2.0) + 0.5 &&
1133 lg
->mouse_x
>= ox
+ round(handle
->value_x
* sx
- HANDLE_WIDTH
/ 2.0) - 0.5 ) {
1136 } else if (handle
->dimensions
>= 2) {
1137 double dx
= lg
->mouse_x
- round(ox
+ handle
->value_x
* sx
);
1138 double dy
= lg
->mouse_y
- round(oy
+ handle
->value_y
* sy
);
1140 // if mouse clicked inside circle of HANDLE_WIDTH
1141 if (sqrt(dx
* dx
+ dy
* dy
) <= HANDLE_WIDTH
/ 2.0)
1149 calf_line_graph_pointer_motion (GtkWidget
*widget
, GdkEventMotion
*event
)
1151 g_assert(CALF_IS_LINE_GRAPH(widget
));
1152 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1154 int sx
= lg
->size_x
;
1155 int sy
= lg
->size_y
;
1162 lg
->mouse_x
= event
->x
;
1163 lg
->mouse_y
= event
->y
;
1165 if (lg
->handle_grabbed
>= 0) {
1166 FreqHandle
*handle
= &lg
->freq_handles
[lg
->handle_grabbed
];
1168 float new_x_value
= float(lg
->mouse_x
- ox
) / float(sx
);
1169 float new_y_value
= float(lg
->mouse_y
- oy
) / float(sy
);
1171 if (new_x_value
< handle
->left_bound
) {
1172 new_x_value
= handle
->left_bound
;
1173 } else if (new_x_value
> handle
->right_bound
) {
1174 new_x_value
= handle
->right_bound
;
1177 // restrict y range by top and bottom
1178 if (handle
->dimensions
>= 2) {
1179 if(new_y_value
< 0.0) new_y_value
= 0.0;
1180 if(new_y_value
> 1.0) new_y_value
= 1.0;
1183 if (new_x_value
!= handle
->value_x
||
1184 new_y_value
!= handle
->value_y
) {
1185 handle
->value_x
= new_x_value
;
1186 handle
->value_y
= new_y_value
;
1188 g_signal_emit_by_name(widget
, "freqhandle-changed", handle
);
1190 lg
->handle_redraw
= 1;
1191 calf_line_graph_expose_request(widget
, true);
1195 gdk_event_request_motions(event
);
1198 int handle_hovered
= calf_line_graph_get_handle_at(lg
, lg
->mouse_x
, lg
->mouse_y
);
1199 if (handle_hovered
!= lg
->handle_hovered
) {
1200 if (lg
->handle_grabbed
>= 0 ||
1201 handle_hovered
!= -1) {
1202 gdk_window_set_cursor(widget
->window
, lg
->hand_cursor
);
1203 lg
->handle_hovered
= handle_hovered
;
1205 gdk_window_set_cursor(widget
->window
, lg
->arrow_cursor
);
1206 lg
->handle_hovered
= -1;
1208 lg
->handle_redraw
= 1;
1209 calf_line_graph_expose_request(widget
, true);
1211 if(lg
->crosshairs_active
and lg
->use_crosshairs
) {
1212 calf_line_graph_expose_request(widget
, true);
1218 calf_line_graph_button_press (GtkWidget
*widget
, GdkEventButton
*event
)
1220 g_assert(CALF_IS_LINE_GRAPH(widget
));
1221 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1222 bool inside_handle
= false;
1224 int i
= calf_line_graph_get_handle_at(lg
, lg
->mouse_x
, lg
->mouse_y
);
1227 FreqHandle
*handle
= &lg
->freq_handles
[i
];
1229 if (handle
->dimensions
== 1) {
1230 // if user clicked inside a vertical band with width HANDLE_WIDTH handle is considered grabbed
1231 lg
->handle_grabbed
= i
;
1232 inside_handle
= true;
1234 if (lg
->enforce_handle_order
) {
1235 // look for previous one dimensional handle to find left_bound
1236 for (int j
= i
- 1; j
>= 0; j
--) {
1237 FreqHandle
*prevhandle
= &lg
->freq_handles
[j
];
1238 if(prevhandle
->is_active() && prevhandle
->dimensions
== 1) {
1239 handle
->left_bound
= prevhandle
->value_x
+ lg
->min_handle_distance
;
1244 // look for next one dimensional handle to find right_bound
1245 for (int j
= i
+ 1; j
< lg
->freqhandles
; j
++) {
1246 FreqHandle
*nexthandle
= &lg
->freq_handles
[j
];
1247 if(nexthandle
->is_active() && nexthandle
->dimensions
== 1) {
1248 handle
->right_bound
= nexthandle
->value_x
- lg
->min_handle_distance
;
1253 } else if (handle
->dimensions
>= 2) {
1254 lg
->handle_grabbed
= i
;
1255 inside_handle
= true;
1259 if (inside_handle
&& event
->type
== GDK_2BUTTON_PRESS
) {
1260 FreqHandle
&handle
= lg
->freq_handles
[lg
->handle_grabbed
];
1261 handle
.value_x
= handle
.default_value_x
;
1262 handle
.value_y
= handle
.default_value_y
;
1263 g_signal_emit_by_name(widget
, "freqhandle-changed", &handle
);
1266 if(!inside_handle
) {
1267 lg
->crosshairs_active
= !lg
->crosshairs_active
;
1270 calf_line_graph_expose_request(widget
, true);
1271 gtk_widget_grab_focus(widget
);
1272 gtk_grab_add(widget
);
1278 calf_line_graph_button_release (GtkWidget
*widget
, GdkEventButton
*event
)
1280 g_assert(CALF_IS_LINE_GRAPH(widget
));
1281 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1283 lg
->handle_grabbed
= -1;
1285 if (GTK_WIDGET_HAS_GRAB(widget
))
1286 gtk_grab_remove(widget
);
1288 calf_line_graph_expose_request(widget
, true);
1293 calf_line_graph_scroll (GtkWidget
*widget
, GdkEventScroll
*event
)
1295 g_assert(CALF_IS_LINE_GRAPH(widget
));
1296 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1298 int i
= calf_line_graph_get_handle_at(lg
, lg
->mouse_x
, lg
->mouse_y
);
1301 FreqHandle
*handle
= &lg
->freq_handles
[i
];
1302 if (handle
->dimensions
== 3) {
1303 if (event
->direction
== GDK_SCROLL_UP
) {
1304 handle
->value_z
+= 0.05;
1305 if(handle
->value_z
> 1.0) {
1306 handle
->value_z
= 1.0;
1308 g_signal_emit_by_name(widget
, "freqhandle-changed", handle
);
1309 } else if (event
->direction
== GDK_SCROLL_DOWN
) {
1310 handle
->value_z
-= 0.05;
1311 if(handle
->value_z
< 0.0) {
1312 handle
->value_z
= 0.0;
1314 g_signal_emit_by_name(widget
, "freqhandle-changed", handle
);
1316 lg
->handle_redraw
= 1;
1323 calf_line_graph_enter (GtkWidget
*widget
, GdkEventCrossing
*event
)
1325 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1327 if (lg
->debug
) printf("[enter]\n");
1332 calf_line_graph_leave (GtkWidget
*widget
, GdkEventCrossing
*event
)
1335 g_assert(CALF_IS_LINE_GRAPH(widget
));
1336 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1338 if (lg
->debug
) printf("[leave]\n");
1339 if (lg
->mouse_x
>= 0 or lg
->mouse_y
>= 0)
1340 calf_line_graph_expose_request(widget
, true);
1348 void calf_line_graph_set_square(CalfLineGraph
*graph
, bool is_square
)
1350 g_assert(CALF_IS_LINE_GRAPH(graph
));
1351 graph
->is_square
= is_square
;
1355 calf_line_graph_size_request (GtkWidget
*widget
,
1356 GtkRequisition
*requisition
)
1358 g_assert(CALF_IS_LINE_GRAPH(widget
));
1360 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1362 if (lg
->debug
) printf("size request\n");
1366 calf_line_graph_size_allocate (GtkWidget
*widget
,
1367 GtkAllocation
*allocation
)
1369 g_assert(CALF_IS_LINE_GRAPH(widget
));
1370 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1372 if (lg
->debug
) printf("size allocation\n");
1374 GtkWidgetClass
*parent_class
= (GtkWidgetClass
*) g_type_class_peek_parent( CALF_LINE_GRAPH_GET_CLASS( lg
) );
1376 // remember the allocation
1377 widget
->allocation
= *allocation
;
1379 // reset the allocation if a square widget is requested
1380 GtkAllocation
&a
= widget
->allocation
;
1383 if (a
.width
> a
.height
)
1385 a
.x
+= (a
.width
- a
.height
) / 2;
1388 if (a
.width
< a
.height
)
1390 a
.y
+= (a
.height
- a
.width
) / 2;
1395 lg
->size_x
= a
.width
- lg
->pad_x
* 2;
1396 lg
->size_y
= a
.height
- lg
->pad_y
* 2;
1398 lg
->recreate_surfaces
= 1;
1399 parent_class
->size_allocate( widget
, &a
);
1404 calf_line_graph_class_init (CalfLineGraphClass
*klass
)
1406 // GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
1407 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS(klass
);
1408 widget_class
->expose_event
= calf_line_graph_expose
;
1409 widget_class
->size_request
= calf_line_graph_size_request
;
1410 widget_class
->size_allocate
= calf_line_graph_size_allocate
;
1411 widget_class
->button_press_event
= calf_line_graph_button_press
;
1412 widget_class
->button_release_event
= calf_line_graph_button_release
;
1413 widget_class
->motion_notify_event
= calf_line_graph_pointer_motion
;
1414 widget_class
->scroll_event
= calf_line_graph_scroll
;
1415 widget_class
->enter_notify_event
= calf_line_graph_enter
;
1416 widget_class
->leave_notify_event
= calf_line_graph_leave
;
1417 gtk_widget_class_install_style_property(
1418 widget_class
, g_param_spec_float("border-radius", "Border Radius", "Generate round edges",
1419 0, 24, 4, GParamFlags(G_PARAM_READWRITE
)));
1420 gtk_widget_class_install_style_property(
1421 widget_class
, g_param_spec_float("bevel", "Bevel", "Bevel the object",
1422 -2, 2, 0.2, GParamFlags(G_PARAM_READWRITE
)));
1424 g_signal_new("freqhandle-changed",
1425 G_TYPE_OBJECT
, G_SIGNAL_RUN_FIRST
,
1427 g_cclosure_marshal_VOID__POINTER
,
1428 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
1432 calf_line_graph_unrealize (GtkWidget
*widget
, CalfLineGraph
*lg
)
1434 if (lg
->debug
) printf("unrealize\n");
1435 calf_line_graph_destroy_surfaces(widget
);
1439 calf_line_graph_init (CalfLineGraph
*lg
)
1441 GtkWidget
*widget
= GTK_WIDGET(lg
);
1443 if (lg
->debug
) printf("lg init\n");
1445 GTK_WIDGET_SET_FLAGS (widget
, GTK_CAN_FOCUS
| GTK_SENSITIVE
| GTK_PARENT_SENSITIVE
);
1446 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
);
1448 widget
->requisition
.width
= 40;
1449 widget
->requisition
.height
= 40;
1450 lg
->pad_x
= widget
->style
->xthickness
;
1451 lg
->pad_y
= widget
->style
->ythickness
;
1452 lg
->force_cache
= true;
1453 lg
->force_redraw
= false;
1455 lg
->param_zoom
= -1;
1457 lg
->param_offset
= -1;
1458 lg
->recreate_surfaces
= 1;
1462 lg
->arrow_cursor
= gdk_cursor_new(GDK_LEFT_PTR
);
1463 lg
->hand_cursor
= gdk_cursor_new(GDK_FLEUR
);
1464 lg
->layers
= LG_CACHE_GRID
| LG_CACHE_GRAPH
1465 | LG_CACHE_DOT
| LG_CACHE_MOVING
1466 | LG_REALTIME_GRID
| LG_REALTIME_GRAPH
1467 | LG_REALTIME_DOT
| LG_REALTIME_MOVING
;
1469 g_signal_connect(GTK_OBJECT(widget
), "unrealize", G_CALLBACK(calf_line_graph_unrealize
), (gpointer
)lg
);
1471 for(int i
= 0; i
< FREQ_HANDLES
; i
++) {
1472 FreqHandle
*handle
= &lg
->freq_handles
[i
];
1473 handle
->active
= false;
1474 handle
->param_active_no
= -1;
1475 handle
->param_x_no
= -1;
1476 handle
->param_y_no
= -1;
1477 handle
->value_x
= -1.0;
1478 handle
->value_y
= -1.0;
1479 handle
->param_x_no
= -1;
1480 handle
->label
= NULL
;
1481 handle
->left_bound
= 0.0 + lg
->min_handle_distance
;
1482 handle
->right_bound
= 1.0 - lg
->min_handle_distance
;
1485 lg
->handle_grabbed
= -1;
1486 lg
->handle_hovered
= -1;
1487 lg
->handle_redraw
= 1;
1488 lg
->min_handle_distance
= 0.025;
1490 lg
->background_surface
= NULL
;
1491 lg
->grid_surface
= NULL
;
1492 lg
->cache_surface
= NULL
;
1493 lg
->moving_surface
[0] = NULL
;
1494 lg
->moving_surface
[1] = NULL
;
1495 lg
->handles_surface
= NULL
;
1496 lg
->realtime_surface
= NULL
;
1498 gtk_event_box_set_visible_window(GTK_EVENT_BOX(widget
), FALSE
);
1499 //gtk_widget_set_has_window(widget, FALSE);
1503 calf_line_graph_new()
1505 return GTK_WIDGET( g_object_new (CALF_TYPE_LINE_GRAPH
, NULL
));
1509 calf_line_graph_get_type (void)
1511 static GType type
= 0;
1513 static const GTypeInfo type_info
= {
1514 sizeof(CalfLineGraphClass
),
1515 NULL
, /* base_init */
1516 NULL
, /* base_finalize */
1517 (GClassInitFunc
)calf_line_graph_class_init
,
1518 NULL
, /* class_finalize */
1519 NULL
, /* class_data */
1520 sizeof(CalfLineGraph
),
1521 0, /* n_preallocs */
1522 (GInstanceInitFunc
)calf_line_graph_init
1525 GTypeInfo
*type_info_copy
= new GTypeInfo(type_info
);
1527 for (int i
= 0; ; i
++) {
1528 //const char *name = "CalfLineGraph";
1529 char *name
= g_strdup_printf("CalfLineGraph%u%d", ((unsigned int)(intptr_t)calf_line_graph_class_init
) >> 16, i
);
1530 if (g_type_from_name(name
)) {
1534 type
= g_type_register_static( GTK_TYPE_EVENT_BOX
,