Add Russian translation provided by Валерий Крувялис <valkru@mail.ru>
[xiph-mirror.git] / sushivision / plot.c
blobbbc5874401ba47ef1c2e1d291a0b3f1cb2ed4a3c
1 /*
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)
8 * any later version.
9 *
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.
22 #define _GNU_SOURCE
23 #include <gtk/gtk.h>
24 #include <gtk/gtkmain.h>
25 #include <gdk/gdk.h>
26 #include <gdk/gdkkeysyms.h>
27 #include <stdlib.h>
28 #include <math.h>
29 #include <stdio.h>
30 #include <string.h>
32 #include "scale.h"
33 #include "plot.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));
47 }else{
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){
54 p->bg_inv = setp;
57 void _sv_plot_set_grid(_sv_plot_t *p, int mode){
58 p->grid_mode = 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.);
64 else
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);
71 else
72 cairo_set_source_rgba(c,1.,1.,1.,.8);
75 static void draw_scales_work(cairo_t *c, int w, int h,
76 double page_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){
81 int i=0,x,y;
82 char buffer[80];
83 int y_width=0;
84 int x_height=0;
85 int off = (grid == _SV_PLOT_GRID_TICS?6:0);
87 cairo_set_miter_limit(c,2.);
89 // draw all axis lines, then stroke
90 if(grid){
92 cairo_set_line_width(c,1.);
93 if(grid & _SV_PLOT_GRID_NORMAL){
94 switch(grid&0xf00){
95 case 256:
96 cairo_set_source_rgba(c,.9,.9,.9,.4);
97 break;
98 case 512:
99 cairo_set_source_rgba(c,.1,.1,.1,.4);
100 break;
101 default:
102 cairo_set_source_rgba(c,.7,.7,.7,.3);
103 break;
106 i=0;
107 x = scale_demark(&xs, &xs_v, i++, NULL);
108 while(x < w){
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);
114 i=0;
115 y = scale_demark(&ys, &ys_v, i++, NULL);
116 while(y < h){
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);
122 cairo_stroke(c);
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);
133 i=0;
134 y = scale_demark(&ys, &ys_v, i++, buffer);
136 while(y < h){
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);
149 cairo_stroke(c);
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);
160 // set sideways text
161 cairo_save(c);
162 cairo_matrix_t a;
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!
165 cairo_matrix_t d;
166 cairo_matrix_multiply(&d,&a,&b);
167 cairo_set_matrix(c,&d);
169 // text y scale label
170 if(ys.legend){
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);
177 cairo_stroke(c);
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);
184 if(grid){
185 i=0;
186 x = scale_demark(&xs, &xs_v, i++, buffer);
188 while(x < w){
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);
199 cairo_stroke(c);
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);
210 cairo_restore(c);
211 // text x scale label
212 if(xs.legend){
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);
219 cairo_stroke(c);
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);
230 i=0;
231 x = scale_demark(&xs, &xs_v, i++, NULL);
232 while(x < w){
233 if(x > y_width+5 ){
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);
242 i=0;
243 y = scale_demark(&ys, &ys_v, i++, NULL);
244 while(y < h){
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);
252 cairo_stroke(c);
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){
258 int i;
259 int textw=0, texth=0;
260 int totalh=0;
261 int x,y;
262 cairo_text_extents_t extents;
264 gdk_threads_enter();
265 int inv = p->bg_inv;
266 int n = p->legend_entries;
267 char *buffer[n];
268 u_int32_t colors[n];
269 for(i=0;i<n;i++){
270 buffer[i] = strdup(p->legend_list[i]);
271 colors[i] = p->legend_colors[i];
273 gdk_threads_leave();
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 */
284 for(i=0;i<n;i++){
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;
292 y = 15+texth;
293 texth = ceil(texth * 1.2+3);
294 totalh = texth*n;
296 x = w - textw - 15;
298 // draw the enclosing rectangle
299 if(p->legend_active==2){
300 cairo_rectangle(c,x-6.5,5.5,textw+15,totalh+15);
301 set_shadow(inv,c);
302 cairo_fill_preserve(c);
303 set_text(inv,c);
304 cairo_stroke(c);
307 for(i=0;i<n;i++){
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]);
314 set_shadow(inv,c);
315 cairo_set_line_width(c,3);
316 cairo_stroke(c);
319 if(colors[i] == 0xffffffffUL){
320 set_text(inv,c);
321 }else{
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]);
331 y+=texth;
334 for(i=0;i<n;i++)
335 free(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.
342 gdk_threads_enter();
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);
350 int inv = p->bg_inv;
351 int grid = p->grid_mode;
352 gdk_threads_leave();
354 cairo_t *c = cairo_create(s);
355 cairo_save(c);
356 cairo_set_operator(c,CAIRO_OPERATOR_CLEAR);
357 cairo_set_source_rgba (c, 1,1,1,1);
358 cairo_paint(c);
359 cairo_restore(c);
361 draw_scales_work(c,w,h,h,inv,grid,x,y,xv,yv);
362 draw_legend_work(p,c,w);
363 cairo_destroy(c);
365 gdk_threads_enter();
366 // swap fore/temp
367 cairo_surface_t *temp = p->fore;
368 p->fore = s;
369 cairo_surface_destroy(temp);
370 //_sv_plot_expose_request(p);
371 gdk_threads_leave();
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
388 if(p->wc){
389 cairo_destroy(p->wc);
390 p->wc=0;
392 if(p->back){
393 cairo_surface_destroy(p->back);
394 p->back=0;
396 if(p->fore){
397 cairo_surface_destroy(p->fore);
398 p->fore=0;
400 if(p->stage){
401 cairo_surface_destroy(p->stage);
402 p->stage=0;
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){
420 vals[0]=-1;
421 vals[2]=widget->allocation.width+2;
424 if(p->flags & _SV_PLOT_NO_Y_CROSS){
425 vals[1]=-1;
426 vals[3]=widget->allocation.height+2;
431 static int inside_box(_sv_plot_t *p, int x, int y){
432 double vals[4];
433 box_corners(p,vals);
435 return (x >= vals[0] &&
436 x <= vals[0]+vals[2] &&
437 y >= vals[1] &&
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;
449 int inv = p->bg_inv;
450 int grid = p->grid_mode;
452 gdk_threads_enter(); // double lock
454 cairo_save(c);
455 cairo_new_path(c);
456 cairo_rectangle(c,0,0,pw,ph);
457 cairo_clip(c);
458 cairo_new_path(c);
460 // render the background
461 if(datarender)
462 datarender(data,c);
464 // render scales
465 draw_scales_work(c,pw,ph,page_h,inv,grid,x,y,xv,yv);
467 // transient foreground crosshairs
468 if(p->cross_active){
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);
484 cairo_stroke(c);
487 // transient foreground box
488 if(p->box_active){
489 double vals[4];
490 box_corners(p,vals);
491 cairo_set_line_width(c,1.);
493 cairo_rectangle(c,vals[0],vals[1],vals[2]+1,vals[3]+1);
494 if(p->box_active>1)
495 cairo_set_source_rgba(c,1.,1.,.6,.4);
496 else
497 cairo_set_source_rgba(c,.8,.8,.8,.3);
498 cairo_fill(c);
499 cairo_rectangle(c,vals[0]+.5,vals[1]+.5,vals[2],vals[3]);
500 if(p->box_active>1)
501 cairo_set_source_rgba(c,1.,1.,.2,.9);
502 else
503 cairo_set_source_rgba(c,1.,1.,0,.8);
504 cairo_stroke(c);
507 // render legend
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);
514 cairo_stroke(c);
516 cairo_restore(c);
518 gdk_threads_leave();
520 return 0;
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);
533 cairo_fill(c);
535 // transient foreground
536 if(p->cross_active){
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);
552 cairo_stroke(c);
555 if(p->box_active){
556 double vals[4];
557 box_corners(p,vals);
558 cairo_set_line_width(c,1.);
560 cairo_rectangle(c,vals[0],vals[1],vals[2]+1,vals[3]+1);
561 if(p->box_active>1)
562 cairo_set_source_rgba(c,1.,1.,.6,.4);
563 else
564 cairo_set_source_rgba(c,.8,.8,.8,.3);
565 cairo_fill(c);
566 cairo_rectangle(c,vals[0]+.5,vals[1]+.5,vals[2],vals[3]);
567 if(p->box_active>1)
568 cairo_set_source_rgba(c,1.,1.,.2,.9);
569 else
570 cairo_set_source_rgba(c,1.,1.,0,.8);
571 cairo_stroke(c);
574 if(p->widgetfocus){
575 double dashes[] = {1., /* ink */
576 1.};
577 cairo_save(c);
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.);
587 cairo_stroke(c);
588 cairo_restore(c);
591 // main foreground
592 cairo_set_source_surface(c,p->fore,0,0);
593 cairo_rectangle(c,x,y,w,h);
594 cairo_fill(c);
596 cairo_destroy(c);
598 // blit to window
599 cairo_set_source_surface(p->wc,p->stage,0,0);
600 cairo_rectangle(p->wc,x,y,w,h);
601 cairo_fill(p->wc);
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);
619 return FALSE;
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;
628 }else{
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) |
649 GDK_EXPOSURE_MASK|
650 GDK_POINTER_MOTION_MASK|
651 GDK_BUTTON_PRESS_MASK |
652 GDK_BUTTON_RELEASE_MASK|
653 GDK_KEY_PRESS_MASK |
654 GDK_KEY_RELEASE_MASK |
655 GDK_STRUCTURE_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)){
676 if(p->wc &&
677 allocation->width == widget->allocation.width &&
678 allocation->height == widget->allocation.height) return;
680 if(p->wc && !p->resizable)return;
682 if(p->wc)
683 cairo_destroy(p->wc);
684 if (p->fore)
685 cairo_surface_destroy(p->fore);
686 if (p->back)
687 cairo_surface_destroy(p->back);
688 if(p->datarect)
689 free(p->datarect);
690 if (p->stage)
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,
702 CAIRO_FORMAT_RGB24,
703 allocation->width,
704 allocation->height,
705 allocation->width*4);
707 p->stage = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
708 allocation->width,
709 allocation->height);
710 p->fore = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
711 allocation->width,
712 allocation->height);
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){
726 if(p->box_active){
727 double vals[4];
728 box_corners(p,vals);
730 if(inside_box(p,x,y) && !p->button_down)
731 p->box_active = 2;
732 else
733 p->box_active = 1;
735 _sv_plot_expose_request_partial(p,
736 (int)(vals[0]),
737 (int)(vals[1]),
738 (int)(vals[2]+3), // account for floor/ceil *and* roundoff potential
739 (int)(vals[3]+3));
743 static gint mouse_motion(GtkWidget *widget,
744 GdkEventMotion *event){
745 _sv_plot_t *p = PLOT (widget);
747 int x = event->x;
748 int y = event->y;
749 int bx = _sv_scalespace_pixel(&p->x,p->box_x1);
750 int by = _sv_scalespace_pixel(&p->y,p->box_y1);
752 if(p->button_down){
753 if(abs(bx - x)>5 ||
754 abs(by - y)>5)
755 p->box_active = 1;
757 if(p->box_active){
758 double vals[4];
759 box_corners(p,vals);
760 _sv_plot_expose_request_partial(p,
761 (int)(vals[0]),
762 (int)(vals[1]),
763 (int)(vals[2]+3), // account for floor/ceil *and* roundoff potential
764 (int)(vals[3]+3));
767 p->box_x2 = _sv_scalespace_value(&p->x,x);
768 p->box_y2 = _sv_scalespace_value(&p->y,y);
771 box_check(p,x,y);
773 return TRUE;
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);
789 p->cross_active=1;
791 if(p->box_callback)
792 p->box_callback(p->cross_data,1);
794 p->button_down=0;
795 p->box_active=0;
797 }else{
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);
800 p->box_active = 0;
801 p->button_down=1;
804 gtk_widget_grab_focus(widget);
805 return TRUE;
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);
823 p->cross_active=1;
824 _sv_plot_expose_request(p);
827 p->button_down=0;
828 box_check(p,event->x, event->y);
830 if(p->box_active){
831 if(p->box_callback)
832 p->box_callback(p->cross_data,0);
836 return TRUE;
839 void _sv_plot_do_enter(_sv_plot_t *p){
840 // if box is active, effect it
842 if(p->box_active){
844 if(p->box_callback){
845 p->box_callback(p->cross_data,0);
846 p->box_callback(p->cross_data,1);
848 p->button_down=0;
849 p->box_active=0;
850 }else{
851 if(p->button_down){
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);
857 }else{
858 p->box_x2=p->box_x1 = p->selx;
859 p->box_y2=p->box_y1 = p->sely;
860 p->box_active = 1;
861 p->button_down=1;
866 void _sv_plot_set_crossactive(_sv_plot_t *p, int active){
867 if(!active){
868 p->button_down=0;
869 p->box_active=0;
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){
878 p->legend_active++;
879 if (p->legend_active>2)
880 p->legend_active=0;
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,
890 GdkEventKey *event){
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){
900 case GDK_Left:
902 double x = _sv_scalespace_pixel(&p->x_v,p->selx)-1;
903 p->cross_active=1;
904 if(shift)
905 x-=9;
906 p->selx = _sv_scalespace_value(&p->x_v,x);
907 if(p->crosshairs_callback)
908 p->crosshairs_callback(p->cross_data);
910 if(p->button_down){
911 p->box_active=1;
912 p->box_x2 = p->selx;
913 }else
914 p->box_active=0;
916 _sv_plot_expose_request(p);
918 return TRUE;
920 case GDK_Right:
922 double x = _sv_scalespace_pixel(&p->x_v,p->selx)+1;
923 p->cross_active=1;
924 if(shift)
925 x+=9;
926 p->selx = _sv_scalespace_value(&p->x_v,x);
927 if(p->crosshairs_callback)
928 p->crosshairs_callback(p->cross_data);
930 if(p->button_down){
931 p->box_active=1;
932 p->box_x2 = p->selx;
933 }else
934 p->box_active=0;
936 _sv_plot_expose_request(p);
939 return TRUE;
940 case GDK_Up:
942 double y = _sv_scalespace_pixel(&p->y_v,p->sely)-1;
943 p->cross_active=1;
944 if(shift)
945 y-=9;
946 p->sely = _sv_scalespace_value(&p->y_v,y);
947 if(p->crosshairs_callback)
948 p->crosshairs_callback(p->cross_data);
950 if(p->button_down){
951 p->box_active=1;
952 p->box_y2 = p->sely;
953 }else
954 p->box_active=0;
956 _sv_plot_expose_request(p);
958 return TRUE;
959 case GDK_Down:
961 double y = _sv_scalespace_pixel(&p->y_v,p->sely)+1;
962 p->cross_active=1;
963 if(shift)
964 y+=9;
965 p->sely = _sv_scalespace_value(&p->y_v,y);
966 if(p->crosshairs_callback)
967 p->crosshairs_callback(p->cross_data);
969 if(p->button_down){
970 p->box_active=1;
971 p->box_y2 = p->sely;
972 }else
973 p->box_active=0;
975 _sv_plot_expose_request(p);
978 return TRUE;
982 return FALSE;
985 static gboolean _sv_plot_unfocus(GtkWidget *widget,
986 GdkEventFocus *event){
987 _sv_plot_t *p=PLOT(widget);
988 p->widgetfocus=0;
989 _sv_plot_expose_request(p);
990 return TRUE;
993 static gboolean _sv_plot_refocus(GtkWidget *widget,
994 GdkEventFocus *event){
995 _sv_plot_t *p=PLOT(widget);
996 p->widgetfocus=1;
997 _sv_plot_expose_request(p);
998 return TRUE;
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;
1033 if (!plot_type)
1035 static const GTypeInfo plot_info = {
1036 sizeof (_sv_plot_class_t),
1037 NULL,
1038 NULL,
1039 (GClassInitFunc) _sv_plot_class_init,
1040 NULL,
1041 NULL,
1042 sizeof (_sv_plot_t),
1044 (GInstanceInitFunc) _sv_plot_init,
1048 plot_type = g_type_register_static (GTK_TYPE_WIDGET, "Plot",
1049 &plot_info, 0);
1052 return plot_type;
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,
1058 unsigned flags) {
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;
1067 p->flags = flags;
1068 p->grid_mode = _SV_PLOT_GRID_NORMAL;
1069 p->resizable = 1;
1070 p->legend_active = 1;
1072 return p;
1075 void _sv_plot_expose_request(_sv_plot_t *p){
1076 gdk_threads_enter();
1078 GtkWidget *widget = GTK_WIDGET(p);
1079 GdkRectangle r;
1081 if (GTK_WIDGET_REALIZED (widget)){
1082 r.x=0;
1083 r.y=0;
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);
1096 GdkRectangle r;
1098 r.x=x;
1099 r.y=y;
1100 r.width=w;
1101 r.height=h;
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();
1110 p->selx = x;
1111 p->sely = y;
1112 p->cross_active=1;
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);
1125 p->cross_active=1;
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){
1145 _sv_scalespace_t x;
1146 double v;
1148 gdk_threads_enter();
1149 x = p->x;
1150 v = p->selx;
1151 gdk_threads_leave();
1153 return (int)rint(_sv_scalespace_pixel(&x,v));
1156 int _sv_plot_get_crosshair_ypixel(_sv_plot_t *p){
1157 _sv_scalespace_t y;
1158 double v;
1160 gdk_threads_enter();
1161 y = p->y;
1162 v = p->sely;
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();
1170 p->box_active = 0;
1171 gdk_threads_leave();
1174 void _sv_plot_box_vals(_sv_plot_t *p, double ret[4]){
1175 gdk_threads_enter();
1176 int n = p->x.neg;
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);
1181 n = p->y.neg;
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();
1190 p->box_x1=vals[0];
1191 p->box_x2=vals[1];
1192 p->box_y1=vals[2];
1193 p->box_y2=vals[3];
1194 p->box_active = 1;
1196 _sv_plot_expose_request(p);
1197 gdk_threads_leave();
1200 void _sv_plot_legend_clear(_sv_plot_t *p){
1201 int i;
1202 if(p->legend_list){
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;
1221 }else{
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));
1227 if(entry)
1228 p->legend_list[p->legend_entries-1] = strdup(entry);
1229 else
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);
1236 if(!rp){
1238 p->resizable = 0;
1239 gtk_widget_set_size_request(widget,
1240 widget->allocation.width,
1241 widget->allocation.height);
1242 while(gtk_events_pending()){
1243 gtk_main_iteration();
1244 gdk_flush();
1247 }else{
1248 gtk_widget_set_size_request(widget,400,200);
1249 while(gtk_events_pending()){
1250 gtk_main_iteration();
1251 gdk_flush();
1253 p->resizable = 1;