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
, float fade
= 1.f
)
602 // copy a surface to a cairo context
604 cairo_set_source_surface(ctx
, source
, 0, 0);
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
));
674 // recreate surfaces if someone needs it (init of the widget,
675 // resizing the window..)
676 if (lg
->recreate_surfaces
) {
677 if (lg
->debug
) printf("recreation...\n");
678 calf_line_graph_create_surfaces(widget
);
680 // all surfaces were recreated, so background is empty.
681 // draw the yellowish lighting on the background surface
682 cairo_t
*bg
= cairo_create(lg
->background_surface
);
683 if (lg
->debug
) printf("(draw background)\n");
684 display_background(widget
, bg
, 0, 0, lg
->size_x
, lg
->size_y
, lg
->pad_x
, lg
->pad_y
);
688 // the cache, grid and realtime surface wrapped in a cairo context
689 cairo_t
*grid_c
= cairo_create( lg
->grid_surface
);
690 cairo_t
*cache_c
= cairo_create( lg
->cache_surface
);
691 cairo_t
*realtime_c
= cairo_create( lg
->realtime_surface
);
693 if (lg
->recreate_surfaces
) {
694 // and copy it to the grid surface in case no grid is drawn
695 if (lg
->debug
) printf("copy bg->grid\n");
696 calf_line_graph_copy_surface(grid_c
, lg
->background_surface
);
698 // and copy it to the cache surface in case no cache is drawn
699 if (lg
->debug
) printf("copy bg->cache\n");
700 calf_line_graph_copy_surface(cache_c
, lg
->background_surface
);
702 // and copy it to the realtime surface in case no realtime is drawn
703 if (lg
->debug
) printf("copy bg->realtime\n");
704 calf_line_graph_copy_surface(realtime_c
, lg
->background_surface
);
706 if (lg
->recreate_surfaces
or lg
->force_redraw
) {
707 // reset generation value and request a new expose event
709 lg
->source
->get_layers(lg
->source_id
, lg
->generation
, lg
->layers
);
717 if (lg
->debug
) printf("width: %d height: %d x: %d y: %d\n", sx
, sy
, ox
, oy
);
721 dsp::print_bits(sizeof(lg
->layers
), &lg
->layers
);
725 // context used for the actual surface we want to draw on. It is
726 // switched over the drawing process via calf_line_graph_switch_context
728 cairo_t
*_ctx
= NULL
;
730 // the contexts for both moving curve caches
731 cairo_t
*moving_c
[2];
732 moving_c
[0] = cairo_create( lg
->moving_surface
[0] );
733 moving_c
[1] = cairo_create( lg
->moving_surface
[1] );
735 // the line widths to switch to between cycles
736 float grid_width
= 1.0;
737 float graph_width
= 1.5;
738 float dot_width
= 0.0;
740 // more vars we have to initialize, mainly stuff we use in callback
742 float *data
= new float[2 * std::max(lg
->size_x
, lg
->size_y
)];
744 bool vertical
= false;
750 // a cairo wrapper to hand over contexts to the plugin for setting
751 // line colors, widths aso
758 // some state variables used to determine what has happened
759 bool realtime_drawn
= false;
760 bool cache_drawn
= false;
761 bool grid_drawn
= false;
765 // check if we can skip the whole drawing stuff and go on with
766 // copying everything we drawed before
772 // 
778 or lg
->layers
& LG_CACHE_GRID
779 or lg
->layers
& LG_CACHE_GRAPH
780 or lg
->layers
& LG_CACHE_DOT
) {
781 if (lg
->debug
) printf("\n->cache\n");
783 // someone needs a redraw of the cache so start with the cache
787 // set the right context to work with
790 // and switch to grid surface in case we want to draw on it
791 if (lg
->debug
) printf("switch to grid\n");
792 ctx
= calf_line_graph_switch_context(lg
, grid_c
, &cimpl
);
794 if (lg
->debug
) printf("\n->realtime\n");
796 // no cache drawing neccessary, so skip the first drawing phase
799 // set the right context to work with
802 // and switch to the realtime surface
803 if (lg
->debug
) printf("switch to realtime\n");
804 ctx
= calf_line_graph_switch_context(lg
, realtime_c
, &cimpl
);
809 for (int phase
= drawing_phase
; phase
< 2; phase
++) {
810 // draw elements on the realtime and/or the cache surface
812 if (lg
->debug
) printf("\n### drawing phase %d\n", phase
);
814 ///////////////////////////////////////////////////////////////
816 ///////////////////////////////////////////////////////////////
818 if ((lg
->layers
& LG_CACHE_GRID
and !phase
) || (lg
->layers
& LG_REALTIME_GRID
and phase
)) {
819 // The plugin can set "vertical" to 1
820 // to force drawing of vertical lines instead of horizontal ones
821 // (which is the default)
822 // size and color of the grid (which can be set by the plugin
823 // via the context) are reset for every line.
825 // we're in cache phase and it seems we really want to
826 // draw new grid lines. so "clear" the grid surface
827 // with a pure background
828 if (lg
->debug
) printf("copy bg->grid\n");
829 calf_line_graph_copy_surface(ctx
, lg
->background_surface
);
834 cairo_set_source_rgba(ctx
, 0.15, 0.2, 0.0, 0.66),
835 cairo_set_line_width(ctx
, grid_width
),
836 lg
->source
->get_gridline(lg
->source_id
, a
, phase
, pos
, vertical
, legend
, &cimpl
);
839 if (!a
and lg
->debug
) printf("(draw grid)\n");
840 calf_line_graph_draw_grid( lg
, ctx
, legend
, vertical
, pos
);
844 // we're in cache phase so we have to switch back to
845 // the cache surface after drawing the grid on its surface
846 if (lg
->debug
) printf("switch to cache\n");
847 ctx
= calf_line_graph_switch_context(lg
, _ctx
, &cimpl
);
848 // if a grid was drawn copy it to cache
850 if (lg
->debug
) printf("copy grid->cache\n");
851 calf_line_graph_copy_surface(ctx
, lg
->grid_surface
);
856 ///////////////////////////////////////////////////////////////
858 ///////////////////////////////////////////////////////////////
860 if ((lg
->layers
& LG_CACHE_GRAPH
and !phase
) || (lg
->layers
& LG_REALTIME_GRAPH
and phase
)) {
861 // Cycle through all graphs and hand over the amount of horizontal
862 // pixels. The plugin is expected to set all corresponding vertical
863 // values in an array.
864 // size and color of the graph (which can be set by the plugin
865 // via the context) are reset for every graph.
868 // we are drawing the first graph in cache phase, so
869 // prepare the cache surface with the grid surface
870 if (lg
->debug
) printf("copy grid->cache\n");
871 calf_line_graph_copy_surface(ctx
, lg
->grid_surface
);
873 } else if (!realtime_drawn
) {
874 // we're in realtime phase and the realtime surface wasn't
875 // reset to cache by now (because there was no cache
876 // phase and no realtime grid was drawn)
877 // so "clear" the realtime surface with the cache
878 if (lg
->debug
) printf("copy cache->realtime\n");
879 calf_line_graph_copy_surface(ctx
, lg
->cache_surface
, lg
->force_cache
? 1 : lg
->fade
);
880 realtime_drawn
= true;
885 cairo_set_source_rgba(ctx
, 0.15, 0.2, 0.0, 0.8),
886 cairo_set_line_width(ctx
, graph_width
),
887 lg
->source
->get_graph(lg
->source_id
, a
, phase
, data
, lg
->size_x
, &cimpl
, &lg
->mode
);
890 if (lg
->debug
) printf("graph %d\n", a
);
891 calf_line_graph_draw_graph( lg
, ctx
, data
, lg
->mode
);
895 ///////////////////////////////////////////////////////////////
897 ///////////////////////////////////////////////////////////////
899 if ((lg
->layers
& LG_CACHE_MOVING
and !phase
) || (lg
->layers
& LG_REALTIME_MOVING
and phase
)) {
900 // we have a moving curve. switch to moving surface and
901 // clear it before we start to draw
902 if (lg
->debug
) printf("switch to moving %d\n", lg
->movesurf
);
903 ctx
= calf_line_graph_switch_context(lg
, moving_c
[lg
->movesurf
], &cimpl
);
904 calf_line_graph_clear_surface(ctx
);
906 if (!phase
and !cache_drawn
) {
907 // we are drawing the first moving in cache phase and
908 // no cache has been created by now, so
909 // prepare the cache surface with the grid surface
910 if (lg
->debug
) printf("copy grid->cache\n");
911 calf_line_graph_copy_surface(cache_c
, lg
->grid_surface
);
913 } else if (phase
and !realtime_drawn
) {
914 // we're in realtime phase and the realtime surface wasn't
915 // reset to cache by now (because there was no cache
916 // phase and no realtime grid was drawn)
917 // so "clear" the realtime surface with the cache
918 if (lg
->debug
) printf("copy cache->realtime\n");
919 calf_line_graph_copy_surface(realtime_c
, lg
->cache_surface
);
920 realtime_drawn
= true;
929 color
= RGBAtoINT(0.35, 0.4, 0.2, 1),
930 lg
->source
->get_moving(lg
->source_id
, a
, direction
, data
, lg
->size_x
, lg
->size_y
, offset
, color
);
933 if (lg
->debug
) printf("moving %d\n", a
);
934 calf_line_graph_draw_moving(lg
, ctx
, data
, direction
, offset
, color
);
938 // set moving distances according to direction
947 case LG_MOVING_RIGHT
:
960 // copy the old moving surface to the right position on the
962 if (lg
->debug
) printf("copy cached moving->moving\n");
963 cairo_set_source_surface(ctx
, lg
->moving_surface
[(int)!lg
->movesurf
], x
, y
);
966 // switch back to the actual context
967 if (lg
->debug
) printf("switch to realtime/cache\n");
968 ctx
= calf_line_graph_switch_context(lg
, _ctx
, &cimpl
);
970 if (lg
->debug
) printf("copy moving->realtime/cache\n");
971 calf_line_graph_copy_surface(ctx
, lg
->moving_surface
[lg
->movesurf
], 1);
973 // toggle the moving cache
974 lg
->movesurf
= (int)!lg
->movesurf
;
977 ///////////////////////////////////////////////////////////////
979 ///////////////////////////////////////////////////////////////
981 if ((lg
->layers
& LG_CACHE_DOT
and !phase
) || (lg
->layers
& LG_REALTIME_DOT
and phase
)) {
982 // Cycle through all dots. The plugin is expected to set the x
983 // and y value of the dot.
984 // color of the dot (which can be set by the plugin
985 // via the context) is reset for every graph.
987 if (!cache_drawn
and !phase
) {
988 // we are drawing dots in cache phase while
989 // the cache wasn't renewed (no graph was drawn), so
990 // prepare the cache surface with the grid surface
991 if (lg
->debug
) printf("copy grid->cache\n");
992 calf_line_graph_copy_surface(ctx
, lg
->grid_surface
);
995 if (!realtime_drawn
and phase
) {
996 // we're in realtime phase and the realtime surface wasn't
997 // reset to cache by now (because there was no cache
998 // phase and no realtime grid or graph was drawn)
999 // so "clear" the realtime surface with the cache
1000 if (lg
->debug
) printf("copy cache->realtime\n");
1001 calf_line_graph_copy_surface(ctx
, lg
->cache_surface
, lg
->force_cache
? 1 : lg
->fade
);
1002 realtime_drawn
= true;
1005 cairo_set_source_rgba(ctx
, 0.15, 0.2, 0.0, 1),
1006 cairo_set_line_width(ctx
, dot_width
),
1007 lg
->source
->get_dot(lg
->source_id
, a
, phase
, x
, y
, size
= 3, &cimpl
);
1010 if (lg
->debug
) printf("dot %d\n", a
);
1011 float yv
= oy
+ sy
/ 2 - (sy
/ 2 - 1) * y
;
1012 cairo_arc(ctx
, ox
+ x
* sx
, yv
, size
, 0, 2 * M_PI
);
1018 // if we have a second cycle for drawing on the realtime
1019 // after the cache was renewed it's time to copy the
1020 // cache to the realtime and switch the target surface
1021 if (lg
->debug
) printf("switch to realtime\n");
1022 ctx
= calf_line_graph_switch_context(lg
, realtime_c
, &cimpl
);
1026 // copy the cache to the realtime if it was changed
1027 if (lg
->debug
) printf("copy cache->realtime\n");
1028 calf_line_graph_copy_surface(ctx
, lg
->cache_surface
, lg
->force_cache
? 1 : lg
->fade
);
1029 realtime_drawn
= true;
1030 } else if (grid_drawn
) {
1031 // copy the grid to the realtime if it was changed
1032 if (lg
->debug
) printf("copy grid->realtime\n");
1033 calf_line_graph_copy_surface(ctx
, lg
->grid_surface
, lg
->force_cache
? 1 : lg
->fade
);
1034 realtime_drawn
= true;
1037 // check if we can skip the whole realtime phase
1038 if (!(lg
->layers
& LG_REALTIME_GRID
)
1039 and !(lg
->layers
& LG_REALTIME_GRAPH
)
1040 and !(lg
->layers
& LG_REALTIME_DOT
)) {
1044 } // one or two cycles for drawing cached and non-cached elements
1047 // îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚îš‚
1052 if (lg
->debug
) printf("\n### finalize\n");
1054 // whatever happened - we need to copy the realtime surface to the
1056 //if (lg->debug) printf("switch to window\n");
1057 //ctx = calf_line_graph_switch_context(lg, c, &cimpl);
1058 if (lg
->debug
) printf("copy realtime->window\n");
1059 calf_line_graph_copy_surface(c
, lg
->realtime_surface
);
1061 // if someone changed the handles via drag'n'drop or externally we
1062 // need a redraw of the handles surface
1063 if (lg
->freqhandles
and (lg
->handle_redraw
or lg
->force_redraw
)) {
1064 cairo_t
*hs
= cairo_create(lg
->handles_surface
);
1065 calf_line_graph_clear_surface(hs
);
1066 calf_line_graph_draw_freqhandles(lg
, hs
);
1070 // if we're using frequency handles we need to copy them to the
1072 if (lg
->freqhandles
) {
1073 if (lg
->debug
) printf("copy handles->window\n");
1074 calf_line_graph_copy_surface(c
, lg
->handles_surface
);
1077 // and draw the crosshairs on top if neccessary
1078 if (lg
->use_crosshairs
&& lg
->crosshairs_active
&& lg
->mouse_x
> 0
1079 && lg
->mouse_y
> 0 && lg
->handle_grabbed
< 0) {
1081 s
= lg
->source
->get_crosshair_label((int)(lg
->mouse_x
- ox
), (int)(lg
->mouse_y
- oy
), sx
, sy
, 1, 1, 1, 1);
1082 cairo_set_line_width(c
, 1),
1083 calf_line_graph_draw_crosshairs(lg
, c
, false, 0, 0.5, 5, false, lg
->mouse_x
- ox
, lg
->mouse_y
- oy
, s
, 1.5);
1086 lg
->force_cache
= false;
1087 lg
->force_redraw
= false;
1088 lg
->handle_redraw
= 0;
1089 lg
->recreate_surfaces
= 0;
1092 // destroy all temporarily created cairo contexts
1094 cairo_destroy(realtime_c
);
1095 cairo_destroy(grid_c
);
1096 cairo_destroy(cache_c
);
1097 cairo_destroy(moving_c
[0]);
1098 cairo_destroy(moving_c
[1]);
1100 lg
->generation
+= 1;
1106 calf_line_graph_get_handle_at(CalfLineGraph
*lg
, double x
, double y
)
1108 int sx
= lg
->size_x
;
1109 int sy
= lg
->size_y
;
1116 // loop on all handles
1117 for (int i
= 0; i
< lg
->freqhandles
; i
++) {
1118 FreqHandle
*handle
= &lg
->freq_handles
[i
];
1119 if (!handle
->is_active())
1122 if (handle
->dimensions
== 1) {
1123 // if user clicked inside a vertical band with width HANDLE_WIDTH handle is considered grabbed
1124 if (lg
->mouse_x
<= ox
+ round(handle
->value_x
* sx
+ HANDLE_WIDTH
/ 2.0) + 0.5 &&
1125 lg
->mouse_x
>= ox
+ round(handle
->value_x
* sx
- HANDLE_WIDTH
/ 2.0) - 0.5 ) {
1128 } else if (handle
->dimensions
>= 2) {
1129 double dx
= lg
->mouse_x
- round(ox
+ handle
->value_x
* sx
);
1130 double dy
= lg
->mouse_y
- round(oy
+ handle
->value_y
* sy
);
1132 // if mouse clicked inside circle of HANDLE_WIDTH
1133 if (sqrt(dx
* dx
+ dy
* dy
) <= HANDLE_WIDTH
/ 2.0)
1141 calf_line_graph_pointer_motion (GtkWidget
*widget
, GdkEventMotion
*event
)
1143 g_assert(CALF_IS_LINE_GRAPH(widget
));
1144 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1146 int sx
= lg
->size_x
;
1147 int sy
= lg
->size_y
;
1154 lg
->mouse_x
= event
->x
;
1155 lg
->mouse_y
= event
->y
;
1157 if (lg
->handle_grabbed
>= 0) {
1158 FreqHandle
*handle
= &lg
->freq_handles
[lg
->handle_grabbed
];
1160 float new_x_value
= float(event
->x
- ox
) / float(sx
);
1161 float new_y_value
= float(event
->y
- oy
) / float(sy
);
1163 if (new_x_value
< handle
->left_bound
) {
1164 new_x_value
= handle
->left_bound
;
1165 } else if (new_x_value
> handle
->right_bound
) {
1166 new_x_value
= handle
->right_bound
;
1169 // restrict y range by top and bottom
1170 if (handle
->dimensions
>= 2) {
1171 if(new_y_value
< 0.0) new_y_value
= 0.0;
1172 if(new_y_value
> 1.0) new_y_value
= 1.0;
1175 if (new_x_value
!= handle
->value_x
||
1176 new_y_value
!= handle
->value_y
) {
1177 handle
->value_x
= new_x_value
;
1178 handle
->value_y
= new_y_value
;
1180 g_signal_emit_by_name(widget
, "freqhandle-changed", handle
);
1182 lg
->handle_redraw
= 1;
1183 calf_line_graph_expose_request(widget
, true);
1187 gdk_event_request_motions(event
);
1190 int handle_hovered
= calf_line_graph_get_handle_at(lg
, event
->x
, event
->y
);
1191 if (handle_hovered
!= lg
->handle_hovered
) {
1192 if (lg
->handle_grabbed
>= 0 ||
1193 handle_hovered
!= -1) {
1194 gdk_window_set_cursor(widget
->window
, lg
->hand_cursor
);
1195 lg
->handle_hovered
= handle_hovered
;
1197 gdk_window_set_cursor(widget
->window
, lg
->arrow_cursor
);
1198 lg
->handle_hovered
= -1;
1200 lg
->handle_redraw
= 1;
1201 calf_line_graph_expose_request(widget
, true);
1203 if(lg
->crosshairs_active
and lg
->use_crosshairs
) {
1204 calf_line_graph_expose_request(widget
, true);
1210 calf_line_graph_button_press (GtkWidget
*widget
, GdkEventButton
*event
)
1212 g_assert(CALF_IS_LINE_GRAPH(widget
));
1213 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1214 bool inside_handle
= false;
1216 int i
= calf_line_graph_get_handle_at(lg
, lg
->mouse_x
, lg
->mouse_y
);
1219 FreqHandle
*handle
= &lg
->freq_handles
[i
];
1221 if (handle
->dimensions
== 1) {
1222 // if user clicked inside a vertical band with width HANDLE_WIDTH handle is considered grabbed
1223 lg
->handle_grabbed
= i
;
1224 inside_handle
= true;
1226 if (lg
->enforce_handle_order
) {
1227 // look for previous one dimensional handle to find left_bound
1228 for (int j
= i
- 1; j
>= 0; j
--) {
1229 FreqHandle
*prevhandle
= &lg
->freq_handles
[j
];
1230 if(prevhandle
->is_active() && prevhandle
->dimensions
== 1) {
1231 handle
->left_bound
= prevhandle
->value_x
+ lg
->min_handle_distance
;
1236 // look for next one dimensional handle to find right_bound
1237 for (int j
= i
+ 1; j
< lg
->freqhandles
; j
++) {
1238 FreqHandle
*nexthandle
= &lg
->freq_handles
[j
];
1239 if(nexthandle
->is_active() && nexthandle
->dimensions
== 1) {
1240 handle
->right_bound
= nexthandle
->value_x
- lg
->min_handle_distance
;
1245 } else if (handle
->dimensions
>= 2) {
1246 lg
->handle_grabbed
= i
;
1247 inside_handle
= true;
1251 if (inside_handle
&& event
->type
== GDK_2BUTTON_PRESS
) {
1252 FreqHandle
&handle
= lg
->freq_handles
[lg
->handle_grabbed
];
1253 handle
.value_x
= handle
.default_value_x
;
1254 handle
.value_y
= handle
.default_value_y
;
1255 g_signal_emit_by_name(widget
, "freqhandle-changed", &handle
);
1258 if(!inside_handle
) {
1259 lg
->crosshairs_active
= !lg
->crosshairs_active
;
1262 calf_line_graph_expose_request(widget
, true);
1263 gtk_widget_grab_focus(widget
);
1264 gtk_grab_add(widget
);
1270 calf_line_graph_button_release (GtkWidget
*widget
, GdkEventButton
*event
)
1272 g_assert(CALF_IS_LINE_GRAPH(widget
));
1273 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1275 lg
->handle_grabbed
= -1;
1277 if (GTK_WIDGET_HAS_GRAB(widget
))
1278 gtk_grab_remove(widget
);
1280 calf_line_graph_expose_request(widget
, true);
1285 calf_line_graph_scroll (GtkWidget
*widget
, GdkEventScroll
*event
)
1287 g_assert(CALF_IS_LINE_GRAPH(widget
));
1288 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1290 int i
= calf_line_graph_get_handle_at(lg
, lg
->mouse_x
, lg
->mouse_y
);
1293 FreqHandle
*handle
= &lg
->freq_handles
[i
];
1294 if (handle
->dimensions
== 3) {
1295 if (event
->direction
== GDK_SCROLL_UP
) {
1296 handle
->value_z
+= 0.05;
1297 if(handle
->value_z
> 1.0) {
1298 handle
->value_z
= 1.0;
1300 g_signal_emit_by_name(widget
, "freqhandle-changed", handle
);
1301 } else if (event
->direction
== GDK_SCROLL_DOWN
) {
1302 handle
->value_z
-= 0.05;
1303 if(handle
->value_z
< 0.0) {
1304 handle
->value_z
= 0.0;
1306 g_signal_emit_by_name(widget
, "freqhandle-changed", handle
);
1308 lg
->handle_redraw
= 1;
1315 calf_line_graph_enter (GtkWidget
*widget
, GdkEventCrossing
*event
)
1317 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1319 if (lg
->debug
) printf("[enter]\n");
1324 calf_line_graph_leave (GtkWidget
*widget
, GdkEventCrossing
*event
)
1327 g_assert(CALF_IS_LINE_GRAPH(widget
));
1328 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1330 if (lg
->debug
) printf("[leave]\n");
1331 if (lg
->mouse_x
>= 0 or lg
->mouse_y
>= 0)
1332 calf_line_graph_expose_request(widget
, true);
1340 void calf_line_graph_set_square(CalfLineGraph
*graph
, bool is_square
)
1342 g_assert(CALF_IS_LINE_GRAPH(graph
));
1343 graph
->is_square
= is_square
;
1347 calf_line_graph_size_request (GtkWidget
*widget
,
1348 GtkRequisition
*requisition
)
1350 g_assert(CALF_IS_LINE_GRAPH(widget
));
1352 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1354 if (lg
->debug
) printf("size request\n");
1358 calf_line_graph_size_allocate (GtkWidget
*widget
,
1359 GtkAllocation
*allocation
)
1361 g_assert(CALF_IS_LINE_GRAPH(widget
));
1362 CalfLineGraph
*lg
= CALF_LINE_GRAPH(widget
);
1364 if (lg
->debug
) printf("size allocation\n");
1366 GtkWidgetClass
*parent_class
= (GtkWidgetClass
*) g_type_class_peek_parent( CALF_LINE_GRAPH_GET_CLASS( lg
) );
1368 // remember the allocation
1369 widget
->allocation
= *allocation
;
1371 // reset the allocation if a square widget is requested
1372 GtkAllocation
&a
= widget
->allocation
;
1375 if (a
.width
> a
.height
)
1377 a
.x
+= (a
.width
- a
.height
) / 2;
1380 if (a
.width
< a
.height
)
1382 a
.y
+= (a
.height
- a
.width
) / 2;
1387 lg
->size_x
= a
.width
- lg
->pad_x
* 2;
1388 lg
->size_y
= a
.height
- lg
->pad_y
* 2;
1390 lg
->recreate_surfaces
= 1;
1391 parent_class
->size_allocate( widget
, &a
);
1396 calf_line_graph_class_init (CalfLineGraphClass
*klass
)
1398 // GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
1399 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS(klass
);
1400 widget_class
->expose_event
= calf_line_graph_expose
;
1401 widget_class
->size_request
= calf_line_graph_size_request
;
1402 widget_class
->size_allocate
= calf_line_graph_size_allocate
;
1403 widget_class
->button_press_event
= calf_line_graph_button_press
;
1404 widget_class
->button_release_event
= calf_line_graph_button_release
;
1405 widget_class
->motion_notify_event
= calf_line_graph_pointer_motion
;
1406 widget_class
->scroll_event
= calf_line_graph_scroll
;
1407 widget_class
->enter_notify_event
= calf_line_graph_enter
;
1408 widget_class
->leave_notify_event
= calf_line_graph_leave
;
1410 g_signal_new("freqhandle-changed",
1411 G_TYPE_OBJECT
, G_SIGNAL_RUN_FIRST
,
1413 g_cclosure_marshal_VOID__POINTER
,
1414 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
1418 calf_line_graph_unrealize (GtkWidget
*widget
, CalfLineGraph
*lg
)
1420 if (lg
->debug
) printf("unrealize\n");
1421 calf_line_graph_destroy_surfaces(widget
);
1425 calf_line_graph_init (CalfLineGraph
*lg
)
1427 GtkWidget
*widget
= GTK_WIDGET(lg
);
1429 if (lg
->debug
) printf("lg init\n");
1431 GTK_WIDGET_SET_FLAGS (widget
, GTK_CAN_FOCUS
| GTK_SENSITIVE
| GTK_PARENT_SENSITIVE
);
1432 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
);
1434 widget
->requisition
.width
= 40;
1435 widget
->requisition
.height
= 40;
1436 lg
->force_cache
= true;
1437 lg
->force_redraw
= false;
1439 lg
->param_zoom
= -1;
1441 lg
->param_offset
= -1;
1442 lg
->recreate_surfaces
= 1;
1446 lg
->arrow_cursor
= gdk_cursor_new(GDK_LEFT_PTR
);
1447 lg
->hand_cursor
= gdk_cursor_new(GDK_FLEUR
);
1448 lg
->layers
= LG_CACHE_GRID
| LG_CACHE_GRAPH
1449 | LG_CACHE_DOT
| LG_CACHE_MOVING
1450 | LG_REALTIME_GRID
| LG_REALTIME_GRAPH
1451 | LG_REALTIME_DOT
| LG_REALTIME_MOVING
;
1453 g_signal_connect(GTK_OBJECT(widget
), "unrealize", G_CALLBACK(calf_line_graph_unrealize
), (gpointer
)lg
);
1455 for(int i
= 0; i
< FREQ_HANDLES
; i
++) {
1456 FreqHandle
*handle
= &lg
->freq_handles
[i
];
1457 handle
->active
= false;
1458 handle
->param_active_no
= -1;
1459 handle
->param_x_no
= -1;
1460 handle
->param_y_no
= -1;
1461 handle
->value_x
= -1.0;
1462 handle
->value_y
= -1.0;
1463 handle
->param_x_no
= -1;
1464 handle
->label
= NULL
;
1465 handle
->left_bound
= 0.0 + lg
->min_handle_distance
;
1466 handle
->right_bound
= 1.0 - lg
->min_handle_distance
;
1469 lg
->handle_grabbed
= -1;
1470 lg
->handle_hovered
= -1;
1471 lg
->handle_redraw
= 1;
1472 lg
->min_handle_distance
= 0.025;
1474 lg
->background_surface
= NULL
;
1475 lg
->grid_surface
= NULL
;
1476 lg
->cache_surface
= NULL
;
1477 lg
->moving_surface
[0] = NULL
;
1478 lg
->moving_surface
[1] = NULL
;
1479 lg
->handles_surface
= NULL
;
1480 lg
->realtime_surface
= NULL
;
1484 calf_line_graph_new()
1486 return GTK_WIDGET( g_object_new (CALF_TYPE_LINE_GRAPH
, NULL
));
1490 calf_line_graph_get_type (void)
1492 static GType type
= 0;
1494 static const GTypeInfo type_info
= {
1495 sizeof(CalfLineGraphClass
),
1496 NULL
, /* base_init */
1497 NULL
, /* base_finalize */
1498 (GClassInitFunc
)calf_line_graph_class_init
,
1499 NULL
, /* class_finalize */
1500 NULL
, /* class_data */
1501 sizeof(CalfLineGraph
),
1502 0, /* n_preallocs */
1503 (GInstanceInitFunc
)calf_line_graph_init
1506 GTypeInfo
*type_info_copy
= new GTypeInfo(type_info
);
1508 for (int i
= 0; ; i
++) {
1509 char *name
= g_strdup_printf("CalfLineGraph%u%d", ((unsigned int)(intptr_t)calf_line_graph_class_init
) >> 16, i
);
1510 if (g_type_from_name(name
)) {
1514 type
= g_type_register_static( GTK_TYPE_DRAWING_AREA
,