3 * sushivision copyright (C) 2006-2007 Monty <monty@xiph.org>
5 * sushivision is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * sushivision is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with sushivision; see the file COPYING. If not, write to the
17 * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include <gtk/gtkmain.h>
26 #include <gdk/gdkkeysyms.h>
35 static GtkWidgetClass
*parent_class
= NULL
;
37 // When sufficienty zoomed in that the demarks of the data scale are a
38 // coarser mesh than the resolution of the data scale (eg, when the
39 // data scale is undersampled or discrete), we want the demarks from
40 // the data scale (plotted in terms of the panel scale). In most
41 // cases they will be the same, but not always.
42 static double scale_demark(_sv_scalespace_t
*panel
, _sv_scalespace_t
*data
, int i
, char *buffer
){
43 if(data
->expm
> panel
->expm
){
44 double x
=_sv_scalespace_mark(data
,i
);
45 if(buffer
) _sv_scalespace_label(data
,i
,buffer
);
46 return _sv_scalespace_pixel(panel
, _sv_scalespace_value(data
, x
));
48 if(buffer
) _sv_scalespace_label(panel
,i
,buffer
);
49 return _sv_scalespace_mark(panel
,i
);
53 void _sv_plot_set_bg_invert(_sv_plot_t
*p
, int setp
){
57 void _sv_plot_set_grid(_sv_plot_t
*p
, int mode
){
61 static void set_text(int inv
, cairo_t
*c
){
62 if(inv
== _SV_PLOT_TEXT_LIGHT
)
63 cairo_set_source_rgba(c
,1.,1.,1.,1.);
65 cairo_set_source_rgba(c
,0.,0.,0.,1.);
68 static void set_shadow(int inv
, cairo_t
*c
){
69 if(inv
== _SV_PLOT_TEXT_LIGHT
)
70 cairo_set_source_rgba(c
,0.,0.,0.,.6);
72 cairo_set_source_rgba(c
,1.,1.,1.,.8);
75 static void draw_scales_work(cairo_t
*c
, int w
, int h
,
77 int inv_text
, int grid
,
78 _sv_scalespace_t xs
, _sv_scalespace_t ys
,
79 _sv_scalespace_t xs_v
, _sv_scalespace_t ys_v
){
85 int off
= (grid
== _SV_PLOT_GRID_TICS
?6:0);
87 cairo_set_miter_limit(c
,2.);
89 // draw all axis lines, then stroke
92 cairo_set_line_width(c
,1.);
93 if(grid
& _SV_PLOT_GRID_NORMAL
){
96 cairo_set_source_rgba(c
,.9,.9,.9,.4);
99 cairo_set_source_rgba(c
,.1,.1,.1,.4);
102 cairo_set_source_rgba(c
,.7,.7,.7,.3);
107 x
= scale_demark(&xs
, &xs_v
, i
++, NULL
);
109 cairo_move_to(c
,x
+.5,0);
110 cairo_line_to(c
,x
+.5,h
);
111 x
= scale_demark(&xs
, &xs_v
, i
++, NULL
);
115 y
= scale_demark(&ys
, &ys_v
, i
++, NULL
);
117 cairo_move_to(c
,0,y
+.5);
118 cairo_line_to(c
,w
,y
+.5);
119 y
= scale_demark(&ys
, &ys_v
, i
++, NULL
);
126 // text number labels
127 cairo_select_font_face (c
, "Sans",
128 CAIRO_FONT_SLANT_NORMAL
,
129 CAIRO_FONT_WEIGHT_NORMAL
);
130 cairo_set_font_size (c
, 10);
131 cairo_set_line_width(c
,2);
134 y
= scale_demark(&ys
, &ys_v
, i
++, buffer
);
137 cairo_text_extents_t extents
;
138 cairo_text_extents (c
, buffer
, &extents
);
140 if(extents
.width
> y_width
) y_width
= extents
.width
;
142 if(y
- extents
.height
> 0){
144 double yy
= y
+.5-(extents
.height
/2 + extents
.y_bearing
);
146 cairo_move_to(c
,2+off
, yy
);
147 set_shadow(inv_text
,c
);
148 cairo_text_path (c
, buffer
);
151 set_text(inv_text
,c
);
152 cairo_move_to(c
,2+off
, yy
);
153 cairo_show_text (c
, buffer
);
156 y
= scale_demark(&ys
, &ys_v
, i
++, buffer
);
163 cairo_get_matrix(c
,&a
);
164 cairo_matrix_t b
= {0.,-1., 1.,0., 0.,page_h
+a
.y0
+a
.y0
}; // account for border!
166 cairo_matrix_multiply(&d
,&a
,&b
);
167 cairo_set_matrix(c
,&d
);
169 // text y scale label
171 cairo_text_extents_t extents
;
172 cairo_text_extents (c
, ys
.legend
, &extents
);
174 cairo_move_to(c
,h
/2 - (extents
.width
/2 +extents
.x_bearing
), y_width
-extents
.y_bearing
+5+off
);
175 set_shadow(inv_text
,c
);
176 cairo_text_path (c
, ys
.legend
);
179 cairo_move_to(c
,h
/2 - (extents
.width
/2 +extents
.x_bearing
), y_width
-extents
.y_bearing
+5+off
);
180 set_text(inv_text
,c
);
181 cairo_show_text (c
, ys
.legend
);
186 x
= scale_demark(&xs
, &xs_v
, i
++, buffer
);
189 cairo_text_extents_t extents
;
190 cairo_text_extents (c
, buffer
, &extents
);
192 if(extents
.width
> x_height
) x_height
= extents
.width
;
194 if(x
- extents
.height
> y_width
+5 ){
196 cairo_move_to(c
,2+off
-extents
.x_bearing
, x
+.5-(extents
.height
/2 + extents
.y_bearing
));
197 set_shadow(inv_text
,c
);
198 cairo_text_path (c
, buffer
);
201 cairo_move_to(c
,2+off
-extents
.x_bearing
, x
+.5-(extents
.height
/2 + extents
.y_bearing
));
202 set_text(inv_text
,c
);
203 cairo_show_text (c
, buffer
);
206 x
= scale_demark(&xs
, &xs_v
, i
++, buffer
);
211 // text x scale label
213 cairo_text_extents_t extents
;
214 cairo_text_extents (c
, xs
.legend
, &extents
);
216 cairo_move_to(c
,w
/2 - (extents
.width
/2 + extents
.x_bearing
), h
- x_height
+ extents
.y_bearing
-3-off
);
217 set_shadow(inv_text
,c
);
218 cairo_text_path (c
, xs
.legend
);
221 cairo_move_to(c
,w
/2 - (extents
.width
/2 + extents
.x_bearing
), h
- x_height
+ extents
.y_bearing
-3-off
);
222 set_text(inv_text
,c
);
223 cairo_show_text (c
, xs
.legend
);
226 if(grid
== _SV_PLOT_GRID_TICS
){
227 cairo_set_line_width(c
,1.);
228 set_text(inv_text
,c
);
231 x
= scale_demark(&xs
, &xs_v
, i
++, NULL
);
234 cairo_move_to(c
,x
+.5,0);
235 cairo_line_to(c
,x
+.5,off
);
236 cairo_move_to(c
,x
+.5,h
-off
);
237 cairo_line_to(c
,x
+.5,h
);
239 x
= scale_demark(&xs
, &xs_v
, i
++, NULL
);
243 y
= scale_demark(&ys
, &ys_v
, i
++, NULL
);
245 cairo_move_to(c
,0,y
+.5);
246 cairo_line_to(c
,off
,y
+.5);
247 cairo_move_to(c
,w
-off
,y
+.5);
248 cairo_line_to(c
,w
,y
+.5);
249 y
= scale_demark(&ys
, &ys_v
, i
++, NULL
);
256 static void draw_legend_work(_sv_plot_t
*p
, cairo_t
*c
, int w
){
257 if(p
->legend_entries
&& p
->legend_list
&& p
->legend_active
){
259 int textw
=0, texth
=0;
262 cairo_text_extents_t extents
;
266 int n
= p
->legend_entries
;
270 buffer
[i
] = strdup(p
->legend_list
[i
]);
271 colors
[i
] = p
->legend_colors
[i
];
275 cairo_select_font_face (c
, "Sans",
276 CAIRO_FONT_SLANT_NORMAL
,
277 CAIRO_FONT_WEIGHT_NORMAL
);
278 cairo_set_font_size (c
, 10);
279 cairo_set_line_width(c
,1);
280 cairo_set_miter_limit(c
,2.);
282 /* determine complete x/y extents of text */
285 cairo_text_extents (c
, buffer
[i
], &extents
);
286 if(texth
< extents
.height
)
287 texth
= extents
.height
;
288 if(textw
< extents
.width
)
289 textw
= extents
.width
;
293 texth
= ceil(texth
* 1.2+3);
298 // draw the enclosing rectangle
299 if(p
->legend_active
==2){
300 cairo_rectangle(c
,x
-6.5,5.5,textw
+15,totalh
+15);
302 cairo_fill_preserve(c
);
308 cairo_text_extents (c
, buffer
[i
], &extents
);
309 x
= w
- extents
.width
- 15;
311 if(p
->legend_active
==1){
312 cairo_move_to(c
,x
, y
);
313 cairo_text_path (c
, buffer
[i
]);
315 cairo_set_line_width(c
,3);
319 if(colors
[i
] == 0xffffffffUL
){
322 cairo_set_source_rgba(c
,
323 ((colors
[i
]>>16)&0xff)/255.,
324 ((colors
[i
]>>8)&0xff)/255.,
325 ((colors
[i
])&0xff)/255.,
326 ((colors
[i
]>>24)&0xff)/255.);
328 cairo_move_to(c
,x
, y
);
329 cairo_show_text (c
, buffer
[i
]);
340 void _sv_plot_draw_scales(_sv_plot_t
*p
){
341 // render into a temporary surface; do it [potentially] outside the global Gtk lock.
343 _sv_scalespace_t x
= p
->x
;
344 _sv_scalespace_t y
= p
->y
;
345 _sv_scalespace_t xv
= p
->x_v
;
346 _sv_scalespace_t yv
= p
->y_v
;
347 int w
= GTK_WIDGET(p
)->allocation
.width
;
348 int h
= GTK_WIDGET(p
)->allocation
.height
;
349 cairo_surface_t
*s
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32
,w
,h
);
351 int grid
= p
->grid_mode
;
354 cairo_t
*c
= cairo_create(s
);
356 cairo_set_operator(c
,CAIRO_OPERATOR_CLEAR
);
357 cairo_set_source_rgba (c
, 1,1,1,1);
361 draw_scales_work(c
,w
,h
,h
,inv
,grid
,x
,y
,xv
,yv
);
362 draw_legend_work(p
,c
,w
);
367 cairo_surface_t
*temp
= p
->fore
;
369 cairo_surface_destroy(temp
);
370 //_sv_plot_expose_request(p);
374 static void _sv_plot_init (_sv_plot_t
*p
){
375 // instance initialization
376 p
->scalespacing
= 50;
377 p
->x_v
= p
->x
= _sv_scalespace_linear(0.0,1.0,400,p
->scalespacing
,NULL
);
378 p
->y_v
= p
->y
= _sv_scalespace_linear(0.0,1.0,200,p
->scalespacing
,NULL
);
381 static void _sv_plot_destroy (GtkObject
*object
){
382 if (GTK_OBJECT_CLASS (parent_class
)->destroy
)
383 (*GTK_OBJECT_CLASS (parent_class
)->destroy
) (object
);
385 GtkWidget
*widget
= GTK_WIDGET(object
);
386 _sv_plot_t
*p
= PLOT (widget
);
387 // free local resources
389 cairo_destroy(p
->wc
);
393 cairo_surface_destroy(p
->back
);
397 cairo_surface_destroy(p
->fore
);
401 cairo_surface_destroy(p
->stage
);
407 static void box_corners(_sv_plot_t
*p
, double vals
[4]){
408 GtkWidget
*widget
= GTK_WIDGET(p
);
409 double x1
= _sv_scalespace_pixel(&p
->x
,p
->box_x1
);
410 double x2
= _sv_scalespace_pixel(&p
->x
,p
->box_x2
);
411 double y1
= _sv_scalespace_pixel(&p
->y
,p
->box_y1
);
412 double y2
= _sv_scalespace_pixel(&p
->y
,p
->box_y2
);
414 vals
[0] = (x1
<x2
? x1
: x2
);
415 vals
[1] = (y1
<y2
? y1
: y2
);
416 vals
[2] = fabs(x1
-x2
);
417 vals
[3] = fabs(y1
-y2
);
419 if(p
->flags
& _SV_PLOT_NO_X_CROSS
){
421 vals
[2]=widget
->allocation
.width
+2;
424 if(p
->flags
& _SV_PLOT_NO_Y_CROSS
){
426 vals
[3]=widget
->allocation
.height
+2;
431 static int inside_box(_sv_plot_t
*p
, int x
, int y
){
435 return (x
>= vals
[0] &&
436 x
<= vals
[0]+vals
[2] &&
438 y
<= vals
[1]+vals
[3]);
441 int _sv_plot_print(_sv_plot_t
*p
, cairo_t
*c
, double page_h
, void (*datarender
)(void *, cairo_t
*), void *data
){
442 GtkWidget
*widget
= GTK_WIDGET(p
);
443 int pw
= widget
->allocation
.width
;
444 int ph
= widget
->allocation
.height
;
445 _sv_scalespace_t x
= p
->x
;
446 _sv_scalespace_t y
= p
->y
;
447 _sv_scalespace_t xv
= p
->x_v
;
448 _sv_scalespace_t yv
= p
->y_v
;
450 int grid
= p
->grid_mode
;
452 gdk_threads_enter(); // double lock
456 cairo_rectangle(c
,0,0,pw
,ph
);
460 // render the background
465 draw_scales_work(c
,pw
,ph
,page_h
,inv
,grid
,x
,y
,xv
,yv
);
467 // transient foreground crosshairs
469 double sx
= _sv_plot_get_crosshair_xpixel(p
);
470 double sy
= _sv_plot_get_crosshair_ypixel(p
);
472 cairo_set_source_rgba(c
,1.,1.,0.,.8);
473 cairo_set_line_width(c
,1.);
475 if(! (p
->flags
& _SV_PLOT_NO_Y_CROSS
)){
476 cairo_move_to(c
,0,sy
+.5);
477 cairo_line_to(c
,widget
->allocation
.width
,sy
+.5);
480 if(! (p
->flags
& _SV_PLOT_NO_X_CROSS
)){
481 cairo_move_to(c
,sx
+.5,0);
482 cairo_line_to(c
,sx
+.5,widget
->allocation
.height
);
487 // transient foreground box
491 cairo_set_line_width(c
,1.);
493 cairo_rectangle(c
,vals
[0],vals
[1],vals
[2]+1,vals
[3]+1);
495 cairo_set_source_rgba(c
,1.,1.,.6,.4);
497 cairo_set_source_rgba(c
,.8,.8,.8,.3);
499 cairo_rectangle(c
,vals
[0]+.5,vals
[1]+.5,vals
[2],vals
[3]);
501 cairo_set_source_rgba(c
,1.,1.,.2,.9);
503 cairo_set_source_rgba(c
,1.,1.,0,.8);
508 draw_legend_work(p
,c
,pw
);
510 // put a border on it
511 cairo_set_source_rgb(c
,0,0,0);
512 cairo_set_line_width(c
,1.0);
513 cairo_rectangle(c
,0,0,pw
,ph
);
523 static void _sv_plot_draw (_sv_plot_t
*p
,
524 int x
, int y
, int w
, int h
){
526 GtkWidget
*widget
= GTK_WIDGET(p
);
528 if (GTK_WIDGET_REALIZED (widget
)){
530 cairo_t
*c
= cairo_create(p
->stage
);
531 cairo_set_source_surface(c
,p
->back
,0,0);
532 cairo_rectangle(c
,x
,y
,w
,h
);
535 // transient foreground
537 double sx
= _sv_plot_get_crosshair_xpixel(p
);
538 double sy
= _sv_plot_get_crosshair_ypixel(p
);
540 cairo_set_source_rgba(c
,1.,1.,0.,.8);
541 cairo_set_line_width(c
,1.);
543 if(! (p
->flags
& _SV_PLOT_NO_Y_CROSS
)){
544 cairo_move_to(c
,0,sy
+.5);
545 cairo_line_to(c
,widget
->allocation
.width
,sy
+.5);
548 if(! (p
->flags
& _SV_PLOT_NO_X_CROSS
)){
549 cairo_move_to(c
,sx
+.5,0);
550 cairo_line_to(c
,sx
+.5,widget
->allocation
.height
);
558 cairo_set_line_width(c
,1.);
560 cairo_rectangle(c
,vals
[0],vals
[1],vals
[2]+1,vals
[3]+1);
562 cairo_set_source_rgba(c
,1.,1.,.6,.4);
564 cairo_set_source_rgba(c
,.8,.8,.8,.3);
566 cairo_rectangle(c
,vals
[0]+.5,vals
[1]+.5,vals
[2],vals
[3]);
568 cairo_set_source_rgba(c
,1.,1.,.2,.9);
570 cairo_set_source_rgba(c
,1.,1.,0,.8);
575 double dashes
[] = {1., /* ink */
578 cairo_set_dash (c
, dashes
, 2, .5);
579 cairo_set_source_rgba(c
,0.,0.,0.,1.);
580 cairo_set_line_width(c
,1.);
581 cairo_rectangle(c
,.5,.5,
582 widget
->allocation
.width
-1.,
583 widget
->allocation
.height
-1.);
584 cairo_stroke_preserve (c
);
585 cairo_set_dash (c
, dashes
, 2, 1.5);
586 cairo_set_source_rgba(c
,1.,1.,1.,1.);
592 cairo_set_source_surface(c
,p
->fore
,0,0);
593 cairo_rectangle(c
,x
,y
,w
,h
);
599 cairo_set_source_surface(p
->wc
,p
->stage
,0,0);
600 cairo_rectangle(p
->wc
,x
,y
,w
,h
);
606 static gint
_sv_plot_expose (GtkWidget
*widget
,
607 GdkEventExpose
*event
){
608 if (GTK_WIDGET_REALIZED (widget
)){
609 _sv_plot_t
*p
= PLOT (widget
);
611 int x
= event
->area
.x
;
612 int y
= event
->area
.y
;
613 int w
= event
->area
.width
;
614 int h
= event
->area
.height
;
616 _sv_plot_draw(p
, x
, y
, w
, h
);
622 static void _sv_plot_size_request (GtkWidget
*widget
,
623 GtkRequisition
*requisition
){
624 _sv_plot_t
*p
= PLOT (widget
);
625 if(p
->resizable
|| !p
->wc
){
626 requisition
->width
= 400;
627 requisition
->height
= 200;
629 requisition
->width
= widget
->allocation
.width
;
630 requisition
->height
= widget
->allocation
.height
;
634 static void _sv_plot_realize (GtkWidget
*widget
){
635 GdkWindowAttr attributes
;
636 gint attributes_mask
;
638 GTK_WIDGET_SET_FLAGS (widget
, GTK_REALIZED
);
639 GTK_WIDGET_SET_FLAGS (widget
, GTK_CAN_FOCUS
);
641 attributes
.x
= widget
->allocation
.x
;
642 attributes
.y
= widget
->allocation
.y
;
643 attributes
.width
= widget
->allocation
.width
;
644 attributes
.height
= widget
->allocation
.height
;
645 attributes
.wclass
= GDK_INPUT_OUTPUT
;
646 attributes
.window_type
= GDK_WINDOW_CHILD
;
647 attributes
.event_mask
=
648 gtk_widget_get_events (widget
) |
650 GDK_POINTER_MOTION_MASK
|
651 GDK_BUTTON_PRESS_MASK
|
652 GDK_BUTTON_RELEASE_MASK
|
654 GDK_KEY_RELEASE_MASK
|
656 GDK_ENTER_NOTIFY_MASK
|
657 GDK_LEAVE_NOTIFY_MASK
;
659 attributes
.visual
= gtk_widget_get_visual (widget
);
660 attributes
.colormap
= gtk_widget_get_colormap (widget
);
661 attributes_mask
= GDK_WA_X
| GDK_WA_Y
| GDK_WA_VISUAL
| GDK_WA_COLORMAP
;
662 widget
->window
= gdk_window_new (widget
->parent
->window
,
663 &attributes
, attributes_mask
);
664 gtk_style_attach (widget
->style
, widget
->window
);
665 gtk_style_set_background (widget
->style
, widget
->window
, GTK_STATE_NORMAL
);
666 gdk_window_set_user_data (widget
->window
, widget
);
667 gtk_widget_set_double_buffered (widget
, FALSE
);
670 static void _sv_plot_size_allocate (GtkWidget
*widget
,
671 GtkAllocation
*allocation
){
672 _sv_plot_t
*p
= PLOT (widget
);
674 if (GTK_WIDGET_REALIZED (widget
)){
677 allocation
->width
== widget
->allocation
.width
&&
678 allocation
->height
== widget
->allocation
.height
) return;
680 if(p
->wc
&& !p
->resizable
)return;
683 cairo_destroy(p
->wc
);
685 cairo_surface_destroy(p
->fore
);
687 cairo_surface_destroy(p
->back
);
691 cairo_surface_destroy(p
->stage
);
693 gdk_window_move_resize (widget
->window
, allocation
->x
, allocation
->y
,
694 allocation
->width
, allocation
->height
);
696 p
->wc
= gdk_cairo_create(widget
->window
);
698 // the background is created from data
699 p
->datarect
= calloc(allocation
->width
* allocation
->height
,4);
701 p
->back
= cairo_image_surface_create_for_data ((unsigned char *)p
->datarect
,
705 allocation
->width
*4);
707 p
->stage
= cairo_image_surface_create(CAIRO_FORMAT_RGB24
,
710 p
->fore
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32
,
716 widget
->allocation
= *allocation
;
717 p
->x
= _sv_scalespace_linear(p
->x
.lo
,p
->x
.hi
,widget
->allocation
.width
,p
->scalespacing
,p
->x
.legend
);
718 p
->y
= _sv_scalespace_linear(p
->y
.lo
,p
->y
.hi
,widget
->allocation
.height
,p
->scalespacing
,p
->y
.legend
);
719 _sv_plot_unset_box(p
);
720 if(p
->recompute_callback
)p
->recompute_callback(p
->app_data
);
721 //_sv_plot_draw_scales(p); geenrally done in callback after scale massaging
725 static void box_check(_sv_plot_t
*p
, int x
, int y
){
730 if(inside_box(p
,x
,y
) && !p
->button_down
)
735 _sv_plot_expose_request_partial(p
,
738 (int)(vals
[2]+3), // account for floor/ceil *and* roundoff potential
743 static gint
mouse_motion(GtkWidget
*widget
,
744 GdkEventMotion
*event
){
745 _sv_plot_t
*p
= PLOT (widget
);
749 int bx
= _sv_scalespace_pixel(&p
->x
,p
->box_x1
);
750 int by
= _sv_scalespace_pixel(&p
->y
,p
->box_y1
);
760 _sv_plot_expose_request_partial(p
,
763 (int)(vals
[2]+3), // account for floor/ceil *and* roundoff potential
767 p
->box_x2
= _sv_scalespace_value(&p
->x
,x
);
768 p
->box_y2
= _sv_scalespace_value(&p
->y
,y
);
776 static gboolean
mouse_press (GtkWidget
*widget
,
777 GdkEventButton
*event
){
779 if (event
->button
== 3) return FALSE
;
780 if (event
->button
== 1){
782 _sv_plot_t
*p
= PLOT (widget
);
784 if(p
->box_active
&& inside_box(p
,event
->x
,event
->y
) && !p
->button_down
){
786 p
->selx
= _sv_scalespace_value(&p
->x
,event
->x
);
787 p
->sely
= _sv_scalespace_value(&p
->y
,event
->y
);
788 _sv_plot_snap_crosshairs(p
);
792 p
->box_callback(p
->cross_data
,1);
798 p
->box_x2
=p
->box_x1
= _sv_scalespace_value(&p
->x
,event
->x
);
799 p
->box_y2
=p
->box_y1
= _sv_scalespace_value(&p
->y
,event
->y
);
804 gtk_widget_grab_focus(widget
);
808 static gboolean
mouse_release (GtkWidget
*widget
,
809 GdkEventButton
*event
){
810 if (event
->button
== 3) return FALSE
;
812 _sv_plot_t
*p
= PLOT (widget
);
813 _sv_plot_expose_request(p
);
815 if(!p
->box_active
&& p
->button_down
){
816 p
->selx
= _sv_scalespace_value(&p
->x
,event
->x
);
817 p
->sely
= _sv_scalespace_value(&p
->y
,event
->y
);
818 _sv_plot_snap_crosshairs(p
);
820 if(p
->crosshairs_callback
)
821 p
->crosshairs_callback(p
->cross_data
);
824 _sv_plot_expose_request(p
);
828 box_check(p
,event
->x
, event
->y
);
832 p
->box_callback(p
->cross_data
,0);
839 void _sv_plot_do_enter(_sv_plot_t
*p
){
840 // if box is active, effect it
845 p
->box_callback(p
->cross_data
,0);
846 p
->box_callback(p
->cross_data
,1);
852 GdkEventButton event
;
853 event
.x
= _sv_scalespace_pixel(&p
->x
,p
->selx
);
854 event
.y
= _sv_scalespace_pixel(&p
->y
,p
->sely
);
856 mouse_release(GTK_WIDGET(p
),&event
);
858 p
->box_x2
=p
->box_x1
= p
->selx
;
859 p
->box_y2
=p
->box_y1
= p
->sely
;
866 void _sv_plot_set_crossactive(_sv_plot_t
*p
, int active
){
872 p
->cross_active
=active
;
873 _sv_plot_draw_scales(p
);
874 _sv_plot_expose_request(p
);
877 void _sv_plot_toggle_legend(_sv_plot_t
*p
){
879 if (p
->legend_active
>2)
881 _sv_plot_expose_request(p
);
884 void _sv_plot_set_legendactive(_sv_plot_t
*p
, int active
){
885 p
->legend_active
=active
;
886 _sv_plot_expose_request(p
);
889 static gboolean
_sv_plot_key_press(GtkWidget
*widget
,
891 _sv_plot_t
*p
= PLOT(widget
);
893 int shift
= (event
->state
&GDK_SHIFT_MASK
);
894 if(event
->state
&GDK_MOD1_MASK
) return FALSE
;
895 if(event
->state
&GDK_CONTROL_MASK
)return FALSE
;
897 /* non-control keypresses */
898 switch(event
->keyval
){
902 double x
= _sv_scalespace_pixel(&p
->x_v
,p
->selx
)-1;
906 p
->selx
= _sv_scalespace_value(&p
->x_v
,x
);
907 if(p
->crosshairs_callback
)
908 p
->crosshairs_callback(p
->cross_data
);
916 _sv_plot_expose_request(p
);
922 double x
= _sv_scalespace_pixel(&p
->x_v
,p
->selx
)+1;
926 p
->selx
= _sv_scalespace_value(&p
->x_v
,x
);
927 if(p
->crosshairs_callback
)
928 p
->crosshairs_callback(p
->cross_data
);
936 _sv_plot_expose_request(p
);
942 double y
= _sv_scalespace_pixel(&p
->y_v
,p
->sely
)-1;
946 p
->sely
= _sv_scalespace_value(&p
->y_v
,y
);
947 if(p
->crosshairs_callback
)
948 p
->crosshairs_callback(p
->cross_data
);
956 _sv_plot_expose_request(p
);
961 double y
= _sv_scalespace_pixel(&p
->y_v
,p
->sely
)+1;
965 p
->sely
= _sv_scalespace_value(&p
->y_v
,y
);
966 if(p
->crosshairs_callback
)
967 p
->crosshairs_callback(p
->cross_data
);
975 _sv_plot_expose_request(p
);
985 static gboolean
_sv_plot_unfocus(GtkWidget
*widget
,
986 GdkEventFocus
*event
){
987 _sv_plot_t
*p
=PLOT(widget
);
989 _sv_plot_expose_request(p
);
993 static gboolean
_sv_plot_refocus(GtkWidget
*widget
,
994 GdkEventFocus
*event
){
995 _sv_plot_t
*p
=PLOT(widget
);
997 _sv_plot_expose_request(p
);
1001 static void _sv_plot_class_init (_sv_plot_class_t
* class) {
1003 GtkObjectClass
*object_class
;
1004 GtkWidgetClass
*widget_class
;
1006 object_class
= (GtkObjectClass
*) class;
1007 widget_class
= (GtkWidgetClass
*) class;
1009 parent_class
= gtk_type_class (GTK_TYPE_WIDGET
);
1011 object_class
->destroy
= _sv_plot_destroy
;
1013 widget_class
->realize
= _sv_plot_realize
;
1014 widget_class
->expose_event
= _sv_plot_expose
;
1015 widget_class
->size_request
= _sv_plot_size_request
;
1016 widget_class
->size_allocate
= _sv_plot_size_allocate
;
1017 widget_class
->button_press_event
= mouse_press
;
1018 widget_class
->button_release_event
= mouse_release
;
1019 widget_class
->motion_notify_event
= mouse_motion
;
1020 //widget_class->enter_notify_event = _sv_plot_enter;
1021 //widget_class->leave_notify_event = _sv_plot_leave;
1022 widget_class
->key_press_event
= _sv_plot_key_press
;
1024 widget_class
->focus_out_event
= _sv_plot_unfocus
;
1025 widget_class
->focus_in_event
= _sv_plot_refocus
;
1029 GType
_sv_plot_get_type (void){
1031 static GType plot_type
= 0;
1035 static const GTypeInfo plot_info
= {
1036 sizeof (_sv_plot_class_t
),
1039 (GClassInitFunc
) _sv_plot_class_init
,
1042 sizeof (_sv_plot_t
),
1044 (GInstanceInitFunc
) _sv_plot_init
,
1048 plot_type
= g_type_register_static (GTK_TYPE_WIDGET
, "Plot",
1055 _sv_plot_t
*_sv_plot_new (void (*callback
)(void *),void *app_data
,
1056 void (*cross_callback
)(void *),void *cross_data
,
1057 void (*box_callback
)(void *, int),void *box_data
,
1059 GtkWidget
*g
= GTK_WIDGET (g_object_new (PLOT_TYPE
, NULL
));
1060 _sv_plot_t
*p
= PLOT (g
);
1061 p
->recompute_callback
= callback
;
1062 p
->app_data
= app_data
;
1063 p
->crosshairs_callback
= cross_callback
;
1064 p
->cross_data
= cross_data
;
1065 p
->box_callback
= box_callback
;
1066 p
->box_data
= box_data
;
1068 p
->grid_mode
= _SV_PLOT_GRID_NORMAL
;
1070 p
->legend_active
= 1;
1075 void _sv_plot_expose_request(_sv_plot_t
*p
){
1076 gdk_threads_enter();
1078 GtkWidget
*widget
= GTK_WIDGET(p
);
1081 if (GTK_WIDGET_REALIZED (widget
)){
1084 r
.width
=widget
->allocation
.width
;
1085 r
.height
=widget
->allocation
.height
;
1087 gdk_window_invalidate_rect (widget
->window
, &r
, FALSE
);
1089 gdk_threads_leave();
1092 void _sv_plot_expose_request_partial(_sv_plot_t
*p
,int x
, int y
, int w
, int h
){
1093 gdk_threads_enter();
1095 GtkWidget
*widget
= GTK_WIDGET(p
);
1103 gdk_window_invalidate_rect (widget
->window
, &r
, FALSE
);
1104 gdk_threads_leave();
1107 void _sv_plot_set_crosshairs(_sv_plot_t
*p
, double x
, double y
){
1108 gdk_threads_enter();
1114 _sv_plot_expose_request(p
);
1115 gdk_threads_leave();
1118 void _sv_plot_set_crosshairs_snap(_sv_plot_t
*p
, double x
, double y
){
1119 gdk_threads_enter();
1120 double xpixel
= rint(_sv_scalespace_pixel(&p
->x_v
,x
));
1121 double ypixel
= rint(_sv_scalespace_pixel(&p
->y_v
,y
));
1123 p
->selx
= _sv_scalespace_value(&p
->x_v
,xpixel
);
1124 p
->sely
= _sv_scalespace_value(&p
->y_v
,ypixel
);
1127 _sv_plot_expose_request(p
);
1128 gdk_threads_leave();
1131 void _sv_plot_snap_crosshairs(_sv_plot_t
*p
){
1132 gdk_threads_enter();
1134 double xpixel
= rint(_sv_scalespace_pixel(&p
->x_v
,p
->selx
));
1135 double ypixel
= rint(_sv_scalespace_pixel(&p
->y_v
,p
->sely
));
1137 p
->selx
= _sv_scalespace_value(&p
->x_v
,xpixel
);
1138 p
->sely
= _sv_scalespace_value(&p
->y_v
,ypixel
);
1140 _sv_plot_expose_request(p
);
1141 gdk_threads_leave();
1144 int _sv_plot_get_crosshair_xpixel(_sv_plot_t
*p
){
1148 gdk_threads_enter();
1151 gdk_threads_leave();
1153 return (int)rint(_sv_scalespace_pixel(&x
,v
));
1156 int _sv_plot_get_crosshair_ypixel(_sv_plot_t
*p
){
1160 gdk_threads_enter();
1163 gdk_threads_leave();
1165 return (int)rint(_sv_scalespace_pixel(&y
,v
));
1168 void _sv_plot_unset_box(_sv_plot_t
*p
){
1169 gdk_threads_enter();
1171 gdk_threads_leave();
1174 void _sv_plot_box_vals(_sv_plot_t
*p
, double ret
[4]){
1175 gdk_threads_enter();
1178 ret
[0] = (p
->box_x1
*n
<p
->box_x2
*n
?p
->box_x1
:p
->box_x2
);
1179 ret
[1] = (p
->box_x1
*n
>p
->box_x2
*n
?p
->box_x1
:p
->box_x2
);
1182 ret
[2] = (p
->box_y1
*n
>p
->box_y2
*n
?p
->box_y1
:p
->box_y2
);
1183 ret
[3] = (p
->box_y1
*n
<p
->box_y2
*n
?p
->box_y1
:p
->box_y2
);
1184 gdk_threads_leave();
1187 void _sv_plot_box_set(_sv_plot_t
*p
, double vals
[4]){
1188 gdk_threads_enter();
1196 _sv_plot_expose_request(p
);
1197 gdk_threads_leave();
1200 void _sv_plot_legend_clear(_sv_plot_t
*p
){
1203 for(i
=0;i
<p
->legend_entries
;i
++)
1204 if(p
->legend_list
[i
])
1205 free(p
->legend_list
[i
]);
1206 free(p
->legend_list
);
1207 p
->legend_list
=NULL
;
1209 p
->legend_entries
=0;
1212 void _sv_plot_legend_add(_sv_plot_t
*p
, char *entry
){
1213 _sv_plot_legend_add_with_color(p
,entry
,0xffffffffUL
);
1216 void _sv_plot_legend_add_with_color(_sv_plot_t
*p
, char *entry
, u_int32_t color
){
1217 if(!p
->legend_list
|| !p
->legend_colors
){
1218 p
->legend_list
= calloc(1, sizeof(*p
->legend_list
));
1219 p
->legend_colors
= calloc(1, sizeof(*p
->legend_colors
));
1220 p
->legend_entries
=1;
1222 p
->legend_entries
++;
1223 p
->legend_list
= realloc(p
->legend_list
, p
->legend_entries
*sizeof(*p
->legend_list
));
1224 p
->legend_colors
= realloc(p
->legend_colors
, p
->legend_entries
*sizeof(*p
->legend_colors
));
1228 p
->legend_list
[p
->legend_entries
-1] = strdup(entry
);
1230 p
->legend_list
[p
->legend_entries
-1] = strdup("");
1231 p
->legend_colors
[p
->legend_entries
-1] = color
;
1234 void _sv_plot_resizable(_sv_plot_t
*p
, int rp
){
1235 GtkWidget
*widget
= GTK_WIDGET(p
);
1239 gtk_widget_set_size_request(widget
,
1240 widget
->allocation
.width
,
1241 widget
->allocation
.height
);
1242 while(gtk_events_pending()){
1243 gtk_main_iteration();
1248 gtk_widget_set_size_request(widget
,400,200);
1249 while(gtk_events_pending()){
1250 gtk_main_iteration();