/* prefs.c
 * geg, a GTK+ Equation Grapher
 * David Bryant 1998
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* HAVE_CONFIG_H */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include "prefs.h"
#include "colorsel.h"
#include "misc.h"
#include "i18n.h"

/* Default 14 color values */
static gdouble default_rgb_values[] = {
  1.00, 1.00, 1.00,  /* background */
  0.50, 0.60, 0.70,  /* numbers */
  0.80, 0.70, 0.60,  /* axes */
  0.80, 0.80, 0.80,  /* grid */  
  0.20, 0.80, 0.40,  /* selection */
  0.50, 0.50, 0.50,  /* gray */
  0.00, 0.00, 0.00,  /* default */  
  0.80, 0.00, 0.00,  /* curves ... */
  0.00, 0.00, 0.80,
  0.00, 0.80, 0.00,
  0.80, 0.80, 0.00,
  0.80, 0.00, 0.80,
  0.00, 0.80, 0.80,
  0.40, 0.40, 0.40,
  0.00, 0.00, 0.00
};

/* Default preferences */
struct_prefs default_prefs =
{
  /* toolbar stuff */
  DEF_TB, DEF_TT, DEF_PRINT,
  /* startup stuff */
  {
  DEF_XMIN, DEF_XMAX, DEF_YMIN, DEF_YMAX,
  DEF_TMIN, DEF_TMAX, DEF_AMIN, DEF_AMAX,  
  DEF_WIDTH, DEF_HEIGHT,
  DEF_DECIMAL,
  DEF_CARTESIAN
  },
  /* drawing stuff */
  DEF_DO_OX, DEF_DO_OY,
  DEF_DO_XVAL, DEF_DO_YVAL,
  DEF_DO_COLOR, DEF_DO_BOX, DEF_DO_GRID,  
  DEF_TEXT_POSITION,
  DEF_ZOOM_XY,  
  DEF_NUMBER_SIZE,
  DEF_FORMULA_SIZE,  
  /* resolution stuff */
  DEF_ZOOM, DEF_SPACE,
  DEF_MINRES, DEF_MAXRES,
  DEF_AXES_LW, DEF_GRID_LW, DEF_BOX_LW, DEF_CURVES_LW, DEF_POINT_RADIUS,
  DEF_INTERP, DEF_MAXFORM,
  /* color stuff */
  sizeof(default_rgb_values) / (3*sizeof(double)),   /* n_colors */
  0,       /* cur_color */
  NULL,    /* rgb_value */
  NULL,    /* gdk_color */
  /* font stuff */  
  NULL,    /* text_font */
  NULL,    /* numb_font */
  /* utilities */  
  NULL,    /* editor */
  NULL,    /* eps_viewer */  
  NULL,    /* pdf_viewer */
  NULL,    /* svg_viewer */
  NULL,    /* eps_to_pdf */  
  NULL     /* eps_to_svg */
};

struct_prefs prefs;
gchar rc_file[256];

/* locally global data */
static GtkWidget *wi = NULL;
static GtkWidget *pt_bu, *po_bu, *to_bu, *tt_bu, *eps_bu, *pdf_bu, *svg_bu;
static GtkWidget *xmin_en, *xmax_en, *ymin_en, *ymax_en;
static GtkWidget *tmin_en, *tmax_en, *amin_en, *amax_en;
static GtkWidget *formula_en, *editor_en;
static GtkWidget *formula_size_en, *number_size_en;
static GtkWidget *axes_linewidth_en, *grid_linewidth_en, *box_linewidth_en;
static GtkWidget *curves_linewidth_en, *point_radius_en;
static GtkWidget *pdf_viewer_en, *eps_viewer_en, *svg_viewer_en;
static GtkWidget *eps_to_pdf_en, *eps_to_svg_en;
static GtkWidget *width_en, *height_en;
static GtkWidget *zoom_en, *space_en, *interp_en;
static GtkWidget *minres_en, *maxres_en, *maxform_en;
static GtkWidget *maxcolors_en, *current_color_en, *meaning_color_en;
static GtkWidget *sel_pr;
static GtkWidget *dec_bu, *rad_bu, *cart_bu, *polar_bu, *param_bu;
static GtkWidget *ox_bu, *oy_bu, *xt_bu, *yt_bu, *sb_bu, *sf_bu;
static GtkWidget *co_bu, *gr_bu;
static int skip_equals(char **buf);
static gint atov(gchar *buf);
static void set_current_color(guint *n);
static void change_event(GtkWidget *widget, gpointer data);
static void ok_event(GtkWidget *widget, gpointer data);
static void save_event(GtkWidget *widget, gpointer data);
static void cancel_event(GtkWidget *widget, gpointer data);
static gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data);
static void compound_entry(gchar *title, GtkWidget **entry, GtkWidget *vbox);
static void compound_spin(gchar *title, GtkWidget **sb, GtkWidget *vbox,
			  gint min, gint max);
static void compound_preview(gchar *title, GtkWidget **preview,
                             GtkWidget *vbox, GtkSignalFunc callback);

extern void realize_preferences(GtkWidget *widget, gpointer data);

/* Initialization of  preferences */
void free_prefs_allocs()
{
  if (prefs.rgb_value) {
    g_free(prefs.rgb_value);
    prefs.rgb_value = NULL;
  }
  if (prefs.gdk_color) {
    g_free(prefs.gdk_color);
    prefs.gdk_color = NULL;
  }
  if (prefs.text_font) {
    g_free(prefs.text_font);
    prefs.text_font = NULL;
  }
  if (prefs.numb_font) {
    g_free(prefs.numb_font);
    prefs.numb_font = NULL;
  }
  if (prefs.editor) {
    g_free(prefs.editor);
    prefs.editor = NULL;
  }  
  if (prefs.eps_viewer) {
    g_free(prefs.eps_viewer);
    prefs.eps_viewer = NULL;
  }  
  if (prefs.pdf_viewer) {
    g_free(prefs.pdf_viewer);
    prefs.pdf_viewer = NULL;
  }  
  if (prefs.svg_viewer) {
    g_free(prefs.svg_viewer);
    prefs.svg_viewer = NULL;
  }  
  if (prefs.eps_to_pdf) {
    g_free(prefs.eps_to_pdf);
    prefs.eps_to_pdf = NULL;
  }  
  if (prefs.eps_to_svg) {
    g_free(prefs.eps_to_svg);
    prefs.eps_to_svg = NULL;
  }  
}

void initialize_prefs()
{
  guint i;
  memcpy(&prefs, &default_prefs, sizeof(struct_prefs));

  prefs.text_font = g_strdup(DEF_TEXT_FONT);
  prefs.numb_font = g_strdup(DEF_NUMB_FONT);
  prefs.editor = g_strdup(DEF_EDITOR);
  prefs.eps_viewer = g_strdup(DEF_EPS_VIEWER);  
  prefs.pdf_viewer = g_strdup(DEF_PDF_VIEWER);
  prefs.svg_viewer = g_strdup(DEF_SVG_VIEWER);
  prefs.eps_to_pdf = g_strdup(DEF_EPS_TO_PDF);  
  prefs.eps_to_svg = g_strdup(DEF_EPS_TO_SVG);
  
  prefs.rgb_value =
    (gdouble *)malloc(3 * prefs.n_colors * sizeof(double));
  for(i = 0; i < 3 * prefs.n_colors; i++)
    prefs.rgb_value[i] = default_rgb_values[i];  
  prefs.gdk_color =
    (GdkColor *)malloc(prefs.n_colors * sizeof(GdkColor));
}

/* trivial string utility */
GtkWidget *
get_prefs_widget()
{
  return wi;
}

/* trivial string utility */
static void
realloc_string(gchar **str, gchar *data)
{
  if (*str) g_free(*str);
  *str = g_strdup(data);
  data = *str;
  while (*data) {
    if (*data == '\n') *data = '\0';
    ++data;
  }
}

static void
set_current_color(guint *n)
{
  guint i, j;
  char buf[32];

  i = *n;
  j = 0;
  if (i >= prefs.n_colors) {
    *n = i = prefs.n_colors - 1;
    j = 1; 
    gtk_entry_set_text(GTK_ENTRY(current_color_en), ltoa(*n));
  }
  prefs.cur_color = i;
  if (i < DEF_DEFAULT_COLOR) 
    switch(i) {
      case 0: strcpy(buf, _("background")); break;
      case 1: strcpy(buf, _("numbers")); break;
      case 2: strcpy(buf, _("axes")); break;
      case 3: strcpy(buf, _("grid")); break;	
      case 4: strcpy(buf, _("selection")); break;
      case 5: strcpy(buf, _("gray")); break;
      case 6: strcpy(buf, _("default")); break;
    }
  else
    sprintf(buf, "%s[%d mod %d]", _("curve"), 
            *n - DEF_DEFAULT_COLOR, prefs.n_colors - DEF_DEFAULT_COLOR);
  gtk_entry_set_text(GTK_ENTRY(meaning_color_en), buf);
  if (j) return;
  show_color_in_preview(sel_pr, &prefs.rgb_value[3*i]);
}

/* change_event, called by some spin buttons  
 */
static void
change_event(GtkWidget *widget, gpointer data)
{
  guint i, j;
  if(!strncmp((gchar *)data, "max",3)) {
    i = atol(gtk_entry_get_text(GTK_ENTRY(maxcolors_en)));
    if (i < prefs.n_colors) {
      j = atol(gtk_entry_get_text(GTK_ENTRY(current_color_en)));
      prefs.n_colors = i;
      if (j > i)
        gtk_entry_set_text(GTK_ENTRY(current_color_en), ltoa(i));
    } else
    if (i > prefs.n_colors) {
      prefs.rgb_value = (gdouble *)
        realloc(prefs.rgb_value, 3 * i * sizeof(double));
      prefs.gdk_color = (GdkColor *)
        realloc(prefs.gdk_color, i * sizeof(GdkColor));
      for (j = 3*prefs.n_colors; j < 3*i; j++)
        prefs.rgb_value[j] = prefs.rgb_value[15+j%3];
      for (j = prefs.n_colors; j < i; j++)
        prefs.gdk_color[j] = prefs.gdk_color[5];
      prefs.n_colors = i;
    }
    return;
  }
  if(!strncmp((gchar *)data, "index",5)) {
    i = atol(gtk_entry_get_text(GTK_ENTRY(current_color_en)));
    set_current_color(&i);
    return;
  }
}

/* ok_event, called by the ok button, realizes the prefs and closes dialog  
 */
static void
ok_event(GtkWidget *widget, gpointer data)
{
  widgets_to_prefs(widget, data);
  realize_preferences(widget, NULL);
  gdk_window_hide(wi->window);
}

/* save_event, called by the save button, saves the preferences to file     
 */
static void
save_event(GtkWidget *widget, gpointer data)
{
  prefs_rc_write(rc_file, 1);
}

/* cancel_event, called by cancel button, closes the preferences dialog     
 */
static void
cancel_event(GtkWidget *widget, gpointer data)
{
  gdk_window_hide(wi->window);
}

/* delete_event, internal event, not sure if it is correct GTK+ way          
 */
static gint
delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
{
  gdk_window_hide(wi->window);
  return(TRUE);
}

/* prefs_event, called by preferences button in toolbar                      
 */
void
prefs_event(GtkWidget *widget, gpointer data)
{
  GtkWidget *temp_bu;
  GtkWidget *tb_hb, *tb_vb;
  GtkWidget *su_hb, *su_vb;
  GtkWidget *re_hb, *re_vb;
  GtkWidget *dr_hb, *dr_vb;
  GtkWidget *co_hb, *co_vb;
  GtkWidget *ut_hb, *ut_vb;  
  GtkWidget *nb;
  GtkWidget *temp_la;
  GtkWidget *tb_fr, *su_fr, *re_fr, *dr_fr, *co_fr, *ut_fr;

  if(wi) {
    prefs_to_widgets();    
    gdk_window_show_unraised(wi->window);
    return;
  }

  wi = gtk_dialog_new();

  gtk_signal_connect(GTK_OBJECT(wi), "delete_event",
                     GTK_SIGNAL_FUNC(delete_event), wi);

  /* setup window properties */
  gtk_window_set_wmclass(GTK_WINDOW(wi), "preferences", "Geg");
  gtk_window_set_title(GTK_WINDOW(wi), _("Preferences"));
  gtk_window_set_policy(GTK_WINDOW(wi), FALSE, FALSE, FALSE);
  gtk_window_position(GTK_WINDOW(wi), GTK_WIN_POS_NONE);

  gtk_container_border_width(GTK_CONTAINER(GTK_DIALOG(wi)->vbox), 5);

  nb = gtk_notebook_new();
  gtk_notebook_set_tab_pos(GTK_NOTEBOOK(nb), GTK_POS_TOP);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(wi)->vbox), nb, TRUE, TRUE, 0);
  gtk_widget_show(nb);

  /* toolbar */
  tb_fr = gtk_frame_new(_("Toolbar options:"));
  gtk_container_set_border_width(GTK_CONTAINER(tb_fr), 15);
  gtk_widget_show(tb_fr);
  
  temp_la = gtk_label_new(_("Toolbar"));
  gtk_notebook_append_page(GTK_NOTEBOOK(nb), tb_fr, temp_la);

  /* startup */
  su_fr = gtk_frame_new(_("Default ranges and dimensions:"));
  gtk_container_set_border_width(GTK_CONTAINER(su_fr), 15);
  gtk_widget_show(su_fr);

  temp_la = gtk_label_new(_("Ranges"));
  gtk_notebook_append_page(GTK_NOTEBOOK(nb), su_fr, temp_la);

  /* resolution */
  re_fr = gtk_frame_new(_("Resolution options:"));
  gtk_container_set_border_width(GTK_CONTAINER(re_fr), 15);  
  gtk_widget_show(re_fr);

  temp_la = gtk_label_new(_("Resolution"));
  gtk_notebook_append_page(GTK_NOTEBOOK(nb), re_fr, temp_la);

  /* drawing */
  dr_fr = gtk_frame_new(_("Drawing options:"));
  gtk_container_set_border_width(GTK_CONTAINER(dr_fr), 15);  
  gtk_widget_show(dr_fr);

  temp_la = gtk_label_new(_("Drawing"));
  gtk_notebook_append_page(GTK_NOTEBOOK(nb), dr_fr, temp_la);

  /* color */
  co_fr = gtk_frame_new(_("Color options:"));
  gtk_widget_show(co_fr);

  temp_la = gtk_label_new(_("Colors"));
  gtk_notebook_append_page(GTK_NOTEBOOK(nb), co_fr, temp_la);

  ut_fr = gtk_frame_new(_("External utility programs:"));
  gtk_container_set_border_width(GTK_CONTAINER(ut_fr), 15);  
  gtk_widget_show(ut_fr);
  
  temp_la = gtk_label_new(_("Utilities"));
  gtk_notebook_append_page(GTK_NOTEBOOK(nb), ut_fr, temp_la);

  /* tool(bar/tips) stuff */
  tb_hb = gtk_hbox_new(TRUE, 0);
  tb_vb = gtk_vbox_new(TRUE, 0);
  gtk_container_set_border_width(GTK_CONTAINER(tb_vb), 15);    
  gtk_container_add(GTK_CONTAINER(tb_fr), tb_hb);
  gtk_box_pack_start(GTK_BOX(tb_hb), tb_vb, TRUE, TRUE, 0);
  gtk_widget_show(tb_vb);

  /* toolbar radio buttons (p&t, ponly, tonly) */
  pt_bu = gtk_radio_button_new_with_label_from_widget(NULL, _("Show Pictures and Text"));
  po_bu = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(pt_bu), _("Show Pictures Only"));
  to_bu = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(pt_bu), _("Show Text Only"));
  tt_bu = gtk_check_button_new_with_label(_("Show Tooltips"));
  gtk_box_pack_start(GTK_BOX(tb_vb), pt_bu, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(tb_vb), po_bu, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(tb_vb), to_bu, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(tb_vb), tt_bu, TRUE, TRUE, 0);  
  gtk_widget_show(pt_bu);
  gtk_widget_show(po_bu);
  gtk_widget_show(to_bu);
  gtk_widget_show(tb_vb);
  
  /* toolbar check button (tooltips on/off) */
  tb_vb = gtk_vbox_new(TRUE, 0);
  gtk_container_set_border_width(GTK_CONTAINER(tb_vb), 15);    
  gtk_box_pack_start(GTK_BOX(tb_hb), tb_vb, TRUE, TRUE, 0);
  eps_bu = gtk_radio_button_new_with_label_from_widget(NULL, _("Print as EPS"));
  pdf_bu = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(eps_bu), _("Print as PDF"));
						       svg_bu = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(eps_bu), _("Print as SVG"));
  gtk_box_pack_start(GTK_BOX(tb_vb), eps_bu, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(tb_vb), pdf_bu, TRUE, TRUE, 0);  
  gtk_box_pack_start(GTK_BOX(tb_vb), svg_bu, TRUE, TRUE, 0);
  gtk_widget_show(tt_bu);
  gtk_widget_show(eps_bu);
  gtk_widget_show(pdf_bu);  
  gtk_widget_show(svg_bu);
  gtk_widget_show(tb_vb);
  
  tb_vb = gtk_vbox_new(TRUE, 0);
  gtk_box_pack_start(GTK_BOX(tb_hb), tb_vb, TRUE, TRUE, 40);
  dec_bu = gtk_radio_button_new_with_label_from_widget(NULL, _("Decimal x coordinates"));
  rad_bu = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(dec_bu), _("Radian x coordinates"));
  cart_bu = gtk_radio_button_new_with_label_from_widget(NULL, _("Cartesian mode"));
  polar_bu = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(cart_bu), _("Polar mode"));
  param_bu = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(cart_bu), _("Parametric mode"));
  gtk_box_pack_start(GTK_BOX(tb_vb), dec_bu, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(tb_vb), rad_bu, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(tb_vb), cart_bu, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(tb_vb), polar_bu, TRUE, TRUE, 0);  
  gtk_box_pack_start(GTK_BOX(tb_vb), param_bu, TRUE, TRUE, 0);
  gtk_widget_show(dec_bu);
  gtk_widget_show(rad_bu);
  gtk_widget_show(cart_bu);
  gtk_widget_show(polar_bu);  
  gtk_widget_show(param_bu);
  gtk_widget_show(tb_vb);
  gtk_widget_show(tb_hb);  
  
  /* startup stuff */
  su_hb = gtk_hbox_new(TRUE, 0);
  gtk_container_add(GTK_CONTAINER(su_fr), su_hb);

  su_vb = gtk_vbox_new(TRUE, 10);
  gtk_container_set_border_width(GTK_CONTAINER(su_vb), 15);    
  gtk_box_pack_start(GTK_BOX(su_hb), su_vb, FALSE, FALSE, 0);
  compound_entry(_("Xmin:"), &xmin_en, su_vb);
  compound_entry(_("Xmax:"), &xmax_en, su_vb);
  compound_entry(_("Ymin:"), &ymin_en, su_vb);
  compound_entry(_("Ymax:"), &ymax_en, su_vb);
  gtk_widget_show(su_vb);
  
  su_vb = gtk_vbox_new(TRUE, 10);
  gtk_container_set_border_width(GTK_CONTAINER(su_vb), 15);   
  gtk_box_pack_start(GTK_BOX(su_hb), su_vb, FALSE, FALSE, 0);
  compound_entry(_("Tmin:"), &tmin_en, su_vb);
  compound_entry(_("Tmax:"), &tmax_en, su_vb);
  compound_entry(_("Amin:"), &amin_en, su_vb);
  compound_entry(_("Amax:"), &amax_en, su_vb);  
  gtk_widget_show(su_vb);  

  su_vb = gtk_vbox_new(TRUE, 0);
  gtk_container_set_border_width(GTK_CONTAINER(su_vb), 15);  
  gtk_box_pack_start(GTK_BOX(su_hb), su_vb, FALSE, FALSE, 0);
  compound_entry(_("Width:"), &width_en, su_vb);
  compound_entry(_("Height:"), &height_en, su_vb);
  gtk_widget_show(su_vb);
  gtk_widget_show(su_hb);
  
  /* resolution stuff */
  re_hb = gtk_hbox_new(TRUE, 0);
  gtk_container_add(GTK_CONTAINER(re_fr), re_hb);  
  re_vb = gtk_vbox_new(TRUE, 0);
  gtk_container_set_border_width(GTK_CONTAINER(re_vb), 15);      
  gtk_box_pack_start(GTK_BOX(re_hb), re_vb, FALSE, FALSE, 0);  
  compound_entry(_("Zoom Factor:"), &zoom_en, re_vb);
  compound_entry(_("Notch Spacing:"), &space_en, re_vb);
  compound_spin(_("Interpolation:"), &interp_en, re_vb, 0, 12);
  gtk_widget_show(re_vb);

  re_vb = gtk_vbox_new(TRUE, 0);
  gtk_container_set_border_width(GTK_CONTAINER(re_vb), 15);
  gtk_box_pack_start(GTK_BOX(re_hb), re_vb, FALSE, FALSE, 0);
  compound_entry(_("Min Resolution:"), &minres_en, re_vb);
  compound_entry(_("Max Resolution:"), &maxres_en, re_vb);
  compound_spin(_("Max Formulas:"), &maxform_en, re_vb, 1, 500);
  gtk_widget_show(re_vb);
  gtk_widget_show(re_hb);

  /* drawing stuff */
  dr_hb = gtk_hbox_new(TRUE, 0);
  gtk_container_add(GTK_CONTAINER(dr_fr), dr_hb);
  dr_vb = gtk_vbox_new(FALSE, 0);
  gtk_container_set_border_width(GTK_CONTAINER(dr_vb), 15);  
  gtk_box_pack_start(GTK_BOX(dr_hb), dr_vb, TRUE, FALSE, 0);
  gtk_widget_show(dr_hb);
  gtk_widget_show(dr_vb);

  ox_bu = gtk_check_button_new_with_label(_("Show Ox axis"));
  gtk_box_pack_start(GTK_BOX(dr_vb), ox_bu, TRUE, TRUE, 0);
  xt_bu = gtk_check_button_new_with_label(_("Show x values"));
  gtk_box_pack_start(GTK_BOX(dr_vb), xt_bu, TRUE, TRUE, 0);
  oy_bu = gtk_check_button_new_with_label(_("Show Oy axis"));
  gtk_box_pack_start(GTK_BOX(dr_vb), oy_bu, TRUE, TRUE, 0);
  yt_bu = gtk_check_button_new_with_label(_("Show y values"));
  gtk_box_pack_start(GTK_BOX(dr_vb), yt_bu, TRUE, TRUE, 0);
  sb_bu = gtk_check_button_new_with_label(_("Show box"));
  gtk_box_pack_start(GTK_BOX(dr_vb), sb_bu, TRUE, TRUE, 0);
  sf_bu = gtk_check_button_new_with_label(_("Show formulas"));
  gtk_box_pack_start(GTK_BOX(dr_vb), sf_bu, TRUE, TRUE, 0);  
  gtk_widget_show(ox_bu);
  gtk_widget_show(oy_bu);
  gtk_widget_show(xt_bu);
  gtk_widget_show(yt_bu);
  gtk_widget_show(sb_bu);

  dr_vb = gtk_vbox_new(FALSE, 10);
  gtk_container_set_border_width(GTK_CONTAINER(dr_vb), 15);  
  gtk_box_pack_start(GTK_BOX(dr_hb), dr_vb, TRUE, FALSE, 0);
  gtk_widget_show(dr_vb);
  compound_entry(_("Formula position:"), &formula_en, dr_vb);
  compound_entry(_("Formula font size:"), &formula_size_en, dr_vb);
  compound_entry(_("Numbers font size:"), &number_size_en, dr_vb);
  compound_entry(_("Point radius:"), &point_radius_en, dr_vb);  

  dr_vb = gtk_vbox_new(FALSE, 10);
  gtk_container_set_border_width(GTK_CONTAINER(dr_vb), 15);
  gtk_box_pack_start(GTK_BOX(dr_hb), dr_vb, TRUE, FALSE, 0);
  gtk_widget_show(dr_vb);
  compound_entry(_("Axes linewidth:"), &axes_linewidth_en, dr_vb);
  compound_entry(_("Grid linewidth:"), &grid_linewidth_en, dr_vb);
  compound_entry(_("Box linewidth:"), &box_linewidth_en, dr_vb);  
  compound_entry(_("Curves linewidth:"), &curves_linewidth_en, dr_vb);
  gtk_widget_show(sf_bu);  
  
  /* color stuff */
  co_hb = gtk_hbox_new(TRUE, 0);
  gtk_container_add(GTK_CONTAINER(co_fr), co_hb);
  co_vb = gtk_vbox_new(TRUE, 0);  
  gtk_container_set_border_width(GTK_CONTAINER(co_vb), 15);  
  gtk_box_pack_start(GTK_BOX(co_hb), co_vb, TRUE, TRUE, 0);
  compound_spin(_("Number of Colors:"), &maxcolors_en, co_vb, 7, 30);
  gtk_signal_connect(GTK_OBJECT(maxcolors_en), "value-changed",
                     GTK_SIGNAL_FUNC(change_event), "max");
  compound_spin(_("Index:"), &current_color_en, co_vb, 0, 29);
  gtk_signal_connect(GTK_OBJECT(current_color_en), "value-changed",
                     GTK_SIGNAL_FUNC(change_event), "index");
  gtk_signal_connect(GTK_OBJECT(current_color_en), "change-value",
                     GTK_SIGNAL_FUNC(change_event), "index");
  compound_entry(_("Current color:"), &meaning_color_en, co_vb);
  gtk_entry_set_editable((GtkEntry *)meaning_color_en, FALSE);
  gtk_widget_show(co_vb);

  compound_preview(_("Color chooser"), &sel_pr, co_hb,
                   GTK_SIGNAL_FUNC(colorsel_event));
  gtk_widget_show(sel_pr);

  co_vb = gtk_vbox_new(TRUE, 0);
  gtk_container_set_border_width(GTK_CONTAINER(co_vb), 15);  
  gtk_box_pack_start(GTK_BOX(co_hb), co_vb, TRUE, TRUE, 40);
  co_bu = gtk_radio_button_new_with_label_from_widget(NULL, _("Color output"));
  gtk_box_pack_start(GTK_BOX(co_vb), co_bu, TRUE, TRUE, 0);
  gr_bu = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(co_bu), _("Gray output"));
  gtk_box_pack_start(GTK_BOX(co_vb), gr_bu, TRUE, TRUE, 0);
  gtk_widget_show(co_bu);
  gtk_widget_show(gr_bu);
  gtk_widget_show(co_vb);
  gtk_widget_show(co_hb);

  /* utilities stuff */
  ut_hb = gtk_hbox_new(TRUE, 0);
  gtk_container_add(GTK_CONTAINER(ut_fr), ut_hb);

  ut_vb = gtk_vbox_new(FALSE, 20);
  gtk_container_set_border_width(GTK_CONTAINER(ut_vb), 15);   
  gtk_box_pack_start(GTK_BOX(ut_hb), ut_vb, TRUE, FALSE, 0);
  compound_entry(_("Editor:"), &editor_en, ut_vb);
  gtk_widget_set_usize(editor_en, 320, -1);    
  compound_entry(_("EPS viewer:"), &eps_viewer_en, ut_vb);
  gtk_widget_set_usize(eps_viewer_en, 320, -1);    
  compound_entry(_("EPS to PDF:"), &eps_to_pdf_en, ut_vb);
  gtk_widget_set_usize(eps_to_pdf_en, 320, -1);  
  gtk_widget_show(ut_hb);
  gtk_widget_show(ut_vb);

  ut_vb = gtk_vbox_new(FALSE, 20);
  gtk_container_set_border_width(GTK_CONTAINER(ut_vb), 15);
  gtk_box_pack_start(GTK_BOX(ut_hb), ut_vb, TRUE, FALSE, 0);
  compound_entry(_("PDF viewer:"), &pdf_viewer_en, ut_vb);
  gtk_widget_set_usize(pdf_viewer_en, 320, -1);    
  compound_entry(_("SVG viewer:"), &svg_viewer_en, ut_vb);
  gtk_widget_set_usize(svg_viewer_en, 320, -1);      
  compound_entry(_("EPS to SVG:"), &eps_to_svg_en, ut_vb);
  gtk_widget_set_usize(eps_to_svg_en, 320, -1);      
  gtk_widget_show(ut_vb);

  
  /* buttons */
  temp_bu = gtk_button_new_with_label(_("Ok"));
  gtk_signal_connect(GTK_OBJECT(temp_bu), "clicked",
                     GTK_SIGNAL_FUNC(ok_event), NULL);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(wi)->action_area), temp_bu, 
                     TRUE, TRUE, 0);
  gtk_widget_show(temp_bu);

  temp_bu = gtk_button_new_with_label(_("Save"));
  gtk_signal_connect(GTK_OBJECT(temp_bu), "clicked",
                     GTK_SIGNAL_FUNC(widgets_to_prefs), NULL);
  gtk_signal_connect(GTK_OBJECT(temp_bu), "clicked",
                      GTK_SIGNAL_FUNC(save_event), NULL);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(wi)->action_area),
                     temp_bu, TRUE, TRUE, 0);
  gtk_widget_show(temp_bu);

  temp_bu = gtk_button_new_with_label(_("Cancel"));
  gtk_signal_connect(GTK_OBJECT(temp_bu), "clicked", 
                     GTK_SIGNAL_FUNC(cancel_event), NULL);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(wi)->action_area), temp_bu, 
                     TRUE, TRUE, 0);
  gtk_widget_show(temp_bu);
  gtk_widget_show(wi);
  prefs_to_widgets();
}

/* compound_entry, a convenience function to create a label and an entry in
 * a horizontal box, and pack it into the vbox provided                      
 */
static void
compound_entry(gchar *title, GtkWidget **entry, GtkWidget *vbox)
{
  GtkWidget *hbox, *label;

  hbox = gtk_hbox_new(FALSE, 0);
  *entry = gtk_entry_new();
  gtk_widget_set_usize(*entry, 150, -1);
  gtk_box_pack_end(GTK_BOX(hbox), *entry, FALSE, FALSE, 0);
  label = gtk_label_new(title);
  gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 5);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show(label);
  gtk_widget_show(*entry);
  gtk_widget_show(hbox);
}

static void
compound_spin(gchar *title, GtkWidget **sb, GtkWidget *vbox,
		    gint min, gint max)
{
  GtkWidget *hbox, *label;
  GtkObject *adj;

  hbox = gtk_hbox_new(FALSE, 0);

  adj = gtk_adjustment_new(min, min, max, 1, 1, 1);
  *sb = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 1, 0);
  gtk_widget_set_usize(*sb, 80, -1);
  
  gtk_box_pack_end(GTK_BOX(hbox), *sb, FALSE, FALSE, 0);
  label = gtk_label_new(title);
  gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 5);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show(label);
  gtk_widget_show(*sb);
  gtk_widget_show(hbox);
}


/* compound_preview, a convenience function to create a label and a preview
 * in a horizontal box, and pack it into the vbox provided
 */
static void
compound_preview(gchar *title, GtkWidget **preview, GtkWidget *vbox, GtkSignalFunc callback)
{
  GtkWidget *hbox, *label, *frame, *button;

  hbox = gtk_hbox_new(FALSE, 0);
  gtk_container_set_border_width(GTK_CONTAINER(hbox), 10);  
  frame = gtk_frame_new(NULL);
  gtk_box_pack_end(GTK_BOX(hbox), frame, FALSE, FALSE, 0);
  *preview = gtk_preview_new(GTK_PREVIEW_COLOR);
  gtk_preview_set_expand(GTK_PREVIEW(*preview), TRUE);
  gtk_preview_size(GTK_PREVIEW(*preview), 75, -1);
  button = gtk_button_new_with_label("...");
  gtk_signal_connect(GTK_OBJECT(button), "clicked", callback, *preview);
  gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
  gtk_container_add(GTK_CONTAINER(frame), *preview);
  label = gtk_label_new(title);
  gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 5);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
  gtk_widget_show(label);
  gtk_widget_show(button);
  gtk_widget_show(*preview);
  gtk_widget_show(frame);
  gtk_widget_show(hbox);
}

/* prefs_to_widgets, called explicitly by prefs_event, only after all the
 * widgets have been created, takes the information
 * in the prefs struct, and puts it into the appropriate widgets
 */
void
prefs_to_widgets(void)
{
  /* show toolbar as: pics and text, pics only, text only */
  switch(prefs.tb) {
  case(GEG_RC_PICTURES_AND_TEXT):
    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(pt_bu), TRUE);
    break;
  case(GEG_RC_PICTURES_ONLY):
    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(po_bu), TRUE);
    break;
  case(GEG_RC_TEXT_ONLY):
    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(to_bu), TRUE);
  }

  /* show tooltips: on, off */
  if(prefs.tt == GEG_RC_OFF)
    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(tt_bu), FALSE);
  else if(prefs.tt == GEG_RC_ON)
    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(tt_bu), TRUE);
  else
    g_assert_not_reached();

  /* print as SVG: on, off */
  gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(eps_bu), (prefs.print == GEG_RC_EPS));
  gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(pdf_bu), (prefs.print == GEG_RC_PDF));    
  gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(svg_bu), (prefs.print == GEG_RC_SVG));
  
  /* startup options */
  gtk_entry_set_text(GTK_ENTRY(xmin_en), ftoa(prefs.ranges.xmin));
  gtk_entry_set_text(GTK_ENTRY(xmax_en), ftoa(prefs.ranges.xmax));
  gtk_entry_set_text(GTK_ENTRY(ymin_en), ftoa(prefs.ranges.ymin));
  gtk_entry_set_text(GTK_ENTRY(ymax_en), ftoa(prefs.ranges.ymax));
  gtk_entry_set_text(GTK_ENTRY(tmin_en), ftoa(prefs.ranges.tmin));
  gtk_entry_set_text(GTK_ENTRY(tmax_en), ftoa(prefs.ranges.tmax));
  gtk_entry_set_text(GTK_ENTRY(amin_en), ftoa(prefs.ranges.amin));
  gtk_entry_set_text(GTK_ENTRY(amax_en), ftoa(prefs.ranges.amax));
  gtk_entry_set_text(GTK_ENTRY(width_en), ltoa(prefs.ranges.width));
  gtk_entry_set_text(GTK_ENTRY(height_en), ltoa(prefs.ranges.height));
  gtk_entry_set_text(GTK_ENTRY(axes_linewidth_en), ftoa(prefs.axes_linewidth));
  gtk_entry_set_text(GTK_ENTRY(grid_linewidth_en), ftoa(prefs.grid_linewidth)); 
  gtk_entry_set_text(GTK_ENTRY(box_linewidth_en), ftoa(prefs.box_linewidth));
  gtk_entry_set_text(GTK_ENTRY(curves_linewidth_en), ftoa(prefs.curves_linewidth));
  gtk_entry_set_text(GTK_ENTRY(point_radius_en), ftoa(prefs.point_radius));  

  gtk_entry_set_text(GTK_ENTRY(editor_en), prefs.editor);  
  gtk_entry_set_text(GTK_ENTRY(eps_viewer_en), prefs.eps_viewer);
  gtk_entry_set_text(GTK_ENTRY(pdf_viewer_en), prefs.pdf_viewer);  
  gtk_entry_set_text(GTK_ENTRY(svg_viewer_en), prefs.svg_viewer);  
  gtk_entry_set_text(GTK_ENTRY(eps_to_pdf_en), prefs.eps_to_pdf);
  gtk_entry_set_text(GTK_ENTRY(eps_to_svg_en), prefs.eps_to_svg);  

  if(prefs.ranges.radian == DEF_DECIMAL)
    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(dec_bu), TRUE);
  else if(prefs.ranges.radian == DEF_RADIAN)
    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(rad_bu), TRUE);
  else
    g_assert_not_reached();

  gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(cart_bu), (prefs.ranges.mode == DEF_CARTESIAN));
  gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(polar_bu), (prefs.ranges.mode == DEF_POLAR));
  gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(param_bu), (prefs.ranges.mode == DEF_PARAMETRIC));    
  
  /* drawing options */
  gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(ox_bu), (prefs.do_Ox!=0));
  gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(oy_bu), (prefs.do_Oy!=0));
  gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(xt_bu), (prefs.do_xval!=0));
  gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(yt_bu), (prefs.do_yval!=0));
  gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(co_bu), (prefs.do_color!=0));
  gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(sb_bu), (prefs.do_box!=0));
  gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(sf_bu), (prefs.text_position>=0));
  gtk_entry_set_text(GTK_ENTRY(formula_en), ltoa(prefs.text_position & 3));
  gtk_entry_set_text(GTK_ENTRY(formula_size_en), ftoa(prefs.formula_size));
  gtk_entry_set_text(GTK_ENTRY(number_size_en), ftoa(prefs.number_size));    

  /* resolution options */
  gtk_entry_set_text(GTK_ENTRY(zoom_en), ftoa(prefs.zoom));
  gtk_entry_set_text(GTK_ENTRY(space_en), ltoa(prefs.space));
  gtk_entry_set_text(GTK_ENTRY(interp_en), ltoa(prefs.interp));
  gtk_entry_set_text(GTK_ENTRY(minres_en), ftoa(prefs.minres));
  gtk_entry_set_text(GTK_ENTRY(maxres_en), ftoa(prefs.maxres));
  gtk_entry_set_text(GTK_ENTRY(maxform_en), ltoa(prefs.maxform));

  /* color selector */
  gtk_entry_set_text(GTK_ENTRY(maxcolors_en), ltoa(prefs.n_colors));
  gtk_entry_set_text(GTK_ENTRY(current_color_en), ltoa(prefs.cur_color));
  set_current_color(&prefs.cur_color);
  show_color_in_preview(sel_pr, &prefs.rgb_value[3*prefs.cur_color]);  
}

/* widgets_to_prefs, first callback for save button and ok button,
 * puts the information in the widgets into the prefs struct
 */
void
widgets_to_prefs(GtkWidget *widget, gpointer data)
{
  if (!wi) return;

  /* toolbar stuff */
  if(GTK_TOGGLE_BUTTON(pt_bu)->active)
    prefs.tb = GEG_RC_PICTURES_AND_TEXT;
  else if(GTK_TOGGLE_BUTTON(po_bu)->active)
    prefs.tb = GEG_RC_PICTURES_ONLY;
  else
    prefs.tb = GEG_RC_TEXT_ONLY;

  /* tooltips stuff */
  if(GTK_TOGGLE_BUTTON(tt_bu)->active)
    prefs.tt = GEG_RC_ON;
  else
    prefs.tt = GEG_RC_OFF;

  if(GTK_TOGGLE_BUTTON(eps_bu)->active)
    prefs.print = GEG_RC_EPS;
  if(GTK_TOGGLE_BUTTON(pdf_bu)->active)
    prefs.print = GEG_RC_PDF;
  if(GTK_TOGGLE_BUTTON(svg_bu)->active)
    prefs.print = GEG_RC_SVG;

  
  /* resolution options */
  prefs.zoom    = CLAMP(atof(gtk_entry_get_text(GTK_ENTRY(zoom_en))), 0.1, 5);
  prefs.space   = CLAMP(atol(gtk_entry_get_text(GTK_ENTRY(space_en))), 10, 500);
  prefs.minres  = ABS(atof(gtk_entry_get_text(GTK_ENTRY(minres_en))));
  prefs.maxres  = MAX(atof(gtk_entry_get_text(GTK_ENTRY(maxres_en))),
		      10 * prefs.minres);
  prefs.interp  = CLAMP(atol(gtk_entry_get_text(GTK_ENTRY(interp_en))), 0, 12);
  prefs.maxform = CLAMP(atol(gtk_entry_get_text(GTK_ENTRY(maxform_en))), 1, 500);

  /* startup options */
  prefs.ranges.xmin   = atof(gtk_entry_get_text(GTK_ENTRY(xmin_en)));
  prefs.ranges.xmax   = MAX(atof(gtk_entry_get_text(GTK_ENTRY(xmax_en))),
		        prefs.ranges.xmin + 10 * prefs.minres);
  prefs.ranges.ymin   = atof(gtk_entry_get_text(GTK_ENTRY(ymin_en)));
  prefs.ranges.ymax   = MAX(atof(gtk_entry_get_text(GTK_ENTRY(ymax_en))),
		        prefs.ranges.ymin + 10 * prefs.minres);
  prefs.ranges.tmin   = atof(gtk_entry_get_text(GTK_ENTRY(tmin_en)));
  prefs.ranges.tmax   = MAX(atof(gtk_entry_get_text(GTK_ENTRY(tmax_en))),
		        prefs.ranges.tmin + 10 * prefs.minres);
  prefs.ranges.amin   = atof(gtk_entry_get_text(GTK_ENTRY(amin_en)));
  prefs.ranges.amax   = MAX(atof(gtk_entry_get_text(GTK_ENTRY(amax_en))),
		        prefs.ranges.amin + 10 * prefs.minres);
  prefs.ranges.width  = MAX(atol(gtk_entry_get_text(GTK_ENTRY(width_en))),
		         10 * prefs.minres);
  prefs.ranges.height = MAX(atol(gtk_entry_get_text(GTK_ENTRY(height_en))),
		        10 * prefs.minres);
  prefs.axes_linewidth = MAX(atof(gtk_entry_get_text(
			          GTK_ENTRY(axes_linewidth_en))),0);
  prefs.grid_linewidth = MAX(atof(gtk_entry_get_text(
			          GTK_ENTRY(grid_linewidth_en))),0);
  prefs.box_linewidth = MAX(atof(gtk_entry_get_text(
			         GTK_ENTRY(box_linewidth_en))),0);
  prefs.curves_linewidth = MAX(atof(gtk_entry_get_text(
			            GTK_ENTRY(curves_linewidth_en))),0);
  prefs.point_radius = MAX(atof(gtk_entry_get_text(
			        GTK_ENTRY(point_radius_en))),0);

  /* utilities */
  realloc_string(&prefs.editor, (gchar *)gtk_entry_get_text(GTK_ENTRY(editor_en)));
  realloc_string(&prefs.eps_viewer, (gchar *)gtk_entry_get_text(GTK_ENTRY(eps_viewer_en)));
  realloc_string(&prefs.pdf_viewer, (gchar *)gtk_entry_get_text(GTK_ENTRY(pdf_viewer_en)));  
  realloc_string(&prefs.svg_viewer, (gchar *)gtk_entry_get_text(GTK_ENTRY(svg_viewer_en)));
  realloc_string(&prefs.eps_to_pdf, (gchar *)gtk_entry_get_text(GTK_ENTRY(eps_to_pdf_en)));
  realloc_string(&prefs.eps_to_svg, (gchar *)gtk_entry_get_text(GTK_ENTRY(eps_to_svg_en)));
  
  if(GTK_TOGGLE_BUTTON(dec_bu)->active)
    prefs.ranges.radian = DEF_DECIMAL;
  else if(GTK_TOGGLE_BUTTON(rad_bu)->active)
    prefs.ranges.radian = DEF_RADIAN;
  else
    g_assert_not_reached();

  if(GTK_TOGGLE_BUTTON(cart_bu)->active)
    prefs.ranges.mode = DEF_CARTESIAN;
  else if(GTK_TOGGLE_BUTTON(polar_bu)->active)
    prefs.ranges.mode = DEF_POLAR;
  else if(GTK_TOGGLE_BUTTON(param_bu)->active)
    prefs.ranges.mode = DEF_PARAMETRIC;  
  else
    g_assert_not_reached();

  prefs.do_Ox = (GTK_TOGGLE_BUTTON(ox_bu)->active);
  prefs.do_Oy = (GTK_TOGGLE_BUTTON(oy_bu)->active);
  prefs.do_xval = (GTK_TOGGLE_BUTTON(xt_bu)->active);
  prefs.do_yval = (GTK_TOGGLE_BUTTON(yt_bu)->active);
  prefs.do_color = (GTK_TOGGLE_BUTTON(co_bu)->active);
  prefs.do_box = (GTK_TOGGLE_BUTTON(sb_bu)->active);  
  prefs.text_position  = atoi(gtk_entry_get_text(GTK_ENTRY(formula_en))) & 3;
  prefs.formula_size = atof(gtk_entry_get_text(GTK_ENTRY(formula_size_en)));
  prefs.number_size = atof(gtk_entry_get_text(GTK_ENTRY(number_size_en)));  
  
  if (!GTK_TOGGLE_BUTTON(sf_bu)->active) prefs.text_position |= -4;

  /* color options */
}

/* atov, this function extracts the color values from the format:
 *   %d : { %f, %f, %f }
 * and stores them in prefs.rgb_value[]
 */
static gint
atov(gchar *buf)
{
  guint i, j;

  while(*buf == ' ') buf++;
  if(*buf == '\0') return 0;
  j = atoi(buf);
  if (j >= 7) {
    i = prefs.n_colors;
    prefs.n_colors = j + 1;
    prefs.rgb_value = (gdouble *)
      realloc(prefs.rgb_value, 3 * prefs.n_colors * sizeof(double));
    prefs.gdk_color = (GdkColor *)
      realloc(prefs.gdk_color, prefs.n_colors * sizeof(GdkColor));
    for (j = 3*i; j < 3*prefs.n_colors; j++)
      prefs.rgb_value[j] = prefs.rgb_value[15+j%3];
    for (j = i; j < prefs.n_colors; j++)
      prefs.gdk_color[j] = prefs.gdk_color[5];
    i = prefs.n_colors - 1;
  } else
    i = j;

  /* skip to the first number (hopefully) */
  while(*buf && *buf != '{') buf++;
  if(*buf == '\0') return 0;
  buf++;
  if(*buf == '\0') return 0;

  j = 0;
  while(1) {
    /* skip possible whitespace and { */    
    while(*buf == ' ') buf++;
    /* snag the values */
    prefs.rgb_value[3*i+j] = atof(buf);
    if (j==2) return 1;    
    ++j;
    while(*buf && *buf != ',') buf++;
    if(*buf == '\0') return 0;
    buf++;
    if(*buf == '\0') return 0;
  }
  return 1;
}

/* skip_equals, skips to the first non-whitespace character after equals sign
 */
static int
skip_equals(char **buf)
{
  int i;

  for(i = 0; i < 200; i++) {
    if(**buf == '=') {
      (*buf)++;
      while(**buf == ' ')
      (*buf)++;
      return(TRUE);
    }
    (*buf)++;
  }

  g_warning(_("error in rcfile, expected equals"));
  return(FALSE);
}

FILE *
prefs_rc_parse(gchar *filename, gint rc)
{
  FILE *fp;
  char buf[256], *i;

  fp = fopen(filename, "r");

  if(!fp)
    return(NULL);

  while(fgets(buf, 256, fp)) {
    i = &buf[0];
    while(*i == ' ')
      i++;
    if((*i == '#') || (*i == '\n'))
      continue;

    if(!strncasecmp(i, "end_parameters", 14)) {
      if (rc) {
	fclose(fp);
	return(NULL);
      }
      return(fp);  
    }
      
    /* colors */
    if(!strncasecmp(i, "color[", 6)) {
      if (!atov(&i[6]))
        g_warning(_("error in rcfile %s : %s"), filename, buf);	
    }
    
    /* work out what the setting is */
    else if(!strncasecmp(i, "toolbar", 7)) {
      if(skip_equals(&i)) {
        if(!strncasecmp(i, "pictures_and_text", 17))
          prefs.tb = GEG_RC_PICTURES_AND_TEXT;
	else if(!strncasecmp(i, "pictures_only", 13))
          prefs.tb = GEG_RC_PICTURES_ONLY;
	else if(!strncasecmp(i, "text_only", 9))
          prefs.tb = GEG_RC_TEXT_ONLY;
	else
	  g_warning(_("error in rcfile %s: %s"), filename, buf);
      }
    }
    else if(!strncasecmp(i, "tooltips", 8)) {
      if(skip_equals(&i)) {
        if(!strncasecmp(i, "on", 2))
          prefs.tt = GEG_RC_ON;
	else if(!strncasecmp(i, "off", 3))
          prefs.tt = GEG_RC_OFF;
	else
	  g_warning(_("error in rcfile %s: %s"), filename, buf);
      }
    }
    else if(!strncasecmp(i, "print_as", 8)) {
      if(skip_equals(&i)) {
        if(!strncasecmp(i, "eps", 3))
          prefs.print = GEG_RC_EPS;
        else if(!strncasecmp(i, "pdf", 3))
          prefs.print = GEG_RC_PDF;	
	else if(!strncasecmp(i, "svg", 3))
          prefs.print = GEG_RC_SVG;
	else
	  g_warning(_("error in rcfile %s: %s"), filename, buf);
      }
    }
    else if(!strncasecmp(i, "xmin", 4)) {
      if(skip_equals(&i))
        prefs.ranges.xmin = atof(i);
    }
    else if(!strncasecmp(i, "xmax", 4)) {
      if(skip_equals(&i))
        prefs.ranges.xmax = atof(i);
    }
    else if(!strncasecmp(i, "ymin", 4)) {
      if(skip_equals(&i))
        prefs.ranges.ymin = atof(i);
    }
    else if(!strncasecmp(i, "ymax", 4)) {
      if(skip_equals(&i))
        prefs.ranges.ymax = atof(i);
    }
    else if(!strncasecmp(i, "tmin", 4)) {
      if(skip_equals(&i))
        prefs.ranges.tmin = atof(i);
    }
    else if(!strncasecmp(i, "tmax", 4)) {
      if(skip_equals(&i))
        prefs.ranges.tmax = atof(i);
    }
    else if(!strncasecmp(i, "amin", 4)) {
      if(skip_equals(&i))
        prefs.ranges.amin = atof(i);
    }
    else if(!strncasecmp(i, "amax", 4)) {
      if(skip_equals(&i))
        prefs.ranges.amax = atof(i);
    }
    else if(!strncasecmp(i, "graph_width", 11)) {
      if(skip_equals(&i))
        prefs.ranges.width = atol(i);
    }
    else if(!strncasecmp(i, "graph_height", 12)) {
      if(skip_equals(&i))
        prefs.ranges.height = atol(i);
    }
    else if(!strncasecmp(i, "axes_lw", 7)) {
      if(skip_equals(&i))
        prefs.axes_linewidth = atof(i);
    }
    else if(!strncasecmp(i, "grid_lw", 7)) {
      if(skip_equals(&i))
        prefs.grid_linewidth = atof(i);
    }
    else if(!strncasecmp(i, "box_lw", 6)) {
      if(skip_equals(&i))
        prefs.box_linewidth = atof(i);
    }
    else if(!strncasecmp(i, "curves_lw", 7)) {
      if(skip_equals(&i))
        prefs.curves_linewidth = atof(i);
    }
    else if(!strncasecmp(i, "point_radius", 12)) {
      if(skip_equals(&i))
        prefs.point_radius = atof(i);
    }
    else if(!strncasecmp(i, "coordinates_type", 16)) {
      if(skip_equals(&i)) {
	if(!strncasecmp(i, "decimal", 7))
	  prefs.ranges.radian = DEF_DECIMAL;
	else if(!strncasecmp(i, "radian", 6))
	  prefs.ranges.radian = DEF_RADIAN;
	else
	  g_warning(_("error in rcfile %s: %s"), filename, buf);
      }
    }
    else if(!strncasecmp(i, "mode_type", 9)) {
      if(skip_equals(&i)) {
	if(!strncasecmp(i, "cartesian", 9))
	  prefs.ranges.mode = DEF_CARTESIAN;
	else if(!strncasecmp(i, "polar", 5))
	  prefs.ranges.mode = DEF_POLAR;	
	else if(!strncasecmp(i, "parametric", 10))
	  prefs.ranges.mode = DEF_PARAMETRIC;
	else if(!strncasecmp(i, "sequence", 8))
	  prefs.ranges.mode = DEF_SEQUENCE;	
	else
	  g_warning(_("error in rcfile %s: %s"), filename, buf);
      }
    }
    else if(!strncasecmp(i, "do_Ox", 5)) {
      if(skip_equals(&i))
        prefs.do_Ox = atoi(i);
    }
    else if(!strncasecmp(i, "do_Oy", 5)) {
      if(skip_equals(&i))
        prefs.do_Oy = atoi(i);
    }
    else if(!strncasecmp(i, "do_xval", 7)) {
      if(skip_equals(&i))
        prefs.do_xval = atoi(i);
    }
    else if(!strncasecmp(i, "do_yval", 7)) {
      if(skip_equals(&i))
        prefs.do_yval = atoi(i);
    }
    else if(!strncasecmp(i, "do_color", 8)) {
      if(skip_equals(&i))
        prefs.do_color = atoi(i);
    }
    else if(!strncasecmp(i, "do_box", 6)) {
      if(skip_equals(&i))
        prefs.do_box = atoi(i);
    }
    else if(!strncasecmp(i, "text_position", 13)) {
      if(skip_equals(&i))
        prefs.text_position = atoi(i);
    }
    else if(!strncasecmp(i, "zoom_xy", 7)) {
      if(skip_equals(&i)) {
        prefs.zoom_xy = atoi(i);
	if (prefs.zoom_xy <= 0) prefs.zoom_xy = 1;
	if (prefs.zoom_xy > 4) prefs.zoom_xy = 4;
      }
    }
    else if(!strncasecmp(i, "formula_size", 12)) {
      if(skip_equals(&i))
        prefs.formula_size = atof(i);
    }
    else if(!strncasecmp(i, "number_size", 11)) {
      if(skip_equals(&i))
        prefs.number_size = atof(i);
    }
    else if(!strncasecmp(i, "zoom_factor", 11)) {
      if(skip_equals(&i))
        prefs.zoom = atof(i);
    }
    else if(!strncasecmp(i, "notch_spacing", 13)) {
      if(skip_equals(&i))
        prefs.space = atol(i);
    }
    else if(!strncasecmp(i, "minimum_resolution", 18)) {
      if(skip_equals(&i))
        prefs.minres = atof(i);
    }
    else if(!strncasecmp(i, "maximum_resolution", 18)) {
      if(skip_equals(&i))
        prefs.maxres = atof(i);
    }
    else if(!strncasecmp(i, "interpolation_factor", 19)) {
      if(skip_equals(&i))
        prefs.interp = atol(i);
    }
    else if(!strncasecmp(i, "maximum_formulas", 16)) {
      if(skip_equals(&i))
        prefs.maxform = atol(i);
    }
    else if(!strncasecmp(i, "utility_editor", 14)) {
      if(skip_equals(&i))
	realloc_string(&prefs.editor, i);
    }
    else if(!strncasecmp(i, "utility_eps_viewer", 18)) {
      if(skip_equals(&i))
	realloc_string(&prefs.eps_viewer, i);
    }
    else if(!strncasecmp(i, "utility_pdf_viewer", 18)) {
      if(skip_equals(&i))
	realloc_string(&prefs.pdf_viewer, i);
    }
    else if(!strncasecmp(i, "utility_svg_viewer", 18)) {
      if(skip_equals(&i))
	realloc_string(&prefs.svg_viewer, i);
    }
    else if(!strncasecmp(i, "utility_eps_to_pdf", 17)) {
      if(skip_equals(&i))
	realloc_string(&prefs.eps_to_pdf, i);
    }
    else if(!strncasecmp(i, "utility_eps_to_svg", 18)) {
      if(skip_equals(&i))
	realloc_string(&prefs.eps_to_svg, i);
    }
    else
      g_warning(_("error in rcfile %s: %s"), filename, buf);
  }

  fclose(fp);
  return(NULL);
}

/* prefs_rc_write, writes the user's preferences back to file
 */

FILE *
prefs_rc_write(gchar *filename, gint rc)
{
  FILE *fp;
  char *buf;
  guint i;

  fp = fopen(filename, "w+");

  if(!fp)
    return(NULL);

  /* write header */
  fprintf(fp, "# geg %s\n", (rc)? "preferences" : "data file");
  fprintf(fp, "# all options can be changed within geg\n\n");

  /* toolbar */
  switch(prefs.tb) {
  case GEG_RC_PICTURES_AND_TEXT:
    buf = "pictures_and_text";
    break;
  case GEG_RC_PICTURES_ONLY:
    buf = "pictures_only";
    break;
  case GEG_RC_TEXT_ONLY:
    buf = "text_only";
    break;
  default:
    buf = NULL;
  }

  fprintf(fp, "toolbar = %s\n", buf);

  /* tooltips */
  if(prefs.tt == GEG_RC_ON)
    buf = "on";
  else if(prefs.tt == GEG_RC_OFF)
    buf = "off";
  else
    g_assert_not_reached();

  fprintf(fp, "tooltips = %s\n", buf);

  /* SVG on/off */
  if(prefs.print == GEG_RC_EPS)
    buf = "eps";
  else if(prefs.print == GEG_RC_PDF)
    buf = "pdf";
  else if(prefs.print == GEG_RC_SVG)
    buf = "svg";
  else
    g_assert_not_reached();

  fprintf(fp, "print_as = %s\n\n", buf);
  
  /* xmin, xmax, ymin, ymax, width, height */
  fprintf(fp, "xmin = %f\n", prefs.ranges.xmin);
  fprintf(fp, "xmax = %f\n", prefs.ranges.xmax);
  fprintf(fp, "ymin = %f\n", prefs.ranges.ymin);
  fprintf(fp, "ymax = %f\n", prefs.ranges.ymax);
  fprintf(fp, "tmin = %f\n", prefs.ranges.tmin);
  fprintf(fp, "tmax = %f\n", prefs.ranges.tmax);
  fprintf(fp, "amin = %f\n", prefs.ranges.amin);
  fprintf(fp, "amax = %f\n\n", prefs.ranges.amax);
  fprintf(fp, "graph_width = %d\n", prefs.ranges.width);
  fprintf(fp, "graph_height = %d\n", prefs.ranges.height);

  /* linewidth */
  fprintf(fp, "axes_lw = %g\n", prefs.axes_linewidth);
  fprintf(fp, "grid_lw = %g\n", prefs.grid_linewidth);  
  fprintf(fp, "box_lw = %g\n", prefs.box_linewidth);
  fprintf(fp, "curves_lw = %g\n", prefs.curves_linewidth);
  fprintf(fp, "point_radius = %g\n", prefs.point_radius);  
  
  /* coordinates_type */
  if(prefs.ranges.radian == DEF_DECIMAL)
    buf = "decimal";
  else if(prefs.ranges.radian == DEF_RADIAN)
    buf = "radian";
  else
    g_assert_not_reached();

  fprintf(fp, "coordinates_type = %s\n", buf);

  /* mode_type */  
  if(prefs.ranges.mode == DEF_CARTESIAN)
    buf = "cartesian";
  else if(prefs.ranges.mode == DEF_POLAR)
    buf = "polar";  
  else if(prefs.ranges.mode == DEF_PARAMETRIC)
    buf = "parametric";
  else if(prefs.ranges.mode == DEF_SEQUENCE)
    buf = "sequence";  
  else
    g_assert_not_reached();

  fprintf(fp, "mode_type = %s\n", buf);
  fprintf(fp, "zoom_xy = %d\n\n", prefs.zoom_xy);  
  
  /* zoom, space, minres, maxres */
  fprintf(fp, "zoom_factor = %f\n", prefs.zoom);
  fprintf(fp, "notch_spacing = %d\n", prefs.space);
  fprintf(fp, "minimum_resolution = %f\n", prefs.minres);
  fprintf(fp, "maximum_resolution = %f\n", prefs.maxres);
  fprintf(fp, "interpolation_factor = %d\n", prefs.interp);
  fprintf(fp, "maximum_formulas = %d\n\n", prefs.maxform);

  /* drawing choices */
  fprintf(fp, "do_Ox = %d\n", prefs.do_Ox);
  fprintf(fp, "do_Oy = %d\n", prefs.do_Oy);
  fprintf(fp, "do_xval = %d\n", prefs.do_xval);
  fprintf(fp, "do_yval = %d\n", prefs.do_yval);
  fprintf(fp, "do_color = %d\n", prefs.do_color);
  fprintf(fp, "do_box = %d\n\n", prefs.do_box);
  fprintf(fp, "# positions: 0=top left, 1=top right, 2=bottom left, 3=bottom right \n");
  fprintf(fp, "text_position = %d\n", prefs.text_position);
  fprintf(fp, "formula_size = %g\n", prefs.formula_size);
  fprintf(fp, "number_size = %g\n\n", prefs.number_size);    

  /* utilities */
  fprintf(fp, "utility_editor = %s\n", prefs.editor);
  fprintf(fp, "utility_eps_viewer = %s\n", prefs.eps_viewer);  
  fprintf(fp, "utility_pdf_viewer = %s\n", prefs.pdf_viewer);
  fprintf(fp, "utility_eps_to_pdf = %s\n", prefs.eps_to_pdf);
  fprintf(fp, "utility_svg_viewer = %s\n", prefs.svg_viewer);
  fprintf(fp, "utility_eps_to_svg = %s\n\n", prefs.eps_to_svg);    
  
  /* background, numbers, axes, selection */
  for (i=0; i<prefs.n_colors; i++)
    fprintf(fp, "color[%d] = { %f, %f, %f }\n", i,
         prefs.rgb_value[3*i], prefs.rgb_value[3*i+1], prefs.rgb_value[3*i+2]);

  fprintf(fp, "\nend_parameters\n");      

  if (rc) {
    fclose(fp);
    return(NULL);
  }
  return(fp);
}

