Don't forget the new file
[xiph/unicode.git] / sushivision / panel.c
blobbfec8d4f8000665a0913e2efd1621836ff985c43
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 <string.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <pthread.h>
27 #include <errno.h>
28 #include <math.h>
29 #include <signal.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <gtk/gtk.h>
33 #include <cairo-ft.h>
34 #include "internal.h"
36 int _sv_panels=0;
37 sv_panel_t **_sv_panel_list=NULL;
38 pthread_rwlock_t panellist_m;
40 // panel.panel_m: rwlock, protects all panel and plane heaps
41 // panel.status_m: mutex, protects status variables
43 // Plane data in the panels is protected as follows:
45 // Modifications of pointers, heap allocations, etc are protected by
46 // write-locking panel_m.
48 // Both read and write access to the contents of plane.data and
49 // plane.image are protected by read-locking panel_m; reads may happen
50 // at any time, writes are checked only that they've not been superceded
51 // by a later request. Writes are not locked against reads at all; a
52 // read from inconsistent state is temporary and cosmetic only. It will
53 // always be immediately replaced by complete/correct data when the write
54 // finishes and triggers a flush.
56 // comp_serialno is used to to verify 'freshness' of operations; anytime a
57 // thread needs to synchronize with panel/plane data before continuing
58 // (read or write), it compares the serialno that dispatched it to the
59 // current serialno. A mismatch immediately aborts the task in progress
60 // with STATUS_WORKING.
62 // map_serialno performs the same task with respect to remap requests
63 // for a given plane.
65 // worker thread process order:
67 // > recompute setup
68 // > images resize
69 // > scale render
70 // > legend render
71 // > bg render
72 // > expose
73 // > data resize
74 // > image map render (throttleable)
75 // > computation work
76 // > idle
78 // proceed to later steps only if there's no work immediately
79 // dispatchable from earlier steps.
81 // UI output comes first (UI is never dead, even briefly, and that
82 // includes graphs), however progressive UI work is purposely
83 // throttled.
85 // wake_workers() only explicitly needed when triggering tasks via
86 // GDK/API; it is already called implicitly in the main loop whenever
87 // a task step completes.
89 static void payload_free(sv_dim_data_t *dd, int dims){
90 for(i=0;i<dims;i++)
91 _sv_dim_data_clear(dd+i);
92 free(dd);
95 static int image_resize(sv_plane_t *pl,
96 sv_panel_t *p){
97 return pl->c.image_resize(pl, p);
100 static int data_resize(sv_plane_t *pl,
101 sv_panel_t *p){
102 return pl->c.data_resize(pl, p);
105 static int image_work(sv_plane_t *pl,
106 sv_panel_t *p){
107 return pl->c.image_work(pl, p);
110 static int data_work(sv_plane_t *pl,
111 sv_panel_t *p){
112 return pl->c.data_work(pl, p);
115 static int plane_loop(sv_panel_t *p, int *next,
116 int(*function)(_sv_plane_t *,
117 sv_panel_t *)){
118 int finishedflag=1;
119 int last = *next;
120 int serialno = p->serialno;
122 int i = *next++;
123 if(*next>=p->planes)*next=0;
124 int status = function(p->plane_list[i],p);
125 if(status == STATUS_WORKING) return STATUS_WORKING;
126 if(status != STATUS_IDLE) finishedflag=0;
127 }while(i!=last);
129 if(finishedflag)
130 return STATUS_IDLE;
131 return STATUS_BUSY;
134 static int done_working(sv_panel_t *p){
135 pthread_mutex_unlock(p->status_m);
136 pthread_rwlock_unlock(p->panel_m);
137 return STATUS_WORKING;
140 static int done_busy(sv_panel_t *p){
141 pthread_mutex_unlock(p->status_m);
142 pthread_rwlock_unlock(p->panel_m);
143 return STATUS_BUSY;
146 static int done_idle(sv_panel_t *p){
147 pthread_mutex_unlock(p->status_m);
148 pthread_rwlock_unlock(p->panel_m);
149 return STATUS_IDLE;
152 int _sv_panel_work(sv_panel_t *p){
153 int i,serialno,status;
154 pthread_rwlock_rdlock(p->panel_m);
155 pthread_mutex_lock(p->status_m);
157 // recomute setup
159 // plane recompute calls will do nothing if recomputation is not
160 // required. Even if computation not required, may still request
161 // an image resize/resample
162 if(p->recompute_pending){
163 p->recompute_pending=0;
164 p->comp_serialno++;
165 p->legend_serialno++;
166 p->image_resize=1;
167 p->data_resize=1;
168 p->data_work=1;
169 p->rescale=1;
170 p->relegend=1;
171 p->bgrender=0;
172 p->image_next_plane=0;
174 bg_recompute_setup(p);
176 for(i=0;i<p->planes;i++)
177 p->plane_list[i]->c.recompute_setup(p->plane_list[i], p);
179 pthread_mutex_unlock(p->status_m);
180 pthread_rwlock_unlock(p->panel_m);
182 return STATUS_WORKING;
185 if(p->relegend_pending){
186 p->relegend=1;
187 p->relegend_pending=0;
190 serialno = p->comp_serialno;
192 // bg/image resize
193 // image resizes assume bg resize has completed
194 if(p->bg_resize){
195 status = bg_resize(p);
196 if(status == STATUS_WORKING) return done_working(p);
197 if(status == STATUS_BUSY) return done_busy(p);
198 p->bg_resize=0;
201 // image resize
202 if(p->image_resize){
203 status = plane_loop(p,&p->image_next_plane,image_resize);
204 if(status == STATUS_WORKING) return done_working(p);
205 if(status == STATUS_IDLE){
206 p->image_resize = 0;
207 p->bg_render = 1;
211 // legend regeneration
212 if(p->relegend){
213 p->relegend=0; // must be unset before relegend
214 bg_legend(p);
215 p->expose=1;
216 return done_working(p);
219 // axis scale redraw
220 if(p->rescale){
221 p->rescale=0; // must be unset before rescale
222 bg_scale(p);
223 p->expose=1;
224 return done_working(p);
227 // need to join on image resizing before proceeding to background redraw
228 if(p->image_resize)
229 return done_busy(p);
231 // bg render
232 if(p->bg_render){
233 if(bg_render(p) == STATUS_IDLE){
234 p->expose=1;
235 p->bg_render=0;
237 return done_working(p);
240 // expose
241 if(p->expose &&
242 !p->relegend &&
243 !p->rescale &&
244 !p->bgrender){
245 // wait till all these ops are done
246 p->expose = 0;
247 bg_expose(p);
248 // bg_expose does not reclaim loks before exit
249 return STATUS_WORKING;
252 // data resize
253 if(p->data_resize){
254 status = plane_loop(p,&p->data_next_plane,data_resize);
255 if(status == STATUS_WORKING) return done_working(p);
256 if(status == STATUS_IDLE) p->data_resize = 0;
259 // need to join on data resizing before proceeding to map/compute work
260 if(p->data_resize)
261 return done_busy(p);
263 // image map render
264 if(p->map_render){
265 status = plane_loop(p,&p->image_next_plane,image_work);
266 if(status == STATUS_WORKING) return done_working(p);
267 if(status == STATUS_IDLE){
268 p->map_render = 0;
269 p->bg_render = 1;
273 // computation work
274 if(p->data_work){
275 status = plane_loop(p,&p->data_next_plane,data_work);
276 if(status == STATUS_WORKING){
278 // throttled image render
279 if(p->map_render==0){
280 // no render currently in progress
281 struct timeval now;
282 gettimeofday(&now,NULL);
284 if(p->map_throttle_last.tv_sec==0){
285 p->map_throttle_last=now;
286 }else{
287 long test = (now.tv_sec - p->map_throttle_last.tv_sec)*1000 +
288 (now.tv_usec - p->map_throttle_last.tv_usec)/1000;
289 if(test>500)
290 // first request since throttle
291 p->map_render=1;
294 return done_working(p);
296 if(status == STATUS_IDLE){
297 p->map_render = 1;
298 p->relegend = 1;
299 p->data_work = 0;
302 return done_idle(p);
305 // looks like a cop-out but is actually the correct thing to do; the
306 // data *must* be WYSIWYG from panel display.
307 static void _sv_panel2d_print_bg(sv_panel_t *p, cairo_t *c){
308 _sv_plot_t *plot = PLOT(p->private->graph);
310 if(!plot) return;
312 cairo_pattern_t *pattern = cairo_pattern_create_for_surface(plot->back);
313 cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST);
314 cairo_set_source(c,pattern);
315 cairo_paint(c);
317 cairo_pattern_destroy(pattern);
320 static void _sv_panel2d_print(sv_panel_t *p, cairo_t *c, int w, int h){
321 _sv_panel2d_t *p2 = p->subtype->p2;
322 _sv_plot_t *plot = PLOT(p->private->graph);
323 double pw = p->private->graph->allocation.width;
324 double ph = p->private->graph->allocation.height;
325 double scale;
326 int i;
327 double maxlabelw=0;
328 double y;
330 if(w/pw < h/ph)
331 scale = w/pw;
332 else
333 scale = h/ph;
335 cairo_matrix_t m;
336 cairo_save(c);
337 cairo_get_matrix(c,&m);
338 cairo_matrix_scale(&m,scale,scale);
339 cairo_set_matrix(c,&m);
341 _sv_plot_print(plot, c, ph*scale, (void(*)(void *, cairo_t *))_sv_panel2d_print_bg, p);
342 cairo_restore(c);
344 // find extents widths for objective scale labels
345 cairo_set_font_size(c,10);
346 for(i=0;i<p->objectives;i++){
347 cairo_text_extents_t ex;
348 sv_obj_t *o = p->objective_list[i].o;
349 cairo_text_extents(c, o->name, &ex);
350 if(ex.width > maxlabelw) maxlabelw=ex.width;
354 y = ph * scale + 10;
356 for(i=0;i<p->objectives;i++){
357 sv_obj_t *o = p->objective_list[i].o;
358 _sv_slider_t *s = p2->range_scales[i];
360 // get scale height
361 double labelh = _sv_slider_print_height(s);
362 cairo_text_extents_t ex;
363 cairo_text_extents (c, o->name, &ex);
365 int lx = maxlabelw - ex.width;
366 int ly = labelh/2 + ex.height/2;
368 // print objective labels
369 cairo_set_source_rgb(c,0.,0.,0.);
370 cairo_move_to (c, lx,ly+y);
371 cairo_show_text (c, o->name);
373 // draw slider
374 // set translation
375 cairo_save(c);
376 cairo_translate (c, maxlabelw + 10, y);
377 _sv_slider_print(s, c, pw*scale - maxlabelw - 10, labelh);
378 cairo_restore(c);
380 y += labelh;
385 // enter with lock
386 static void _sv_panel2d_update_legend(sv_panel_t *p){
387 _sv_panel2d_t *p2 = p->subtype->p2;
388 _sv_plot_t *plot = PLOT(p->private->graph);
390 if(plot){
391 int i;
392 char buffer[320];
393 int depth = 0;
394 _sv_plot_legend_clear(plot);
396 // potentially add each dimension to the legend; add axis
397 // dimensions only if crosshairs are active
399 // display decimal precision relative to display scales
400 if(3-_sv_scalespace_decimal_exponent(&p2->x) > depth)
401 depth = 3-_sv_scalespace_decimal_exponent(&p2->x);
402 if(3-_sv_scalespace_decimal_exponent(&p2->y) > depth)
403 depth = 3-_sv_scalespace_decimal_exponent(&p2->y);
404 for(i=0;i<p->dimensions;i++){
405 sv_dim_t *d = p->dimension_list[i].d;
406 if( (d!=p->private->x_d && d!=p->private->y_d) ||
407 plot->cross_active){
408 snprintf(buffer,320,"%s = %+.*f",
409 p->dimension_list[i].d->legend,
410 depth,
411 p->dimension_list[i].d->val);
412 _sv_plot_legend_add(plot,buffer);
416 // add each active objective plane to the legend
417 // choose the value under the crosshairs
418 if(plot->cross_active){
419 // one space
420 _sv_plot_legend_add(plot,NULL);
422 for(i=0;i<p->objectives;i++){
424 if(!_sv_mapping_inactive_p(p2->mappings+i)){
425 compute_result vals;
426 _sv_panel2d_compute_point(p,p->objective_list[i].o, plot->selx, plot->sely, &vals);
428 if(!isnan(vals.y)){
430 snprintf(buffer,320,"%s = %f",
431 p->objective_list[i].o->name,
432 vals.y);
433 _sv_plot_legend_add(plot,buffer);
441 static void _sv_panel2d_mapchange_callback(GtkWidget *w,gpointer in){
442 sv_obj_list_t *optr = (sv_obj_list_t *)in;
443 //sv_obj_t *o = optr->o;
444 sv_panel_t *p = optr->p;
445 _sv_panel2d_t *p2 = p->subtype->p2;
446 int onum = optr - p->objective_list;
448 _sv_undo_push();
449 _sv_undo_suspend();
451 _sv_mapping_set_func(&p2->mappings[onum],gtk_combo_box_get_active(GTK_COMBO_BOX(w)));
453 //redraw the map slider
454 _sv_slider_set_gradient(p2->range_scales[onum], &p2->mappings[onum]);
456 // in the event the mapping active state changed
457 _sv_panel_dirty_legend(p);
459 //redraw the plot
460 _sv_panel2d_mark_map_plane(p,onum,1,0,0);
461 _sv_panel_dirty_map(p);
462 _sv_undo_resume();
465 static void _sv_panel2d_map_callback(void *in,int buttonstate){
466 sv_obj_list_t *optr = (sv_obj_list_t *)in;
467 //sv_obj_t *o = optr->o;
468 sv_panel_t *p = optr->p;
469 _sv_panel2d_t *p2 = p->subtype->p2;
470 int onum = optr - p->objective_list;
472 if(buttonstate == 0){
473 _sv_undo_push();
474 _sv_undo_suspend();
477 // recache alpha del */
478 p2->alphadel[onum] =
479 _sv_slider_val_to_del(p2->range_scales[onum],
480 _sv_slider_get_value(p2->range_scales[onum],1));
482 // redraw the plot on motion
483 if(buttonstate == 1){
484 _sv_panel2d_mark_map_plane(p,onum,1,0,0);
485 _sv_panel_dirty_map(p);
487 if(buttonstate == 2)
488 _sv_undo_resume();
491 static void _sv_panel2d_update_xysel(sv_panel_t *p){
492 _sv_panel2d_t *p2 = p->subtype->p2;
493 int i;
494 // update which x/y buttons are pressable */
495 // enable/disable dimension slider thumbs
497 for(i=0;i<p->dimensions;i++){
498 if(p2->dim_xb[i] &&
499 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p2->dim_xb[i]))){
500 // make the y insensitive
501 if(p2->dim_yb[i])
502 _gtk_widget_set_sensitive_fixup(p2->dim_yb[i],FALSE);
504 // set the x dim flag
505 p->private->x_d = p->dimension_list[i].d;
506 p2->x_scale = p->private->dim_scales[i];
507 p2->x_dnum = i;
508 }else{
509 // if there is a y, make it sensitive
510 if(p2->dim_yb[i])
511 _gtk_widget_set_sensitive_fixup(p2->dim_yb[i],TRUE);
513 if(p2->dim_yb[i] &&
514 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p2->dim_yb[i]))){
515 // make the x insensitive
516 if(p2->dim_xb[i])
517 _gtk_widget_set_sensitive_fixup(p2->dim_xb[i],FALSE);
519 // set the y dim
520 p->private->y_d = p->dimension_list[i].d;
521 p2->y_scale = p->private->dim_scales[i];
522 p2->y_dnum = i;
523 }else{
524 // if there is a x, make it sensitive
525 if(p2->dim_xb[i])
526 _gtk_widget_set_sensitive_fixup(p2->dim_xb[i],TRUE);
528 if((p2->dim_xb[i] &&
529 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p2->dim_xb[i]))) ||
530 (p2->dim_yb[i] &&
531 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p2->dim_yb[i])))){
532 // make all thumbs visible
533 _sv_dim_widget_set_thumb_active(p->private->dim_scales[i],0,1);
534 _sv_dim_widget_set_thumb_active(p->private->dim_scales[i],2,1);
535 }else{
536 // make bracket thumbs invisible */
537 _sv_dim_widget_set_thumb_active(p->private->dim_scales[i],0,0);
538 _sv_dim_widget_set_thumb_active(p->private->dim_scales[i],2,0);
543 static int _v_swizzle(int y, int height){
544 int yy = height >> 5;
545 if(y < yy)
546 return (y<<5)+31;
548 y -= yy;
549 yy = (height+16) >> 5;
550 if(y < yy)
551 return (y<<5)+15;
553 y -= yy;
554 yy = (height+8) >> 4;
555 if(y < yy)
556 return (y<<4)+7;
558 y -= yy;
559 yy = (height+4) >> 3;
560 if(y < yy)
561 return (y<<3)+3;
563 y -= yy;
564 yy = (height+2) >> 2;
565 if(y < yy)
566 return (y<<2)+1;
568 y -= yy;
569 return y<<1;
572 static void _sv_panel2d_center_callback(sv_dim_list_t *dptr){
573 sv_dim_t *d = dptr->d;
574 sv_panel_t *p = dptr->p;
575 int axisp = (d == p->private->x_d || d == p->private->y_d);
577 if(!axisp){
578 // mid slider of a non-axis dimension changed, rerender
579 _sv_panel2d_mark_recompute(p);
580 }else{
581 // mid slider of an axis dimension changed, move crosshairs
582 _sv_panel2d_update_crosshairs(p);
586 static void _sv_panel2d_bracket_callback(sv_dim_list_t *dptr){
587 sv_dim_t *d = dptr->d;
588 sv_panel_t *p = dptr->p;
589 int axisp = (d == p->private->x_d || d == p->private->y_d);
591 if(axisp)
592 _sv_panel2d_mark_recompute(p);
596 static void _sv_panel2d_dimchange_callback(GtkWidget *button,gpointer in){
597 sv_panel_t *p = (sv_panel_t *)in;
599 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))){
601 _sv_undo_push();
602 _sv_undo_suspend();
604 _sv_plot_unset_box(PLOT(p->private->graph));
605 _sv_panel2d_update_xysel(p);
607 _sv_panel2d_clear_pane(p);
608 _sv_panel2d_mark_recompute(p);
609 _sv_panel2d_update_crosshairs(p);
611 _sv_undo_resume();
615 static void _sv_panel2d_crosshairs_callback(sv_panel_t *p){
616 double x=PLOT(p->private->graph)->selx;
617 double y=PLOT(p->private->graph)->sely;
618 int i;
620 _sv_undo_push();
621 _sv_undo_suspend();
623 //plot_snap_crosshairs(PLOT(p->private->graph));
625 for(i=0;i<p->dimensions;i++){
626 sv_dim_t *d = p->dimension_list[i].d;
627 if(d == p->private->x_d){
628 _sv_dim_widget_set_thumb(p->private->dim_scales[i],1,x);
631 if(d == p->private->y_d){
632 _sv_dim_widget_set_thumb(p->private->dim_scales[i],1,y);
635 p->private->oldbox_active = 0;
638 // dimension setting might have enforced granularity restrictions;
639 // have the display reflect that
640 x = p->private->x_d->val;
641 y = p->private->y_d->val;
643 _sv_plot_set_crosshairs(PLOT(p->private->graph),x,y);
645 _sv_panel_dirty_legend(p);
646 _sv_undo_resume();
649 static void _sv_panel2d_box_callback(void *in, int state){
650 sv_panel_t *p = (sv_panel_t *)in;
651 _sv_panel2d_t *p2 = p->subtype->p2;
652 _sv_plot_t *plot = PLOT(p->private->graph);
654 switch(state){
655 case 0: // box set
656 _sv_undo_push();
657 _sv_plot_box_vals(plot,p2->oldbox);
658 p->private->oldbox_active = plot->box_active;
659 break;
660 case 1: // box activate
661 _sv_undo_push();
662 _sv_undo_suspend();
664 _sv_panel2d_crosshairs_callback(p);
666 _sv_dim_widget_set_thumb(p2->x_scale,0,p2->oldbox[0]);
667 _sv_dim_widget_set_thumb(p2->x_scale,2,p2->oldbox[1]);
668 _sv_dim_widget_set_thumb(p2->y_scale,0,p2->oldbox[2]);
669 _sv_dim_widget_set_thumb(p2->y_scale,2,p2->oldbox[3]);
670 p->private->oldbox_active = 0;
671 _sv_undo_resume();
672 break;
674 _sv_panel_update_menus(p);
677 // subtype entry point for legend redraws; lock held
678 static int _sv_panel2d_legend_redraw(sv_panel_t *p){
679 _sv_plot_t *plot = PLOT(p->private->graph);
681 if(p->private->legend_progress_count)return 0;
682 p->private->legend_progress_count++;
683 _sv_panel2d_update_legend(p);
684 _sv_panel_clean_legend(p);
686 gdk_unlock();
687 _sv_plot_draw_scales(plot);
688 gdk_lock();
690 _sv_plot_expose_request(plot);
691 return 1;
694 // only called for resize events
695 static void _sv_panel2d_recompute_callback(void *ptr){
696 sv_panel_t *p = (sv_panel_t *)ptr;
697 int i;
699 gdk_lock ();
700 _sv_panel2d_mark_recompute(p);
701 _sv_panel2d_compute(p,NULL); // initial scale setup
703 // temporary: blank background to checks
704 _sv_plot_t *plot = PLOT(p->private->graph);
705 int pw = plot->x.pixels;
706 int ph = plot->y.pixels;
707 for(i=0;i<ph;i++)
708 render_checks((_sv_ucolor_t *)plot->datarect+pw*i, pw, i);
710 gdk_unlock();
713 static void _sv_panel2d_undo_log(_sv_panel_undo_t *u, sv_panel_t *p){
714 _sv_panel2d_t *p2 = p->subtype->p2;
715 int i;
717 // alloc fields as necessary
719 if(!u->mappings)
720 u->mappings = calloc(p->objectives,sizeof(*u->mappings));
721 if(!u->scale_vals[0])
722 u->scale_vals[0] = calloc(p->objectives,sizeof(**u->scale_vals));
723 if(!u->scale_vals[1])
724 u->scale_vals[1] = calloc(p->objectives,sizeof(**u->scale_vals));
725 if(!u->scale_vals[2])
726 u->scale_vals[2] = calloc(p->objectives,sizeof(**u->scale_vals));
728 // populate undo
729 for(i=0;i<p->objectives;i++){
730 u->mappings[i] = p2->mappings[i].mapnum;
731 u->scale_vals[0][i] = _sv_slider_get_value(p2->range_scales[i],0);
732 u->scale_vals[1][i] = _sv_slider_get_value(p2->range_scales[i],1);
733 u->scale_vals[2][i] = _sv_slider_get_value(p2->range_scales[i],2);
736 u->x_d = p2->x_dnum;
737 u->y_d = p2->y_dnum;
738 u->box[0] = p2->oldbox[0];
739 u->box[1] = p2->oldbox[1];
740 u->box[2] = p2->oldbox[2];
741 u->box[3] = p2->oldbox[3];
742 u->box_active = p->private->oldbox_active;
745 static void _sv_panel2d_undo_restore(_sv_panel_undo_t *u, sv_panel_t *p){
746 _sv_panel2d_t *p2 = p->subtype->p2;
747 _sv_plot_t *plot = PLOT(p->private->graph);
748 int i;
750 // go in through widgets
751 for(i=0;i<p->objectives;i++){
752 gtk_combo_box_set_active(GTK_COMBO_BOX(p2->range_pulldowns[i]),u->mappings[i]);
753 _sv_slider_set_value(p2->range_scales[i],0,u->scale_vals[0][i]);
754 _sv_slider_set_value(p2->range_scales[i],1,u->scale_vals[1][i]);
755 _sv_slider_set_value(p2->range_scales[i],2,u->scale_vals[2][i]);
758 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p2->dim_xb[u->x_d]),TRUE);
759 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p2->dim_yb[u->y_d]),TRUE);
761 _sv_panel2d_update_xysel(p);
763 if(u->box_active){
764 p2->oldbox[0] = u->box[0];
765 p2->oldbox[1] = u->box[1];
766 p2->oldbox[2] = u->box[2];
767 p2->oldbox[3] = u->box[3];
768 _sv_plot_box_set(plot,u->box);
769 p->private->oldbox_active = 1;
770 }else{
771 _sv_plot_unset_box(plot);
772 p->private->oldbox_active = 0;
776 static void _sv_panel2d_realize(sv_panel_t *p){
777 _sv_panel2d_t *p2 = p->subtype->p2;
778 int i;
780 _sv_undo_suspend();
782 p->private->toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL);
783 g_signal_connect_swapped (G_OBJECT (p->private->toplevel), "delete-event",
784 G_CALLBACK (_sv_clean_exit), (void *)SIGINT);
786 // add border to sides with hbox/padding
787 GtkWidget *borderbox = gtk_hbox_new(0,0);
788 gtk_container_add (GTK_CONTAINER (p->private->toplevel), borderbox);
790 // main layout vbox
791 p->private->topbox = gtk_vbox_new(0,0);
792 gtk_box_pack_start(GTK_BOX(borderbox), p->private->topbox, 1,1,4);
793 gtk_container_set_border_width (GTK_CONTAINER (p->private->toplevel), 1);
795 /* spinner, top bar */
797 GtkWidget *hbox = gtk_hbox_new(0,0);
798 gtk_box_pack_start(GTK_BOX(p->private->topbox), hbox, 0,0,0);
799 gtk_box_pack_end(GTK_BOX(hbox),GTK_WIDGET(p->private->spinner),0,0,0);
802 /* plotbox, graph */
804 p->private->graph = GTK_WIDGET(_sv_plot_new(_sv_panel2d_recompute_callback,p,
805 (void *)(void *)_sv_panel2d_crosshairs_callback,p,
806 _sv_panel2d_box_callback,p,0));
807 p->private->plotbox = p->private->graph;
808 gtk_box_pack_start(GTK_BOX(p->private->topbox), p->private->plotbox, 1,1,2);
811 /* obj box */
813 p2->obj_table = gtk_table_new(p->objectives, 5, 0);
814 gtk_box_pack_start(GTK_BOX(p->private->topbox), p2->obj_table, 0,0,1);
816 /* objective sliders */
817 p2->range_scales = calloc(p->objectives,sizeof(*p2->range_scales));
818 p2->range_pulldowns = calloc(p->objectives,sizeof(*p2->range_pulldowns));
819 p2->alphadel = calloc(p->objectives,sizeof(*p2->alphadel));
820 p2->mappings = calloc(p->objectives,sizeof(*p2->mappings));
821 for(i=0;i<p->objectives;i++){
822 GtkWidget **sl = calloc(3,sizeof(*sl));
823 sv_obj_t *o = p->objective_list[i].o;
824 int lo = o->scale->val_list[0];
825 int hi = o->scale->val_list[o->scale->vals-1];
827 /* label */
828 GtkWidget *label = gtk_label_new(o->name);
829 gtk_misc_set_alignment(GTK_MISC(label),1.,.5);
830 gtk_table_attach(GTK_TABLE(p2->obj_table),label,0,1,i,i+1,
831 GTK_FILL,0,8,0);
833 /* mapping pulldown */
835 GtkWidget *menu=_gtk_combo_box_new_markup();
836 int j;
837 for(j=0;j<_sv_mapping_names();j++)
838 gtk_combo_box_append_text (GTK_COMBO_BOX (menu), _sv_mapping_name(j));
839 gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
840 g_signal_connect (G_OBJECT (menu), "changed",
841 G_CALLBACK (_sv_panel2d_mapchange_callback), p->objective_list+i);
842 gtk_table_attach(GTK_TABLE(p2->obj_table),menu,4,5,i,i+1,
843 GTK_SHRINK,GTK_SHRINK,0,0);
844 p2->range_pulldowns[i] = menu;
847 /* the range mapping slices/slider */
848 sl[0] = _sv_slice_new(_sv_panel2d_map_callback,p->objective_list+i);
849 sl[1] = _sv_slice_new(_sv_panel2d_map_callback,p->objective_list+i);
850 sl[2] = _sv_slice_new(_sv_panel2d_map_callback,p->objective_list+i);
852 gtk_table_attach(GTK_TABLE(p2->obj_table),sl[0],1,2,i,i+1,
853 GTK_EXPAND|GTK_FILL,0,0,0);
854 gtk_table_attach(GTK_TABLE(p2->obj_table),sl[1],2,3,i,i+1,
855 GTK_EXPAND|GTK_FILL,0,0,0);
856 gtk_table_attach(GTK_TABLE(p2->obj_table),sl[2],3,4,i,i+1,
857 GTK_EXPAND|GTK_FILL,0,0,0);
858 p2->range_scales[i] = _sv_slider_new((_sv_slice_t **)sl,3,o->scale->label_list,o->scale->val_list,
859 o->scale->vals,_SV_SLIDER_FLAG_INDEPENDENT_MIDDLE);
860 gtk_table_set_col_spacing(GTK_TABLE(p2->obj_table),3,5);
862 _sv_slice_thumb_set((_sv_slice_t *)sl[0],lo);
863 _sv_slice_thumb_set((_sv_slice_t *)sl[1],lo);
864 _sv_slice_thumb_set((_sv_slice_t *)sl[2],hi);
865 _sv_mapping_setup(&p2->mappings[i],0.,1.,0);
866 _sv_slider_set_gradient(p2->range_scales[i], &p2->mappings[i]);
870 /* dims */
872 p2->dim_table = gtk_table_new(p->dimensions,4,0);
873 gtk_box_pack_start(GTK_BOX(p->private->topbox), p2->dim_table, 0,0,4);
875 GtkWidget *first_x = NULL;
876 GtkWidget *first_y = NULL;
877 GtkWidget *pressed_y = NULL;
878 p->private->dim_scales = calloc(p->dimensions,sizeof(*p->private->dim_scales));
879 p2->dim_xb = calloc(p->dimensions,sizeof(*p2->dim_xb));
880 p2->dim_yb = calloc(p->dimensions,sizeof(*p2->dim_yb));
882 for(i=0;i<p->dimensions;i++){
883 sv_dim_t *d = p->dimension_list[i].d;
885 /* label */
886 GtkWidget *label = gtk_label_new(d->legend);
887 gtk_misc_set_alignment(GTK_MISC(label),1.,.5);
888 gtk_table_attach(GTK_TABLE(p2->dim_table),label,0,1,i,i+1,
889 GTK_FILL,0,5,0);
891 /* x/y radio buttons */
892 if(!(d->flags & SV_DIM_NO_X)){
893 if(first_x)
894 p2->dim_xb[i] = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(first_x),"X");
895 else{
896 first_x = p2->dim_xb[i] = gtk_radio_button_new_with_label(NULL,"X");
897 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p2->dim_xb[i]),TRUE);
899 gtk_table_attach(GTK_TABLE(p2->dim_table),p2->dim_xb[i],1,2,i,i+1,
900 GTK_SHRINK,0,3,0);
903 if(!(d->flags & SV_DIM_NO_Y)){
904 if(first_y)
905 p2->dim_yb[i] = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(first_y),"Y");
906 else
907 first_y = p2->dim_yb[i] = gtk_radio_button_new_with_label(NULL,"Y");
908 if(!pressed_y && p2->dim_xb[i]!=first_x){
909 pressed_y = p2->dim_yb[i];
910 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p2->dim_yb[i]),TRUE);
912 gtk_table_attach(GTK_TABLE(p2->dim_table),p2->dim_yb[i],2,3,i,i+1,
913 GTK_SHRINK,0,3,0);
916 p->private->dim_scales[i] =
917 _sv_dim_widget_new(p->dimension_list+i,_sv_panel2d_center_callback,_sv_panel2d_bracket_callback);
919 gtk_table_attach(GTK_TABLE(p2->dim_table),
920 p->private->dim_scales[i]->t,
921 3,4,i,i+1,
922 GTK_EXPAND|GTK_FILL,0,0,0);
925 for(i=0;i<p->dimensions;i++){
926 if(p2->dim_xb[i])
927 g_signal_connect (G_OBJECT (p2->dim_xb[i]), "toggled",
928 G_CALLBACK (_sv_panel2d_dimchange_callback), p);
929 if(p2->dim_yb[i])
930 g_signal_connect (G_OBJECT (p2->dim_yb[i]), "toggled",
931 G_CALLBACK (_sv_panel2d_dimchange_callback), p);
935 _sv_panel2d_update_xysel(p);
937 gtk_widget_realize(p->private->toplevel);
938 gtk_widget_realize(p->private->graph);
939 gtk_widget_realize(GTK_WIDGET(p->private->spinner));
940 gtk_widget_show_all(p->private->toplevel);
941 _sv_panel2d_update_xysel(p); // yes, this was already done; however,
942 // gtk clobbered the event setup on the
943 // insensitive buttons when it realized
944 // them. This call will restore them.
946 _sv_undo_resume();
949 static int _sv_panel2d_save(sv_panel_t *p, xmlNodePtr pn){
950 _sv_panel2d_t *p2 = p->subtype->p2;
951 int ret=0,i;
953 xmlNodePtr n;
955 xmlNewProp(pn, (xmlChar *)"type", (xmlChar *)"2d");
957 // box
958 if(p->private->oldbox_active){
959 xmlNodePtr boxn = xmlNewChild(pn, NULL, (xmlChar *) "box", NULL);
960 _xmlNewPropF(boxn, "x1", p2->oldbox[0]);
961 _xmlNewPropF(boxn, "x2", p2->oldbox[1]);
962 _xmlNewPropF(boxn, "y1", p2->oldbox[2]);
963 _xmlNewPropF(boxn, "y2", p2->oldbox[3]);
966 // objective map settings
967 for(i=0;i<p->objectives;i++){
968 sv_obj_t *o = p->objective_list[i].o;
969 xmlNodePtr on = xmlNewChild(pn, NULL, (xmlChar *) "objective", NULL);
970 _xmlNewPropI(on, "position", i);
971 _xmlNewPropI(on, "number", o->number);
972 _xmlNewPropS(on, "name", o->name);
973 _xmlNewPropS(on, "type", o->output_types);
975 // right now Y is the only type; the below is Y-specific
976 n = xmlNewChild(on, NULL, (xmlChar *) "y-map", NULL);
977 _xmlNewPropS(n, "color", _sv_mapping_name(p2->mappings[i].mapnum));
978 _xmlNewPropF(n, "low-bracket", _sv_slider_get_value(p2->range_scales[i],0));
979 _xmlNewPropF(n, "alpha", _sv_slider_get_value(p2->range_scales[i],1));
980 _xmlNewPropF(n, "high-bracket", _sv_slider_get_value(p2->range_scales[i],2));
983 // x/y dim selection
984 n = xmlNewChild(pn, NULL, (xmlChar *) "axes", NULL);
985 _xmlNewPropI(n, "xpos", p2->x_dnum);
986 _xmlNewPropI(n, "ypos", p2->y_dnum);
988 return ret;
991 int _sv_panel2d_load(sv_panel_t *p,
992 _sv_panel_undo_t *u,
993 xmlNodePtr pn,
994 int warn){
995 int i;
997 // check type
998 _xmlCheckPropS(pn,"type","2d", "Panel %d type mismatch in save file.",p->number,&warn);
1000 // box
1001 u->box_active = 0;
1002 _xmlGetChildPropFPreserve(pn, "box", "x1", &u->box[0]);
1003 _xmlGetChildPropFPreserve(pn, "box", "x2", &u->box[1]);
1004 _xmlGetChildPropFPreserve(pn, "box", "y1", &u->box[2]);
1005 _xmlGetChildPropFPreserve(pn, "box", "y2", &u->box[3]);
1007 xmlNodePtr n = _xmlGetChildS(pn, "box", NULL, NULL);
1008 if(n){
1009 u->box_active = 1;
1010 xmlFree(n);
1013 // objective map settings
1014 for(i=0;i<p->objectives;i++){
1015 sv_obj_t *o = p->objective_list[i].o;
1016 xmlNodePtr on = _xmlGetChildI(pn, "objective", "position", i);
1017 if(!on){
1018 _sv_first_load_warning(&warn);
1019 fprintf(stderr,"No save data found for panel %d objective \"%s\".\n",p->number, o->name);
1020 }else{
1021 // check name, type
1022 _xmlCheckPropS(on,"name",o->name, "Objectve position %d name mismatch in save file.",i,&warn);
1023 _xmlCheckPropS(on,"type",o->output_types, "Objectve position %d type mismatch in save file.",i,&warn);
1025 // right now Y is the only type; the below is Y-specific
1026 // load maptype, values
1027 _xmlGetChildPropFPreserve(on, "y-map", "low-bracket", &u->scale_vals[0][i]);
1028 _xmlGetChildPropFPreserve(on, "y-map", "alpha", &u->scale_vals[1][i]);
1029 _xmlGetChildPropFPreserve(on, "y-map", "high-bracket", &u->scale_vals[2][i]);
1030 _xmlGetChildMap(on, "y-map", "color", _sv_mapping_map(), &u->mappings[i],
1031 "Panel %d objective unknown mapping setting", p->number, &warn);
1033 xmlFreeNode(on);
1037 // x/y dim selection
1038 _xmlGetChildPropIPreserve(pn, "axes", "xpos", &u->x_d);
1039 _xmlGetChildPropI(pn, "axes", "ypos", &u->y_d);
1041 return warn;
1045 void _sv_panel_realize(sv_panel_t *p){
1046 if(p && !p->private->realized){
1047 p->private->realize(p);
1049 g_signal_connect (G_OBJECT (p->private->toplevel), "key-press-event",
1050 G_CALLBACK (panel_keypress), p);
1051 gtk_window_set_title (GTK_WINDOW (p->private->toplevel), p->name);
1053 p->private->realized=1;
1055 // generic things that happen in all panel realizations...
1057 // text black or white in the plot?
1058 decide_text_inv(p);
1059 p->private->popmenu = _gtk_menu_new_twocol(p->private->toplevel, menu, p);
1060 _sv_panel_update_menus(p);
1065 int sv_panel_background(int number,
1066 enum sv_background bg){
1068 if(number<0){
1069 fprintf(stderr,"sv_panel_background: Panel number must be >= 0\n");
1070 return -EINVAL;
1073 if(number>_sv_panels || !_sv_panel_list[number]){
1074 fprintf(stderr,"sv_panel_background: Panel number %d does not exist\n",number);
1075 return -EINVAL;
1078 sv_panel_t *p = _sv_panel_list[number];
1079 return set_background(p,bg);
1082 sv_panel_t *_sv_panel(char *name){
1083 int i;
1085 if(name == NULL || name == 0 || !strcmp(name,"")){
1086 return (sv_panel_t *)pthread_getspecific(_sv_panel_key);
1089 for(i=0;i<_sv_panels;i++){
1090 sv_panel_t *p=_sv_panel_list[i];
1091 if(p && p->name && !strcmp(name,p->name)){
1092 pthread_setspecific(_sv_panel_key, (void *)p);
1093 return p;
1096 return NULL;
1099 int sv_panel(char *name){
1100 sv_panel_t *p = _sv_panel(name);
1101 if(p)return 0;
1102 return -EINVAL;
1105 sv_panel_t *sv_panel_new(char *name,
1106 char *objlist,
1107 char *dimlist){
1109 sv_panel_t *p = NULL;
1110 int number;
1111 sv_token *decl = _sv_tokenize_declparam(name);
1112 sv_tokenlist *dim_tokens = NULL;
1113 sv_tokenlist *obj_tokens = NULL;
1114 int i,j;
1116 if(!decl){
1117 fprintf(stderr,"sushivision: Unable to parse panel declaration \"%s\".\n",name);
1118 goto err;
1121 // panel and panel list manipulation must be locked
1122 gdk_threads_enter();
1123 pthread_rwlock_wrlock(panellist_m);
1125 if(_sv_panels == 0){
1126 number=0;
1127 _sv_panel_list = calloc (number+1,sizeof(*_sv_panel_list));
1128 _sv_panels=1;
1129 }else{
1130 for(number=0;number<_sv_panels;number++)
1131 if(!_sv_panel_list[number])break;
1132 if(number==_sv_panels){
1133 _sv_panels=number+1;
1134 _sv_panel_list = realloc (_sv_panel_list,_sv_panels * sizeof(*_sv_panel_list));
1138 p = _sv_panel_list[number] = calloc(1, sizeof(**_sv_panel_list));
1139 p->name = strdup(decl->name);
1140 p->legend = strdup(decl->label);
1141 p->number = number;
1142 p->spinner = _sv_spinner_new();
1144 // parse and sanity check the maps
1145 i=0;
1146 obj_tokens = _sv_tokenize_namelist(objectivelist);
1147 p->objectives = obj_tokens->n;
1148 p->objective_list = malloc(p->objectives*sizeof(*p->objective_list));
1149 for(i=0;i<p->objectives;i++){
1150 char *name = obj_tokens->list[i]->name;
1151 p->objective_list[i].o = _sv_obj(name);
1152 p->objective_list[i].p = p;
1155 i=0;
1156 dim_tokens = _sv_tokenize_namelist(dimensionlist);
1157 p->dimensions = dim_tokens->n;
1158 p->dimension_list = malloc(p->dimensions*sizeof(*p->dimension_list));
1159 for(i=0;i<p->dimensions;i++){
1160 char *name = dim_tokens->list[i]->name;
1161 sv_dim_t *d = sv_dim(name);
1163 if(!d){
1164 fprintf(stderr,"Panel %d (\"%s\"): Dimension \"%s\" does not exist\n",
1165 number,p->name,name);
1166 errno = -EINVAL;
1167 //XXX leak
1168 return NULL;
1171 if(!d->scale){
1172 fprintf(stderr,"Panel %d (\"%s\"): Dimension \"%s\" has a NULL scale\n",
1173 number,p->name,name);
1174 errno = -EINVAL;
1175 //XXX leak
1176 return NULL;
1179 p->dimension_list[i].d = d;
1180 p->dimension_list[i].p = p;
1183 _sv_tokenlist_free(obj_tokens);
1184 _sv_tokenlist_free(dim_tokens);
1185 return p;
1193 if(!p)return NULL;
1195 _sv_panel2d_t *p2 = calloc(1, sizeof(*p2));
1196 int fout_offsets[_sv_functions];
1198 p->subtype =
1199 calloc(1, sizeof(*p->subtype)); /* the union is alloced not
1200 embedded as its internal
1201 structure must be hidden */
1202 p->subtype->p2 = p2;
1203 p->type = SV_PANEL_2D;
1204 p->private->bg_type = SV_BG_CHECKS;
1206 // verify all the objectives have scales
1207 for(i=0;i<p->objectives;i++){
1208 if(!p->objective_list[i].o->scale){
1209 fprintf(stderr,"All objectives in a 2d panel must have a scale\n");
1210 errno = -EINVAL;
1211 return NULL;
1215 p->private->realize = _sv_panel2d_realize;
1216 p->private->map_action = _sv_panel2d_map_redraw;
1217 p->private->legend_action = _sv_panel2d_legend_redraw;
1218 p->private->compute_action = _sv_panel2d_compute;
1219 p->private->request_compute = _sv_panel2d_mark_recompute;
1220 p->private->crosshair_action = _sv_panel2d_crosshairs_callback;
1221 p->private->print_action = _sv_panel2d_print;
1222 p->private->undo_log = _sv_panel2d_undo_log;
1223 p->private->undo_restore = _sv_panel2d_undo_restore;
1224 p->private->save_action = _sv_panel2d_save;
1225 p->private->load_action = _sv_panel2d_load;
1227 /* set up helper data structures for rendering */
1229 /* determine which functions are actually needed; if it's referenced
1230 by an objective, it's used. Precache them in dense form. */
1232 int fn = _sv_functions;
1233 int used[fn],count=0,offcount=0;
1234 memset(used,0,sizeof(used));
1235 memset(fout_offsets,-1,sizeof(fout_offsets));
1237 for(i=0;i<p->objectives;i++){
1238 sv_obj_t *o = p->objective_list[i].o;
1239 for(j=0;j<o->outputs;j++)
1240 used[o->function_map[j]]=1;
1243 for(i=0;i<fn;i++)
1244 if(used[i]){
1245 sv_func_t *f = _sv_function_list[i];
1246 fout_offsets[i] = offcount;
1247 offcount += f->outputs;
1248 count++;
1251 p2->used_functions = count;
1252 p2->used_function_list = calloc(count, sizeof(*p2->used_function_list));
1254 for(count=0,i=0;i<fn;i++)
1255 if(used[i]){
1256 p2->used_function_list[count]=_sv_function_list[i];
1257 count++;
1261 /* set up computation/render helpers for Y planes */
1263 /* set up Y object mapping index */
1265 int yobj_count = 0;
1267 for(i=0;i<p->objectives;i++){
1268 sv_obj_t *o = p->objective_list[i].o;
1269 if(o->private->y_func) yobj_count++;
1272 p2->y_obj_num = yobj_count;
1273 p2->y_obj_list = calloc(yobj_count, sizeof(*p2->y_obj_list));
1274 p2->y_obj_to_panel = calloc(yobj_count, sizeof(*p2->y_obj_to_panel));
1275 p2->y_obj_from_panel = calloc(p->objectives, sizeof(*p2->y_obj_from_panel));
1277 yobj_count=0;
1278 for(i=0;i<p->objectives;i++){
1279 sv_obj_t *o = p->objective_list[i].o;
1280 if(o->private->y_func){
1281 p2->y_obj_list[yobj_count] = o;
1282 p2->y_obj_to_panel[yobj_count] = i;
1283 p2->y_obj_from_panel[i] = yobj_count;
1284 yobj_count++;
1285 }else
1286 p2->y_obj_from_panel[i] = -1;
1291 /* set up function Y output value demultiplex helper */
1293 p2->y_fout_offset = calloc(p2->y_obj_num, sizeof(*p2->y_fout_offset));
1294 for(i=0;i<p2->y_obj_num;i++){
1295 sv_obj_t *o = p2->y_obj_list[i];
1296 int funcnum = o->private->y_func->number;
1297 p2->y_fout_offset[i] = fout_offsets[funcnum] + o->private->y_fout;
1301 p2->y_map = calloc(p2->y_obj_num,sizeof(*p2->y_map));
1302 p2->y_planetodo = calloc(p2->y_obj_num,sizeof(*p2->y_planetodo));
1303 p2->y_planes = calloc(p2->y_obj_num,sizeof(*p2->y_planes));
1305 return p;
1311 void _sv_panel_undo_log(sv_panel_t *p, _sv_panel_undo_t *u){
1312 u->cross_mode = PLOT(p->private->graph)->cross_active;
1313 u->legend_mode = PLOT(p->private->graph)->legend_active;
1314 u->grid_mode = PLOT(p->private->graph)->grid_mode;
1315 u->text_mode = PLOT(p->private->graph)->bg_inv;
1316 u->bg_mode = p->private->bg_type;
1317 u->menu_cursamp = p->private->menu_cursamp;
1318 u->oversample_n = p->private->oversample_n;
1319 u->oversample_d = p->private->oversample_d;
1321 // panel-subtype-specific population
1322 p->private->undo_log(u,p);
1325 void _sv_panel_undo_restore(sv_panel_t *p, _sv_panel_undo_t *u){
1326 // go in through setting routines
1327 _sv_plot_set_crossactive(PLOT(p->private->graph),u->cross_mode);
1328 _sv_plot_set_legendactive(PLOT(p->private->graph),u->legend_mode);
1329 set_background(p, u->bg_mode); // must be first; it can frob grid and test
1330 set_text(p, u->text_mode);
1331 set_grid(p, u->grid_mode);
1332 p->private->menu_cursamp = u->menu_cursamp;
1333 res_set(p, u->oversample_n, u->oversample_d);
1335 // panel-subtype-specific restore
1336 p->private->undo_restore(u,p);
1338 _sv_panel_dirty_legend(p);
1341 int _sv_panel_save(sv_panel_t *p, xmlNodePtr instance){
1342 if(!p) return 0;
1343 char buffer[80];
1344 int ret=0;
1346 xmlNodePtr pn = xmlNewChild(instance, NULL, (xmlChar *) "panel", NULL);
1347 xmlNodePtr n;
1349 _xmlNewPropI(pn, "number", p->number);
1350 _xmlNewPropS(pn, "name", p->name);
1352 // let the panel subtype handler fill in type
1353 // we're only saving settings independent of subtype
1355 // background
1356 n = xmlNewChild(pn, NULL, (xmlChar *) "background", NULL);
1357 _xmlNewMapProp(n, "color", bgmap, p->private->bg_type);
1359 // grid
1360 n = xmlNewChild(pn, NULL, (xmlChar *) "grid", NULL);
1361 _xmlNewMapProp(n, "mode", gridmap, PLOT(p->private->graph)->grid_mode);
1363 // crosshairs
1364 n = xmlNewChild(pn, NULL, (xmlChar *) "crosshairs", NULL);
1365 _xmlNewMapProp(n, "active", crossmap, PLOT(p->private->graph)->cross_active);
1367 // legend
1368 n = xmlNewChild(pn, NULL, (xmlChar *) "legend", NULL);
1369 _xmlNewMapProp(n,"mode", legendmap, PLOT(p->private->graph)->legend_active);
1371 // text
1372 n = xmlNewChild(pn, NULL, (xmlChar *) "text", NULL);
1373 _xmlNewMapProp(n,"color", textmap, PLOT(p->private->graph)->bg_inv);
1375 // resample
1376 n = xmlNewChild(pn, NULL, (xmlChar *) "sampling", NULL);
1377 snprintf(buffer,sizeof(buffer),"%d:%d",
1378 p->private->oversample_n, p->private->oversample_d);
1379 xmlNewProp(n, (xmlChar *)"ratio", (xmlChar *)buffer);
1381 // subtype
1382 if(p->private->save_action)
1383 ret |= p->private->save_action(p, pn);
1385 return ret;
1388 int _sv_panel_load(sv_panel_t *p,
1389 _sv_panel_undo_t *u,
1390 xmlNodePtr pn,
1391 int warn){
1393 // check name
1394 _xmlCheckPropS(pn,"name",p->name,"Panel %d name mismatch in save file.",p->number,&warn);
1396 // background
1397 _xmlGetChildMap(pn, "background", "color", bgmap, &u->bg_mode,
1398 "Panel %d unknown background setting", p->number, &warn);
1399 // grid
1400 _xmlGetChildMap(pn, "grid", "mode", gridmap, &u->grid_mode,
1401 "Panel %d unknown grid mode setting", p->number, &warn);
1402 // crosshairs
1403 _xmlGetChildMap(pn, "crosshairs", "active", crossmap, &u->cross_mode,
1404 "Panel %d unknown crosshair setting", p->number, &warn);
1405 // legend
1406 _xmlGetChildMap(pn, "legend", "mode", legendmap, &u->legend_mode,
1407 "Panel %d unknown legend setting", p->number, &warn);
1408 // text
1409 _xmlGetChildMap(pn, "text", "color", textmap, &u->text_mode,
1410 "Panel %d unknown text color setting", p->number, &warn);
1411 // resample
1412 char *prop = NULL;
1413 _xmlGetChildPropS(pn, "sampling", "ratio", &prop);
1414 if(prop){
1415 int res = sscanf(prop,"%d:%d", &u->oversample_n, &u->oversample_d);
1416 if(res<2){
1417 fprintf(stderr,"Unable to parse sample setting (%s) for panel %d.\n",prop,p->number);
1418 u->oversample_n = p->private->def_oversample_n;
1419 u->oversample_d = p->private->def_oversample_d;
1421 if(u->oversample_d == 0) u->oversample_d = 1;
1422 xmlFree(prop);
1425 // subtype
1426 if(p->private->load_action)
1427 warn = p->private->load_action(p, u, pn, warn);
1429 // any unparsed elements?
1430 xmlNodePtr n = pn->xmlChildrenNode;
1432 while(n){
1433 if (n->type == XML_ELEMENT_NODE) {
1434 _sv_first_load_warning(&warn);
1435 fprintf(stderr,"Unknown option (%s) set for panel %d.\n",n->name,p->number);
1437 n = n->next;
1440 return warn;
1443 int sv_panel_callback_recompute (sv_panel_t *p,
1444 int (*callback)(sv_panel_t *p,void *data),
1445 void *data){
1447 p->private->callback_precompute = callback;
1448 p->private->callback_precompute_data = data;
1449 return 0;