[xiph-commits] r12702 - trunk/sushivision

xiphmont at svn.xiph.org xiphmont at svn.xiph.org
Fri Mar 9 22:33:59 PST 2007


Author: xiphmont
Date: 2007-03-09 22:33:52 -0800 (Fri, 09 Mar 2007)
New Revision: 12702

Added:
   trunk/sushivision/xml.c
   trunk/sushivision/xml.h
Modified:
   trunk/sushivision/Makefile
   trunk/sushivision/dimension.c
   trunk/sushivision/dimension.h
   trunk/sushivision/gtksucks.c
   trunk/sushivision/gtksucks.h
   trunk/sushivision/internal.h
   trunk/sushivision/main.c
   trunk/sushivision/panel-2d.c
   trunk/sushivision/panel.c
   trunk/sushivision/plot.c
   trunk/sushivision/plot.h
   trunk/sushivision/undo.c
Log:
Thorough rehashing of save/load/menu handling such that one struct
centralizes gluing an internal setting to the semantic labelling in
menus and save files.  Mostly eliminates the possibility of new
options resulting in missing one of many scattered pieces of glue
elsewhere.

Load still not 100% implemented




Modified: trunk/sushivision/Makefile
===================================================================
--- trunk/sushivision/Makefile	2007-03-10 02:27:48 UTC (rev 12701)
+++ trunk/sushivision/Makefile	2007-03-10 06:33:52 UTC (rev 12702)
@@ -25,16 +25,16 @@
 SOLDFLAGS = -shared -nostdlib -Wl,-soname="lib$(NAME).so.$(MAJOR)"
 
 SRC       = main.c scale.c plot.c slider.c slice.c spinner.c panel.c panel-1d.c panel-2d.c \
-	mapping.c dimension.c function.c objective.c undo.c gtksucks.c \
+	mapping.c dimension.c function.c objective.c undo.c gtksucks.c xml.c \
 	example_fractal.c example_discrete.c example_chirp.c
 INC       = sushivision.h
 MAN	  =
 EXAMPLES  = sushivision_fractal sushivision_discrete sushivision_chirp
 EX_OBJ    = example_fractal.o example_discrete.o example_chirp.o
 OBJ       = main.o scale.o plot.o slider.o slice.o spinner.c panel.o panel-1d.o panel-2d.o \
-	mapping.o dimension.o function.o objective.o undo.o gtksucks.o
+	mapping.o dimension.o function.o objective.o undo.o gtksucks.o xml.o
 LIBS      = -lpthread -ldl
-CAIROVER  =  >= 1.3.16
+CAIROVER  =  >= 1.4.1
 GTKVER    =  >= 2.10.0
 PKGARG	  = "gtk+-2.0 $(GTKVER) cairo $(CAIROVER) cairo-ft $(CAIROVER) gthread-2.0 libxml-2.0"
 GCF       = -std=gnu99 `pkg-config --cflags $(PKGARG)`

Modified: trunk/sushivision/dimension.c
===================================================================
--- trunk/sushivision/dimension.c	2007-03-10 02:27:48 UTC (rev 12701)
+++ trunk/sushivision/dimension.c	2007-03-10 06:33:52 UTC (rev 12702)
@@ -760,41 +760,50 @@
 
 }
 
+static propmap *typemap[]={
+  &(propmap){"continuous",SUSHIV_DIM_CONTINUOUS, NULL,NULL,NULL},
+  &(propmap){"discrete",SUSHIV_DIM_DISCRETE,     NULL,NULL,NULL},
+  &(propmap){"picklist",SUSHIV_DIM_PICKLIST,     NULL,NULL,NULL},
+  NULL
+};
+
+int _load_dimension(sushiv_dimension_t *d,
+		    sushiv_instance_undo_t *u,
+		    xmlNodePtr dn,
+		    int warn){
+  
+  // check name 
+  xmlCheckPropS(dn,"name",d->name,"Dimension %d name mismatch in save file.",d->number,&warn);
+ 
+  // check type
+  xmlCheckMap(dn,"type",typemap, d->type, "Dimension %d type mismatch in save file.",d->number,&warn);
+
+  // load vals
+  xmlGetPropF(dn,"low-bracket", &u->dim_vals[0][d->number]);
+  xmlGetPropF(dn,"value", &u->dim_vals[1][d->number]);
+  xmlGetPropF(dn,"high-bracket", &u->dim_vals[2][d->number]);
+
+  return warn;
+}
+
 int _save_dimension(sushiv_dimension_t *d, xmlNodePtr instance){  
   if(!d) return 0;
-  char buffer[80];
   int ret=0;
 
   xmlNodePtr dn = xmlNewChild(instance, NULL, (xmlChar *) "dimension", NULL);
 
-  snprintf(buffer,sizeof(buffer),"%d",d->number);
-  xmlNewProp(dn, (xmlChar *)"number", (xmlChar *)buffer);
-  if(d->name)
-    xmlNewProp(dn, (xmlChar *)"name", (xmlChar *)d->name);
+  xmlNewPropI(dn, "number", d->number);
+  xmlNewPropS(dn, "name", d->name);
+  xmlNewMapProp(dn, "type", typemap, d->type);
 
   switch(d->type){
   case SUSHIV_DIM_CONTINUOUS:
-    xmlNewProp(dn, (xmlChar *)"type", (xmlChar *)"continuous");
-    break;
   case SUSHIV_DIM_DISCRETE:
-    xmlNewProp(dn, (xmlChar *)"type", (xmlChar *)"discrete");
-    break;
-  case SUSHIV_DIM_PICKLIST:
-    xmlNewProp(dn, (xmlChar *)"type", (xmlChar *)"picklist");
-    break;
-  }
-
-  switch(d->type){
-  case SUSHIV_DIM_CONTINUOUS:
-  case SUSHIV_DIM_DISCRETE:
-    snprintf(buffer,sizeof(buffer),"%.20g",d->bracket[0]);
-    xmlNewProp(dn, (xmlChar *) "low-bracket", (xmlChar *)buffer);
-    snprintf(buffer,sizeof(buffer),"%.20g",d->bracket[1]);
-    xmlNewProp(dn, (xmlChar *) "high-bracket", (xmlChar *)buffer);
+    xmlNewPropF(dn, "low-bracket", d->bracket[0]);
+    xmlNewPropF(dn, "high-bracket", d->bracket[1]);
     
   case SUSHIV_DIM_PICKLIST:
-    snprintf(buffer,sizeof(buffer),"%.20g",d->val);
-    xmlNewProp(dn, (xmlChar *) "value", (xmlChar *)buffer);
+    xmlNewPropF(dn, "value", d->val);
     break;
   }
 

Modified: trunk/sushivision/dimension.h
===================================================================
--- trunk/sushivision/dimension.h	2007-03-10 02:27:48 UTC (rev 12701)
+++ trunk/sushivision/dimension.h	2007-03-10 06:33:52 UTC (rev 12702)
@@ -73,3 +73,4 @@
 							 void (*center_callback)(sushiv_dimension_list_t *),
 							 void (*bracket_callback)(sushiv_dimension_list_t *));
 extern int _save_dimension(sushiv_dimension_t *d, xmlNodePtr instance);
+extern int _load_dimension(sushiv_dimension_t *d, sushiv_instance_undo_t *u, xmlNodePtr dn, int warn);

Modified: trunk/sushivision/gtksucks.c
===================================================================
--- trunk/sushivision/gtksucks.c	2007-03-10 02:27:48 UTC (rev 12701)
+++ trunk/sushivision/gtksucks.c	2007-03-10 06:33:52 UTC (rev 12702)
@@ -255,14 +255,14 @@
 }
 
 GtkWidget *gtk_menu_new_twocol(GtkWidget *bind, 
-			       menuitem **items,
+			       propmap **items,
 			       void *callback_data){
   
-  menuitem *ptr = *items++;
+  propmap *ptr = *items++;
   GtkWidget *ret = gtk_menu_new();
    
   /* create packable boxes for labels, put left labels in */
-  while(ptr->left){
+  while(ptr){
     GtkWidget *item;
     if(!strcmp(ptr->left,"")){
       // seperator, not item
@@ -289,8 +289,10 @@
       if(ptr->callback)
 	g_signal_connect_swapped (G_OBJECT (item), "activate",
 				  G_CALLBACK (ptr->callback), callback_data);
-      if(ptr->submenu)
-	gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),ptr->submenu);
+      if(ptr->submenu){
+	GtkWidget *submenu = gtk_menu_new_twocol(ret,ptr->submenu,callback_data);
+	gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),submenu);
+      }
     }
     gtk_widget_show_all(item);
     
@@ -324,6 +326,24 @@
   return NULL;
 }
 
+int gtk_menu_item_position(GtkWidget *w){
+  //GtkMenuItem *mi = GTK_MENU_ITEM(w);
+  GtkWidget *box = gtk_widget_get_parent(w);
+  GList *l = gtk_container_get_children(GTK_CONTAINER(box));
+  int i = 0;
+
+  while(l){
+    if((GtkWidget *)l->data == w){
+      g_list_free (l);
+      return i;
+    }
+    i++;
+    l = l->next;
+  }
+
+  return 0;
+}
+
 void gtk_menu_alter_item_label(GtkMenu *m, int pos, char *text){
   GList *l;
   GtkWidget *box=NULL;

Modified: trunk/sushivision/gtksucks.h
===================================================================
--- trunk/sushivision/gtksucks.h	2007-03-10 02:27:48 UTC (rev 12701)
+++ trunk/sushivision/gtksucks.h	2007-03-10 06:33:52 UTC (rev 12702)
@@ -22,22 +22,17 @@
 #ifndef _GTK_SUCKS_H_
 #define _GTK_SUCKS_H_
 
-typedef struct {
-  char *left;
-  char *right;
-  GtkWidget *submenu;
-  void (*callback)(sushiv_panel_t *);
-} menuitem;
-
 extern void gtk_widget_set_sensitive_fixup(GtkWidget *w, gboolean state);
 extern void gtk_widget_remove_events (GtkWidget *widget, gint events);
 extern void gtk_button3_fixup();
 extern void gtk_mutex_fixup();
 extern pthread_mutex_t *gtk_get_mutex();
+
 extern GtkWidget *gtk_menu_new_twocol(GtkWidget *bind, 
-				      menuitem **items,
+				      propmap **items,
 				      void *callback_data);
 extern GtkWidget *gtk_menu_get_item(GtkMenu *m, int pos);
+extern int gtk_menu_item_position(GtkWidget *w);
 extern void gtk_menu_alter_item_label(GtkMenu *m, int pos, char *text);
 extern void gtk_menu_alter_item_right(GtkMenu *m, int pos, char *text);
 extern GtkWidget * gtk_combo_box_new_markup (void);

Modified: trunk/sushivision/internal.h
===================================================================
--- trunk/sushivision/internal.h	2007-03-10 02:27:48 UTC (rev 12701)
+++ trunk/sushivision/internal.h	2007-03-10 06:33:52 UTC (rev 12702)
@@ -19,6 +19,8 @@
  * 
  */
 
+typedef struct sushiv_instance_undo sushiv_instance_undo_t;
+
 #include <time.h>
 #include <signal.h>
 #include <gtk/gtk.h>
@@ -30,13 +32,25 @@
 #include "spinner.h"
 #include "slider.h"
 #include "scale.h"
-#include "gtksucks.h"
 #include "plot.h"
 #include "dimension.h"
 #include "objective.h"
 #include "panel-1d.h"
 #include "panel-2d.h"
 
+// used to glue numeric settings to semantic labels for menus/save files
+typedef struct propmap {
+  char *left;
+  int value;
+
+  char *right;
+  struct propmap **submenu;
+  void (*callback)(sushiv_panel_t *, GtkWidget *);
+} propmap;
+
+#include "xml.h"
+#include "gtksucks.h"
+
 union sushiv_panel_subtype {
   sushiv_panel1d_t *p1;
   sushiv_panel2d_t *p2;
@@ -118,15 +132,16 @@
   void (*crosshair_action)(sushiv_panel_t *p);
   void (*print_action)(sushiv_panel_t *p, cairo_t *c, int w, int h);
   int (*save_action)(sushiv_panel_t *p, xmlNodePtr pn);
+  int (*load_action)(sushiv_panel_t *p, sushiv_panel_undo_t *u, xmlNodePtr pn, int warn);
 
   void (*undo_log)(sushiv_panel_undo_t *u, sushiv_panel_t *p);
   void (*undo_restore)(sushiv_panel_undo_t *u, sushiv_panel_t *p);
 };
 
-typedef struct sushiv_instance_undo {
+struct sushiv_instance_undo {
   sushiv_panel_undo_t *panels;
   double *dim_vals[3];
-} sushiv_instance_undo_t;
+};
 
 struct sushiv_instance_internal {
   int undo_level;
@@ -157,6 +172,7 @@
 
 extern void _sushiv_undo_log(sushiv_instance_t *s);
 extern void _sushiv_undo_push(sushiv_instance_t *s);
+extern void _sushiv_undo_pop(sushiv_instance_t *s);
 extern void _sushiv_undo_suspend(sushiv_instance_t *s);
 extern void _sushiv_undo_resume(sushiv_instance_t *s);
 extern void _sushiv_undo_restore(sushiv_instance_t *s);
@@ -171,7 +187,10 @@
 extern void _sushiv_panel_update_menus(sushiv_panel_t *p);
 
 extern int save_main();
+extern int load_main();
 extern int _save_panel(sushiv_panel_t *p, xmlNodePtr instance);
+extern int _load_panel(sushiv_panel_t *p, sushiv_panel_undo_t *u, xmlNodePtr instance, int warn);
+extern void first_load_warning(int *);
 
 extern sig_atomic_t _sushiv_exiting;
 extern char *filebase;

Modified: trunk/sushivision/main.c
===================================================================
--- trunk/sushivision/main.c	2007-03-10 02:27:48 UTC (rev 12701)
+++ trunk/sushivision/main.c	2007-03-10 06:33:52 UTC (rev 12702)
@@ -23,6 +23,10 @@
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 #include <errno.h>
 #include <math.h>
 #include <signal.h>
@@ -246,30 +250,32 @@
   dirname = strdup(cwdname);
   if(argc>1){
     // file to load specified on commandline
-    filebase = strdup(argv[argc-1]);
-    char *base = strrchr(filebase,'/');
-    
-    // filebase may include a path; pull it off and apply it toward dirname
-    if(base){
-      base[0] = '\0';
-      char *dirbit = strdup(filebase);
-      filebase = base+1;
-      if(g_path_is_absolute(dirbit)){
-	// replace dirname
-	free(dirname);
-	dirname = dirbit;
-      }else{
-	// append to dirname
-	char *buf;
-	asprintf(&buf,"%s/%s",dirname,dirbit);
-	free(dirname);
-	dirname = buf;
+    if(argv[argc-1][0] != '-'){
+      filebase = strdup(argv[argc-1]);
+      char *base = strrchr(filebase,'/');
+      
+      // filebase may include a path; pull it off and apply it toward dirname
+      if(base){
+	base[0] = '\0';
+	char *dirbit = strdup(filebase);
+	filebase = base+1;
+	if(g_path_is_absolute(dirbit)){
+	  // replace dirname
+	  free(dirname);
+	  dirname = dirbit;
+	}else{
+	  // append to dirname
+	  char *buf;
+	  asprintf(&buf,"%s/%s",dirname,dirbit);
+	  free(dirname);
+	  dirname = buf;
+	}
       }
+      asprintf(&filename,"%s/%s",dirname,filebase);
     }
-    
-    // load the state file
+  }
 
-  }else{
+  if(!filename || load_main()){
     if(appname){
       char *base = strrchr(appname,'/');
       if(!base) 
@@ -280,8 +286,8 @@
       asprintf(&filebase, "%s.sushi",base);
     }else
       filebase = strdup("default.sushi");
+    asprintf(&filename,"%s/%s",dirname,filebase);
   }
-  asprintf(&filename,"%s/%s",dirname,filebase);
 
   {
     pthread_t dummy;
@@ -308,15 +314,12 @@
 
 static int save_instance(sushiv_instance_t *s, xmlNodePtr root){
   if(!s) return 0;
-  char buffer[80];
   int i, ret=0;
 
   xmlNodePtr instance = xmlNewChild(root, NULL, (xmlChar *) "instance", NULL);
 
-  snprintf(buffer,sizeof(buffer),"%d",s->number);
-  xmlNewProp(instance, (xmlChar *)"number", (xmlChar *)buffer);
-  if(s->name)
-    xmlNewProp(instance, (xmlChar *)"name", (xmlChar *)s->name);
+  xmlNewPropI(instance, "number", s->number);
+  xmlNewPropS(instance, "name", s->name);
   
   // dimension values are independent of panel
   for(i=0;i<s->dimensions;i++)
@@ -331,6 +334,81 @@
   return ret;
 }
 
+void first_load_warning(int *warn){
+  if(!*warn)
+    fprintf(stderr,"\nWARNING: The data file to be opened is not a perfect match to\n"
+	    "%s.\n\nThis may be because the file is for a different version of\n"
+	    "the executable, or because it is is not a save file for \n%s at all.\n\n"
+	    "Specific warnings follow:\n\n",appname,appname);
+  *warn = 1;
+}
+
+static int load_instance(sushiv_instance_t *s, xmlNodePtr in, int warn){
+  int i;
+
+  // piggyback off undo (as it already goes through the trouble of
+  // doing correct unrolling, which can be tricky)
+
+  // if this instance has an undo stack, pop it all.
+  s->private->undo_level=0;
+  _sushiv_undo_pop(s);
+
+  sushiv_instance_undo_t *u = s->private->undo_stack[s->private->undo_level];
+
+  // load dimensions
+  for(i=0;i<s->dimensions;i++){
+    sushiv_dimension_t *d = s->dimension_list[i];
+    if(d){
+      xmlNodePtr dn = xmlGetChildI(in,"dimension","number",d->number);
+      if(dn){ 
+	warn |= _load_dimension(d,u,dn,warn);
+	xmlFreeNode(dn);
+      }else{
+	first_load_warning(&warn);
+	fprintf(stderr,"Could not find data for dimension \"%s\" in save file.\n",
+		d->name);
+      }
+    }
+  }
+
+  // load panels
+  for(i=0;i<s->panels;i++){
+    sushiv_panel_t *p = s->panel_list[i];
+    if(p){
+      xmlNodePtr pn = xmlGetChildI(in,"panel","number",p->number);
+      if(pn){ 
+	warn |= _load_panel(p,u->panels+i,pn,warn);
+	xmlFreeNode(pn);
+      }else{
+	first_load_warning(&warn);
+	fprintf(stderr,"Could not find data for panel \"%s\" in save file.\n",
+		p->name);
+      }
+    }
+  }
+
+  // if any elements are unclaimed, warn 
+  xmlNodePtr node = in->xmlChildrenNode;
+  while(node){
+    if (node->type == XML_ELEMENT_NODE) {
+      xmlChar *name = xmlGetProp(node, (xmlChar *)"name");
+      first_load_warning(&warn);
+      if(name){
+	fprintf(stderr,"Save file contains data for nonexistant object \"%s\".\n",
+		name);
+	xmlFree(name);
+      }else
+	fprintf(stderr,"Save file root node contains an extra unknown elements.\n");
+    }
+    node = node->next;
+  }
+  
+  // effect the loaded values
+  _sushiv_undo_restore(s);
+
+  return warn;
+}
+
 int save_main(){
   xmlDocPtr doc = NULL;
   xmlNodePtr root_node = NULL;
@@ -344,7 +422,7 @@
 
   // save each instance 
   for(i=0;i<instances;i++)
-   ret |= save_instance(instance_list[i],root_node);
+    ret |= save_instance(instance_list[i],root_node);
 
   xmlSaveFormatFileEnc(filename, doc, "UTF-8", 1);
 
@@ -353,3 +431,78 @@
 
   return ret;
 }
+
+int load_main(){
+  xmlDoc *doc = NULL;
+  xmlNode *root = NULL;
+  int fd,warn=0;
+  int i;
+
+  LIBXML_TEST_VERSION;
+
+  fd = open(filename, O_RDONLY);
+  if(fd<0){
+    GtkWidget *dialog = gtk_message_dialog_new (NULL,0,
+						GTK_MESSAGE_ERROR,
+						GTK_BUTTONS_CLOSE,
+						"Error opening file '%s': %s",
+						filename, strerror (errno));
+    gtk_dialog_run (GTK_DIALOG (dialog));
+    gtk_widget_destroy (dialog);
+    return 1;
+  }
+
+  doc = xmlReadFd(fd, NULL, NULL, 0);
+  close(fd);
+
+  if (doc == NULL) {
+    GtkWidget *dialog = gtk_message_dialog_new (NULL,0,
+						GTK_MESSAGE_ERROR,
+						GTK_BUTTONS_CLOSE,
+						"Error parsing file '%s'",
+						filename);
+    gtk_dialog_run (GTK_DIALOG (dialog));
+    gtk_widget_destroy (dialog);
+    return 1;
+  }
+
+  root = xmlDocGetRootElement(doc);
+
+  // load each instance 
+  for(i=0;i<instances;i++){
+    sushiv_instance_t *s = instance_list[i];
+    if(s){
+      xmlNodePtr in = xmlGetChildI(root,"instance","number",s->number);
+      if(in){ 
+	warn |= load_instance(s,in,warn);
+	xmlFreeNode(in);
+      }else{
+	first_load_warning(&warn);
+	fprintf(stderr,"Could not find data for instance \"%s\" in save file.\n",
+		s->name);
+      }
+    }
+  }
+
+  // if any instances are unclaimed, warn 
+  xmlNodePtr node = root->xmlChildrenNode;
+  
+  while(node){
+    if (node->type == XML_ELEMENT_NODE) {
+      char *name = xmlGetPropS(node, "name");
+      first_load_warning(&warn);
+      if(name){
+	fprintf(stderr,"Save file contains data for nonexistant object \"%s\".\n",
+		  name);
+	xmlFree(name);
+      }else
+	fprintf(stderr,"Save file root node contains an extra unknown elements.\n");
+    }
+    node = node->next; 
+  }
+
+  xmlFreeDoc(doc);
+  xmlCleanupParser();
+  
+  return 0;
+}

Modified: trunk/sushivision/panel-2d.c
===================================================================
--- trunk/sushivision/panel-2d.c	2007-03-10 02:27:48 UTC (rev 12701)
+++ trunk/sushivision/panel-2d.c	2007-03-10 06:33:52 UTC (rev 12702)
@@ -1896,6 +1896,17 @@
   return ret;
 }
 
+int _load_panel_2d(sushiv_panel_t *d,
+		   sushiv_panel_undo_t *u,
+		   xmlNodePtr pn,
+		   int warn){
+  
+  // check type
+
+
+  return warn;
+}
+
 int sushiv_new_panel_2d(sushiv_instance_t *s,
 			int number,
 			const char *name, 

Modified: trunk/sushivision/panel.c
===================================================================
--- trunk/sushivision/panel.c	2007-03-10 02:27:48 UTC (rev 12701)
+++ trunk/sushivision/panel.c	2007-03-10 06:33:52 UTC (rev 12702)
@@ -35,14 +35,113 @@
 #include "internal.h"
 
 extern void _sushiv_wake_workers(void);
+static void wrap_exit(sushiv_panel_t *dummy, GtkWidget *dummyw);
+static void wrap_bg(sushiv_panel_t *p, GtkWidget *w);
+static void wrap_grid(sushiv_panel_t *p, GtkWidget *w);
+static void wrap_text(sushiv_panel_t *p, GtkWidget *w);
+static void wrap_res(sushiv_panel_t *p, GtkWidget *w);
+static void do_load(sushiv_panel_t *p, GtkWidget *dummy);
+static void do_save(sushiv_panel_t *p, GtkWidget *dummy);
+static void _sushiv_panel_print(sushiv_panel_t *p, GtkWidget *dummy);
+static void wrap_undo_up(sushiv_panel_t *p, GtkWidget *dummy);
+static void wrap_undo_down(sushiv_panel_t *p, GtkWidget *dummy);
+static void wrap_legend(sushiv_panel_t *p, GtkWidget *dummy);
+static void wrap_escape(sushiv_panel_t *p, GtkWidget *dummy);
+static void wrap_enter(sushiv_panel_t *p, GtkWidget *dummy);
 
+static propmap *bgmap[]={
+  &(propmap){"white",SUSHIV_BG_WHITE,   "[<i>b</i>]",NULL,wrap_bg},
+  &(propmap){"black",SUSHIV_BG_BLACK,   "[<i>b</i>]",NULL,wrap_bg},
+  &(propmap){"checks",SUSHIV_BG_CHECKS, "[<i>b</i>]",NULL,wrap_bg},
+  NULL
+};
+
+static propmap *gridmap[]={
+  &(propmap){"light",PLOT_GRID_LIGHT,   "[<i>g</i>]",NULL,wrap_grid},
+  &(propmap){"normal",PLOT_GRID_NORMAL, "[<i>g</i>]",NULL,wrap_grid},
+  &(propmap){"dark",PLOT_GRID_DARK,     "[<i>g</i>]",NULL,wrap_grid},
+  &(propmap){"tics",PLOT_GRID_TICS,     "[<i>g</i>]",NULL,wrap_grid},
+  &(propmap){"none",PLOT_GRID_NONE,     "[<i>g</i>]",NULL,wrap_grid},
+  NULL
+};
+
+static propmap *textmap[]={
+  &(propmap){"dark",PLOT_TEXT_DARK,   "[<i>t</i>]",NULL,wrap_text},
+  &(propmap){"light",PLOT_TEXT_LIGHT, "[<i>t</i>]",NULL,wrap_text},
+  NULL
+};
+
+#define RES_DEF 0
+#define RES_1_32 1
+#define RES_1_16 2
+#define RES_1_8 3
+#define RES_1_4 4
+#define RES_1_2 5
+#define RES_1_1 6
+#define RES_2_1 7
+#define RES_4_1 8
+
+// only used for the menus
+static propmap *resmap[]={
+  &(propmap){"default",RES_DEF,  "[<i>m</i>]",NULL,wrap_res},
+  &(propmap){"1:32",RES_1_32,     "[<i>m</i>]",NULL,wrap_res},
+  &(propmap){"1:16",RES_1_16,     "[<i>m</i>]",NULL,wrap_res},
+  &(propmap){"1:8",RES_1_8,      "[<i>m</i>]",NULL,wrap_res},
+  &(propmap){"1:4",RES_1_4,      "[<i>m</i>]",NULL,wrap_res},
+  &(propmap){"1:2",RES_1_2,      "[<i>m</i>]",NULL,wrap_res},
+  &(propmap){"1",RES_1_1,        "[<i>m</i>]",NULL,wrap_res},
+  &(propmap){"2:1",RES_2_1,      "[<i>m</i>]",NULL,wrap_res},
+  &(propmap){"4:1",RES_4_1,      "[<i>m</i>]",NULL,wrap_res},
+  NULL,
+};
+
+static propmap *crossmap[]={
+  &(propmap){"no",0     ,NULL,NULL,NULL},
+  &(propmap){"yes",1    ,NULL,NULL,NULL},
+  NULL
+};
+
+static propmap *legendmap[]={
+  &(propmap){"none",PLOT_LEGEND_NONE,       NULL,NULL,NULL},
+  &(propmap){"shadowed",PLOT_LEGEND_SHADOW, NULL,NULL,NULL},
+  &(propmap){"boxed",PLOT_LEGEND_BOX,       NULL,NULL,NULL},
+  NULL
+};
+
+static propmap *menu[]={
+  &(propmap){"Open",0,"[<i>o</i>]",NULL,do_load},
+  &(propmap){"Save",1,"[<i>s</i>]",NULL,do_save},
+  &(propmap){"Print/Export",2,"[<i>p</i>]",NULL,_sushiv_panel_print},
+
+  &(propmap){"",3,NULL,NULL,NULL},
+
+  &(propmap){"Undo",4,"[<i>bksp</i>]",NULL,&wrap_undo_down},
+  &(propmap){"Redo",5,"[<i>space</i>]",NULL,&wrap_undo_up},
+  &(propmap){"Start zoom box",6,"[<i>enter</i>]",NULL,&wrap_enter},
+  &(propmap){"Clear selection",7,"[<i>escape</i>]",NULL,&wrap_escape},
+  &(propmap){"Toggle Legend",8,"[<i>l</i>]",NULL,&wrap_legend},
+
+  &(propmap){"",9,NULL,NULL,NULL},
+
+  &(propmap){"Background",10,"...",bgmap,NULL},
+  &(propmap){"Text color",11,"...",textmap,NULL},
+  &(propmap){"Grid mode",12,"...",gridmap,NULL},
+  &(propmap){"Sampling",13,"...",resmap,NULL},
+
+  &(propmap){"",14,NULL,NULL,NULL},
+
+  &(propmap){"Quit",15,"[<i>q</i>]",NULL,&wrap_exit},
+
+  NULL
+};
+
 static void decide_text_inv(sushiv_panel_t *p){
   if(p->private->graph){
     Plot *plot = PLOT(p->private->graph);
     if(p->private->bg_type == SUSHIV_BG_WHITE)
-      plot_set_bg_invert(plot,0);
+      plot_set_bg_invert(plot,PLOT_TEXT_DARK);
     else
-      plot_set_bg_invert(plot,1);
+      plot_set_bg_invert(plot,PLOT_TEXT_LIGHT);
   }
 }
 
@@ -66,16 +165,16 @@
   }
 }
 
-static void wrap_exit(sushiv_panel_t *dummy){
+static void wrap_exit(sushiv_panel_t *dummy, GtkWidget *dummyw){
   _sushiv_clean_exit(SIGINT);
 }
 
 // precipitated actions perform undo push
-static void wrap_enter(sushiv_panel_t *p){
+static void wrap_enter(sushiv_panel_t *p, GtkWidget *dummy){
   plot_do_enter(PLOT(p->private->graph));
 }
 
-static void wrap_escape(sushiv_panel_t *p){
+static void wrap_escape(sushiv_panel_t *p, GtkWidget *dummy){
   _sushiv_undo_push(p->sushi);
   _sushiv_undo_suspend(p->sushi);
 
@@ -85,7 +184,7 @@
   _sushiv_undo_resume(p->sushi);
 }
 
-static void wrap_legend(sushiv_panel_t *p){
+static void wrap_legend(sushiv_panel_t *p, GtkWidget *dummy){
   _sushiv_undo_push(p->sushi);
   _sushiv_undo_suspend(p->sushi);
 
@@ -106,21 +205,10 @@
   _sushiv_undo_resume(p->sushi);
 }
 
-static void light_scale(sushiv_panel_t *p){
-  set_grid(p,PLOT_GRID_LIGHT);
+static void wrap_grid(sushiv_panel_t *p, GtkWidget *w){
+  int pos = gtk_menu_item_position(w);
+  set_grid(p, gridmap[pos]->value);
 }
-static void mid_scale(sushiv_panel_t *p){
-  set_grid(p,PLOT_GRID_NORMAL);
-}
-static void dark_scale(sushiv_panel_t *p){
-  set_grid(p,PLOT_GRID_DARK);
-}
-static void tic_scale(sushiv_panel_t *p){
-  set_grid(p,PLOT_GRID_TICS);
-}
-static void no_scale(sushiv_panel_t *p){
-  set_grid(p,0);
-}
 
 static int set_background(sushiv_panel_t *p,
 			  enum sushiv_background bg){
@@ -133,7 +221,7 @@
   pi->bg_type = bg;
   
   decide_text_inv(p);
-  mid_scale(p);
+  set_grid(p,PLOT_GRID_NORMAL);
   redraw_if_running(p);
   _sushiv_panel_update_menus(p);
 
@@ -141,40 +229,21 @@
   return 0;
 }
 
-static void white_bg(sushiv_panel_t *p){
-  set_background(p,SUSHIV_BG_WHITE);
+static void wrap_bg(sushiv_panel_t *p, GtkWidget *w){
+  int pos = gtk_menu_item_position(w);
+  set_background(p, bgmap[pos]->value);
 }
-static void black_bg(sushiv_panel_t *p){
-  set_background(p,SUSHIV_BG_BLACK);
-}
-static void checked_bg(sushiv_panel_t *p){
-  set_background(p,SUSHIV_BG_CHECKS);
-}
+
 static void cycle_bg(sushiv_panel_t *p){
-  switch(p->private->bg_type){
-  case 0:
-    black_bg(p);
-    break;
-  case 1:
-    checked_bg(p);
-    break;
-  default:
-    white_bg(p);
-    break;
-  }
+  int menupos = propmap_pos(bgmap, p->private->bg_type) + 1;
+  if(bgmap[menupos] == NULL) menupos = 0;
+  set_background(p, bgmap[menupos]->value);
 }
+
 static void cycleB_bg(sushiv_panel_t *p){
-  switch(p->private->bg_type){
-  case 0:
-    checked_bg(p);
-    break;
-  case 1:
-    white_bg(p);
-    break;
-  default:
-    black_bg(p);
-    break;
-  }
+  int menupos = propmap_pos(bgmap, p->private->bg_type) - 1;
+  if(menupos<0) menupos = propmap_last(bgmap);
+  set_background(p, bgmap[menupos]->value);
 }
 
 static void set_text(sushiv_panel_t *p, int mode){
@@ -188,70 +257,35 @@
   _sushiv_undo_resume(p->sushi);
 }
 
-static void black_text(sushiv_panel_t *p){
-  set_text(p,0);
+static void wrap_text(sushiv_panel_t *p, GtkWidget *w){
+  int pos = gtk_menu_item_position(w);
+  set_text(p, textmap[pos]->value);
 }
-static void white_text(sushiv_panel_t *p){
-  set_text(p,1);
-}
+
 static void cycle_text(sushiv_panel_t *p){
-  switch(PLOT(p->private->graph)->bg_inv){
-  case 0:
-    white_text(p);
-    break;
-  default:
-    black_text(p);
-    break;
-  }
+  int menupos = propmap_pos(textmap, PLOT(p->private->graph)->bg_inv) + 1;
+  if(textmap[menupos] == NULL) menupos = 0;
+  set_text(p, textmap[menupos]->value);
 }
 
 static void cycle_grid(sushiv_panel_t *p){
-  switch(PLOT(p->private->graph)->grid_mode){
-  case PLOT_GRID_LIGHT:
-    mid_scale(p);
-    break;
-  case PLOT_GRID_NORMAL:
-    dark_scale(p);
-    break;
-  case PLOT_GRID_DARK:
-    tic_scale(p);
-    break;
-  case PLOT_GRID_TICS:
-    no_scale(p);
-    break;
-  default:
-    light_scale(p);
-    break;
-  }
+  int menupos = propmap_pos(gridmap, PLOT(p->private->graph)->grid_mode) + 1;
+  if(gridmap[menupos] == NULL) menupos = 0;
+  set_grid(p, gridmap[menupos]->value);
 }
 static void cycleB_grid(sushiv_panel_t *p){
-  switch(PLOT(p->private->graph)->grid_mode){
-  case PLOT_GRID_LIGHT:
-    no_scale(p);
-    break;
-  case PLOT_GRID_NORMAL:
-    light_scale(p);
-    break;
-  case PLOT_GRID_DARK:
-    mid_scale(p);
-    break;
-  case PLOT_GRID_TICS:
-    dark_scale(p);
-    break;
-  default:
-    tic_scale(p);
-    break;
-  }
+  int menupos = propmap_pos(gridmap, PLOT(p->private->graph)->grid_mode) - 1;
+  if(menupos<0) menupos = propmap_last(gridmap);
+  set_grid(p, gridmap[menupos]->value);
 }
 
-static void res_set(sushiv_panel_t *p, int n, int d, int menu){
+static void res_set(sushiv_panel_t *p, int n, int d){
   if(n != p->private->oversample_n ||
      d != p->private->oversample_d){
 
     _sushiv_undo_push(p->sushi);
     _sushiv_undo_suspend(p->sushi);
     
-    p->private->menu_cursamp=menu;
     p->private->oversample_n = n;
     p->private->oversample_d = d;
     _sushiv_panel_update_menus(p);
@@ -261,97 +295,55 @@
   }
 }
 
-static void res_def(sushiv_panel_t *p){
-  p->private->menu_cursamp=0;
-  res_set(p,p->private->def_oversample_n,p->private->def_oversample_d,0);
-}
-static void res_1_32(sushiv_panel_t *p){
-  res_set(p,1,32,1);
-}
-static void res_1_16(sushiv_panel_t *p){
-  res_set(p,1,16,2);
-}
-static void res_1_8(sushiv_panel_t *p){
-  res_set(p,1,8,3);
-}
-static void res_1_4(sushiv_panel_t *p){
-  res_set(p,1,4,4);
-}
-static void res_1_2(sushiv_panel_t *p){
-  res_set(p,1,2,5);
-}
-static void res_1_1(sushiv_panel_t *p){
-  res_set(p,1,1,6);
-}
-static void res_2_1(sushiv_panel_t *p){
-  res_set(p,2,1,7);
-}
-static void res_4_1(sushiv_panel_t *p){
-  res_set(p,4,1,8);
-}
-
-static void cycle_res(sushiv_panel_t *p){
-  switch(p->private->menu_cursamp){
-  case 0:
-    res_1_32(p);
+// a little different; the menu value is not the internal setting
+static void res_set_pos(sushiv_panel_t *p, int pos){
+  p->private->menu_cursamp = pos;
+  switch(pos){
+  case RES_DEF:
+    res_set(p,p->private->def_oversample_n,p->private->def_oversample_d);
     break;
-  case 1:
-    res_1_16(p);
+  case RES_1_32:
+    res_set(p,1,32);
     break;
-  case 2:
-    res_1_8(p);
+  case RES_1_16:
+    res_set(p,1,16);
     break;
-  case 3:
-    res_1_4(p);
+  case RES_1_8:
+    res_set(p,1,8);
     break;
-  case 4:
-    res_1_2(p);
+  case RES_1_4:
+    res_set(p,1,4);
     break;
-  case 5:
-    res_1_1(p);
+  case RES_1_2:
+    res_set(p,1,2);
     break;
-  case 6:
-    res_2_1(p);
+  case RES_1_1:
+    res_set(p,1,1);
     break;
-  case 7:
-    res_4_1(p);
+  case RES_2_1:
+    res_set(p,2,1);
     break;
-  default:
-    res_def(p);
+  case RES_4_1:
+    res_set(p,4,1);
     break;
   }
 }
 
+static void wrap_res(sushiv_panel_t *p, GtkWidget *w){
+  int pos = gtk_menu_item_position(w);
+  res_set_pos(p, resmap[pos]->value);
+}
+
+static void cycle_res(sushiv_panel_t *p){
+  int menupos = propmap_pos(resmap, p->private->menu_cursamp) + 1;
+  if(resmap[menupos] == NULL) menupos = 0;
+  res_set_pos(p, resmap[menupos]->value);
+}
+
 static void cycleB_res(sushiv_panel_t *p){
-  switch(p->private->menu_cursamp){
-  case 2:
-    res_1_32(p);
-    break;
-  case 3:
-    res_1_16(p);
-    break;
-  case 4:
-    res_1_8(p);
-    break;
-  case 5:
-    res_1_4(p);
-    break;
-  case 6:
-    res_1_2(p);
-    break;
-  case 7:
-    res_1_1(p);
-    break;
-  case 8:
-    res_2_1(p);
-    break;
-  case 0:
-    res_4_1(p);
-    break;
-  default:
-    res_def(p);
-    break;
-  }
+  int menupos = propmap_pos(resmap, p->private->menu_cursamp) - 1;
+  if(menupos<0) menupos = propmap_last(resmap);
+  res_set_pos(p, resmap[menupos]->value);
 }
 
 static GtkPrintSettings *printset=NULL;
@@ -379,7 +371,7 @@
   p->private->print_action(p,c,w,h);
 }
 
-static void _sushiv_panel_print(sushiv_panel_t *p){
+static void _sushiv_panel_print(sushiv_panel_t *p, GtkWidget *dummy){
   GtkPrintOperation *op = gtk_print_operation_new ();
 
   if (printset != NULL) 
@@ -411,14 +403,14 @@
   g_object_unref (op);
 }
 
-static void wrap_undo_down(sushiv_panel_t *p){
+static void wrap_undo_down(sushiv_panel_t *p, GtkWidget *dummy){
   _sushiv_undo_down(p->sushi);
 }
-static void wrap_undo_up(sushiv_panel_t *p){
+static void wrap_undo_up(sushiv_panel_t *p, GtkWidget *dummy){
   _sushiv_undo_up(p->sushi);
 }
 
-static void do_save(sushiv_panel_t *p){
+static void do_save(sushiv_panel_t *p, GtkWidget *dummy){
   GtkWidget *dialog = gtk_file_chooser_dialog_new ("Save",
 						   NULL,
 						   GTK_FILE_CHOOSER_ACTION_SAVE,
@@ -446,7 +438,7 @@
 
 }
 
-static void do_load(sushiv_panel_t *p){
+static void do_load(sushiv_panel_t *p, GtkWidget *dummy){
   GtkWidget *dialog = gtk_file_chooser_dialog_new ("Open",
 						   NULL,
 						   GTK_FILE_CHOOSER_ACTION_OPEN,
@@ -458,83 +450,34 @@
   gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), dirname);
 
   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT){
-    if(filebase)free(filebase);
-    if(filename)free(filename);
-    if(dirname)free(dirname);
-
+    char *temp_filebase = filebase;
+    char *temp_filename = filename;
+    char *temp_dirname = dirname;
     filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
     dirname = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
     filebase = g_path_get_basename(filename);
 
-    //_sushiv_panel_load();
+    if(load_main()){
+
+      free(filebase);
+      free(filename);
+      free(dirname);
+
+      filebase = temp_filebase;
+      filename = temp_filename;
+      dirname = temp_dirname;
+
+    }else{
+      free(temp_filebase);
+      free(temp_filename);
+      free(temp_dirname);
+    }
   }
 
   gtk_widget_destroy (dialog);
 
 }
 
-static menuitem *menu[]={
-  &(menuitem){"Open","[<i>o</i>]",NULL,&do_load},
-  &(menuitem){"Save","[<i>s</i>]",NULL,&do_save},
-  &(menuitem){"Print/Export","[<i>p</i>]",NULL,&_sushiv_panel_print},
-
-  &(menuitem){"",NULL,NULL,NULL},
-
-  &(menuitem){"Undo","[<i>bksp</i>]",NULL,&wrap_undo_down},
-  &(menuitem){"Redo","[<i>space</i>]",NULL,&wrap_undo_up},
-  &(menuitem){"Start zoom box","[<i>enter</i>]",NULL,&wrap_enter},
-  &(menuitem){"Clear selection","[<i>escape</i>]",NULL,&wrap_escape},
-  &(menuitem){"Toggle Legend","[<i>l</i>]",NULL,&wrap_legend},
-
-  &(menuitem){"",NULL,NULL,NULL},
-
-  &(menuitem){"Background","...",NULL,NULL},
-  &(menuitem){"Text color","...",NULL,NULL},
-  &(menuitem){"Grid mode","...",NULL,NULL},
-  &(menuitem){"Sampling","...",NULL,NULL},
-
-  &(menuitem){"",NULL,NULL,NULL},
-
-  &(menuitem){"Quit","[<i>q</i>]",NULL,&wrap_exit},
-
-  &(menuitem){NULL,NULL,NULL,NULL}
-};
-
-static menuitem *menu_bg[]={
-  &(menuitem){"white","[<i>b</i>]",NULL,&white_bg},
-  &(menuitem){"black","[<i>b</i>]",NULL,&black_bg},
-  &(menuitem){"checks","[<i>b</i>]",NULL,&checked_bg},
-  &(menuitem){NULL,NULL,NULL,NULL}
-};
-
-static menuitem *menu_text[]={
-  &(menuitem){"dark","[<i>t</i>]",NULL,&black_text},
-  &(menuitem){"light","[<i>t</i>]",NULL,&white_text},
-  &(menuitem){NULL,NULL,NULL,NULL}
-};
-
-static menuitem *menu_scales[]={
-  &(menuitem){"light","[<i>g</i>]",NULL,light_scale},
-  &(menuitem){"mid","[<i>g</i>]",NULL,mid_scale},
-  &(menuitem){"dark","[<i>g</i>]",NULL,dark_scale},
-  &(menuitem){"tics","[<i>g</i>]",NULL,tic_scale},
-  &(menuitem){"none","[<i>g</i>]",NULL,no_scale},
-  &(menuitem){NULL,NULL,NULL,NULL}
-};
-
-static menuitem *menu_res[]={
-  &(menuitem){"default","[<i>m</i>]",NULL,res_def},
-  &(menuitem){"1:32","[<i>m</i>]",NULL,res_1_32},
-  &(menuitem){"1:16","[<i>m</i>]",NULL,res_1_16},
-  &(menuitem){"1:8","[<i>m</i>]",NULL,res_1_8},
-  &(menuitem){"1:4","[<i>m</i>]",NULL,res_1_4},
-  &(menuitem){"1:2","[<i>m</i>]",NULL,res_1_2},
-  &(menuitem){"1","[<i>m</i>]",NULL,res_1_1},
-  &(menuitem){"2:1","[<i>m</i>]",NULL,res_2_1},
-  &(menuitem){"4:1","[<i>m</i>]",NULL,res_4_1},
-  &(menuitem){NULL,NULL,NULL,NULL}
-};
-
 void _sushiv_panel_update_menus(sushiv_panel_t *p){
 
   // is undo active?
@@ -562,52 +505,25 @@
   }
 
   // make sure menu reflects plot configuration
-  switch(p->private->bg_type){ 
-  case SUSHIV_BG_WHITE:
-    gtk_menu_alter_item_right(GTK_MENU(p->private->popmenu),10,menu_bg[0]->left);
-    break;
-  case SUSHIV_BG_BLACK:
-    gtk_menu_alter_item_right(GTK_MENU(p->private->popmenu),10,menu_bg[1]->left);
-    break;
-  default:
-    gtk_menu_alter_item_right(GTK_MENU(p->private->popmenu),10,menu_bg[2]->left);
-    break;
-  }
+  gtk_menu_alter_item_right(GTK_MENU(p->private->popmenu),
+			    propmap_label_pos(menu,"Background"),
+			    bgmap[propmap_pos(bgmap,p->private->bg_type)]->left);
 
-  switch(PLOT(p->private->graph)->bg_inv){ 
-  case 0:
-    gtk_menu_alter_item_right(GTK_MENU(p->private->popmenu),11,menu_text[0]->left);
-    break;
-  default:
-    gtk_menu_alter_item_right(GTK_MENU(p->private->popmenu),11,menu_text[1]->left);
-    break;
-  }
+  gtk_menu_alter_item_right(GTK_MENU(p->private->popmenu),
+			    propmap_label_pos(menu,"Text color"),
+			    textmap[propmap_pos(textmap,PLOT(p->private->graph)->bg_inv)]->left);
 
-  switch(PLOT(p->private->graph)->grid_mode){ 
-  case PLOT_GRID_LIGHT:
-    gtk_menu_alter_item_right(GTK_MENU(p->private->popmenu),12,menu_scales[0]->left);
-    break;
-  case PLOT_GRID_NORMAL:
-    gtk_menu_alter_item_right(GTK_MENU(p->private->popmenu),12,menu_scales[1]->left);
-    break;
-  case PLOT_GRID_DARK:
-    gtk_menu_alter_item_right(GTK_MENU(p->private->popmenu),12,menu_scales[2]->left);
-    break;
-  case PLOT_GRID_TICS:
-    gtk_menu_alter_item_right(GTK_MENU(p->private->popmenu),12,menu_scales[3]->left);
-    break;
-  default:
-    gtk_menu_alter_item_right(GTK_MENU(p->private->popmenu),12,menu_scales[4]->left);
-    break;
-  }
-
-  {
+  gtk_menu_alter_item_right(GTK_MENU(p->private->popmenu),
+			    propmap_label_pos(menu,"Grid mode"),
+			    gridmap[propmap_pos(gridmap,PLOT(p->private->graph)->grid_mode)]->left);
+   {
     char buffer[80];
     snprintf(buffer,60,"%d:%d",p->private->oversample_n,p->private->oversample_d);
     if(p->private->def_oversample_n == p->private->oversample_n &&
        p->private->def_oversample_d == p->private->oversample_d)
       strcat(buffer," (default)");
-    gtk_menu_alter_item_right(GTK_MENU(p->private->popmenu),13,buffer);
+    gtk_menu_alter_item_right(GTK_MENU(p->private->popmenu),
+			      propmap_label_pos(menu,"Sampling"),buffer);
   }
 }
 
@@ -687,18 +603,18 @@
     return TRUE;
 
   case GDK_s:
-    do_save(p);
+    do_save(p,NULL);
     return TRUE;
   case GDK_o:
-    do_load(p);
+    do_load(p,NULL);
     return TRUE;
     
    case GDK_Escape:
-     wrap_escape(p);
+     wrap_escape(p,NULL);
     return TRUE;
 
   case GDK_Return:case GDK_ISO_Enter:
-    wrap_enter(p);
+    wrap_enter(p,NULL);
     return TRUE;
    
   case GDK_Q:
@@ -719,11 +635,11 @@
     return TRUE;
 
   case GDK_p:
-    _sushiv_panel_print(p);
+    _sushiv_panel_print(p,NULL);
     return TRUE;
 
   case GDK_l:
-    wrap_legend(p);
+    wrap_legend(p,NULL);
     return TRUE;
   } 
 
@@ -744,22 +660,9 @@
 
     // text black or white in the plot?
     decide_text_inv(p);
-
-    // panel right-click menus
-    GtkWidget *bgmenu = gtk_menu_new_twocol(NULL,menu_bg,p);
-    GtkWidget *textmenu = gtk_menu_new_twocol(NULL,menu_text,p);
-    GtkWidget *scalemenu = gtk_menu_new_twocol(NULL,menu_scales,p);
-    GtkWidget *resmenu = gtk_menu_new_twocol(NULL,menu_res,p);
-
-    // not thread safe, we're not threading yet
-    menu[10]->submenu = bgmenu;
-    menu[11]->submenu = textmenu;
-    menu[12]->submenu = scalemenu;
-    menu[13]->submenu = resmenu;
-
     p->private->popmenu = gtk_menu_new_twocol(p->private->toplevel, menu, p);
     _sushiv_panel_update_menus(p);
-
+    
   }
 }
 
@@ -782,7 +685,6 @@
   return 0;  
 }
 
-
 /* request a recomputation with full setup (eg, linking, scales,
    etc) */
 void _sushiv_panel_recompute(sushiv_panel_t *p){
@@ -985,7 +887,8 @@
   set_background(p, u->bg_mode); // must be first; it can frob grid and test
   set_text(p, u->text_mode);
   set_grid(p, u->grid_mode);
-  res_set(p, u->oversample_n, u->oversample_d, u->menu_cursamp);
+  p->private->menu_cursamp = u->menu_cursamp;
+  res_set(p, u->oversample_n, u->oversample_d);
 
   // panel-subtype-specific restore
   p->private->undo_restore(u,p);
@@ -1001,80 +904,31 @@
   xmlNodePtr pn = xmlNewChild(instance, NULL, (xmlChar *) "panel", NULL);
   xmlNodePtr n;
 
-  snprintf(buffer,sizeof(buffer),"%d",p->number);
-  xmlNewProp(pn, (xmlChar *)"number", (xmlChar *)buffer);
-  if(p->name)
-    xmlNewProp(pn, (xmlChar *)"name", (xmlChar *)p->name);
+  xmlNewPropI(pn, "number", p->number);
+  xmlNewPropS(pn, "name", p->name);
 
   // let the panel subtype handler fill in type
   // we're only saving settings independent of subtype
 
   // background
   n = xmlNewChild(pn, NULL, (xmlChar *) "background", NULL);
-  switch(p->private->bg_type){
-  case 0:
-    xmlNewProp(n, (xmlChar *)"color", (xmlChar *)"white");
-    break;
-  case 1:
-    xmlNewProp(n, (xmlChar *)"color", (xmlChar *)"black");
-    break;
-  case 2:
-    xmlNewProp(n, (xmlChar *)"color", (xmlChar *)"checked");
-    break;
-  }
+  xmlNewMapProp(n, "color", bgmap, p->private->bg_type);
 
   // grid
   n = xmlNewChild(pn, NULL, (xmlChar *) "grid", NULL);
-  switch(PLOT(p->private->graph)->grid_mode){
-  case PLOT_GRID_LIGHT:
-    xmlNewProp(n, (xmlChar *)"mode", (xmlChar *)"normal");
-    xmlNewProp(n, (xmlChar *)"color", (xmlChar *)"light");
-    break;
-  case PLOT_GRID_NORMAL:
-    xmlNewProp(n, (xmlChar *)"mode", (xmlChar *)"normal");
-    xmlNewProp(n, (xmlChar *)"color", (xmlChar *)"mid");
-    break;
-  case PLOT_GRID_DARK:
-    xmlNewProp(n, (xmlChar *)"mode", (xmlChar *)"normal");
-    xmlNewProp(n, (xmlChar *)"color", (xmlChar *)"dark");
-    break;
-  case PLOT_GRID_TICS:
-    xmlNewProp(n, (xmlChar *)"mode", (xmlChar *)"tics");
-    break;
-  default:
-    xmlNewProp(n, (xmlChar *)"mode", (xmlChar *)"none");
-    break;
-  }
+  xmlNewMapProp(n, "mode", gridmap, PLOT(p->private->graph)->grid_mode);
 
   // crosshairs
-  xmlNodePtr boxn = xmlNewChild(pn, NULL, (xmlChar *) "crosshairs", NULL);
-  xmlNewProp(boxn, (xmlChar *)"active", 
-	     (xmlChar *) (PLOT(p->private->graph)->cross_active ? "yes" : "no"));
-    
+  n = xmlNewChild(pn, NULL, (xmlChar *) "crosshairs", NULL);
+  xmlNewMapProp(n, "active", crossmap, PLOT(p->private->graph)->cross_active);
+
   // legend
   n = xmlNewChild(pn, NULL, (xmlChar *) "legend", NULL);
-  switch(PLOT(p->private->graph)->legend_active){
-  case 0: //inactive
-    xmlNewProp(n, (xmlChar *)"mode", (xmlChar *)"none");
-    break;
-  case 1: //shadowed
-    xmlNewProp(n, (xmlChar *)"mode", (xmlChar *)"shadowed");
-    break;
-  case 2: //boxed
-    xmlNewProp(n, (xmlChar *)"mode", (xmlChar *)"boxed");
-    break;
-  }
+  xmlNewMapProp(n,"mode", legendmap, PLOT(p->private->graph)->legend_active);
 
   // text
   n = xmlNewChild(pn, NULL, (xmlChar *) "text", NULL);
-  switch(PLOT(p->private->graph)->bg_inv){
-  case 0:
-    xmlNewProp(n, (xmlChar *)"color", (xmlChar *)"white");
-    break;
-  default:
-    xmlNewProp(n, (xmlChar *)"color", (xmlChar *)"black");
-    break;
-  }
+  xmlNewMapProp(n,"color", textmap, PLOT(p->private->graph)->bg_inv);
 
   // resample
   n = xmlNewChild(pn, NULL, (xmlChar *) "sampling", NULL);
@@ -1088,3 +942,59 @@
 
   return ret;
 }
+
+int _load_panel(sushiv_panel_t *p,
+		sushiv_panel_undo_t *u,
+		xmlNodePtr pn,
+		int warn){
+
+  // check name 
+  xmlCheckPropS(pn,"name",p->name,"Panel %d name mismatch in save file.",p->number,&warn);
+
+  // background
+  u->bg_mode = xmlGetChildMap(pn, "background", "color", bgmap, p->private->bg_type,
+			      "Panel %d unknown background setting", p->number, &warn);
+  // grid
+  u->grid_mode = xmlGetChildMap(pn, "grid", "mode", gridmap, PLOT(p->private->graph)->grid_mode,
+				"Panel %d unknown grid mode setting", p->number, &warn);
+  // crosshairs
+  u->cross_mode = xmlGetChildMap(pn, "crosshairs", "active", crossmap, PLOT(p->private->graph)->cross_active,
+				"Panel %d unknown crosshair setting", p->number, &warn);
+  // legend
+  u->legend_mode = xmlGetChildMap(pn, "legend", "mode", legendmap, PLOT(p->private->graph)->legend_active,
+				"Panel %d unknown legend setting", p->number, &warn);
+  // text
+  u->text_mode = xmlGetChildMap(pn, "text", "color", textmap, PLOT(p->private->graph)->bg_inv,
+				"Panel %d unknown text color setting", p->number, &warn);
+  // resample
+  char *prop = xmlGetChildPropS(pn, "sampling", "ratio");
+  if(!prop){
+    u->oversample_n = p->private->def_oversample_n;
+    u->oversample_d = p->private->def_oversample_d;
+  }else{
+    int res = sscanf(prop,"%d:%d", &u->oversample_n, &u->oversample_d);
+    if(res<2){
+      fprintf(stderr,"Unable to parse sample setting (%s) for panel %d.\n",prop,p->number);
+      u->oversample_n = p->private->def_oversample_n;
+      u->oversample_d = p->private->def_oversample_d;
+    }
+    xmlFree(prop);
+  }
+
+  // subtype 
+  if(p->private->load_action)
+    warn = p->private->load_action(p, u, pn, warn);
+
+  // any unparsed elements? 
+  xmlNodePtr n = pn->xmlChildrenNode;
+  
+  while(n){
+    if (n->type == XML_ELEMENT_NODE) {
+      first_load_warning(&warn);
+      fprintf(stderr,"Unknown option (%s) set for panel %d.\n",n->name,p->number);
+    }
+    n = n->next; 
+  }
+  
+  return warn;
+}

Modified: trunk/sushivision/plot.c
===================================================================
--- trunk/sushivision/plot.c	2007-03-10 02:27:48 UTC (rev 12701)
+++ trunk/sushivision/plot.c	2007-03-10 06:33:52 UTC (rev 12702)
@@ -59,14 +59,14 @@
 }
 
 static void set_text(int inv, cairo_t *c){
-  if(inv)
+  if(inv == PLOT_TEXT_LIGHT)
     cairo_set_source_rgba(c,1.,1.,1.,1.);
   else
     cairo_set_source_rgba(c,0.,0.,0.,1.);
 }
 
 static void set_shadow(int inv, cairo_t *c){
-  if(inv)
+  if(inv == PLOT_TEXT_LIGHT)
     cairo_set_source_rgba(c,0.,0.,0.,.6);
   else
     cairo_set_source_rgba(c,1.,1.,1.,.8);

Modified: trunk/sushivision/plot.h
===================================================================
--- trunk/sushivision/plot.h	2007-03-10 02:27:48 UTC (rev 12701)
+++ trunk/sushivision/plot.h	2007-03-10 06:33:52 UTC (rev 12702)
@@ -131,7 +131,18 @@
 #define PLOT_NO_X_CROSS 1
 #define PLOT_NO_Y_CROSS 2
 
+#define PLOT_GRID_NONE   0
 #define PLOT_GRID_NORMAL 4
 #define PLOT_GRID_TICS   8
 #define PLOT_GRID_LIGHT  (256+4)
 #define PLOT_GRID_DARK   (512+4)
+
+#define PLOT_GRID_MODEMASK  0x00fc
+#define PLOT_GRID_COLORMASK 0x0f00
+
+#define PLOT_TEXT_DARK 0
+#define PLOT_TEXT_LIGHT 1
+
+#define PLOT_LEGEND_NONE 0
+#define PLOT_LEGEND_SHADOW 1
+#define PLOT_LEGEND_BOX 2

Modified: trunk/sushivision/undo.c
===================================================================
--- trunk/sushivision/undo.c	2007-03-10 02:27:48 UTC (rev 12701)
+++ trunk/sushivision/undo.c	2007-03-10 06:33:52 UTC (rev 12702)
@@ -49,19 +49,17 @@
   }
 }
 
-void _sushiv_undo_log(sushiv_instance_t *s){
+void _sushiv_undo_pop(sushiv_instance_t *s){
   sushiv_instance_undo_t *u;
   int i,j;
-
   if(!s->private->undo_stack)
     s->private->undo_stack = calloc(2,sizeof(*s->private->undo_stack));
-
-  // log into a fresh entry; pop this level and all above it 
+  
   if(s->private->undo_stack[s->private->undo_level]){
     i=s->private->undo_level;
     while(s->private->undo_stack[i]){
       u = s->private->undo_stack[i];
-
+      
       if(u->dim_vals[0]) free(u->dim_vals[0]);
       if(u->dim_vals[1]) free(u->dim_vals[1]);
       if(u->dim_vals[2]) free(u->dim_vals[2]);
@@ -88,7 +86,16 @@
   u->dim_vals[0] = calloc(s->dimensions,sizeof(**u->dim_vals));
   u->dim_vals[1] = calloc(s->dimensions,sizeof(**u->dim_vals));
   u->dim_vals[2] = calloc(s->dimensions,sizeof(**u->dim_vals));
+}
 
+void _sushiv_undo_log(sushiv_instance_t *s){
+  sushiv_instance_undo_t *u;
+  int i,j;
+
+  // log into a fresh entry; pop this level and all above it 
+  _sushiv_undo_pop(s);
+  u = s->private->undo_stack[s->private->undo_level];
+  
   // save dim values
   for(i=0;i<s->dimensions;i++){
     sushiv_dimension_t *d = s->dimension_list[i];

Added: trunk/sushivision/xml.c
===================================================================
--- trunk/sushivision/xml.c	2007-03-10 02:27:48 UTC (rev 12701)
+++ trunk/sushivision/xml.c	2007-03-10 06:33:52 UTC (rev 12702)
@@ -0,0 +1,245 @@
+/*
+ *
+ *     sushivision copyright (C) 2006-2007 Monty <monty at xiph.org>
+ *
+ *  sushivision is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  sushivision is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with sushivision; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "internal.h"
+
+/* a few helpers to make specific libxml2 call patterns more concise */
+
+xmlNodePtr xmlGetChildS(xmlNodePtr n, char *name,char *prop, char *val){
+  xmlNodePtr child = n->xmlChildrenNode;
+  while(child){
+    // is this the child we want?
+    if (child->type == XML_ELEMENT_NODE && !xmlStrcmp(child->name, (const xmlChar *)name)) {
+      // does the desired attribut match?
+      xmlChar *test = (prop?xmlGetProp(child, (const xmlChar *)prop):NULL);
+      if(!prop || test){
+	if (!prop || !xmlStrcmp(test, (const xmlChar *)val)) {
+	  if(test)
+	    xmlFree(test);
+	  // this is what we want 
+	  // remove it from the tree
+	  xmlUnlinkNode(child);
+
+	  // return it
+	  return child;
+	}
+	if(test)
+	  xmlFree(test);
+      }
+    }
+    child = child->next;
+  }
+  return NULL;
+}
+
+xmlNodePtr xmlGetChildI(xmlNodePtr n, char *name,char *prop, int val){
+  char buffer[80];
+  snprintf(buffer,sizeof(buffer),"%d",val);
+  return xmlGetChildS(n, name, prop, buffer);
+}
+
+void xmlNewMapProp(xmlNodePtr n, char *name, propmap **map, int val){
+  propmap *m = *map++;
+  while(m){
+    if(m->value == val){
+      xmlNewProp(n, (xmlChar *)name, (xmlChar *)m->left);
+      break;
+    }
+    m = *map++;
+  }
+}
+
+void xmlNewPropF(xmlNodePtr n, char *name, double val){
+  char buffer[80];
+  snprintf(buffer,sizeof(buffer),"%.20g",val);
+  xmlNewProp(n, (xmlChar *) name, (xmlChar *)buffer);
+}
+
+void xmlNewPropI(xmlNodePtr n, char *name, int val){
+  char buffer[80];
+  snprintf(buffer,sizeof(buffer),"%d",val);
+  xmlNewProp(n, (xmlChar *) name, (xmlChar *)buffer);
+}
+
+void xmlNewPropS(xmlNodePtr n, char *name, char *val){
+  if(val)
+    xmlNewProp(n, (xmlChar *) name, (xmlChar *)val);
+}
+
+char *xmlGetPropS(xmlNodePtr n, char *name){
+  return (char *)xmlGetProp(n, (xmlChar *)name);
+}
+
+void xmlGetPropF(xmlNodePtr n, char *name, double *val){
+  xmlChar *v = xmlGetProp(n, (xmlChar *)name);
+  if(v){
+    *val = atof((char *)v);
+    xmlFree(v);
+  }
+}
+
+void xmlCheckPropS(xmlNodePtr n, char *prop, char *val, char *msg, int num, int *warn){
+  char *testval = xmlGetPropS(n, prop);
+  if(testval){
+    if(strcmp(val,testval)){
+      first_load_warning(warn);
+      fprintf(stderr,msg, num);
+      fprintf(stderr,"\n\t(found %s, should be %s).\n", testval, val);
+    }
+    xmlFree(testval);
+  }else{
+    first_load_warning(warn);
+    fprintf(stderr,msg, num);
+    fprintf(stderr,"\n\t(null found, should be %s).\n", val);
+  }
+}
+
+void xmlCheckMap(xmlNodePtr n, char *prop, propmap **map, int val, char *msg, int num, int *warn){
+  char *name = NULL;
+  char *testname = xmlGetPropS(n, prop);
+  
+  // look up our desired value
+  propmap *m = *map++;
+  while(m){
+    if(m->value == val){
+      name = m->left;
+      break;
+    }
+    m = *map++;
+  }
+
+  if(testname){
+    if(name){
+      if(strcmp(name,testname)){
+	first_load_warning(warn);
+	fprintf(stderr,msg, num);
+	fprintf(stderr,"\n\t(found %s, should be %s).\n", testname, name);
+      }
+    }else{
+      first_load_warning(warn);
+      fprintf(stderr,msg, num);
+      fprintf(stderr,"\n\t(found %s, should be null).\n", testname);
+    }
+    xmlFree(testname);
+  }else{
+    if(name){
+      first_load_warning(warn);
+      fprintf(stderr,msg, num);
+      fprintf(stderr,"\n\t(null found, should be %s).\n", name);
+    }
+  }
+}
+
+static int xmlGetMapVal(xmlNodePtr n, char *key, propmap **map){
+  char *valname = (char *)xmlGetProp(n, (xmlChar *)key);
+  if(!valname) return -1;
+  
+  propmap *m = *map++;
+  while(m){
+    if(!strcmp(m->left,valname)){
+      xmlFree(valname);
+      return m->value;
+    }
+    m = *map++;
+  }
+  xmlFree(valname);
+  return -1;
+}
+
+int xmlGetChildMap(xmlNodePtr in, char *prop, char *key, propmap **map, int default_val, 
+		   char *msg, int num, int *warn){
+  xmlNodePtr n = xmlGetChildS(in, prop, NULL, NULL);
+  if(!n)return default_val;
+
+  char *val = (char *)xmlGetProp(n, (xmlChar *)key);
+  if(!val){
+    xmlFreeNode(n);
+    return default_val;
+  }
+
+  int ret = xmlGetMapVal(n,key,map);
+  if(ret == -1){
+    if(msg){
+      first_load_warning(warn);
+      fprintf(stderr,msg,num);
+      fprintf(stderr," (%s).\n", val);
+    }
+    ret = default_val;
+  }
+
+  xmlFree(val);
+  xmlFreeNode(n);
+  return ret;
+}
+
+char *xmlGetChildPropS(xmlNodePtr in, char *prop, char *key){
+
+  xmlNodePtr n = xmlGetChildS(in, prop, NULL, NULL);
+  if(!n)return NULL;
+
+  char *val = (char *)xmlGetProp(n, (xmlChar *)key);
+  if(!val){
+    xmlFreeNode(n);
+    return NULL;
+  }
+  return val;
+}
+  
+
+/* convenience helpers for wielding property maps */
+int propmap_pos(propmap **map, int val){
+  int i=0;
+  propmap *m = *map++;
+  while(m){
+    if(m->value == val)
+      return i;
+    i++;
+    m = *map++;
+  }
+  return 0;
+}
+
+int propmap_last(propmap **map){
+  int i=0;
+  propmap *m = *map++;
+  while(m){
+    i++;
+    m = *map++;
+  }
+  return i-1;
+}
+
+int propmap_label_pos(propmap **map, char *label){
+  int i=0;
+  propmap *m = *map++;
+  while(m){
+    if(!strcmp(m->left,label))
+      return i;
+    i++;
+    m = *map++;
+  }
+  return 0;
+}

Added: trunk/sushivision/xml.h
===================================================================
--- trunk/sushivision/xml.h	2007-03-10 02:27:48 UTC (rev 12701)
+++ trunk/sushivision/xml.h	2007-03-10 06:33:52 UTC (rev 12702)
@@ -0,0 +1,38 @@
+/*
+ *
+ *     sushivision copyright (C) 2006-2007 Monty <monty at xiph.org>
+ *
+ *  sushivision is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  sushivision is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with sushivision; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 
+ */
+
+extern xmlNodePtr xmlGetChildS(xmlNodePtr n, char *name,char *prop, char *val);
+extern xmlNodePtr xmlGetChildI(xmlNodePtr n, char *name,char *prop, int val);
+extern void xmlNewMapProp(xmlNodePtr n, char *name, propmap **map, int val);
+extern void xmlNewPropF(xmlNodePtr n, char *name, double val);
+extern void xmlNewPropI(xmlNodePtr n, char *name, int val);
+extern void xmlNewPropS(xmlNodePtr n, char *name, char *val);
+extern char *xmlGetPropS(xmlNodePtr n, char *name);
+extern void xmlGetPropF(xmlNodePtr n, char *name, double *val);
+extern void xmlCheckPropS(xmlNodePtr n, char *prop, char *val, char *msg, int num, int *warn);
+extern void xmlCheckMap(xmlNodePtr n, char *prop, propmap **map, int val, char *msg, int num, int *warn);
+extern int xmlGetChildMap(xmlNodePtr in, char *prop, char *key, propmap **map, int defaultval, 
+			  char *msg, int num, int *warn);
+extern char *xmlGetChildPropS(xmlNodePtr in, char *prop, char *key);
+
+extern int propmap_pos(propmap **map, int val);
+extern int propmap_last(propmap **map);
+extern int propmap_label_pos(propmap **map, char *label);



More information about the commits mailing list