"Fossies" - the Fresh Open Source Software Archive

Member "gfsview-snapshot-121130/view/gfkgl.c" (30 Nov 2012, 133939 Bytes) of package /linux/privat/gfsview-snapshot-121130.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "gfkgl.c" see the Fossies "Dox" file reference documentation.

    1 /* Gerris - The GNU Flow Solver
    2  * Copyright (C) 2004-2012 National Institute of Water and Atmospheric
    3  * Research
    4  *
    5  * This program is free software; you can redistribute it and/or
    6  * modify it under the terms of the GNU General Public License as
    7  * published by the Free Software Foundation; either version 2 of the
    8  * License, or (at your option) any later version.
    9  *
   10  * This program is distributed in the hope that it will be useful,
   11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   13  * General Public License for more details.
   14  *
   15  * You should have received a copy of the GNU General Public License
   16  * along with this program; if not, write to the Free Software
   17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
   18  * 02111-1307, USA.  
   19  */
   20 
   21 #include <stdlib.h>
   22 #include <errno.h>
   23 #include <unistd.h>
   24 #include <string.h>
   25 #include <sys/types.h>
   26 #include <sys/stat.h>
   27 #include <glob.h>
   28 #include <math.h>
   29 #include <gts.h>
   30 #include <gtk/gtkgl.h>
   31 #include <gdk/x11/gdkglx.h>
   32 #include <gdk/x11/gdkglglxext.h>
   33 #if defined(__APPLE__)
   34 #  include <OpenGL/glu.h>
   35 #else
   36 #  include <GL/glu.h>
   37 #endif
   38 
   39 #include "gfkgl.h"
   40 #include "gl/trackball.h"
   41 #include "glade/interface.h"
   42 #include "glade/callbacks.h"
   43 #include "glade/support.h"
   44 
   45 G_LOCK_DEFINE (gfk_gl_scripting);
   46 gboolean gfk_gl_scripting = FALSE;
   47 
   48 gpointer lookup_gl               (gpointer widget);
   49 gpointer lookup_widget_params    (gpointer widget, const gchar * widget_name);
   50 void     gl2ps_ppm_set_sensitive (GtkWidget * w, gboolean s, gboolean s1);
   51 
   52 static void    gl2D_destroy  (GtsObject * o);
   53 
   54 typedef GtsFile * (* GfkFunctionSet) (GfkGl *, const gchar *);
   55 
   56 static GtkWidget * gfk_function (GtkWidget * parent, 
   57                  GfkGl * gl, 
   58                  const gchar * name,
   59                  GfkFunctionSet set);
   60 
   61 /* GfkGlSymmetry: Object */
   62 
   63 static void gl2D_read (GtsObject ** o, GtsFile * fp)
   64 {
   65   GtkWidget * params = GFK_GL2D (*o)->params;
   66   GfsGl2D * gl = GFS_GL2D (GFK_GL (*o)->gl);
   67 
   68   (* GTS_OBJECT_CLASS (gfk_gl2D_class ())->parent_class->read) (o, fp);
   69   if (fp->type == GTS_ERROR)
   70     return;
   71 
   72   GFK_GL2D (*o)->n = gl->n;
   73   gtk_spin_button_set_value (lookup_widget_params (params, "spinbuttonx"), gl->n.x);
   74   gtk_spin_button_set_value (lookup_widget_params (params, "spinbuttony"), gl->n.y);
   75   gtk_spin_button_set_value (lookup_widget_params (params, "spinbuttonz"), gl->n.z);
   76   gtk_spin_button_set_value (lookup_widget_params (params, "spinbuttonpos"), gl->pos);
   77 }
   78 
   79 static void pos_bounds (FttCell * root, GfsGl2D * gl, GtkAdjustment * pos)
   80 {
   81   static FttVector d[8] = {{1.,1.,1.},{1.,1.,-1.},{1.,-1.,1.},{1.,-1.,-1.},
   82                {-1.,1.,1.},{-1.,1.,-1.},{-1.,-1.,1.},{-1.,-1.,-1.}};
   83   FttVector p, o;
   84   gdouble h = ftt_cell_size (root)/2.;
   85   guint i;
   86 
   87   pos->lower = G_MAXDOUBLE;
   88   pos->upper = -G_MAXDOUBLE;
   89   ftt_cell_pos (root, &o);
   90   for (i = 0; i < 8; i++) {
   91     gdouble e;
   92     p.x = o.x + h*d[i].x; p.y = o.y + h*d[i].y; p.z = o.z + h*d[i].z;
   93     e = p.x*gl->n.x + p.y*gl->n.y + p.z*gl->n.z;
   94     if (e > pos->upper) pos->upper = e;
   95     if (e < pos->lower) pos->lower = e;
   96   }
   97 }
   98 
   99 static void find_bounds (FttCell * root, GfsGl2D * gl, GtkAdjustment * pos)
  100 {
  101   GtkAdjustment p;
  102 
  103   pos_bounds (root, gl, &p);
  104   if (FTT_CELL_IS_LEAF (root)) {
  105     if (p.upper > pos->upper) pos->upper = p.upper;
  106     if (p.lower < pos->lower) pos->lower = p.lower;
  107   }
  108   else if (p.upper > pos->upper || p.lower < pos->lower) {
  109     FttCellChildren child;
  110     guint n;
  111 
  112     ftt_cell_children (root, &child);
  113     for (n = 0; n < FTT_CELLS; n++)
  114       if (child.c[n])
  115     find_bounds (child.c[n], gl, pos);
  116   }
  117 }
  118 
  119 static void find_box_bounds (GfsBox * box, gpointer * data)
  120 {
  121   find_bounds (box->root, data[0], data[1]);
  122 }
  123 
  124 void gfk_gl2D_update_pos_bounds (GfkGl2D * gl)
  125 {
  126   GtkSpinButton * spos = lookup_widget_params (gl->params, "spinbuttonpos");
  127   GtkAdjustment * pos = gtk_spin_button_get_adjustment (spos);
  128 
  129   if (GFK_IS_GL_PERIODIC (gl)) {
  130     pos->lower = -G_MAXDOUBLE;
  131     pos->upper = G_MAXDOUBLE;
  132   }
  133   else {
  134     gpointer data[2];
  135     data[0] = GFK_GL (gl)->gl;
  136     data[1] = pos;
  137     pos->lower = G_MAXDOUBLE;
  138     pos->upper = -G_MAXDOUBLE;
  139     gts_container_foreach (GTS_CONTAINER (GFK_GL (gl)->gl->sim), (GtsFunc) find_box_bounds, data);
  140   }
  141   gtk_spin_button_set_adjustment (spos, pos);
  142   if (pos->value < pos->lower)
  143     gtk_spin_button_set_value (spos, pos->lower);
  144   else if (pos->value > pos->upper)
  145     gtk_spin_button_set_value (spos, pos->upper);
  146 }
  147 
  148 static void set_pos_increment (GfkGl * gl)
  149 {
  150   GtkSpinButton * maxlevel = lookup_widget_params (gl->properties, "maxlevel");
  151   GtkAdjustment * amax = gtk_spin_button_get_adjustment (maxlevel);
  152   GtkSpinButton * spos = lookup_widget_params (GFK_GL2D (gl)->params, "spinbuttonpos");
  153   GtkAdjustment * pos = gtk_spin_button_get_adjustment (spos);
  154   pos->step_increment = pos->page_increment = 1./exp (amax->upper*log (2.));
  155   gtk_spin_button_set_adjustment (spos, pos);
  156 }
  157 
  158 static void gl2D_set_simulation (GfkGl * gl, GfsSimulation * sim)
  159 {
  160   (* GFK_GL_CLASS (GTS_OBJECT_CLASS (gfk_gl2D_class ())->parent_class)->set_simulation) (gl, sim);
  161   gfk_gl2D_update_pos_bounds (GFK_GL2D (gl));
  162   set_pos_increment (gl);
  163 }
  164 
  165 static void gl2D_post_init (GfkGl * gl)
  166 {
  167   GfkGl2D * gl2 = GFK_GL2D (gl);
  168 
  169   (* GFK_GL_CLASS (GTS_OBJECT_CLASS (gfk_gl2D_class ())->parent_class)->post_init) (gl);
  170 
  171   FttComponent c;
  172   for (c = 0; c < 3; c++) {
  173     static gchar * name[] = {"spinbuttonx", "spinbuttony", "spinbuttonz"};
  174     GtkSpinButton * spin = lookup_widget_params (gl2->params, name[c]);
  175     gdouble v = gtk_spin_button_get_value (spin);
  176     if (v != (&gl2->n.x)[c])
  177       gtk_spin_button_set_value (spin, (&gl2->n.x)[c]);
  178   }
  179 
  180   GFS_GL2D (gl->gl)->n = gl2->n;
  181   gfs_gl2D_update_plane (GFS_GL2D (gl->gl));
  182 
  183   if (gl->gl->sim) {
  184     gfk_gl2D_update_pos_bounds (gl2);
  185     set_pos_increment (gl);
  186   }
  187 }
  188 
  189 static gchar * gl_symmetry_name (GfkGlClass * klass)
  190 {
  191   static gchar name[] = "Symmetry";
  192   return name;
  193 }
  194 
  195 static GtkWidget * gl_symmetry_icon (GfkGlClass * klass)
  196 {
  197   return create_pixmap (NULL, "symmetry-16x16.png");
  198 }
  199 
  200 static void gl_symmetry_class_init (GfkGlClass * klass)
  201 {
  202   GTS_OBJECT_CLASS (klass)->destroy = gl2D_destroy;
  203   GTS_OBJECT_CLASS (klass)->read = gl2D_read;
  204   klass->post_init = gl2D_post_init;
  205   klass->set_simulation = gl2D_set_simulation;
  206 
  207   klass->gl_class = gfs_gl_symmetry_class ();
  208   klass->name = gl_symmetry_name;
  209   klass->icon = gl_symmetry_icon;
  210 }
  211 
  212 static void gl_symmetry_init (GfkGl2D * object)
  213 {
  214   object->params = create_gl2D_params ();
  215   object->n.x = 1.; object->n.y = 0.; object->n.z = 0.;
  216   gfk_gl_prepend_params (GFK_GL (object), object->params, gtk_label_new ("2D Plane"));
  217 #if FTT_2D
  218   gtk_widget_hide (lookup_widget_params (object->params, "spinbuttonz"));
  219 #endif  
  220   gtk_widget_show (object->params);
  221 }
  222 
  223 GfkGlClass * gfk_gl_symmetry_class (void)
  224 {
  225   static GfkGlClass * klass = NULL;
  226 
  227   if (klass == NULL) {
  228     GtsObjectClassInfo gfk_gl_symmetry_info = {
  229       "GfkGlSymmetry",
  230       sizeof (GfkGl2D),
  231       sizeof (GfkGlClass),
  232       (GtsObjectClassInitFunc) gl_symmetry_class_init,
  233       (GtsObjectInitFunc) gl_symmetry_init,
  234       (GtsArgSetFunc) NULL,
  235       (GtsArgGetFunc) NULL
  236     };
  237     klass = gts_object_class_new (GTS_OBJECT_CLASS (gfk_gl_class ()), &gfk_gl_symmetry_info);
  238   }
  239 
  240   return klass;
  241 }
  242 
  243 /* GfkGlPeriodic: Object */
  244 
  245 static gchar * gl_periodic_name (GfkGlClass * klass)
  246 {
  247   static gchar name[] = "Periodic";
  248   return name;
  249 }
  250 
  251 static GtkWidget * gl_periodic_icon (GfkGlClass * klass)
  252 {
  253   return create_pixmap (NULL, "periodic-16x16.png");
  254 }
  255 
  256 static void gl_periodic_class_init (GfkGlClass * klass)
  257 {
  258   klass->gl_class = gfs_gl_periodic_class ();
  259   klass->name = gl_periodic_name;
  260   klass->icon = gl_periodic_icon;
  261 }
  262 
  263 GfkGlClass * gfk_gl_periodic_class (void)
  264 {
  265   static GfkGlClass * klass = NULL;
  266 
  267   if (klass == NULL) {
  268     GtsObjectClassInfo gfk_gl_periodic_info = {
  269       "GfkGlPeriodic",
  270       sizeof (GfkGl2D),
  271       sizeof (GfkGlClass),
  272       (GtsObjectClassInitFunc) gl_periodic_class_init,
  273       (GtsObjectInitFunc) NULL,
  274       (GtsArgSetFunc) NULL,
  275       (GtsArgGetFunc) NULL
  276     };
  277     klass = gts_object_class_new (GTS_OBJECT_CLASS (gfk_gl_symmetry_class ()), 
  278                   &gfk_gl_periodic_info);
  279   }
  280 
  281   return klass;
  282 }
  283 
  284 #if FTT_2D
  285 # include "gfkgl2D.h"
  286 #else  /* 3D */
  287 # include "gfkgl3D.h"
  288 #endif /* 3D */
  289 
  290 /* GfkGl: Object */
  291 
  292 static void gl_read (GtsObject ** o, GtsFile * fp)
  293 {
  294   GtkWidget * properties =  GFK_GL (*o)->properties;
  295   GfsGl * gl = GFK_GL (*o)->gl;
  296   GtsObject * object = GTS_OBJECT (gl);
  297   GtkSpinButton * maxlevel = lookup_widget_params (properties, "maxlevel");
  298   GtkSpinButton * linewidth = lookup_widget_params (properties, "linewidth");
  299   GtkAdjustment * amax = gtk_spin_button_get_adjustment (maxlevel);
  300 
  301   (* object->klass->read) (&object, fp);
  302   if (fp->type == GTS_ERROR)
  303     return;
  304 
  305   gfk_gl_set_color (GFK_GL (*o), gl->lc);
  306 
  307   gtk_spin_button_set_value (linewidth, gl->line_width);
  308 
  309   if (gl->maxlevel == -1)
  310     gtk_spin_button_set_value (maxlevel, amax->upper);
  311   else {
  312     gtk_spin_button_set_value (maxlevel, gl->maxlevel);
  313     gl->maxlevel = -1;
  314     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget_params (properties, "finest")), 
  315                   FALSE);
  316   }
  317 
  318   GtkWidget * font = GFK_GL (*o)->font;
  319   gtk_spin_button_set_value (lookup_widget_params (font, "font_size"), gl->font_size);
  320   if (!gl->use_raster_font) {
  321     gl->use_raster_font = TRUE;
  322     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget_params (font, "vector_font")), 
  323                   TRUE);
  324   }
  325 }
  326 
  327 static void gl_write (GtsObject * o, FILE * fp)
  328 {
  329   GfsGl * gl = GFK_GL (o)->gl;
  330 
  331   (* GTS_OBJECT (gl)->klass->write) (GTS_OBJECT (gl), fp);
  332 }
  333 
  334 static void gl_destroy (GtsObject * object)
  335 {
  336   GfkGl * gl = GFK_GL (object);
  337 
  338   gtk_widget_destroy (gl->params);
  339   gtk_widget_destroy (gl->color_selector);
  340   gts_object_destroy (GTS_OBJECT (gl->gl));
  341   g_free (gl->props);
  342   (* GTS_OBJECT_CLASS (gfk_gl_class ())->parent_class->destroy) (object);
  343 }
  344 
  345 static gchar * gfk_gl_get_name (GfkGl * gl)
  346 {
  347   return (* GFK_GL_CLASS (GTS_OBJECT (gl)->klass)->name) (GFK_GL_CLASS (GTS_OBJECT (gl)->klass));
  348 }
  349 
  350 static GtkWidget * gfk_gl_get_icon (GfkGl * gl)
  351 {
  352   return (* GFK_GL_CLASS (GTS_OBJECT (gl)->klass)->icon) (GFK_GL_CLASS (GTS_OBJECT (gl)->klass));
  353 }
  354 
  355 static gchar * gfk_gl_get_properties (GfkGl * gl)
  356 {
  357   if (GFK_GL_CLASS (GTS_OBJECT (gl)->klass)->properties)
  358     return (* GFK_GL_CLASS (GTS_OBJECT (gl)->klass)->properties) (gl);
  359   return gfk_gl_get_name (gl);
  360 }
  361 
  362 static void gl_update_interface (GfkGl * gl)
  363 {
  364   GtkSpinButton * maxlevel = lookup_widget_params (gl->properties, "maxlevel");
  365   GtkAdjustment * amax = gtk_spin_button_get_adjustment (maxlevel);
  366   
  367   amax->upper = gfs_domain_depth (GFS_DOMAIN (gl->gl->sim));
  368   gtk_spin_button_set_adjustment (maxlevel, amax);
  369   if (gl->gl->maxlevel == -1) {
  370     gtk_spin_button_set_value (maxlevel, amax->upper);
  371     gl->gl->maxlevel = -1;
  372   }
  373   else {
  374     gtk_spin_button_set_value (maxlevel, gl->gl->maxlevel);
  375     GtkToggleButton * finest = GTK_TOGGLE_BUTTON (lookup_widget_params (gl->properties, "finest"));
  376     if (gtk_toggle_button_get_active (finest)) {
  377       gl->gl->maxlevel = -1;
  378       gtk_toggle_button_set_active (finest, FALSE);
  379     }
  380   }
  381   gtk_spin_button_set_value (lookup_widget_params (gl->font, "font_size"), gl->gl->font_size);
  382 }
  383 
  384 static void gl_set_simulation (GfkGl * gl, GfsSimulation * sim)
  385 {
  386   gfs_gl_set_simulation (gl->gl, sim);
  387   gl_update_interface (gl);
  388 }
  389 
  390 static void gl_post_init (GfkGl * gl)
  391 {
  392   gtk_window_set_transient_for (GTK_WINDOW (gl->params),
  393                 GTK_WINDOW (gtk_widget_get_toplevel (gl->glarea)));
  394   gtk_window_set_destroy_with_parent (GTK_WINDOW (gl->params), FALSE);
  395   gtk_window_set_position (GTK_WINDOW (gl->params), GTK_WIN_POS_CENTER_ON_PARENT);
  396   gtk_window_set_title (GTK_WINDOW (gl->params), gfk_gl_get_name (gl));
  397 
  398   gtk_window_set_transient_for (GTK_WINDOW (gl->color_selector),
  399                 GTK_WINDOW (gtk_widget_get_toplevel (gl->glarea)));
  400   gtk_window_set_destroy_with_parent (GTK_WINDOW (gl->color_selector), FALSE);
  401   gtk_window_set_position (GTK_WINDOW (gl->color_selector), GTK_WIN_POS_CENTER_ON_PARENT);
  402 
  403   if (gl->gl->sim)
  404     gl_update_interface (gl);
  405 }
  406 
  407 static void gl_add_gl (GtkWidget * glarea, GtkWidget * list, GfkGl * gl)
  408 {
  409   GtkListStore * store;
  410   GtkTreeSelection * select;
  411   GtkTreeIter iter;
  412   GtkImage * icon;
  413   
  414   g_return_if_fail (glarea != NULL);
  415   g_return_if_fail (list != NULL);
  416   g_return_if_fail (gl != NULL);
  417 
  418   store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (list)));
  419   select = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
  420   icon = GTK_IMAGE (gfk_gl_get_icon (gl));
  421   
  422   gtk_list_store_append (store, &iter);
  423   gtk_list_store_set (store, &iter,
  424               VISIBLE_COLUMN,    TRUE,
  425               ICON_COLUMN,       icon ? gtk_image_get_pixbuf (icon) : NULL,
  426               PROPERTIES_COLUMN, gfk_gl_get_properties (gl),
  427               GL_COLUMN, gl,
  428               SELECTED_COLUMN,   TRUE,
  429               -1);
  430   gtk_tree_selection_select_iter (select, &iter);
  431 
  432   gtk_widget_set_sensitive (lookup_widget (glarea, "save1"), TRUE);
  433   gtk_widget_set_sensitive (lookup_widget (glarea, "edit1"), TRUE);
  434   gtk_widget_set_sensitive (lookup_widget (glarea, "view1"), TRUE);
  435 }
  436 
  437 static void gl_add (GObject * tool)
  438 {
  439   GtkWidget * glarea = g_object_get_data (tool, "glarea");
  440   GtkWidget * list = g_object_get_data (tool, "list");  
  441   GfkGlClass * klass = g_object_get_data (tool, "klass");
  442   GfsSimulation * sim = g_object_get_data (G_OBJECT (glarea), "sim");
  443   GfkGl * gl = gfk_gl_new (klass, glarea, list);
  444 
  445   gfk_gl_set_simulation (gl, sim);
  446   gl_add_gl (glarea, list, gl);
  447   gfk_gl_expose (gl);
  448 }
  449 
  450 static GtkWidget * gl_icon (GfkGlClass * klass)
  451 {
  452   return NULL;
  453 }
  454 
  455 static gchar * gl_name (GfkGlClass * klass)
  456 {
  457   return GTS_OBJECT_CLASS (klass)->info.name;
  458 }
  459 
  460 static void gl_class_init (GfkGlClass * klass)
  461 {
  462   klass->gl_class = gfs_gl_class ();
  463   klass->post_init = gl_post_init;
  464   klass->set_simulation = gl_set_simulation;
  465   klass->icon = gl_icon;
  466   klass->name = gl_name;
  467   GTS_OBJECT_CLASS (klass)->read = gl_read;
  468   GTS_OBJECT_CLASS (klass)->write = gl_write;
  469   GTS_OBJECT_CLASS (klass)->destroy = gl_destroy;
  470 }
  471 
  472 static gboolean hide_params (GtkWidget * p)
  473 {
  474   GfkGl * gl = g_object_get_data (G_OBJECT (p), "GfkGl");
  475 
  476   gtk_widget_hide (p);
  477   g_object_set_data (G_OBJECT (gl->list), "former", NULL);
  478   return TRUE;
  479 }
  480 
  481 void gfk_gl_prepend_params (GfkGl * gl, GtkWidget * widget, GtkWidget * label)
  482 {
  483   gtk_notebook_prepend_page (g_object_get_data (G_OBJECT (gl->params), "book"), widget, label);
  484 }
  485 
  486 void gfk_gl_set_color (GfkGl * gl, GtsColor c)
  487 {
  488   GdkColor gc;
  489   GtkWidget * b;
  490 
  491   g_return_if_fail (gl != NULL);
  492 
  493   gl->gl->lc = c;
  494   b = lookup_widget_params (gl->properties, "default_color");
  495   gc.red = c.r*65535.;
  496   gc.green = c.g*65535.;
  497   gc.blue = c.b*65535.;
  498   gtk_widget_modify_bg (b, GTK_STATE_NORMAL, &gc);
  499   gtk_widget_modify_bg (b, GTK_STATE_PRELIGHT, &gc);
  500   gtk_widget_modify_bg (b, GTK_STATE_ACTIVE, &gc);
  501   gtk_color_selection_set_current_color (GTK_COLOR_SELECTION (lookup_widget (gl->color_selector, 
  502                                          "colorselection1")),
  503                      &gc);
  504 }
  505 
  506 static void gl_init (GfkGl * gl)
  507 {
  508   GtkWidget * b = gtk_notebook_new ();
  509 
  510   gtk_widget_show (b);
  511   gl->params = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  512 
  513   g_signal_connect (G_OBJECT (gl->params), "delete_event", G_CALLBACK (hide_params), NULL);
  514   g_object_set_data (G_OBJECT (gl->params), "GfkGl", gl);
  515   g_object_set_data (G_OBJECT (gl->params), "book", b);
  516   gtk_container_add (GTK_CONTAINER (gl->params), b);
  517 
  518   gl->properties = create_gl_params ();
  519   gfk_gl_prepend_params (gl, gl->properties, gtk_label_new ("Properties"));
  520   gtk_widget_show (gl->properties);
  521 
  522   gl->font = create_font_params ();
  523   gfk_gl_prepend_params (gl, gl->font, gtk_label_new ("Font"));
  524   /* dot not show by default */
  525 
  526   gl->color_selector = create_color_selector ();
  527   g_signal_connect (G_OBJECT (gl->color_selector), "delete_event", 
  528             G_CALLBACK (gtk_widget_hide_on_delete), NULL);
  529   g_object_set_data (G_OBJECT (gl->color_selector), "GfkGl", gl);
  530 
  531   gl->gl = gfs_gl_new (GFK_GL_CLASS (GTS_OBJECT (gl)->klass)->gl_class);
  532   
  533   gfk_gl_set_color (gl, gl->gl->lc);
  534 
  535   GtkSpinButton * linewidth = lookup_widget_params (gl->properties, "linewidth");
  536   GtkAdjustment * a = gtk_spin_button_get_adjustment (linewidth);
  537   GLint range[2];
  538   glGetIntegerv (GL_ALIASED_LINE_WIDTH_RANGE, range);
  539   a->lower = range[0];
  540   a->upper = range[1];
  541   gtk_spin_button_set_adjustment (linewidth, a);
  542 }
  543 
  544 GfkGlClass * gfk_gl_class (void)
  545 {
  546   static GfkGlClass * klass = NULL;
  547 
  548   if (klass == NULL) {
  549     GtsObjectClassInfo gfk_gl_info = {
  550       "GfkGl",
  551       sizeof (GfkGl),
  552       sizeof (GfkGlClass),
  553       (GtsObjectClassInitFunc) gl_class_init,
  554       (GtsObjectInitFunc) gl_init,
  555       (GtsArgSetFunc) NULL,
  556       (GtsArgGetFunc) NULL
  557     };
  558     klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_object_class ()),
  559                   &gfk_gl_info);
  560   }
  561 
  562   return klass;
  563 }
  564 
  565 GfkGl * gfk_gl_new (GfkGlClass * klass,
  566             GtkWidget * glarea,
  567             GtkWidget * list)
  568 {
  569   GfkGl * object;
  570 
  571   g_return_val_if_fail (klass != NULL, NULL);
  572   g_return_val_if_fail (glarea != NULL, NULL);
  573   g_return_val_if_fail (list != NULL, NULL);
  574 
  575   object = GFK_GL (gts_object_new (GTS_OBJECT_CLASS (klass)));
  576   object->glarea = glarea;
  577   object->gl->p = g_object_get_data (G_OBJECT (glarea), "GfsGlViewParams");
  578   object->list = list;
  579   (* klass->post_init) (object);
  580 
  581   return object;
  582 }
  583 
  584 void gfk_gl_expose (GfkGl * gl)
  585 {
  586   g_return_if_fail (gl != NULL);
  587 
  588   if (gl->glarea->window)
  589     gdk_window_invalidate_rect (gl->glarea->window, &gl->glarea->allocation, FALSE);
  590 }
  591 
  592 void gfk_gl_set_sensitive (GfkGl * gl, GtkWidget * page, gboolean sensitive)
  593 {
  594   GtkNotebook * book;
  595 
  596   g_return_if_fail (gl != NULL);
  597   g_return_if_fail (page != NULL);
  598 
  599   book = g_object_get_data (G_OBJECT (gl->params), "book");
  600   gtk_widget_set_sensitive (page, sensitive);
  601   gtk_widget_set_sensitive (gtk_notebook_get_tab_label (book, page), sensitive);
  602 }
  603 
  604 void gfk_gl_set_simulation (GfkGl * gl, GfsSimulation * sim)
  605 {
  606   g_return_if_fail (gl != NULL);
  607   g_return_if_fail (sim != NULL);
  608 
  609   if (GFK_GL_CLASS (GTS_OBJECT (gl)->klass)->set_simulation)
  610     (* GFK_GL_CLASS (GTS_OBJECT (gl)->klass)->set_simulation) (gl, sim);
  611 }
  612 
  613 void gfk_gl_update_properties (GfkGl * gl)
  614 {
  615   GtkTreeSelection * select = gtk_tree_view_get_selection (GTK_TREE_VIEW (gl->list));
  616   GtkTreeModel * model;
  617   GtkTreeIter iter;
  618 
  619   if (gtk_tree_selection_get_selected (select, &model, &iter)) {
  620     GfkGl * sgl;
  621 
  622     gtk_tree_model_get (model, &iter, GL_COLUMN, &sgl, -1);
  623     if (sgl == gl)
  624       gtk_list_store_set (GTK_LIST_STORE (model), &iter, 
  625               PROPERTIES_COLUMN, gfk_gl_get_properties (gl), 
  626               -1);
  627   }
  628 }
  629 
  630 void gfk_gl_update_interface (GfkGl * gl)
  631 {
  632   g_return_if_fail (gl != NULL);
  633 
  634   if (GFK_GL_CLASS (GTS_OBJECT (gl)->klass)->update_interface)
  635     (* GFK_GL_CLASS (GTS_OBJECT (gl)->klass)->update_interface) (gl);
  636 }
  637 
  638 static void gfk_gl_menu_append (GfkGlClass * klass,
  639                 GtkMenu * objects,
  640                 GtkWidget * list,
  641                 GtkWidget * glarea)
  642 {
  643   GtkWidget * menuitem;
  644 
  645   menuitem = gtk_menu_item_new_with_mnemonic ((* klass->name) (klass));
  646   g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (gl_add), NULL);
  647   g_object_set_data (G_OBJECT (menuitem), "klass", klass);
  648   g_object_set_data (G_OBJECT (menuitem), "list", list);
  649   g_object_set_data (G_OBJECT (menuitem), "glarea", glarea);
  650   gtk_widget_show (menuitem);
  651   gtk_container_add (GTK_CONTAINER (objects), menuitem);
  652 }
  653 
  654 static void gfk_gl_tool_append (GfkGlClass * klass,
  655                 GtkToolbar * toolbar,
  656                 GtkMenu * objects,
  657                 GtkWidget * list,
  658                 GtkWidget * glarea)
  659 {
  660   GtkWidget * tool;
  661 
  662   gfk_gl_menu_append (klass, objects, list, glarea);
  663   tool = gtk_toolbar_append_element (toolbar,
  664                      GTK_TOOLBAR_CHILD_BUTTON,
  665                      NULL,
  666                      (* klass->name) (klass),
  667                      NULL, NULL,
  668                      (* klass->icon) (klass),
  669                      (GtkSignalFunc) gl_add, NULL);
  670   g_object_set_data (G_OBJECT (tool), "klass", klass);
  671   g_object_set_data (G_OBJECT (tool), "list", list);
  672   g_object_set_data (G_OBJECT (tool), "glarea", glarea);
  673   gtk_widget_show (tool);
  674 }
  675 
  676 /* GfkGlLabel: Object */
  677 
  678 static void gl_label_read (GtsObject ** o, GtsFile * fp)
  679 {
  680   (* GTS_OBJECT_CLASS (gfk_gl_label_class ())->parent_class->read) (o, fp);
  681   if (fp->type == GTS_ERROR)
  682     return;
  683 
  684   GfsGlLabel * gl = GFS_GL_LABEL (GFK_GL (*o)->gl);
  685   GtkWidget * label = GFK_GL_LABEL (*o)->label;
  686   gtk_entry_set_text (lookup_widget_params (label, "labelentry"), gl->label);
  687   gtk_spin_button_set_value (lookup_widget_params (label, "labelx"), gl->p.x);
  688   gtk_spin_button_set_value (lookup_widget_params (label, "labely"), gl->p.y);
  689   gtk_spin_button_set_value (lookup_widget_params (label, "labelz"), gl->p.z);
  690   if (gl->symbol) {
  691     gl->symbol = FALSE;
  692     gtk_toggle_button_set_active (lookup_widget_params (label, "label_symbol_check"), TRUE);
  693   }
  694 }
  695 
  696 typedef struct {
  697   GfsSimulation * sim;
  698   GtkAdjustment * pos[3];
  699 } BoundPar;
  700 
  701 static void cell_bounds (FttCell * cell, BoundPar * par)
  702 {
  703   static FttVector d[8] = {{ 1.,1.,1.},{ 1.,1.,-1.},{ 1.,-1.,1.},{ 1.,-1.,-1.},
  704                {-1.,1.,1.},{-1.,1.,-1.},{-1.,-1.,1.},{-1.,-1.,-1.}};
  705   FttVector p, o;
  706   gdouble h = ftt_cell_size (cell)/2.;
  707   guint i;
  708 
  709   ftt_cell_pos (cell, &o);
  710   for (i = 0; i < 8; i++) {
  711     p.x = o.x + h*d[i].x; p.y = o.y + h*d[i].y; p.z = o.z + h*d[i].z;
  712     gfs_simulation_map_inverse (par->sim, &p);
  713     FttComponent c;
  714     for (c = 0; c < 3; c++) {
  715       gdouble e = (&p.x)[c];
  716       if (e > par->pos[c]->upper) par->pos[c]->upper = e;
  717       if (e < par->pos[c]->lower) par->pos[c]->lower = e;
  718     }
  719   }
  720 }
  721 
  722 static void gfk_gl_label_update_bounds (GfkGlLabel * gl)
  723 {
  724   GtkSpinButton * label[3];
  725   BoundPar p;
  726   p.sim = GFK_GL (gl)->gl->sim;
  727   FttComponent c;
  728   for (c = 0; c < 3; c++) {
  729     const char * name[] = { "labelx", "labely", "labelz" };
  730     label[c] = lookup_widget_params (gl->label, name[c]);
  731     p.pos[c] = gtk_spin_button_get_adjustment (label[c]);
  732     p.pos[c]->lower = G_MAXDOUBLE;
  733     p.pos[c]->upper = -G_MAXDOUBLE;
  734   }
  735   gfs_domain_cell_traverse (GFS_DOMAIN (GFK_GL (gl)->gl->sim), 
  736                 FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS | FTT_TRAVERSE_LEVEL, 5,
  737                 (FttCellTraverseFunc) cell_bounds, &p);
  738   for (c = 0; c < 3; c++) {
  739     gdouble range = p.pos[c]->upper - p.pos[c]->lower;
  740     p.pos[c]->lower -= range/2.;
  741     p.pos[c]->upper += range/2.;
  742     gtk_spin_button_set_adjustment (label[c], p.pos[c]);
  743     if (p.pos[c]->value < p.pos[c]->lower)
  744       gtk_spin_button_set_value (label[c], p.pos[c]->lower);
  745     else if (p.pos[c]->value > p.pos[c]->upper)
  746       gtk_spin_button_set_value (label[c], p.pos[c]->upper);
  747     gdouble max = fabs (p.pos[c]->upper);
  748     if (fabs (p.pos[c]->lower) > max)
  749       max = fabs (p.pos[c]->lower);
  750     if (max == 0.)
  751       gtk_spin_button_set_digits (label[c], 3);
  752     else {
  753       gint n = 6. - log10 (max);
  754       gtk_spin_button_set_digits (label[c], MAX (n, 0));
  755     }
  756   }
  757 }
  758 
  759 static void gl_label_set_simulation (GfkGl * gl, GfsSimulation * sim)
  760 {
  761   (* GFK_GL_CLASS (GTS_OBJECT_CLASS (gfk_gl_label_class ())->parent_class)->set_simulation) 
  762     (gl, sim);
  763   gfk_gl_label_update_bounds (GFK_GL_LABEL (gl));
  764 }
  765 
  766 static gchar * gl_label_name (GfkGlClass * klass)
  767 {
  768   static gchar name[] = "Label";
  769   return name;
  770 }
  771 
  772 static GtkWidget * gl_label_icon (GfkGlClass * klass)
  773 {
  774   return create_pixmap (NULL, "label-16x16.png");
  775 }
  776 
  777 #define MAXLENGTH 16
  778 
  779 static gchar * gl_label_properties (GfkGl * gl)
  780 {
  781   g_free (gl->props);
  782   gl->props = g_strndup (GFS_GL_LABEL (gl->gl)->label, MAXLENGTH + 3);
  783   if (strlen (gl->props) > MAXLENGTH) {
  784     guint i;
  785     for (i = 0; i < 3; i++)
  786       gl->props[MAXLENGTH + i] = '.';
  787   }
  788   return gl->props;
  789 }
  790 
  791 static void gl_label_class_init (GfkGlClass * klass)
  792 {
  793   klass->gl_class = gfs_gl_label_class ();
  794   klass->name = gl_label_name;
  795   klass->icon = gl_label_icon;
  796   klass->properties = gl_label_properties;
  797   klass->set_simulation = gl_label_set_simulation;
  798   GTS_OBJECT_CLASS (klass)->read = gl_label_read;
  799 }
  800 
  801 static void gl_label_init (GfkGl * gl)
  802 {
  803   gtk_widget_show (gl->font);
  804 
  805   GFK_GL_LABEL (gl)->label = create_label_params ();
  806   gfk_gl_prepend_params (gl, GFK_GL_LABEL (gl)->label, gtk_label_new ("Label"));
  807   gtk_widget_show (GFK_GL_LABEL (gl)->label);
  808 }
  809 
  810 GfkGlClass * gfk_gl_label_class (void)
  811 {
  812   static GfkGlClass * klass = NULL;
  813 
  814   if (klass == NULL) {
  815     GtsObjectClassInfo gfk_gl_label_info = {
  816       "GfkGlLabel",
  817       sizeof (GfkGlLabel),
  818       sizeof (GfkGlClass),
  819       (GtsObjectClassInitFunc) gl_label_class_init,
  820       (GtsObjectInitFunc) gl_label_init,
  821       (GtsArgSetFunc) NULL,
  822       (GtsArgGetFunc) NULL
  823     };
  824     klass = gts_object_class_new (GTS_OBJECT_CLASS (gfk_gl_class ()), &gfk_gl_label_info);
  825   }
  826 
  827   return klass;
  828 }
  829 
  830 /* GfkGl2D: Object */
  831 
  832 static void gl2D_destroy (GtsObject * o)
  833 {
  834   g_free (GFK_GL2D (o)->pickinfo);
  835 
  836   (* GTS_OBJECT_CLASS (gfk_gl2D_class ())->parent_class->destroy) (o);
  837 }
  838 
  839 static void gl2D_class_init (GfkGlClass * klass)
  840 {
  841   GTS_OBJECT_CLASS (klass)->destroy = gl2D_destroy;
  842   GTS_OBJECT_CLASS (klass)->read = gl2D_read;
  843   klass->post_init = gl2D_post_init;
  844   klass->set_simulation = gl2D_set_simulation;
  845   klass->pickinfo = gl2D_pickinfo;
  846 
  847   klass->gl_class = gfs_gl_symmetry_class ();
  848   klass->name = gl_symmetry_name;
  849   klass->icon = gl_symmetry_icon;
  850 }
  851 
  852 GfkGlClass * gfk_gl2D_class (void)
  853 {
  854   static GfkGlClass * klass = NULL;
  855 
  856   if (klass == NULL) {
  857     GtsObjectClassInfo gfk_gl2D_info = {
  858       "GfkGl2D",
  859       sizeof (GfkGl2D),
  860       sizeof (GfkGlClass),
  861       (GtsObjectClassInitFunc) gl2D_class_init,
  862       (GtsObjectInitFunc) gl2D_init,
  863       (GtsArgSetFunc) NULL,
  864       (GtsArgGetFunc) NULL
  865     };
  866     klass = gts_object_class_new (GTS_OBJECT_CLASS (gfk_gl_class ()), &gfk_gl2D_info);
  867   }
  868 
  869   return klass;
  870 }
  871 
  872 /* GfkGlCells: Object */
  873 
  874 static gchar * gl_cells_name (GfkGlClass * klass)
  875 {
  876   static gchar name[] = "Cells";
  877   return name;
  878 }
  879 
  880 static GtkWidget * gl_cells_icon (GfkGlClass * klass)
  881 {
  882   return create_pixmap (NULL, "cells-16x16.png");
  883 }
  884 
  885 static gboolean coarsenable (FttCell * cell)
  886 {
  887   return TRUE;
  888 }
  889 
  890 static gchar * gl_cells_pickinfo (GfkGl * gl, gboolean motion)
  891 {
  892   GfkGlCells * glk = GFK_GL_CELLS (gl);
  893 
  894   if (gtk_toggle_button_get_active (lookup_widget_params (glk->cells, "edit"))) {
  895     FttCell * cell = GFS_GL2D (gl->gl)->picked;
  896     guint level = gtk_spin_button_get_value (lookup_widget_params (glk->cells, "level"));
  897     guint l = ftt_cell_level (cell);
  898     if (FTT_CELL_IS_LEAF (cell) && l < level) {
  899       GfsDomain * domain = GFS_DOMAIN (gl->gl->sim);
  900       while (l < level) {
  901     ftt_cell_refine_corners (cell, (FttCellInitFunc) gfs_cell_fine_init, domain);
  902     ftt_cell_refine_single (cell, (FttCellInitFunc) gfs_cell_fine_init, domain);
  903     if (++l < level)
  904       cell = ftt_cell_locate (cell, GFS_GL2D (gl->gl)->pickedpos, -1);
  905       }
  906       gfk_gl_expose (gl);
  907     }
  908     else if ((FTT_CELL_IS_LEAF (cell) && l > level) ||
  909          (!FTT_CELL_IS_LEAF (cell) && l >= level)) {
  910       while (l > level) {
  911     cell = ftt_cell_parent (cell);
  912     l--;
  913       }
  914       ftt_cell_coarsen (cell, (FttCellCoarsenFunc) coarsenable, NULL,
  915             (FttCellCleanupFunc) gfs_cell_cleanup, GFS_DOMAIN (gl->gl->sim));
  916       gfk_gl_expose (gl);
  917     }
  918   }
  919   return gl2D_pickinfo (gl, motion);
  920 }
  921 
  922 static void gl_cells_class_init (GfkGlClass * klass)
  923 {
  924   klass->gl_class = gfs_gl_cells_class ();
  925   klass->name = gl_cells_name;
  926   klass->icon = gl_cells_icon;
  927   klass->pickinfo = gl_cells_pickinfo;
  928 }
  929 
  930 static void gl_cells_init (GfkGl * gl)
  931 {
  932   gtk_widget_set_sensitive (lookup_widget_params (gl->properties, "shading_label"), FALSE);
  933   gtk_widget_set_sensitive (lookup_widget_params (gl->properties, "shading"), FALSE);
  934 
  935   GFK_GL_CELLS (gl)->cells = create_cells_params ();
  936   gfk_gl_prepend_params (gl, GFK_GL_CELLS (gl)->cells, gtk_label_new ("Cells"));
  937   gtk_widget_show (GFK_GL_CELLS (gl)->cells);
  938 }
  939 
  940 GfkGlClass * gfk_gl_cells_class (void)
  941 {
  942   static GfkGlClass * klass = NULL;
  943 
  944   if (klass == NULL) {
  945     GtsObjectClassInfo gfk_gl_cells_info = {
  946       "GfkGlCells",
  947       sizeof (GfkGlCells),
  948       sizeof (GfkGlClass),
  949       (GtsObjectClassInitFunc) gl_cells_class_init,
  950       (GtsObjectInitFunc) gl_cells_init,
  951       (GtsArgSetFunc) NULL,
  952       (GtsArgGetFunc) NULL
  953     };
  954     klass = gts_object_class_new (GTS_OBJECT_CLASS (gfk_gl2D_class ()), &gfk_gl_cells_info);
  955   }
  956 
  957   return klass;
  958 }
  959 
  960 /* GfkGlFractions: Object */
  961 
  962 static gchar * gl_fractions_name (GfkGlClass * klass)
  963 {
  964   static gchar name[] = "Fractions";
  965   return name;
  966 }
  967 
  968 static GtkWidget * gl_fractions_icon (GfkGlClass * klass)
  969 {
  970   return create_pixmap (NULL, "fractions-16x16.png");
  971 }
  972 
  973 static void gl_fractions_class_init (GfkGlClass * klass)
  974 {
  975   klass->gl_class = gfs_gl_fractions_class ();
  976   klass->name = gl_fractions_name;
  977   klass->icon = gl_fractions_icon;
  978 }
  979 
  980 GfkGlClass * gfk_gl_fractions_class (void)
  981 {
  982   static GfkGlClass * klass = NULL;
  983 
  984   if (klass == NULL) {
  985     GtsObjectClassInfo gfk_gl_fractions_info = {
  986       "GfkGlFractions",
  987       sizeof (GfkGl),
  988       sizeof (GfkGlClass),
  989       (GtsObjectClassInitFunc) gl_fractions_class_init,
  990       (GtsObjectInitFunc) gl_fractions_init,
  991       (GtsArgSetFunc) NULL,
  992       (GtsArgGetFunc) NULL
  993     };
  994     klass = gts_object_class_new (GTS_OBJECT_CLASS (gfk_gl_class ()), &gfk_gl_fractions_info);
  995   }
  996 
  997   return klass;
  998 }
  999 
 1000 /* GfkGlBoundaries: Object */
 1001 
 1002 static gchar * gl_boundaries_name (GfkGlClass * klass)
 1003 {
 1004   static gchar name[] = "Boundaries";
 1005   return name;
 1006 }
 1007 
 1008 static GtkWidget * gl_boundaries_icon (GfkGlClass * klass)
 1009 {
 1010   return create_pixmap (NULL, "boundaries-16x16.png");
 1011 }
 1012 
 1013 static void gl_boundaries_class_init (GfkGlClass * klass)
 1014 {
 1015   klass->gl_class = gfs_gl_boundaries_class ();
 1016   klass->name = gl_boundaries_name;
 1017   klass->icon = gl_boundaries_icon;
 1018 }
 1019 
 1020 static void gl_boundaries_init (GfkGl * gl)
 1021 {
 1022   gtk_widget_set_sensitive (lookup_widget_params (gl->properties, "shading_label"), FALSE);
 1023   gtk_widget_set_sensitive (lookup_widget_params (gl->properties, "shading"), FALSE);
 1024 }
 1025 
 1026 GfkGlClass * gfk_gl_boundaries_class (void)
 1027 {
 1028   static GfkGlClass * klass = NULL;
 1029 
 1030   if (klass == NULL) {
 1031     GtsObjectClassInfo gfk_gl_boundaries_info = {
 1032       "GfkGlBoundaries",
 1033       sizeof (GfkGl),
 1034       sizeof (GfkGlClass),
 1035       (GtsObjectClassInitFunc) gl_boundaries_class_init,
 1036       (GtsObjectInitFunc) gl_boundaries_init,
 1037       (GtsArgSetFunc) NULL,
 1038       (GtsArgGetFunc) NULL
 1039     };
 1040     klass = gts_object_class_new (GTS_OBJECT_CLASS (gfk_gl_class ()), &gfk_gl_boundaries_info);
 1041   }
 1042 
 1043   return klass;
 1044 }
 1045 
 1046 /* GfkGlLevels: Object */
 1047 
 1048 static gchar * gl_levels_name (GfkGlClass * klass)
 1049 {
 1050   static gchar name[] = "Levels";
 1051   return name;
 1052 }
 1053 
 1054 static GtkWidget * gl_levels_icon (GfkGlClass * klass)
 1055 {
 1056   return create_pixmap (NULL, "levels-16x16.png");
 1057 }
 1058 
 1059 static void gl_levels_class_init (GfkGlClass * klass)
 1060 {
 1061   klass->gl_class = gfs_gl_levels_class ();
 1062   klass->name = gl_levels_name;
 1063   klass->icon = gl_levels_icon;
 1064 }
 1065 
 1066 static void gl_levels_init (GfkGl * gl)
 1067 {
 1068   gtk_widget_set_sensitive (lookup_widget_params (gl->properties, "shading_label"), FALSE);
 1069   gtk_widget_set_sensitive (lookup_widget_params (gl->properties, "shading"), FALSE);
 1070 }
 1071 
 1072 GfkGlClass * gfk_gl_levels_class (void)
 1073 {
 1074   static GfkGlClass * klass = NULL;
 1075 
 1076   if (klass == NULL) {
 1077     GtsObjectClassInfo gfk_gl_levels_info = {
 1078       "GfkGlLevels",
 1079       sizeof (GfkGlLevels),
 1080       sizeof (GfkGlClass),
 1081       (GtsObjectClassInitFunc) gl_levels_class_init,
 1082       (GtsObjectInitFunc) gl_levels_init,
 1083       (GtsArgSetFunc) NULL,
 1084       (GtsArgGetFunc) NULL
 1085     };
 1086     klass = gts_object_class_new (GTS_OBJECT_CLASS (gfk_gl2D_class ()),
 1087                   &gfk_gl_levels_info);
 1088   }
 1089 
 1090   return klass;
 1091 }
 1092 
 1093 /* GfkFunction */
 1094 
 1095 enum {
 1096   COMPLETION_NAME_COLUMN,
 1097   COMPLETION_DESCRIPTION_COLUMN,
 1098   COMPLETION_POINTER_COLUMN
 1099 } CompletionColumns;
 1100 
 1101 #define GLADE_HOOKUP_OBJECT(component,widget,name) \
 1102   g_object_set_data_full (G_OBJECT (component), name, \
 1103     gtk_widget_ref (widget), (GDestroyNotify) gtk_widget_unref)
 1104 
 1105 static void on_function_activate (GtkEntry * entry, GfkFunctionSet set)
 1106 {
 1107   GfkGl * gl = lookup_gl (entry);
 1108   const gchar * needle = gtk_entry_get_text (entry);
 1109   GtsFile * fp = (*set) (gl, needle);
 1110   GtkEntryCompletion * c = gtk_entry_get_completion (entry);
 1111   GtkTreeModel * list = gtk_entry_completion_get_model (c);
 1112   GtkTreeIter iter;
 1113   gboolean valid, found = FALSE;
 1114   gchar * haystack;
 1115   
 1116   valid = gtk_tree_model_get_iter_first (list, &iter);
 1117   while (valid && !found) {
 1118     gtk_tree_model_get (list, &iter, 0, &haystack, -1);
 1119     if (!strcmp (haystack, needle)) {
 1120       found = TRUE;
 1121       break;
 1122     }
 1123     valid = gtk_tree_model_iter_next (list, &iter);
 1124   }
 1125 
 1126   if (fp) {
 1127     GtkWidget * view = lookup_widget (gl->list, "view");
 1128     GtkWidget * msg = gtk_message_dialog_new (GTK_WINDOW (view),
 1129                           GTK_DIALOG_DESTROY_WITH_PARENT,
 1130                           GTK_MESSAGE_WARNING,
 1131                           GTK_BUTTONS_CLOSE,
 1132                           "%s",
 1133                           fp->error);
 1134     gts_file_destroy (fp);
 1135     gtk_dialog_run (GTK_DIALOG (msg));
 1136     gtk_widget_destroy (msg);
 1137     if (found)
 1138       gtk_list_store_remove (GTK_LIST_STORE (list), &iter);
 1139   }
 1140   else {
 1141     if (!found) {
 1142       gtk_list_store_append (GTK_LIST_STORE (list), &iter);
 1143       gtk_list_store_set (GTK_LIST_STORE (list), &iter, 
 1144               COMPLETION_NAME_COLUMN, needle, 
 1145               COMPLETION_DESCRIPTION_COLUMN, NULL,
 1146               COMPLETION_POINTER_COLUMN, NULL, 
 1147               -1);
 1148     }
 1149     gfk_gl_update_interface (gl);
 1150     g_object_set_data (G_OBJECT (entry), "edited", NULL);
 1151   }
 1152 }
 1153 
 1154 static void variable_selected (GtkButton * button, GtkEntry * entry)
 1155 {
 1156   GtkWidget * tree = lookup_widget (GTK_WIDGET (button), "tree");
 1157   GtkTreeSelection * select = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
 1158   GtkTreeModel * model;
 1159   GtkTreeIter iter;
 1160 
 1161   if (gtk_tree_selection_get_selected (select, &model, &iter)) {
 1162     gchar * name;
 1163     gtk_tree_model_get (model, &iter, 0, &name, -1);
 1164     gtk_entry_set_text (entry, name);
 1165     on_function_activate (entry, g_object_get_data (G_OBJECT (entry), "set"));
 1166   }
 1167 }
 1168 
 1169 static GtkWidget * variables_view (GtkEntry * entry, GtkTreeModel * model, GtkWindow * parent)
 1170 {
 1171   GtkWidget * variables = create_variables ();
 1172 
 1173   gtk_window_set_transient_for (GTK_WINDOW (variables), parent);
 1174   gtk_window_set_position (GTK_WINDOW (variables), GTK_WIN_POS_CENTER_ON_PARENT);
 1175   g_signal_connect (G_OBJECT (variables), "delete_event", 
 1176             G_CALLBACK (gtk_widget_hide_on_delete), NULL);
 1177   g_signal_connect_swapped (G_OBJECT (lookup_widget (variables, "cancel")), "clicked", 
 1178                 G_CALLBACK (gtk_widget_hide), variables);
 1179   g_signal_connect_swapped (G_OBJECT (lookup_widget (variables, "OK")), "clicked", 
 1180                 G_CALLBACK (gtk_widget_hide), variables);
 1181   g_signal_connect (G_OBJECT (lookup_widget (variables, "OK")), "clicked",
 1182             G_CALLBACK (variable_selected), entry);
 1183 
 1184   GtkWidget * tree = lookup_widget (variables, "tree");
 1185   gtk_tree_view_set_model (GTK_TREE_VIEW (tree), model);
 1186   GtkTreeSelection * select = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
 1187   gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE);
 1188 
 1189   GtkCellRenderer * renderer = gtk_cell_renderer_text_new ();
 1190   GtkTreeViewColumn * column = 
 1191     gtk_tree_view_column_new_with_attributes ("Name", renderer,
 1192                           "text", COMPLETION_NAME_COLUMN,
 1193                           NULL);
 1194   gtk_tree_view_column_set_resizable (column, TRUE);
 1195   gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
 1196   gtk_tree_view_column_set_fixed_width (column, 80);
 1197   gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
 1198 
 1199   column = gtk_tree_view_column_new_with_attributes ("Description", renderer,
 1200                              "text", COMPLETION_DESCRIPTION_COLUMN,
 1201                              NULL);
 1202   gtk_tree_view_column_set_resizable (column, TRUE);
 1203   gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
 1204   gtk_tree_view_column_set_fixed_width (column, 80);
 1205   gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
 1206 
 1207   return variables;
 1208 }
 1209 
 1210 static gboolean match_selected (GtkEntryCompletion * widget,
 1211                 GtkTreeModel * model,
 1212                 GtkTreeIter * iter)
 1213 {
 1214   GtkWidget * entry = gtk_entry_completion_get_entry (widget);
 1215   gchar * text;
 1216   gtk_tree_model_get (model, iter, COMPLETION_NAME_COLUMN, &text, -1);
 1217   gtk_entry_set_text (GTK_ENTRY (entry), text);
 1218   g_signal_emit_by_name (entry, "activate", entry, NULL);
 1219   return TRUE;
 1220 }
 1221 
 1222 static GtkWidget * gfk_function (GtkWidget * parent,
 1223                  GfkGl * gl, 
 1224                  const gchar * name,
 1225                  GfkFunctionSet set)
 1226 {
 1227   GtkWidget *gfk_function;
 1228   GtkWidget *scalar;
 1229   GtkWidget *select;
 1230 
 1231   gfk_function = gtk_hbox_new (FALSE, 0);
 1232 
 1233   scalar = gtk_entry_new ();
 1234   gtk_widget_show (scalar);
 1235   gtk_box_pack_start (GTK_BOX (gfk_function), scalar, TRUE, TRUE, 0);
 1236   gtk_entry_set_invisible_char (GTK_ENTRY (scalar), 9679);
 1237   g_object_set_data (G_OBJECT (scalar), "set", set);
 1238 
 1239   GtkEntryCompletion * c = gtk_entry_completion_new ();
 1240   GtkTreeModel * completion = g_object_get_data (G_OBJECT (lookup_widget (gl->list, "view")),
 1241                          "completion");
 1242   gtk_entry_completion_set_model (c, completion);
 1243   gtk_entry_completion_set_text_column (c, COMPLETION_NAME_COLUMN);
 1244   g_signal_connect (G_OBJECT (c), "match-selected", G_CALLBACK (match_selected), NULL);
 1245   gtk_entry_set_completion (GTK_ENTRY (scalar), c);
 1246 
 1247   select = gtk_button_new_with_mnemonic ("...");
 1248   gtk_widget_show (select);
 1249   gtk_box_pack_start (GTK_BOX (gfk_function), select, FALSE, FALSE, 0);
 1250 
 1251   GtkWidget * variables = variables_view (GTK_ENTRY (scalar), completion, GTK_WINDOW (gl->params));
 1252   GLADE_HOOKUP_OBJECT (select, variables, "variables");
 1253   g_signal_connect_swapped ((gpointer) select, "clicked",
 1254                 G_CALLBACK (gtk_widget_show), variables);
 1255 
 1256   g_signal_connect ((gpointer) scalar, "changed",
 1257                     G_CALLBACK (mark_edited),
 1258                     NULL);
 1259   g_signal_connect ((gpointer) scalar, "move_cursor",
 1260                     G_CALLBACK (mark_edited),
 1261                     NULL);
 1262   g_signal_connect ((gpointer) scalar, "activate",
 1263                     G_CALLBACK (on_function_activate),
 1264                     set);
 1265 
 1266   /* Store pointers to all widgets, for use by lookup_widget(). */
 1267   GLADE_HOOKUP_OBJECT (parent, gfk_function, "gfk_function");
 1268   GLADE_HOOKUP_OBJECT (parent, scalar, name);
 1269   GLADE_HOOKUP_OBJECT (parent, select, "select");
 1270 
 1271   return gfk_function;
 1272 }
 1273 
 1274 /* GfkGlScalar: Object */
 1275 
 1276 static void gl_scalar_read (GtsObject ** o, GtsFile * fp)
 1277 {
 1278   GtkWidget * scalar = GFK_GL_SCALAR (*o)->scalar;
 1279   GfsGlScalar * gl = GFS_GL_SCALAR (GFK_GL (*o)->gl);
 1280   GtkWidget * amin = lookup_widget_params (scalar, "amin");
 1281   GtkWidget * amax = lookup_widget_params (scalar, "amax");
 1282   GtkSpinButton * spinbuttonmin = lookup_widget_params (scalar, "spinbuttonmin");
 1283   GtkSpinButton * spinbuttonmax = lookup_widget_params (scalar, "spinbuttonmax");
 1284   gdouble maxv = gtk_spin_button_get_value (spinbuttonmax);
 1285 
 1286   (* GTS_OBJECT_CLASS (gfk_gl_scalar_class ())->parent_class->read) (o, fp);
 1287   if (fp->type == GTS_ERROR)
 1288     return;
 1289 
 1290   if (!gl->amin) {
 1291     gl->amin = TRUE;
 1292     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (amin), FALSE);
 1293     if (gl->amax && gl->min > maxv)
 1294       gtk_spin_button_set_value (spinbuttonmax, gl->min);
 1295     gtk_spin_button_set_value (spinbuttonmin, gl->min);
 1296   }
 1297 
 1298   if (!gl->amax) {
 1299     gl->amax = TRUE;
 1300     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (amax), FALSE);
 1301     gtk_spin_button_set_value (spinbuttonmax, gl->max);
 1302   }
 1303 
 1304   if (gl->show) {
 1305     gl->show = FALSE;
 1306     gtk_toggle_button_set_active 
 1307       (GTK_TOGGLE_BUTTON (lookup_widget_params (scalar, "show")), TRUE);
 1308   }
 1309 
 1310   GtkOptionMenu * colormap = lookup_widget_params (scalar, "colormap");
 1311   if (colormap) {
 1312     if (!strcmp (gl->cmap->name, "Jet"))
 1313       gtk_option_menu_set_history (colormap, 0);
 1314     else if (!strcmp (gl->cmap->name, "Cool"))
 1315       gtk_option_menu_set_history (colormap, 1);
 1316     else if (!strcmp (gl->cmap->name, "Gray"))
 1317       gtk_option_menu_set_history (colormap, 2);
 1318   }
 1319 }
 1320 
 1321 guint gfk_decimal_digits (double x, guint significant)
 1322 {
 1323   if (x == 0.)
 1324     return significant;
 1325   else {
 1326     gdouble n = significant - 1. - floor (log10 (fabs (x)));
 1327     return n > 0 ? n : 0;
 1328   }
 1329 }
 1330 
 1331 static void set_value (GtkSpinButton * s, gdouble v)
 1332 {
 1333   GtkAdjustment * as = gtk_spin_button_get_adjustment (s);
 1334   if (as->lower > v)
 1335     as->lower = v;
 1336   if (as->upper < v)
 1337     as->upper = v;
 1338   gtk_spin_button_set_adjustment (s, as);
 1339   gtk_spin_button_set_value (s, v);
 1340 }
 1341 
 1342 static void gl_scalar_update_interface (GfkGl * g)
 1343 {
 1344   GfkGlScalar * gl = GFK_GL_SCALAR (g);
 1345   GfsGlScalar * gls = GFS_GL_SCALAR (GFK_GL (gl)->gl);
 1346   GtkSpinButton * min = lookup_widget_params (gl->scalar, "spinbuttonmin");
 1347   GtkSpinButton * max = lookup_widget_params (gl->scalar, "spinbuttonmax");
 1348   GtkEntry * scalar = lookup_widget_params (gl->scalar, "scalar");
 1349 
 1350   if (!GFK_IS_EDITED (scalar))
 1351     gtk_entry_set_text (scalar, gls->expr->str);
 1352 
 1353   if (gls->amin)
 1354     set_value (min, gls->aminv);
 1355   else if (!GFK_IS_EDITED (min))
 1356     set_value (min, gls->min);
 1357   if (gls->amax)
 1358     set_value (max, gls->amaxv);
 1359   else if (!GFK_IS_EDITED (max))
 1360     set_value (max, gls->max);
 1361 
 1362   if (gls->max > gls->min) {
 1363     gdouble step = (gls->max - gls->min)/1000.;
 1364     guint digits = gfk_decimal_digits (step, 2);
 1365     GtkAdjustment * amin = gtk_spin_button_get_adjustment (min);
 1366     GtkAdjustment * amax = gtk_spin_button_get_adjustment (max);
 1367     amin->step_increment = amax->step_increment = step;
 1368     amin->page_increment = amax->page_increment = step*10.;
 1369     if (!GFK_IS_EDITED (min))
 1370       gtk_spin_button_configure (min, amin, 2.*step, digits);
 1371     if (!GFK_IS_EDITED (max))
 1372       gtk_spin_button_configure (max, amax, 2.*step, digits);
 1373   }
 1374   gfk_gl_update_properties (GFK_GL (gl));
 1375   gfk_gl_expose (GFK_GL (gl));  
 1376 }
 1377 
 1378 static void gl_scalar_set_simulation (GfkGl * object, GfsSimulation * sim)
 1379 {
 1380   (*GFK_GL_CLASS (GTS_OBJECT_CLASS (gfk_gl_scalar_class ())->parent_class)->set_simulation) 
 1381     (object, sim);
 1382   
 1383   gl_scalar_update_interface (object);
 1384 }
 1385 
 1386 static GtsFile * gl_scalar_set (GfkGl * gl, const gchar * s)
 1387 {
 1388   GtsFile * fp = gfs_gl_scalar_set (GFS_GL_SCALAR (gl->gl), s);
 1389   if (!fp)
 1390     gl_scalar_update_interface (gl);
 1391   return fp;
 1392 }
 1393 
 1394 static void gl_scalar_post_init (GfkGl * object)
 1395 {
 1396   GfkGlScalar * gls = GFK_GL_SCALAR (object);
 1397 
 1398   (*GFK_GL_CLASS (GTS_OBJECT_CLASS (gfk_gl_scalar_class ())->parent_class)->post_init) (object);
 1399 
 1400   GtkWidget * scalar = gfk_function (gls->scalar, object, "scalar", gl_scalar_set);
 1401   gtk_widget_show (scalar);
 1402   gtk_table_attach (GTK_TABLE (lookup_widget_params (gls->scalar, "table1")),
 1403             scalar, 1, 3, 0, 1,
 1404                     (GtkAttachOptions) (GTK_FILL),
 1405                     (GtkAttachOptions) (0), 0, 0);
 1406 
 1407   gl_scalar_update_interface (object);
 1408 }
 1409 
 1410 static gchar * gl_scalar_properties (GfkGl * gl)
 1411 {
 1412   g_free (gl->props);
 1413   gl->props = g_strndup (GFS_GL_SCALAR (gl->gl)->expr->str, MAXLENGTH + 3);
 1414   if (strlen (gl->props) > MAXLENGTH) {
 1415     guint i;
 1416     for (i = 0; i < 3; i++)
 1417       gl->props[MAXLENGTH + i] = '.';
 1418   }
 1419   return gl->props;
 1420 }
 1421 
 1422 static gchar * gl_scalar_pickinfo (GfkGl * gl, gboolean motion)
 1423 {
 1424   gchar * s = 
 1425     (* GFK_GL_CLASS (GTS_OBJECT_CLASS (gfk_gl_scalar_class ())->parent_class)->pickinfo) (gl, motion);
 1426   gdouble val = GFS_VALUE (GFS_GL2D (gl->gl)->picked, 
 1427                GFS_GL_SCALAR (gl->gl)->v);
 1428   gchar * s1 = (val != GFS_NODATA ? 
 1429         g_strdup_printf ("%s %s %g", s, gl_scalar_properties (gl), val) :
 1430         g_strdup_printf ("%s %s NODATA", s, gl_scalar_properties (gl))
 1431         );
 1432   g_free (GFK_GL2D (gl)->pickinfo);
 1433   GFK_GL2D (gl)->pickinfo = s1;
 1434   return s1;
 1435 }
 1436 
 1437 static void gl_scalar_class_init (GfkGlClass * klass)
 1438 {
 1439   klass->post_init = gl_scalar_post_init;
 1440   klass->set_simulation = gl_scalar_set_simulation;
 1441   klass->update_interface = gl_scalar_update_interface;
 1442   klass->properties = gl_scalar_properties;
 1443   klass->pickinfo = gl_scalar_pickinfo;
 1444   GTS_OBJECT_CLASS (klass)->read = gl_scalar_read;
 1445 }
 1446 
 1447 static void set_cmap_cool_warm (GtkWidget * cmap, GfkGl * gl)
 1448 {
 1449   gfs_colormap_destroy (GFS_GL_SCALAR (gl->gl)->cmap);
 1450   GFS_GL_SCALAR (gl->gl)->cmap = gfs_colormap_cool_warm ();
 1451   gfk_gl_expose (gl);
 1452 }
 1453 
 1454 static void set_cmap_gray (GtkWidget * cmap, GfkGl * gl)
 1455 {
 1456   gfs_colormap_destroy (GFS_GL_SCALAR (gl->gl)->cmap);
 1457   GFS_GL_SCALAR (gl->gl)->cmap = gfs_colormap_gray ();
 1458   gfk_gl_expose (gl);
 1459 }
 1460 
 1461 static void set_cmap_jet (GtkWidget * cmap, GfkGl * gl)
 1462 {
 1463   gfs_colormap_destroy (GFS_GL_SCALAR (gl->gl)->cmap);
 1464   GFS_GL_SCALAR (gl->gl)->cmap = gfs_colormap_jet ();
 1465   gfk_gl_expose (gl);
 1466 }
 1467 
 1468 static void gl_scalar_init (GfkGlScalar * object)
 1469 {
 1470   GtkOptionMenu * colormap;
 1471 
 1472   object->scalar = create_scalar_params ();
 1473   if ((colormap = lookup_widget_params (object->scalar, "colormap"))) {
 1474     GtkWidget * m = gtk_menu_new (), * i;
 1475 
 1476     i = gtk_menu_item_new_with_label ("Jet");
 1477     g_signal_connect (G_OBJECT (i), "activate", GTK_SIGNAL_FUNC (set_cmap_jet), object);
 1478     gtk_menu_append (m, i);
 1479     gtk_widget_show (i);
 1480     gtk_option_menu_set_menu (colormap, m);
 1481     gtk_widget_show (m);
 1482 
 1483     i = gtk_menu_item_new_with_label ("Cool");
 1484     g_signal_connect (G_OBJECT (i), "activate", GTK_SIGNAL_FUNC (set_cmap_cool_warm), object);
 1485     gtk_menu_append (m, i);
 1486     gtk_widget_show (i);
 1487     gtk_option_menu_set_menu (colormap, m);
 1488     gtk_widget_show (m);
 1489 
 1490     i = gtk_menu_item_new_with_label ("Gray");
 1491     g_signal_connect (G_OBJECT (i), "activate", GTK_SIGNAL_FUNC (set_cmap_gray), object);
 1492     gtk_menu_append (m, i);
 1493     gtk_widget_show (i);
 1494     gtk_option_menu_set_menu (colormap, m);
 1495     gtk_widget_show (m);
 1496   }
 1497   gfk_gl_prepend_params (GFK_GL (object), object->scalar, gtk_label_new ("Scalar"));
 1498   gtk_widget_show (object->scalar);
 1499 }
 1500 
 1501 GfkGlClass * gfk_gl_scalar_class (void)
 1502 {
 1503   static GfkGlClass * klass = NULL;
 1504 
 1505   if (klass == NULL) {
 1506     GtsObjectClassInfo gfk_gl_scalar_info = {
 1507       "GfkGlScalar",
 1508       sizeof (GfkGlScalar),
 1509       sizeof (GfkGlClass),
 1510       (GtsObjectClassInitFunc) gl_scalar_class_init,
 1511       (GtsObjectInitFunc) gl_scalar_init,
 1512       (GtsArgSetFunc) NULL,
 1513       (GtsArgGetFunc) NULL
 1514     };
 1515     klass = gts_object_class_new (GTS_OBJECT_CLASS (gfk_gl2D_class ()),
 1516                   &gfk_gl_scalar_info);
 1517   }
 1518 
 1519   return klass;
 1520 }
 1521 
 1522 /* GfkGlSquares: Object */
 1523 
 1524 static gchar * gl_squares_name (GfkGlClass * klass)
 1525 {
 1526   static gchar name[] = "Squares";
 1527   return name;
 1528 }
 1529 
 1530 static GtkWidget * gl_squares_icon (GfkGlClass * klass)
 1531 {
 1532   return create_pixmap (NULL, "squares-16x16.png");
 1533 }
 1534 
 1535 static void gl_squares_class_init (GfkGlClass * klass)
 1536 {
 1537   klass->gl_class = gfs_gl_squares_class ();
 1538   klass->name = gl_squares_name;
 1539   klass->icon = gl_squares_icon;
 1540 }
 1541 
 1542 static void gl_squares_init (GfkGl * gl)
 1543 {
 1544   gtk_widget_set_sensitive (lookup_widget_params (gl->properties, "default_color_label"), FALSE);
 1545   gtk_widget_set_sensitive (lookup_widget_params (gl->properties, "default_color"), FALSE);
 1546 }
 1547 
 1548 GfkGlClass * gfk_gl_squares_class (void)
 1549 {
 1550   static GfkGlClass * klass = NULL;
 1551 
 1552   if (klass == NULL) {
 1553     GtsObjectClassInfo gfk_gl_squares_info = {
 1554       "GfkGlSquares",
 1555       sizeof (GfkGlSquares),
 1556       sizeof (GfkGlClass),
 1557       (GtsObjectClassInitFunc) gl_squares_class_init,
 1558       (GtsObjectInitFunc) gl_squares_init,
 1559       (GtsArgSetFunc) NULL,
 1560       (GtsArgGetFunc) NULL
 1561     };
 1562     klass = gts_object_class_new (GTS_OBJECT_CLASS (gfk_gl_scalar_class ()),
 1563                   &gfk_gl_squares_info);
 1564   }
 1565 
 1566   return klass;
 1567 }
 1568 
 1569 /* GfkGlLinear: Object */
 1570 
 1571 static void update_linear_color (GfkGl * gl)
 1572 {
 1573   gfk_gl_set_sensitive (GFK_GL (gl), GFK_GL_SCALAR (gl)->scalar, FALSE);
 1574   gfk_gl_update_properties (GFK_GL (gl));
 1575   gtk_widget_set_sensitive (lookup_widget_params (GFK_GL (gl)->properties, "default_color_label"), 
 1576                 TRUE);
 1577   gtk_widget_set_sensitive (lookup_widget_params (GFK_GL (gl)->properties, "default_color"), TRUE);
 1578 }
 1579 
 1580 static void gl_linear_read (GtsObject ** o, GtsFile * fp)
 1581 {
 1582   GtkWidget * scalar = GFK_GL_LINEAR (*o)->scalar;
 1583   GfsGlLinear * gl = GFS_GL_LINEAR (GFK_GL (*o)->gl);
 1584 
 1585   (* GTS_OBJECT_CLASS (gfk_gl_linear_class ())->parent_class->read) (o, fp);
 1586   if (fp->type == GTS_ERROR)
 1587     return;
 1588 
 1589   if (gl->reversed) {
 1590     gl->reversed = FALSE;
 1591     gtk_toggle_button_set_active (lookup_widget_params (scalar, "reversed"), TRUE);
 1592   }
 1593 
 1594   if (!gl->use_scalar) {
 1595     gtk_option_menu_set_history (lookup_widget_params (scalar, "color"), 0);
 1596     update_linear_color (GFK_GL (*o));
 1597   }
 1598 }
 1599 
 1600 static gchar * gl_linear_properties (GfkGl * gl)
 1601 {
 1602   GfsGlLinear * gll = GFS_GL_LINEAR (gl->gl);
 1603   gchar * s;
 1604   if (gll->vf->f && gfs_function_get_constant_value (gll->vf->f) != 0.)
 1605     s = g_strjoin (" ",
 1606            gll->expr->str,
 1607            gll->use_scalar ?
 1608            ((* gfk_gl_scalar_class ()->properties) (gl)) : NULL, 
 1609            NULL);
 1610   else
 1611     s = g_strjoin (" ",
 1612            gll->use_scalar ?
 1613            ((* gfk_gl_scalar_class ()->properties) (gl)) : NULL, 
 1614            NULL);
 1615   g_free (gl->props);
 1616   gl->props = s;
 1617   return gl->props;
 1618 }
 1619 
 1620 static gchar * gl_linear_name (GfkGlClass * klass)
 1621 {
 1622   static gchar name[] = "Linear";
 1623   return name;
 1624 }
 1625 
 1626 static GtkWidget * gl_linear_icon (GfkGlClass * klass)
 1627 {
 1628   return create_pixmap (NULL, "linear-16x16.png");
 1629 }
 1630 
 1631 static gchar * gl_linear_pickinfo (GfkGl * gl, gboolean motion)
 1632 {
 1633   gchar * s = 
 1634     (* GFK_GL_CLASS (GTS_OBJECT_CLASS (gfk_gl_scalar_class ())->parent_class)->pickinfo) 
 1635     (gl, motion);
 1636   gchar * s1 = GFS_VALUE (GFS_GL2D (gl->gl)->picked, GFS_GL_SCALAR (gl->gl)->v) != GFS_NODATA ?
 1637     g_strdup_printf ("%s %s %g", s, gl_scalar_properties (gl),
 1638              gfs_interpolate (GFS_GL2D (gl->gl)->picked,
 1639                       GFS_GL2D (gl->gl)->pickedpos,
 1640                       GFS_GL_SCALAR (gl->gl)->v)) :
 1641     g_strdup_printf ("%s %s NODATA", s, gl_scalar_properties (gl));
 1642   g_free (GFK_GL2D (gl)->pickinfo);
 1643   GFK_GL2D (gl)->pickinfo = s1;
 1644   return s1;
 1645 }
 1646 
 1647 static void gl_linear_update_interface (GfkGl * g)
 1648 {
 1649   GfkGlLinear * gl = GFK_GL_LINEAR (g);
 1650   GfsGlLinear * gli = GFS_GL_LINEAR (GFK_GL (gl)->gl);
 1651   GtkEntry * scalar = lookup_widget_params (gl->scalar, "scalar");
 1652 
 1653   if (!GFK_IS_EDITED (scalar))
 1654     gtk_entry_set_text (scalar, gli->expr->str);
 1655 
 1656   gfk_gl_update_properties (GFK_GL (gl));
 1657   gfk_gl_expose (GFK_GL (gl));
 1658 }
 1659 
 1660 static void gl_linear_set_simulation (GfkGl * object, GfsSimulation * sim)
 1661 {
 1662   (*GFK_GL_CLASS (GTS_OBJECT_CLASS (gfk_gl_linear_class ())->parent_class)->set_simulation)
 1663     (object, sim);
 1664   
 1665   gl_linear_update_interface (object);
 1666 }
 1667 
 1668 static void set_linear_scalar (GtkWidget * scalar, GfkGl * gl)
 1669 {
 1670   GFS_GL_LINEAR (gl->gl)->use_scalar = GFS_GL_SCALAR (gl->gl)->v;
 1671   gfk_gl_set_sensitive (gl, GFK_GL_SCALAR (gl)->scalar, TRUE);
 1672   gfk_gl_update_properties (gl);
 1673   gtk_widget_set_sensitive (lookup_widget_params (gl->properties, "default_color_label"), FALSE);
 1674   gtk_widget_set_sensitive (lookup_widget_params (gl->properties, "default_color"), FALSE);
 1675   gfk_gl_expose (gl);
 1676 }
 1677 
 1678 static void set_linear_color (GtkWidget * color, GfkGlLinear * gl)
 1679 {
 1680   GFS_GL_LINEAR (GFK_GL (gl)->gl)->use_scalar = NULL;
 1681   update_linear_color (GFK_GL (gl));
 1682   gfk_gl_expose (GFK_GL (gl));
 1683 }
 1684 
 1685 static GtsFile * gl_linear_set (GfkGl * gl, const char * s)
 1686 {
 1687   return gfs_gl_linear_set (GFS_GL_LINEAR (gl->gl), s);
 1688 }
 1689 
 1690 static void gl_linear_post_init (GfkGl * object)
 1691 {
 1692   GfkGlLinear * gli = GFK_GL_LINEAR (object);
 1693 
 1694   (*GFK_GL_CLASS (GTS_OBJECT_CLASS (gfk_gl_linear_class ())->parent_class)->post_init)
 1695     (object);
 1696 
 1697   GtkWidget * scalar = gfk_function (gli->scalar, object, "scalar", gl_linear_set);
 1698   gtk_widget_show (scalar);
 1699   gtk_table_attach (GTK_TABLE (lookup_widget_params (gli->scalar, "table1")),
 1700             scalar, 1, 3, 0, 1,
 1701                     (GtkAttachOptions) (GTK_FILL),
 1702                     (GtkAttachOptions) (0), 0, 0);
 1703 
 1704   gl_linear_update_interface (object);
 1705 }
 1706 
 1707 static void gl_linear_class_init (GfkGlClass * klass)
 1708 {
 1709   GTS_OBJECT_CLASS (klass)->read = gl_linear_read;
 1710   klass->gl_class = gfs_gl_linear_class ();
 1711   klass->set_simulation = gl_linear_set_simulation;
 1712   klass->update_interface = gl_linear_update_interface;
 1713   klass->post_init = gl_linear_post_init;
 1714   klass->properties = gl_linear_properties;
 1715   klass->name = gl_linear_name;
 1716   klass->icon = gl_linear_icon;
 1717   klass->pickinfo = gl_linear_pickinfo;
 1718 }
 1719 
 1720 static void gl_linear_init (GfkGl * gl)
 1721 {
 1722   GfkGlLinear * gls = GFK_GL_LINEAR (gl);
 1723   gls->scalar = create_linear_params ();
 1724 
 1725   GtkWidget * m = gtk_menu_new ();
 1726   GtkWidget * i = gtk_menu_item_new_with_label ("Default");
 1727   g_signal_connect (G_OBJECT (i), "activate", GTK_SIGNAL_FUNC (set_linear_color), gl);
 1728   gtk_menu_append (m, i);
 1729   gtk_widget_show (i);
 1730 
 1731   i = gtk_menu_item_new_with_label ("Scalar");
 1732   g_signal_connect (G_OBJECT (i), "activate", GTK_SIGNAL_FUNC (set_linear_scalar), gl);
 1733   gtk_menu_append (m, i);
 1734   gtk_widget_show (i);
 1735   
 1736   gtk_option_menu_set_menu (lookup_widget_params (gls->scalar, "color"), m);
 1737   gtk_option_menu_set_history (lookup_widget_params (gls->scalar, "color"), 1);
 1738   gtk_widget_show (m);  
 1739   gtk_widget_set_sensitive (lookup_widget_params (gl->properties, "default_color_label"), FALSE);
 1740   gtk_widget_set_sensitive (lookup_widget_params (gl->properties, "default_color"), FALSE);  
 1741 
 1742   gfk_gl_prepend_params (gl, gls->scalar, gtk_label_new ("Linear"));
 1743 #if FTT_2D
 1744   gtk_widget_show (gls->scalar);
 1745 #endif
 1746 }
 1747 
 1748 GfkGlClass * gfk_gl_linear_class (void)
 1749 {
 1750   static GfkGlClass * klass = NULL;
 1751 
 1752   if (klass == NULL) {
 1753     GtsObjectClassInfo gfk_gl_linear_info = {
 1754       "GfkGlLinear",
 1755       sizeof (GfkGlLinear),
 1756       sizeof (GfkGlClass),
 1757       (GtsObjectClassInitFunc) gl_linear_class_init,
 1758       (GtsObjectInitFunc) gl_linear_init,
 1759       (GtsArgSetFunc) NULL,
 1760       (GtsArgGetFunc) NULL
 1761     };
 1762     klass = gts_object_class_new (GTS_OBJECT_CLASS (gfk_gl_scalar_class ()),
 1763                   &gfk_gl_linear_info);
 1764   }
 1765 
 1766   return klass;
 1767 }
 1768 
 1769 /* GfkGlIsoline: Object */
 1770 
 1771 static void gl_isoline_read (GtsObject ** o, GtsFile * fp)
 1772 {
 1773   GtkWidget * scalar = GFK_GL_SCALAR (*o)->scalar;
 1774   GfsGlIsoline * gl = GFS_GL_ISOLINE (GFK_GL (*o)->gl);
 1775 
 1776   (* GTS_OBJECT_CLASS (gfk_gl_isoline_class ())->parent_class->read) (o, fp);
 1777   if (fp->type == GTS_ERROR)
 1778     return;
 1779 
 1780   gtk_spin_button_set_value (lookup_widget_params (scalar, "spinbuttoniso"), gl->n);
 1781   if (gl->ls)
 1782     gtk_entry_set_text (lookup_widget_params (scalar, "entryiso"), gl->ls);
 1783 }
 1784 
 1785 static gchar * gl_isoline_name (GfkGlClass * klass)
 1786 {
 1787   static gchar name[] = "Isoline";
 1788   return name;
 1789 }
 1790 
 1791 static GtkWidget * gl_isoline_icon (GfkGlClass * klass)
 1792 {
 1793   return create_pixmap (NULL, "isoline-16x16.png");
 1794 }
 1795 
 1796 static void gl_isoline_class_init (GfkGlClass * klass)
 1797 {
 1798   GTS_OBJECT_CLASS (klass)->read = gl_isoline_read;
 1799   klass->gl_class = gfs_gl_isoline_class ();
 1800   klass->name = gl_isoline_name;
 1801   klass->icon = gl_isoline_icon;
 1802 }
 1803 
 1804 static void gl_isoline_init (GfkGl * gl)
 1805 {
 1806   GfkGlScalar * gls = GFK_GL_SCALAR (gl);
 1807   gtk_widget_destroy (gls->scalar);
 1808   gls->scalar = create_isoline_params ();
 1809   gfk_gl_prepend_params (gl, gls->scalar, gtk_label_new ("Isoline"));
 1810   gtk_widget_show (gls->scalar);
 1811   gtk_widget_set_sensitive (lookup_widget_params (gl->properties, "shading_label"), FALSE);
 1812   gtk_widget_set_sensitive (lookup_widget_params (gl->properties, "shading"), FALSE);
 1813 
 1814   gtk_option_menu_set_history (lookup_widget_params (GFK_GL_LINEAR (gl)->scalar, "color"), 0);
 1815   gtk_widget_set_sensitive (lookup_widget_params (GFK_GL_LINEAR (gl)->scalar, "normals"), FALSE);
 1816   gtk_widget_set_sensitive (lookup_widget_params (GFK_GL_LINEAR (gl)->scalar, "reversed"), FALSE);
 1817   gtk_widget_set_sensitive (lookup_widget_params (GFK_GL (gl)->properties, "default_color_label"), 
 1818                 TRUE);
 1819   gtk_widget_set_sensitive (lookup_widget_params (GFK_GL (gl)->properties, "default_color"), TRUE);
 1820   gtk_widget_set_sensitive (lookup_widget_params (GFK_GL_LINEAR (gl)->scalar, "color"), FALSE);
 1821 }
 1822 
 1823 GfkGlClass * gfk_gl_isoline_class (void)
 1824 {
 1825   static GfkGlClass * klass = NULL;
 1826 
 1827   if (klass == NULL) {
 1828     GtsObjectClassInfo gfk_gl_isoline_info = {
 1829       "GfkGlIsoline",
 1830       sizeof (GfkGlIsoline),
 1831       sizeof (GfkGlClass),
 1832       (GtsObjectClassInitFunc) gl_isoline_class_init,
 1833       (GtsObjectInitFunc) gl_isoline_init,
 1834       (GtsArgSetFunc) NULL,
 1835       (GtsArgGetFunc) NULL
 1836     };
 1837     klass = gts_object_class_new (GTS_OBJECT_CLASS (gfk_gl_linear_class ()),
 1838                   &gfk_gl_isoline_info);
 1839   }
 1840 
 1841   return klass;
 1842 }
 1843 
 1844 /* GfkGlVOF: Object */
 1845 
 1846 static void update_vof_scalar (GfkGl * gl)
 1847 {
 1848   gfk_gl_set_sensitive (gl, GFK_GL_SCALAR (gl)->scalar, TRUE);
 1849   gfk_gl_update_properties (gl);
 1850   gtk_widget_set_sensitive (lookup_widget_params (gl->properties, "default_color_label"), FALSE);
 1851   gtk_widget_set_sensitive (lookup_widget_params (gl->properties, "default_color"), FALSE);
 1852 }
 1853 
 1854 static void gl_vof_read (GtsObject ** o, GtsFile * fp)
 1855 {
 1856   GtkWidget * scalar = GFK_GL_VOF (*o)->scalar;
 1857   GfsGlVOF * gl = GFS_GL_VOF (GFK_GL (*o)->gl);
 1858 
 1859   (* GTS_OBJECT_CLASS (gfk_gl_vof_class ())->parent_class->read) (o, fp);
 1860   if (fp->type == GTS_ERROR)
 1861     return;
 1862 
 1863   if (gl->reversed) {
 1864     gl->reversed = FALSE;
 1865     gtk_toggle_button_set_active (lookup_widget_params (scalar, "reversed"), TRUE);
 1866   }
 1867 
 1868   if (gl->draw_edges) {
 1869     gl->draw_edges = FALSE;
 1870     gtk_toggle_button_set_active (lookup_widget_params (scalar, "visible"), TRUE);
 1871   }
 1872 
 1873   if (gl->use_scalar) {
 1874     gtk_option_menu_set_history (lookup_widget_params (scalar, "color"), gl->interpolate ? 2 : 1);
 1875     update_vof_scalar (GFK_GL (*o));
 1876   }
 1877 }
 1878 
 1879 static gchar * gl_vof_name (GfkGlClass * klass)
 1880 {
 1881   static gchar name[] = "VOF";
 1882   return name;
 1883 }
 1884 
 1885 static GtkWidget * gl_vof_icon (GfkGlClass * klass)
 1886 {
 1887   return create_pixmap (NULL, "vof-16x16.png");
 1888 }
 1889 
 1890 static void gl_vof_update_interface (GfkGl * g)
 1891 {
 1892   GfkGlVOF * gl = GFK_GL_VOF (g);
 1893   GfsGlVOF * gli = GFS_GL_VOF (GFK_GL (gl)->gl);
 1894   GtkEntry * scalar = lookup_widget_params (gl->scalar, "scalar");
 1895 
 1896   if (!GFK_IS_EDITED (scalar))
 1897     gtk_entry_set_text (scalar, gli->expr->str);
 1898 
 1899   gfk_gl_update_properties (GFK_GL (gl));
 1900   gfk_gl_expose (GFK_GL (gl));
 1901 }
 1902 
 1903 static void gl_vof_set_simulation (GfkGl * object, GfsSimulation * sim)
 1904 {
 1905   (*GFK_GL_CLASS (GTS_OBJECT_CLASS (gfk_gl_vof_class ())->parent_class)->set_simulation)
 1906     (object, sim);
 1907   
 1908   gl_vof_update_interface (object);
 1909 }
 1910 
 1911 static void set_vof_scalar (GtkWidget * scalar, GfkGl * gl)
 1912 {
 1913   GFS_GL_VOF (gl->gl)->use_scalar = GFS_GL_SCALAR (gl->gl)->v;
 1914   GFS_GL_VOF (gl->gl)->interpolate = FALSE;
 1915   update_vof_scalar (gl);
 1916   gfk_gl_expose (gl);
 1917 }
 1918 
 1919 static void set_vof_scalar_interpolated (GtkWidget * scalar, GfkGl * gl)
 1920 {
 1921   GFS_GL_VOF (gl->gl)->use_scalar = GFS_GL_SCALAR (gl->gl)->v;
 1922   GFS_GL_VOF (gl->gl)->interpolate = TRUE;
 1923   update_vof_scalar (gl);
 1924   gfk_gl_expose (gl);
 1925 }
 1926 
 1927 static void set_vof_color (GtkWidget * color, GfkGlVOF * gl)
 1928 {
 1929   GFS_GL_VOF (GFK_GL (gl)->gl)->use_scalar = NULL;
 1930   gfk_gl_set_sensitive (GFK_GL (gl), GFK_GL_SCALAR (gl)->scalar, FALSE);
 1931   gfk_gl_update_properties (GFK_GL (gl));
 1932   gtk_widget_set_sensitive (lookup_widget_params (GFK_GL (gl)->properties, "default_color_label"), 
 1933                 TRUE);
 1934   gtk_widget_set_sensitive (lookup_widget_params (GFK_GL (gl)->properties, "default_color"), TRUE);
 1935   gfk_gl_expose (GFK_GL (gl));
 1936 }
 1937 
 1938 static GtsFile * gl_vof_set (GfkGl * gl, const gchar * s)
 1939 {
 1940   return gfs_gl_vof_set (GFS_GL_VOF (gl->gl), s);
 1941 }
 1942 
 1943 static void gl_vof_post_init (GfkGl * object)
 1944 {
 1945   GfkGlVOF * gli = GFK_GL_VOF (object);
 1946 
 1947   (*GFK_GL_CLASS (GTS_OBJECT_CLASS (gfk_gl_vof_class ())->parent_class)->post_init)
 1948     (object);
 1949 
 1950   GtkWidget * scalar = gfk_function (gli->scalar, object, "scalar", gl_vof_set);
 1951   gtk_widget_show (scalar);
 1952   gtk_table_attach (GTK_TABLE (lookup_widget_params (gli->scalar, "table1")),
 1953             scalar, 1, 3, 0, 1,
 1954                     (GtkAttachOptions) (GTK_FILL),
 1955                     (GtkAttachOptions) (0), 0, 0);
 1956   
 1957   gl_vof_update_interface (object);
 1958 }
 1959 
 1960 static gchar * gl_vof_properties (GfkGl * gl)
 1961 {
 1962   gchar * s = g_strjoin (" ",
 1963              GFS_GL_VOF (gl->gl)->expr->str,
 1964              GFS_GL_VOF (gl->gl)->use_scalar ?
 1965              ((* gfk_gl_scalar_class ()->properties) (gl)) : NULL, 
 1966              NULL);
 1967   g_free (gl->props);
 1968   gl->props = s;
 1969   return gl->props;
 1970 }
 1971 
 1972 static void gl_vof_class_init (GfkGlClass * klass)
 1973 {
 1974   GTS_OBJECT_CLASS (klass)->read = gl_vof_read;
 1975   klass->gl_class = gfs_gl_vof_class ();
 1976   klass->set_simulation = gl_vof_set_simulation;
 1977   klass->update_interface = gl_vof_update_interface;
 1978   klass->post_init = gl_vof_post_init;
 1979   klass->properties = gl_vof_properties;
 1980   klass->name = gl_vof_name;
 1981   klass->icon = gl_vof_icon;
 1982 }
 1983 
 1984 static void gl_vof_init (GfkGl * gl)
 1985 {
 1986   GfkGlVOF * gli = GFK_GL_VOF (gl);
 1987 
 1988   gli->scalar = create_vof_params ();
 1989 
 1990   gfk_gl_set_sensitive (gl, GFK_GL_SCALAR (gl)->scalar, FALSE);
 1991 
 1992   GtkWidget * m = gtk_menu_new ();
 1993   GtkWidget * i = gtk_menu_item_new_with_label ("Default");
 1994   g_signal_connect (G_OBJECT (i), "activate", GTK_SIGNAL_FUNC (set_vof_color), gl);
 1995   gtk_menu_append (m, i);
 1996   gtk_widget_show (i);
 1997 
 1998   i = gtk_menu_item_new_with_label ("Scalar");
 1999   g_signal_connect (G_OBJECT (i), "activate", GTK_SIGNAL_FUNC (set_vof_scalar), gl);
 2000   gtk_menu_append (m, i);
 2001   gtk_widget_show (i);
 2002   
 2003   i = gtk_menu_item_new_with_label ("Scalar (interpolated)");
 2004   g_signal_connect (G_OBJECT (i), "activate", GTK_SIGNAL_FUNC (set_vof_scalar_interpolated), gl);
 2005   gtk_menu_append (m, i);
 2006   gtk_widget_show (i);
 2007   
 2008   gtk_option_menu_set_menu (lookup_widget_params (gli->scalar, "color"), m);
 2009   gtk_widget_show (m);
 2010 
 2011 #if FTT_2D
 2012   gtk_widget_hide (lookup_widget_params (gli->scalar, "normals"));
 2013   gtk_widget_hide (lookup_widget_params (gli->scalar, "reversed"));
 2014   gtk_widget_hide (lookup_widget_params (gli->scalar, "edges"));
 2015   gtk_widget_hide (lookup_widget_params (gli->scalar, "visible"));
 2016 #else
 2017   gtk_widget_hide (GFK_GL2D (gl)->params);
 2018 #endif
 2019 
 2020   gfk_gl_prepend_params (gl, gli->scalar, gtk_label_new ("VOF"));
 2021   gtk_widget_show (gli->scalar);
 2022 }
 2023 
 2024 GfkGlClass * gfk_gl_vof_class (void)
 2025 {
 2026   static GfkGlClass * klass = NULL;
 2027 
 2028   if (klass == NULL) {
 2029     GtsObjectClassInfo gfk_gl_vof_info = {
 2030       "GfkGlVOF",
 2031       sizeof (GfkGlVOF),
 2032       sizeof (GfkGlClass),
 2033       (GtsObjectClassInitFunc) gl_vof_class_init,
 2034       (GtsObjectInitFunc) gl_vof_init,
 2035       (GtsArgSetFunc) NULL,
 2036       (GtsArgGetFunc) NULL
 2037     };
 2038     klass = gts_object_class_new (GTS_OBJECT_CLASS (gfk_gl_scalar_class ()),
 2039                   &gfk_gl_vof_info);
 2040   }
 2041 
 2042   return klass;
 2043 }
 2044 
 2045 /* GfkGlVectors: Object */
 2046 
 2047 static void set_vector_color (GtkWidget * color, GfkGlVectors * gl)
 2048 {
 2049   GFS_GL_VECTORS (GFK_GL (gl)->gl)->use_scalar = FALSE;
 2050   gfk_gl_set_sensitive (GFK_GL (gl), GFK_GL_SCALAR (gl)->scalar, FALSE);
 2051   gfk_gl_update_properties (GFK_GL (gl));
 2052   gtk_widget_set_sensitive (lookup_widget_params (GFK_GL (gl)->properties, "default_color_label"), 
 2053                 TRUE);
 2054   gtk_widget_set_sensitive (lookup_widget_params (GFK_GL (gl)->properties, "default_color"), TRUE);
 2055   gfk_gl_expose (GFK_GL (gl));
 2056 }
 2057 
 2058 static void update_vector_scalar (GfkGlVectors * gl)
 2059 {
 2060   gfk_gl_set_sensitive (GFK_GL (gl), GFK_GL_SCALAR (gl)->scalar, TRUE);
 2061   gfk_gl_update_properties (GFK_GL (gl));
 2062   gtk_widget_set_sensitive (lookup_widget_params (GFK_GL (gl)->properties, "default_color_label"), 
 2063                 FALSE);
 2064   gtk_widget_set_sensitive (lookup_widget_params (GFK_GL (gl)->properties, "default_color"), 
 2065                 FALSE);
 2066 }
 2067 
 2068 static void set_vector_scalar (GtkWidget * scalar, GfkGlVectors * gl)
 2069 {
 2070   GFS_GL_VECTORS (GFK_GL (gl)->gl)->use_scalar = TRUE;
 2071   update_vector_scalar (gl);
 2072   gfk_gl_expose (GFK_GL (gl));
 2073 }
 2074 
 2075 static void gl_vectors_read (GtsObject ** o, GtsFile * fp)
 2076 {
 2077   GtkWidget * vector = GFK_GL_VECTORS (*o)->vector;
 2078   GfsGlVectors * gl = GFS_GL_VECTORS (GFK_GL (*o)->gl);
 2079 
 2080   (* GTS_OBJECT_CLASS (gfk_gl_vectors_class ())->parent_class->read) (o, fp);
 2081   if (fp->type == GTS_ERROR)
 2082     return;
 2083 
 2084   gtk_spin_button_set_value (lookup_widget_params (vector, "scale"), gl->scale);
 2085 
 2086   if (gl->use_scalar) {
 2087     gtk_option_menu_set_history (lookup_widget_params (vector, "color"), 1);
 2088     update_vector_scalar (GFK_GL_VECTORS (*o));
 2089   }
 2090 }
 2091 
 2092 static void gl_vectors_update_interface (GfkGlVectors * gl)
 2093 {
 2094   GfsGlVectors * glv = GFS_GL_VECTORS (GFK_GL (gl)->gl);
 2095   GtkSpinButton * scale = lookup_widget_params (gl->vector, "scale");
 2096   GtkAdjustment * ascale = gtk_spin_button_get_adjustment (scale);
 2097   FttComponent c;
 2098 
 2099   for (c = 0; c < FTT_DIMENSION; c++) {
 2100     static gchar * name[] = {"Vx", "Vy", "Vz"};
 2101     GtkEntry * entry = lookup_widget_params (gl->vector, name[c]);
 2102     if (!GFK_IS_EDITED (entry))
 2103       gtk_entry_set_text (entry, glv->expr[c]->str);
 2104   }
 2105 
 2106   ascale->upper = glv->max > 0. ? 10000.*glv->h/glv->max : 1.;
 2107   ascale->step_increment = glv->max > 0. ? glv->h/glv->max/10. : 0.1;
 2108   ascale->page_increment = 10.*ascale->step_increment;
 2109   ascale->value = glv->scale;
 2110   if (!GFK_IS_EDITED (scale))
 2111     gtk_spin_button_configure (scale, ascale, 2.*ascale->step_increment,
 2112                    gfk_decimal_digits (ascale->step_increment, 2));
 2113   gfk_gl_update_properties (GFK_GL (gl));
 2114   gfk_gl_expose (GFK_GL (gl));
 2115 }
 2116 
 2117 static void gl_vectors_set_simulation (GfkGl * object, GfsSimulation * sim)
 2118 {
 2119   (*GFK_GL_CLASS (GTS_OBJECT_CLASS (gfk_gl_vectors_class ())->parent_class)->set_simulation)
 2120     (object, sim);
 2121   
 2122   gl_vectors_update_interface (GFK_GL_VECTORS (object));
 2123 }
 2124 
 2125 static GtsFile * set_x (GfkGl * gl, const gchar * func)
 2126 {
 2127   return gfs_gl_vectors_set (GFS_GL_VECTORS (gl->gl), 0, func);
 2128 }
 2129 
 2130 static GtsFile * set_y (GfkGl * gl, const gchar * func)
 2131 {
 2132   return gfs_gl_vectors_set (GFS_GL_VECTORS (gl->gl), 1, func);
 2133 }
 2134 
 2135 static GtsFile * set_z (GfkGl * gl, const gchar * func)
 2136 {
 2137   return gfs_gl_vectors_set (GFS_GL_VECTORS (gl->gl), 2, func);
 2138 }
 2139 
 2140 static void gl_vectors_post_init (GfkGl * object)
 2141 {
 2142   GfkGlVectors * gls = GFK_GL_VECTORS (object);
 2143   FttComponent c;
 2144 
 2145   (*GFK_GL_CLASS (GTS_OBJECT_CLASS (gfk_gl_vectors_class ())->parent_class)->post_init) (object);
 2146 
 2147   GtkTable * table = GTK_TABLE (lookup_widget_params (gls->vector, "table2"));
 2148   for (c = 0; c < FTT_DIMENSION; c++) {
 2149     static gchar * name[] = {"Vx", "Vy", "Vz"};
 2150     static GfkFunctionSet func[] = {set_x, set_y, set_z};
 2151     
 2152     GtkWidget * scalar = gfk_function (gls->vector, object, name[c], func[c]);
 2153     gtk_widget_show (scalar);
 2154     gtk_table_attach (table, scalar, 1, 2, c, c + 1,
 2155               (GtkAttachOptions) (GTK_FILL),
 2156               (GtkAttachOptions) (0), 0, 0);
 2157 
 2158     GtkWidget * label = gtk_label_new (name[c]);
 2159     gtk_widget_show (label);
 2160     gtk_table_attach (table, label, 0, 1, c, c + 1,
 2161               (GtkAttachOptions) (GTK_FILL),
 2162               (GtkAttachOptions) (0), 0, 0);
 2163   }
 2164 
 2165   gl_vectors_update_interface (gls);
 2166 }
 2167 
 2168 static gchar * gl_vectors_name (GfkGlClass * klass)
 2169 {
 2170   static gchar name[] = "Vectors";
 2171   return name;
 2172 }
 2173 
 2174 static GtkWidget * gl_vectors_icon (GfkGlClass * klass)
 2175 {
 2176   return create_pixmap (NULL, "vectors-16x16.png");
 2177 }
 2178 
 2179 static gchar * gl_vectors_properties (GfkGl * gl)
 2180 {
 2181   gchar * f[3] = { NULL, NULL, NULL };
 2182   FttComponent c;
 2183 
 2184   g_free (gl->props);
 2185   for (c = 0; c < FTT_DIMENSION; c++)
 2186     f[c] = GFS_GL_VECTORS (gl->gl)->expr[c]->str;
 2187   gl->props = g_strjoin (",", f[0], f[1], f[2], NULL);
 2188   return gl->props;
 2189 }
 2190 
 2191 static void gl_vectors_class_init (GfkGlClass * klass)
 2192 {
 2193   GTS_OBJECT_CLASS (klass)->read = gl_vectors_read;
 2194   klass->gl_class = gfs_gl_vectors_class ();
 2195   klass->post_init = gl_vectors_post_init;
 2196   klass->set_simulation = gl_vectors_set_simulation;
 2197   klass->name = gl_vectors_name;
 2198   klass->icon = gl_vectors_icon;
 2199   klass->properties = gl_vectors_properties;
 2200 }
 2201 
 2202 static void gl_vectors_init (GfkGlVectors * object)
 2203 {
 2204   gtk_widget_set_sensitive (lookup_widget_params (GFK_GL (object)->properties, "shading_label"), 
 2205                 FALSE);
 2206   gtk_widget_set_sensitive (lookup_widget_params (GFK_GL (object)->properties, "shading"), FALSE);
 2207   object->vector = create_vector_params ();
 2208   gfk_gl_set_sensitive (GFK_GL (object), GFK_GL_SCALAR (object)->scalar, FALSE);
 2209 
 2210   GtkWidget * m = gtk_menu_new ();
 2211   GtkWidget * i = gtk_menu_item_new_with_label ("Default");
 2212   g_signal_connect (G_OBJECT (i), "activate", GTK_SIGNAL_FUNC (set_vector_color), object);
 2213   gtk_menu_append (m, i);
 2214   gtk_widget_show (i);
 2215 
 2216   i = gtk_menu_item_new_with_label ("Scalar");
 2217   g_signal_connect (G_OBJECT (i), "activate", GTK_SIGNAL_FUNC (set_vector_scalar), object);
 2218   gtk_menu_append (m, i);
 2219   gtk_widget_show (i);
 2220   
 2221   gtk_option_menu_set_menu (lookup_widget_params (object->vector, "color"), m);
 2222   gtk_widget_show (m);
 2223 
 2224   gfk_gl_prepend_params (GFK_GL (object), object->vector, gtk_label_new ("Vector"));
 2225   gtk_widget_show (object->vector);
 2226 }
 2227 
 2228 GfkGlClass * gfk_gl_vectors_class (void)
 2229 {
 2230   static GfkGlClass * klass = NULL;
 2231 
 2232   if (klass == NULL) {
 2233     GtsObjectClassInfo gfk_gl_vectors_info = {
 2234       "GfkGlVectors",
 2235       sizeof (GfkGlVectors),
 2236       sizeof (GfkGlClass),
 2237       (GtsObjectClassInitFunc) gl_vectors_class_init,
 2238       (GtsObjectInitFunc) gl_vectors_init,
 2239       (GtsArgSetFunc) NULL,
 2240       (GtsArgGetFunc) NULL
 2241     };
 2242     klass = gts_object_class_new (GTS_OBJECT_CLASS (gfk_gl_scalar_class ()),
 2243                   &gfk_gl_vectors_info);
 2244   }
 2245 
 2246   return klass;
 2247 }
 2248 
 2249 /* GfkGlStreamlines: Object */
 2250 
 2251 static void gl_streamlines_read (GtsObject ** o, GtsFile * fp)
 2252 {
 2253   GtkWidget * stream = GFK_GL_STREAMLINES (*o)->stream;
 2254   GfsGlStreamlines * gl = GFS_GL_STREAMLINES (GFK_GL (*o)->gl);
 2255 
 2256   (* GTS_OBJECT_CLASS (gfk_gl_streamlines_class ())->parent_class->read) (o, fp);
 2257   if (fp->type == GTS_ERROR)
 2258     return;
 2259 
 2260   if (!gl->show_cells)
 2261     gtk_toggle_button_set_active (lookup_widget_params (stream, "showcells"), FALSE);
 2262   gtk_spin_button_set_value (lookup_widget_params (stream, "dmin"), gl->dmin);
 2263   if (gl->dmin > 0.)
 2264     gtk_widget_set_sensitive (lookup_widget_params (stream, "evenly_spaced"), TRUE);
 2265   gtk_spin_button_set_value (lookup_widget_params (stream, "radius"), gl->radius);
 2266 }
 2267 
 2268 static gchar * gl_streamlines_name (GfkGlClass * klass)
 2269 {
 2270   static gchar name[] = "Stream";
 2271   return name;
 2272 }
 2273 
 2274 static GtkWidget * gl_streamlines_icon (GfkGlClass * klass)
 2275 {
 2276   return create_pixmap (NULL, "stream-16x16.png");
 2277 }
 2278 
 2279 static void snap_to_spacing (GfsGlStreamlines * gls)
 2280 {
 2281   if (gls->dmin > 0.) {
 2282     FttVector * picked = &GFS_GL2D (gls)->pickedpos;
 2283     GtsPoint p;
 2284     
 2285     if (gfs_gl_streamlines_closest (gls, picked, &p) < G_MAXDOUBLE) {
 2286       GtsVector n;
 2287       gts_vector_init (n, &p, picked);
 2288       gts_vector_normalize (n);
 2289       picked->x = p.x + gls->dmin*n[0];
 2290       picked->y = p.y + gls->dmin*n[1];
 2291       picked->z = p.z + gls->dmin*n[2];
 2292     }
 2293   }
 2294 }
 2295 
 2296 static gchar * gl_streamlines_pickinfo (GfkGl * gl, gboolean motion)
 2297 {
 2298   GfkGlStreamlines * glk = GFK_GL_STREAMLINES (gl);
 2299   GfsGlStreamlines * gls = GFS_GL_STREAMLINES (gl->gl);
 2300   gboolean snapspacing = FALSE;
 2301 
 2302   if (gtk_toggle_button_get_active (lookup_widget_params (glk->stream, "snapcenters"))) {
 2303     FttCell * cell = GFS_GL2D (gls)->picked;
 2304     
 2305     while (gl->gl->maxlevel >= 0 && gl->gl->maxlevel < ftt_cell_level (cell))
 2306       cell = ftt_cell_parent (cell);
 2307     ftt_cell_pos (cell, &GFS_GL2D (gls)->pickedpos);
 2308   }
 2309   else
 2310     snapspacing = gtk_toggle_button_get_active (lookup_widget_params (glk->stream, "snapspacing"));
 2311 
 2312   if (motion) {
 2313     if (gls->selected) {
 2314       GfsGlStreamline * s = gls->selected->data;
 2315       
 2316       gfs_gl_streamlines_reset_selected (gls);
 2317       if (snapspacing)
 2318     snap_to_spacing (gls);
 2319       s->c = GFS_GL2D (gls)->pickedpos;
 2320       gfk_gl_expose (gl);
 2321     }
 2322   }
 2323   else if (!glk->edit) {
 2324     if (!gls->selected)
 2325       gtk_widget_set_sensitive (lookup_widget_params (glk->stream, "delete"), TRUE);
 2326     if (snapspacing)
 2327       snap_to_spacing (gls);
 2328     if (gfs_gl_streamlines_add (gls, GFS_GL2D (gls)->pickedpos))
 2329       gfk_gl_expose (gl);
 2330   }
 2331   else if (gls->candidate) { 
 2332     if (gls->candidate != gls->selected) {
 2333       if (!gls->selected)
 2334     gtk_widget_set_sensitive (lookup_widget_params (glk->stream, "delete"), TRUE);
 2335       gls->selected = gls->candidate;
 2336       gfk_gl_expose (gl);
 2337     }
 2338   }
 2339   else if (gls->selected) {
 2340     gtk_widget_set_sensitive (lookup_widget_params (glk->stream, "delete"), FALSE);
 2341     gls->selected = NULL;
 2342     gfk_gl_expose (gl);
 2343   }
 2344   return gl2D_pickinfo (gl, motion);
 2345 }
 2346 
 2347 static void streamlines_changed (GtkEntry * entry, GfsGlStreamlines * gl)
 2348 {
 2349   gfs_gl_streamlines_reset (gl);
 2350 }
 2351 
 2352 static void gl_streamlines_post_init (GfkGl * object)
 2353 {
 2354   FttComponent c;
 2355 
 2356   (*GFK_GL_CLASS (GTS_OBJECT_CLASS (gfk_gl_streamlines_class ())->parent_class)->post_init) 
 2357     (object);
 2358 
 2359   for (c = 0; c < FTT_DIMENSION; c++) {
 2360     static gchar * name[] = {"Vx", "Vy", "Vz"};
 2361     GtkWidget * entry = lookup_widget_params (GFK_GL_VECTORS (object)->vector, name[c]);
 2362 
 2363     g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (streamlines_changed), object->gl);
 2364   }
 2365 }
 2366 
 2367 static void gl_streamlines_class_init (GfkGlClass * klass)
 2368 {
 2369   GTS_OBJECT_CLASS (klass)->read = gl_streamlines_read;
 2370   klass->gl_class = gfs_gl_streamlines_class ();
 2371   klass->name = gl_streamlines_name;
 2372   klass->icon = gl_streamlines_icon;
 2373   klass->pickinfo = gl_streamlines_pickinfo;
 2374   klass->post_init = gl_streamlines_post_init;
 2375 }
 2376 
 2377 static void gl_streamlines_init (GfkGlVectors * gl)
 2378 {
 2379   GfkGlStreamlines * gls = GFK_GL_STREAMLINES (gl);
 2380 
 2381   gtk_widget_set_sensitive (lookup_widget_params (gl->vector, "label11"), FALSE);
 2382   gtk_widget_set_sensitive (lookup_widget_params (gl->vector, "scale"), FALSE);
 2383 
 2384   gls->stream = create_streamlines_params ();
 2385   gtk_widget_set_sensitive (lookup_widget_params (gls->stream, "delete"), FALSE);
 2386   gtk_widget_set_sensitive (lookup_widget_params (gls->stream, "evenly_spaced"), FALSE);
 2387   if (FTT_DIMENSION < 3)
 2388     gtk_widget_hide (lookup_widget_params (gls->stream, "params_3D"));
 2389   gfk_gl_prepend_params (GFK_GL (gl), gls->stream, gtk_label_new ("Streamlines"));
 2390   gtk_widget_show (gls->stream);
 2391 }
 2392 
 2393 GfkGlClass * gfk_gl_streamlines_class (void)
 2394 {
 2395   static GfkGlClass * klass = NULL;
 2396 
 2397   if (klass == NULL) {
 2398     GtsObjectClassInfo gfk_gl_streamlines_info = {
 2399       "GfkGlStreamlines",
 2400       sizeof (GfkGlStreamlines),
 2401       sizeof (GfkGlClass),
 2402       (GtsObjectClassInitFunc) gl_streamlines_class_init,
 2403       (GtsObjectInitFunc) gl_streamlines_init,
 2404       (GtsArgSetFunc) NULL,
 2405       (GtsArgGetFunc) NULL
 2406     };
 2407     klass = gts_object_class_new (GTS_OBJECT_CLASS (gfk_gl_vectors_class ()),
 2408                   &gfk_gl_streamlines_info);
 2409   }
 2410 
 2411   return klass;
 2412 }
 2413 
 2414 /* GfkGlEllipses: Object */
 2415 
 2416 static void update_ellipse_scalar (GfkGlEllipses * gl)
 2417 {
 2418   gfk_gl_set_sensitive (GFK_GL (gl), GFK_GL_SCALAR (gl)->scalar, TRUE);
 2419   gfk_gl_update_properties (GFK_GL (gl));
 2420   gtk_widget_set_sensitive (lookup_widget_params (GFK_GL (gl)->properties, "default_color_label"), 
 2421                 FALSE);
 2422   gtk_widget_set_sensitive (lookup_widget_params (GFK_GL (gl)->properties, "default_color"), 
 2423                 FALSE);
 2424 }
 2425 
 2426 static void gl_ellipses_read (GtsObject ** o, GtsFile * fp)
 2427 {
 2428   GtkWidget * ellipse = GFK_GL_ELLIPSES (*o)->ellipse;
 2429   GfsGlEllipses * gl = GFS_GL_ELLIPSES (GFK_GL (*o)->gl);
 2430 
 2431   (* GTS_OBJECT_CLASS (gfk_gl_ellipses_class ())->parent_class->read) (o, fp);
 2432   if (fp->type == GTS_ERROR)
 2433     return;
 2434 
 2435   gtk_spin_button_set_value (lookup_widget_params (ellipse, "scale"), gl->scale);
 2436 
 2437   if (gl->use_scalar) {
 2438     gtk_option_menu_set_history (lookup_widget_params (ellipse, "color"), 1);
 2439     update_ellipse_scalar (GFK_GL_ELLIPSES (*o));
 2440   }
 2441 }
 2442 
 2443 static void gl_ellipses_update_interface (GfkGl * g)
 2444 {
 2445   GfkGlEllipses * gl = GFK_GL_ELLIPSES (g);
 2446   GfsGlEllipses * gle = GFS_GL_ELLIPSES (GFK_GL (gl)->gl);
 2447   GtkSpinButton * scale = lookup_widget_params (gl->ellipse, "scale");
 2448   GtkAdjustment * ascale = gtk_spin_button_get_adjustment (scale);
 2449   guint j;
 2450   
 2451   for (j = 0; j < 4; j++) {
 2452     static gchar * name[] = {"Ax", "Ay", "Bx", "By"};
 2453     GtkEntry * entry = lookup_widget_params (gl->ellipse, name[j]);
 2454     if (!GFK_IS_EDITED (entry))
 2455       gtk_entry_set_text (entry, gle->expr[j]->str);
 2456   }
 2457 
 2458   ascale->upper = gle->max > 0. ? 10000.*gle->h/gle->max : 1.;
 2459   ascale->step_increment = gle->max > 0. ? gle->h/gle->max/10. : 0.1;
 2460   ascale->page_increment = 10.*ascale->step_increment;
 2461   ascale->value = gle->scale;
 2462   if (!GFK_IS_EDITED (scale))
 2463     gtk_spin_button_configure (scale, ascale, 2.*ascale->step_increment,
 2464                    gfk_decimal_digits (ascale->step_increment, 2));
 2465   gfk_gl_update_properties (GFK_GL (gl));
 2466   gfk_gl_expose (GFK_GL (gl));
 2467 }
 2468 
 2469 static void set_ellipse_color (GtkWidget * color, GfkGlEllipses * gl)
 2470 {
 2471   GFS_GL_ELLIPSES (GFK_GL (gl)->gl)->use_scalar = FALSE;
 2472   gfk_gl_set_sensitive (GFK_GL (gl), GFK_GL_SCALAR (gl)->scalar, FALSE);
 2473   gfk_gl_update_properties (GFK_GL (gl));
 2474   gtk_widget_set_sensitive (lookup_widget_params (GFK_GL (gl)->properties, "default_color_label"), 
 2475                 TRUE);
 2476   gtk_widget_set_sensitive (lookup_widget_params (GFK_GL (gl)->properties, "default_color"), TRUE);
 2477   gfk_gl_expose (GFK_GL (gl));
 2478 }
 2479 
 2480 static void set_ellipse_scalar (GtkWidget * scalar, GfkGlEllipses * gl)
 2481 {
 2482   GFS_GL_ELLIPSES (GFK_GL (gl)->gl)->use_scalar = TRUE;
 2483   update_ellipse_scalar (gl);
 2484   gfk_gl_expose (GFK_GL (gl));
 2485 }
 2486 
 2487 static void gl_ellipses_set_simulation (GfkGl * object, GfsSimulation * sim)
 2488 {
 2489   (*GFK_GL_CLASS (GTS_OBJECT_CLASS (gfk_gl_ellipses_class ())->parent_class)->set_simulation)
 2490     (object, sim);
 2491 
 2492   gl_ellipses_update_interface (object);
 2493 }
 2494 
 2495 static GtsFile * set_ax (GfkGl * gl, const gchar * func)
 2496 {
 2497   return gfs_gl_ellipses_set (GFS_GL_ELLIPSES (gl->gl), 0, func);
 2498 }
 2499 
 2500 static GtsFile * set_ay (GfkGl * gl, const gchar * func)
 2501 {
 2502   return gfs_gl_ellipses_set (GFS_GL_ELLIPSES (gl->gl), 1, func);
 2503 }
 2504 
 2505 static GtsFile * set_bx (GfkGl * gl, const gchar * func)
 2506 {
 2507   return gfs_gl_ellipses_set (GFS_GL_ELLIPSES (gl->gl), 2, func);
 2508 }
 2509 
 2510 static GtsFile * set_by (GfkGl * gl, const gchar * func)
 2511 {
 2512   return gfs_gl_ellipses_set (GFS_GL_ELLIPSES (gl->gl), 3, func);
 2513 }
 2514 
 2515 static void gl_ellipses_post_init (GfkGl * object)
 2516 {
 2517   GfkGlEllipses * gls = GFK_GL_ELLIPSES (object);
 2518   guint i;
 2519 
 2520   if (GFK_GL_CLASS (GTS_OBJECT_CLASS (gfk_gl_ellipses_class ())->parent_class)->post_init)
 2521     (*GFK_GL_CLASS (GTS_OBJECT_CLASS (gfk_gl_ellipses_class ())->parent_class)->post_init)
 2522       (object);
 2523 
 2524   GtkTable * table = GTK_TABLE (lookup_widget_params (gls->ellipse, "table12"));
 2525   for (i = 0; i < 4; i++) {
 2526     static gchar * name[] = {"Ax", "Ay", "Bx", "By"};
 2527     static GfkFunctionSet func[] = {set_ax, set_ay, set_bx, set_by};
 2528 
 2529     GtkWidget * scalar = gfk_function (gls->ellipse, object, name[i], func[i]);
 2530     gtk_widget_show (scalar);
 2531     gtk_table_attach (table, scalar, 1, 2, i, i + 1,
 2532               (GtkAttachOptions) (GTK_FILL),
 2533               (GtkAttachOptions) (0), 0, 0);
 2534   }
 2535 
 2536   gl_ellipses_update_interface (object);
 2537 }
 2538 
 2539 static gchar * gl_ellipses_name (GfkGlClass * klass)
 2540 {
 2541   static gchar name[] = "Ellipses";
 2542   return name;
 2543 }
 2544 
 2545 static GtkWidget * gl_ellipses_icon (GfkGlClass * klass)
 2546 {
 2547   return create_pixmap (NULL, "ellipses-16x16.png");
 2548 }
 2549 
 2550 static gchar * gl_ellipses_properties (GfkGl * gl)
 2551 {
 2552   GfsGlEllipses * gle = GFS_GL_ELLIPSES (gl->gl);
 2553   
 2554   g_free (gl->props);
 2555   gl->props = g_strjoin (",", 
 2556              gle->expr[0]->str, gle->expr[1]->str, 
 2557              gle->expr[2]->str, gle->expr[3]->str, NULL);
 2558   return gl->props;
 2559 }
 2560 
 2561 static void gl_ellipses_class_init (GfkGlClass * klass)
 2562 {
 2563   GTS_OBJECT_CLASS (klass)->read = gl_ellipses_read;
 2564   klass->gl_class = gfs_gl_ellipses_class ();
 2565   klass->post_init = gl_ellipses_post_init;
 2566   klass->set_simulation = gl_ellipses_set_simulation;
 2567   klass->update_interface = gl_ellipses_update_interface;
 2568   klass->name = gl_ellipses_name;
 2569   klass->icon = gl_ellipses_icon;
 2570   klass->properties = gl_ellipses_properties;
 2571 }
 2572 
 2573 static void gl_ellipses_init (GfkGlEllipses * object)
 2574 {
 2575   gtk_widget_set_sensitive (lookup_widget_params (GFK_GL (object)->properties, "shading_label"), 
 2576                 FALSE);
 2577   gtk_widget_set_sensitive (lookup_widget_params (GFK_GL (object)->properties, "shading"), FALSE);
 2578 
 2579   object->ellipse = create_ellipse_params ();
 2580 
 2581   gfk_gl_set_sensitive (GFK_GL (object), GFK_GL_SCALAR (object)->scalar, FALSE);
 2582   GtkWidget * m = gtk_menu_new ();
 2583   GtkWidget * i = gtk_menu_item_new_with_label ("Default");
 2584   g_signal_connect (G_OBJECT (i), "activate", GTK_SIGNAL_FUNC (set_ellipse_color), object);
 2585   gtk_menu_append (m, i);
 2586   gtk_widget_show (i);
 2587 
 2588   i = gtk_menu_item_new_with_label ("Scalar");
 2589   g_signal_connect (G_OBJECT (i), "activate", GTK_SIGNAL_FUNC (set_ellipse_scalar), object);
 2590   gtk_menu_append (m, i);
 2591   gtk_widget_show (i);
 2592   
 2593   gtk_option_menu_set_menu (lookup_widget_params (object->ellipse, "color"), m);
 2594   gtk_widget_show (m);
 2595 
 2596   gfk_gl_prepend_params (GFK_GL (object), object->ellipse, gtk_label_new ("Ellipse"));
 2597   gtk_widget_show (object->ellipse);
 2598 }
 2599 
 2600 GfkGlClass * gfk_gl_ellipses_class (void)
 2601 {
 2602   static GfkGlClass * klass = NULL;
 2603 
 2604   if (klass == NULL) {
 2605     GtsObjectClassInfo gfk_gl_ellipses_info = {
 2606       "GfkGlEllipses",
 2607       sizeof (GfkGlEllipses),
 2608       sizeof (GfkGlClass),
 2609       (GtsObjectClassInitFunc) gl_ellipses_class_init,
 2610       (GtsObjectInitFunc) gl_ellipses_init,
 2611       (GtsArgSetFunc) NULL,
 2612       (GtsArgGetFunc) NULL
 2613     };
 2614     klass = gts_object_class_new (GTS_OBJECT_CLASS (gfk_gl_scalar_class ()),
 2615                   &gfk_gl_ellipses_info);
 2616   }
 2617 
 2618   return klass;
 2619 }
 2620 
 2621 /* GfkGlLocation: Object */
 2622 
 2623 static void gl_location_read (GtsObject ** o, GtsFile * fp)
 2624 {
 2625   GtkWidget * location = GFK_GL_LOCATION (*o)->location;
 2626   GfsGlLocation * gl = GFS_GL_LOCATION (GFK_GL (*o)->gl);
 2627 
 2628   (* GTS_OBJECT_CLASS (gfk_gl_location_class ())->parent_class->read) (o, fp);
 2629   if (fp->type == GTS_ERROR)
 2630     return;
 2631   
 2632   gtk_spin_button_set_value (lookup_widget_params (location, "size"), gl->size);
 2633 #if HAVE_FTGL
 2634   if (gl->label) {
 2635     gl->label = FALSE;
 2636     gtk_toggle_button_set_active (lookup_widget_params (location, "location_label_check"), TRUE);
 2637   }
 2638 #endif /* HAVE_FTGL */
 2639 }
 2640 
 2641 static void gl_location_init (GfkGl * gl)
 2642 {
 2643   GtkWidget * location = create_location_params ();
 2644 
 2645   gtk_widget_set_sensitive (lookup_widget_params (gl->properties, "maxlevel_label"), FALSE);
 2646   gtk_widget_set_sensitive (lookup_widget_params (gl->properties, "maxlevel"), FALSE);
 2647 #if HAVE_FTGL
 2648   gtk_widget_show (gl->font);
 2649 #else /* not HAVE_FTGL */
 2650   gtk_widget_set_sensitive (lookup_widget_params (location, "location_label"), FALSE);
 2651   gtk_widget_set_sensitive (lookup_widget_params (location, "location_label_check"), FALSE);
 2652 #endif /* not HAVE_FTGL */
 2653   gfk_gl_prepend_params (gl, location, gtk_label_new ("Location"));
 2654   gtk_spin_button_set_value (lookup_widget_params (location, "size"), 
 2655                  GFS_GL_LOCATION (gl->gl)->size);
 2656   gtk_widget_show (location);
 2657   GFK_GL_LOCATION (gl)->location = location;
 2658 }
 2659 
 2660 static gchar * gl_location_name (GfkGlClass * klass)
 2661 {
 2662   static gchar name[] = "Location";
 2663   return name;
 2664 }
 2665 
 2666 static GtkWidget * gl_location_icon (GfkGlClass * klass)
 2667 {
 2668   return create_pixmap (NULL, "location-16x16.png");
 2669 }
 2670 
 2671 static void gl_location_class_init (GfkGlClass * klass)
 2672 {
 2673   GTS_OBJECT_CLASS (klass)->read = gl_location_read;
 2674   klass->gl_class = gfs_gl_location_class ();
 2675   klass->name = gl_location_name;
 2676   klass->icon = gl_location_icon;
 2677 }
 2678 
 2679 GfkGlClass * gfk_gl_location_class (void)
 2680 {
 2681   static GfkGlClass * klass = NULL;
 2682 
 2683   if (klass == NULL) {
 2684     GtsObjectClassInfo gfk_gl_location_info = {
 2685       "GfkGlLocation",
 2686       sizeof (GfkGlLocation),
 2687       sizeof (GfkGlClass),
 2688       (GtsObjectClassInitFunc) gl_location_class_init,
 2689       (GtsObjectInitFunc) gl_location_init,
 2690       (GtsArgSetFunc) NULL,
 2691       (GtsArgGetFunc) NULL
 2692     };
 2693     klass = gts_object_class_new (GTS_OBJECT_CLASS (gfk_gl_class ()), &gfk_gl_location_info);
 2694   }
 2695 
 2696   return klass;
 2697 }
 2698 
 2699 /* GfkGlHeight: Object */
 2700 
 2701 static gchar * gl_height_name (GfkGlClass * klass)
 2702 {
 2703   static gchar name[] = "Height";
 2704   return name;
 2705 }
 2706 
 2707 static GtkWidget * gl_height_icon (GfkGlClass * klass)
 2708 {
 2709   return create_pixmap (NULL, "height-16x16.png");
 2710 }
 2711 
 2712 static void gl_height_class_init (GfkGlClass * klass)
 2713 {
 2714   klass->gl_class = gfs_gl_height_class ();
 2715   klass->name = gl_height_name;
 2716   klass->icon = gl_height_icon;
 2717 }
 2718 
 2719 GfkGlClass * gfk_gl_height_class (void)
 2720 {
 2721   static GfkGlClass * klass = NULL;
 2722 
 2723   if (klass == NULL) {
 2724     GtsObjectClassInfo info = {
 2725       "GfkGlHeight",
 2726       sizeof (GfkGlLocation),
 2727       sizeof (GfkGlClass),
 2728       (GtsObjectClassInitFunc) gl_height_class_init,
 2729       (GtsObjectInitFunc) NULL,
 2730       (GtsArgSetFunc) NULL,
 2731       (GtsArgGetFunc) NULL
 2732     };
 2733     klass = gts_object_class_new (GTS_OBJECT_CLASS (gfk_gl_location_class ()), &info);
 2734   }
 2735 
 2736   return klass;
 2737 }
 2738 
 2739 /* GfkGlLocate: Object */
 2740 
 2741 static void gl_locate_read (GtsObject ** o, GtsFile * fp)
 2742 {
 2743   GtkWidget * locate = GFK_GL_LOCATE (*o)->locate;
 2744   GfsGlLocate * gl = GFS_GL_LOCATE (GFK_GL (*o)->gl);
 2745 
 2746   (* GTS_OBJECT_CLASS (gfk_gl_locate_class ())->parent_class->read) (o, fp);
 2747   if (fp->type == GTS_ERROR)
 2748     return;
 2749 
 2750   FttComponent c;
 2751   for (c = 0; c < 3; c++) {
 2752     static gchar * name[] = {"locate_x_entry", "locate_y_entry", "locate_z_entry"};
 2753     gchar * s = g_strdup_printf ("%g", (&gl->p.x)[c]);
 2754     gtk_entry_set_text (lookup_widget_params (locate, name[c]), s);
 2755     g_free (s);
 2756   }
 2757 }
 2758 
 2759 static gchar * gl_locate_name (GfkGlClass * klass)
 2760 {
 2761   static gchar name[] = "Locate";
 2762   return name;
 2763 }
 2764 
 2765 static GtkWidget * gl_locate_icon (GfkGlClass * klass)
 2766 {
 2767   return create_pixmap (NULL, "locate-16x16.png");
 2768 }
 2769 
 2770 static void gl_locate_update_interface (GfkGl * g)
 2771 {
 2772   GtkWidget * locate = GFK_GL_LOCATE (g)->locate;
 2773   GfsGlLocate * gl = GFS_GL_LOCATE (g->gl);
 2774 
 2775   FttComponent c;
 2776   for (c = 0; c < 3; c++) {
 2777     static gchar * name[] = {"locate_x_entry", "locate_y_entry", "locate_z_entry"};
 2778     GtkEntry * entry = lookup_widget_params (locate, name[c]);
 2779     gchar * s = g_strdup_printf ("%g", (&gl->p.x)[c]);
 2780     gtk_entry_set_text (entry, s);
 2781     g_free (s);
 2782   }
 2783   gfk_gl_update_properties (g);
 2784   gfk_gl_expose (g);
 2785 }
 2786 
 2787 static void gl_locate_set_simulation (GfkGl * object, GfsSimulation * sim)
 2788 {
 2789   (*GFK_GL_CLASS (GTS_OBJECT_CLASS (gfk_gl_locate_class ())->parent_class)->set_simulation)
 2790     (object, sim);
 2791 
 2792   gl_locate_update_interface (object);
 2793 }
 2794 
 2795 static void gl_locate_class_init (GfkGlClass * klass)
 2796 {
 2797   GTS_OBJECT_CLASS (klass)->read = gl_locate_read;
 2798   klass->gl_class = gfs_gl_locate_class ();
 2799   klass->name = gl_locate_name;
 2800   klass->icon = gl_locate_icon;
 2801   klass->update_interface = gl_locate_update_interface;
 2802   klass->set_simulation = gl_locate_set_simulation;
 2803 }
 2804 
 2805 static void gl_locate_init (GfkGl * gl)
 2806 {
 2807   GtkWidget * locate = create_locate_params ();
 2808 
 2809   gtk_widget_set_sensitive (lookup_widget_params (gl->properties, "shading_label"), FALSE);
 2810   gtk_widget_set_sensitive (lookup_widget_params (gl->properties, "shading"), FALSE);
 2811   gfk_gl_prepend_params (gl, locate, gtk_label_new ("Locate"));
 2812   gtk_widget_show (locate);
 2813 
 2814   GFK_GL_LOCATE (gl)->locate = locate;
 2815 }
 2816 
 2817 GfkGlClass * gfk_gl_locate_class (void)
 2818 {
 2819   static GfkGlClass * klass = NULL;
 2820 
 2821   if (klass == NULL) {
 2822     GtsObjectClassInfo gfk_gl_locate_info = {
 2823       "GfkGlLocate",
 2824       sizeof (GfkGl),
 2825       sizeof (GfkGlClass),
 2826       (GtsObjectClassInitFunc) gl_locate_class_init,
 2827       (GtsObjectInitFunc) gl_locate_init,
 2828       (GtsArgSetFunc) NULL,
 2829       (GtsArgGetFunc) NULL
 2830     };
 2831     klass = gts_object_class_new (GTS_OBJECT_CLASS (gfk_gl_class ()), &gfk_gl_locate_info);
 2832   }
 2833 
 2834   return klass;
 2835 }
 2836 
 2837 /* GfkGlPipes: Object */
 2838 
 2839 static void gl_pipes_init (GfkGl * gl)
 2840 {
 2841   gtk_widget_hide (lookup_widget_params (gl->properties, "maxlevel_label"));
 2842   gtk_widget_hide (lookup_widget_params (gl->properties, "maxlevel"));
 2843   gtk_widget_hide (lookup_widget_params (gl->properties, "finest"));
 2844   gtk_widget_hide (lookup_widget_params (gl->properties, "shading_label"));
 2845   gtk_widget_hide (lookup_widget_params (gl->properties, "shading"));
 2846   gtk_widget_set_sensitive (lookup_widget_params (gl->properties, "label117"), FALSE);
 2847   gtk_widget_set_sensitive (lookup_widget_params (gl->properties, "linewidth"), FALSE);
 2848 #if HAVE_FTGL
 2849   gtk_widget_show (gl->font);
 2850 #endif
 2851 }
 2852 
 2853 static gchar * gl_pipes_name (GfkGlClass * klass)
 2854 {
 2855   static gchar name[] = "Pipes";
 2856   return name;
 2857 }
 2858 
 2859 static GtkWidget * gl_pipes_icon (GfkGlClass * klass)
 2860 {
 2861   return create_pixmap (NULL, "pipes-16x16.png");
 2862 }
 2863 
 2864 static void gl_pipes_class_init (GfkGlClass * klass)
 2865 {
 2866   klass->gl_class = gfs_gl_pipes_class ();
 2867   klass->name = gl_pipes_name;
 2868   klass->icon = gl_pipes_icon;
 2869 }
 2870 
 2871 GfkGlClass * gfk_gl_pipes_class (void)
 2872 {
 2873   static GfkGlClass * klass = NULL;
 2874 
 2875   if (klass == NULL) {
 2876     GtsObjectClassInfo info = {
 2877       "GfkGlPipes",
 2878       sizeof (GfkGl),
 2879       sizeof (GfkGlClass),
 2880       (GtsObjectClassInitFunc) gl_pipes_class_init,
 2881       (GtsObjectInitFunc) gl_pipes_init,
 2882       (GtsArgSetFunc) NULL,
 2883       (GtsArgGetFunc) NULL
 2884     };
 2885     klass = gts_object_class_new (GTS_OBJECT_CLASS (gfk_gl_class ()), &info);
 2886   }
 2887 
 2888   return klass;
 2889 }
 2890 
 2891 /* GfkGlClipPlane: Object */
 2892 
 2893 static gchar * gl_clip_plane_name (GfkGlClass * klass)
 2894 {
 2895   static gchar name[] = "Clipping";
 2896   return name;
 2897 }
 2898 
 2899 static GtkWidget * gl_clip_plane_icon (GfkGlClass * klass)
 2900 {
 2901   return create_pixmap (NULL, "clipping-16x16.png");
 2902 }
 2903 
 2904 static void gl_clip_plane_class_init (GfkGlClass * klass)
 2905 {
 2906   klass->gl_class = gfs_gl_clip_plane_class ();
 2907   klass->name = gl_clip_plane_name;
 2908   klass->icon = gl_clip_plane_icon;
 2909   klass->pickinfo = NULL;
 2910 }
 2911 
 2912 static void gl_clip_plane_init (GfkGl * gl)
 2913 {
 2914   gtk_widget_show (GFK_GL2D (gl)->params);
 2915   gtk_widget_set_sensitive (lookup_widget_params (gl->properties, "gl_params"), FALSE);
 2916 }
 2917 
 2918 GfkGlClass * gfk_gl_clip_plane_class (void)
 2919 {
 2920   static GfkGlClass * klass = NULL;
 2921 
 2922   if (klass == NULL) {
 2923     GtsObjectClassInfo gfk_gl_clip_plane_info = {
 2924       "GfkGlClipPlane",
 2925       sizeof (GfkGl2D),
 2926       sizeof (GfkGlClass),
 2927       (GtsObjectClassInitFunc) gl_clip_plane_class_init,
 2928       (GtsObjectInitFunc) gl_clip_plane_init,
 2929       (GtsArgSetFunc) NULL,
 2930       (GtsArgGetFunc) NULL
 2931     };
 2932     klass = gts_object_class_new (GTS_OBJECT_CLASS (gfk_gl2D_class ()), &gfk_gl_clip_plane_info);
 2933   }
 2934 
 2935   return klass;
 2936 }
 2937 
 2938 /* GfkGlInfo: Object */
 2939 
 2940 typedef struct {
 2941   guint cells, leaves;
 2942   GArray * level;
 2943 } CellCount;
 2944 
 2945 static void count_cells (FttCell * cell, CellCount * c)
 2946 {
 2947   guint i = ftt_cell_level (cell), n = 0;
 2948   if (i >= c->level->len)
 2949     g_array_append_val (c->level, n);
 2950   c->cells++;
 2951   if (FTT_CELL_IS_LEAF (cell)) {
 2952     c->leaves++;
 2953     g_array_index (c->level, guint, i)++;
 2954   }
 2955 }
 2956 
 2957 static void gl_info_set_simulation (GfkGl * object, GfsSimulation * sim)
 2958 {
 2959   CellCount count = {0, 0, NULL};
 2960 
 2961   (*GFK_GL_CLASS (GTS_OBJECT_CLASS (gfk_gl_info_class ())->parent_class)->set_simulation) 
 2962     (object, sim);
 2963   
 2964   count.level = g_array_new (FALSE, FALSE, sizeof (guint));
 2965   gfs_domain_cell_traverse (GFS_DOMAIN (sim), FTT_PRE_ORDER, FTT_TRAVERSE_ALL, -1,
 2966                 (FttCellTraverseFunc) count_cells, &count);
 2967   GtkLabel * label = lookup_widget_params (GFK_GL_INFO (object)->info, "number_of_levels");
 2968   gchar * s = g_strdup_printf ("%d", count.level->len);
 2969   gtk_label_set_text (label, s);
 2970   g_free (s);
 2971 
 2972   label = lookup_widget_params (GFK_GL_INFO (object)->info, "number_of_cells");
 2973   s = g_strdup_printf ("%d", count.cells);
 2974   gtk_label_set_text (label, s);
 2975   g_free (s);
 2976 
 2977   label = lookup_widget_params (GFK_GL_INFO (object)->info, "number_of_leaf_cells");
 2978   s = g_strdup_printf ("%d", count.leaves);
 2979   gtk_label_set_text (label, s);
 2980   g_free (s);
 2981 
 2982   GtkWidget * table = lookup_widget_params (GFK_GL_INFO (object)->info, "leaf_cells_per_level");
 2983   gtk_container_foreach (GTK_CONTAINER (table), (GtkCallback) gtk_widget_destroy, NULL);
 2984 
 2985   guint i, l = 0;
 2986   for (i = 0; i < count.level->len; i++)
 2987     if (g_array_index (count.level, guint, i)) {
 2988       gchar * s = g_strdup_printf ("Level %d", i);
 2989       GtkWidget * label = gtk_label_new (s);
 2990       gtk_widget_show (label);
 2991       g_free (s);
 2992       gtk_table_attach (GTK_TABLE (table), label, 0, 1, l, l + 1,
 2993             (GtkAttachOptions) (GTK_FILL),
 2994             (GtkAttachOptions) (0), 16, 0);
 2995       gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
 2996       
 2997       s = g_strdup_printf ("%d", g_array_index (count.level, guint, i));
 2998       label = gtk_label_new (s);
 2999       gtk_widget_show (label);
 3000       g_free (s);
 3001       gtk_table_attach (GTK_TABLE (table), label, 1, 2, l, l + 1,
 3002             (GtkAttachOptions) (GTK_FILL),
 3003             (GtkAttachOptions) (0), 0, 0);
 3004       gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_RIGHT);
 3005       gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
 3006       
 3007       l++;
 3008     }
 3009 
 3010   g_array_free (count.level, TRUE);
 3011 }
 3012 
 3013 static gchar * gl_info_name (GfkGlClass * klass)
 3014 {
 3015   static gchar name[] = "Info";
 3016   return name;
 3017 }
 3018 
 3019 static GtkWidget * gl_info_icon (GfkGlClass * klass)
 3020 {
 3021   return create_pixmap (NULL, "info-16x16.png");
 3022 }
 3023 
 3024 static void gl_info_class_init (GfkGlClass * klass)
 3025 {
 3026   klass->set_simulation = gl_info_set_simulation;
 3027   klass->name = gl_info_name;
 3028   klass->icon = gl_info_icon;
 3029 }
 3030 
 3031 static void gl_info_init (GfkGl * gl)
 3032 {
 3033   GFK_GL_INFO (gl)->info = create_info_params ();
 3034   gfk_gl_set_sensitive (gl, gl->properties, FALSE);
 3035   gfk_gl_prepend_params (gl, GFK_GL_INFO (gl)->info, gtk_label_new ("Info"));
 3036   gtk_widget_show (GFK_GL_INFO (gl)->info);
 3037 }
 3038 
 3039 GfkGlClass * gfk_gl_info_class (void)
 3040 {
 3041   static GfkGlClass * klass = NULL;
 3042 
 3043   if (klass == NULL) {
 3044     GtsObjectClassInfo gfk_gl_info_info = {
 3045       "GfkGlInfo",
 3046       sizeof (GfkGlInfo),
 3047       sizeof (GfkGlClass),
 3048       (GtsObjectClassInitFunc) gl_info_class_init,
 3049       (GtsObjectInitFunc) gl_info_init,
 3050       (GtsArgSetFunc) NULL,
 3051       (GtsArgGetFunc) NULL
 3052     };
 3053     klass = gts_object_class_new (GTS_OBJECT_CLASS (gfk_gl_class ()),
 3054                   &gfk_gl_info_info);
 3055   }
 3056 
 3057   return klass;
 3058 }
 3059 
 3060 /* GfkGlView: Object */
 3061 
 3062 static void write_ppm_pixbuf (GdkPixbuf * pixbuf, FILE * fp)
 3063 {
 3064   guint width, height = gdk_pixbuf_get_height (pixbuf);
 3065   guchar * pixels = gdk_pixbuf_get_pixels (pixbuf);
 3066   guint j, rowstride = gdk_pixbuf_get_rowstride (pixbuf);
 3067 
 3068   g_assert (gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB);
 3069   g_assert (gdk_pixbuf_get_bits_per_sample (pixbuf) == 8);
 3070   g_assert (!gdk_pixbuf_get_has_alpha (pixbuf));
 3071   g_assert (gdk_pixbuf_get_n_channels (pixbuf) == 3);
 3072 
 3073   width = rowstride/3;
 3074   if (width % 2)
 3075     width -= 1;
 3076   if (height % 2)
 3077     height -= 1;
 3078   fprintf (fp, "P6 %d %d 255\n", width, height);
 3079   for (j = 0; j < height; j++, pixels += rowstride)
 3080     fwrite (pixels, 3*width, sizeof (guchar), fp);  
 3081 }
 3082 
 3083 static void write_ppm (guint width, guint height, FILE * fp)
 3084 {
 3085   gchar * p, * p1;
 3086   guint j;
 3087 
 3088   p = g_malloc0 (3*width*height*sizeof (gchar));
 3089   glPixelStorei (GL_PACK_ALIGNMENT, 1);
 3090   glReadPixels (0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, p);
 3091   fprintf (fp, "P6 %d %d 255\n", width, height);
 3092   p1 = (gchar *)(p + sizeof (gchar)*(3*width*(height - 1)));
 3093   for (j = height; j--; p1 = (gchar *)(p1 - sizeof (gchar)*3*width))
 3094     fwrite (p1, 3*width, sizeof (gchar), fp);
 3095   g_free (p);
 3096 }
 3097 
 3098 static GList * get_symmetries (GtkTreeModel * list)
 3099 {
 3100   GtkTreeIter iter;  
 3101   gboolean valid = gtk_tree_model_get_iter_first (list, &iter);
 3102   GList * symmetry = NULL;
 3103 
 3104   while (valid) {
 3105     gboolean visible;
 3106     GfkGl * gl;
 3107 
 3108     gtk_tree_model_get (list, &iter, GL_COLUMN, &gl, VISIBLE_COLUMN, &visible, -1);
 3109     if (visible && GFK_IS_GL_SYMMETRY (gl))
 3110       symmetry = g_list_append (symmetry, gl->gl);
 3111     valid = gtk_tree_model_iter_next (list, &iter);
 3112   }
 3113   return symmetry;
 3114 }
 3115 
 3116 void gfs_gl2ps (GfsGl2PSParams * p, 
 3117         FILE * fp, 
 3118         const gchar * fname, 
 3119         GtkWidget * view)
 3120 {
 3121   GtkWidget * glarea;
 3122   GfsGlViewParams * viewp;
 3123 
 3124   g_return_if_fail (p != NULL);
 3125   g_return_if_fail (fp != NULL);
 3126   g_return_if_fail (fname != NULL);
 3127   g_return_if_fail (view != NULL);
 3128 
 3129   glarea = g_object_get_data (G_OBJECT (view), "glarea");
 3130   viewp = g_object_get_data (G_OBJECT (glarea), "GfsGlViewParams");
 3131 
 3132   switch (p->format) {
 3133   case GFSGL_PPM_OFFSCREEN: {
 3134     gboolean written = FALSE;
 3135     GdkGLConfig * glconfig = gdk_gl_config_new_by_mode (GDK_GL_MODE_RGB    |
 3136                             GDK_GL_MODE_DEPTH  |
 3137                             GDK_GL_MODE_SINGLE);
 3138     if (!glconfig)
 3139       g_warning ("Cannot get OpenGL config\n");
 3140     else {
 3141       guint width  = p->width ?  p->width :  glarea->allocation.width;
 3142       guint height = p->height ? p->height : glarea->allocation.height;
 3143       GdkPixmap * pixmap = gdk_pixmap_new (glarea->window, width, height, -1);
 3144       GdkGLDrawable * gldrawable = GDK_GL_DRAWABLE (gdk_pixmap_set_gl_capability (pixmap,
 3145                                           glconfig,
 3146                                           NULL));
 3147       GdkGLContext * glcontext = gdk_gl_context_new (gldrawable,
 3148                              NULL,
 3149                              FALSE,
 3150                              GDK_GL_RGBA_TYPE);
 3151     
 3152       if (!glcontext)
 3153     g_warning ("Cannot create the OpenGL rendering context\n");
 3154       else {
 3155     GfsGlViewParams * info = g_object_get_data (G_OBJECT (glarea), "GfsGlViewParams");
 3156     GLfloat m[4][4];
 3157     GdkPixbuf * pixbuf;
 3158     gdouble max;
 3159 
 3160     gdk_gl_drawable_gl_begin (gldrawable, glcontext);
 3161 
 3162     glViewport (0, 0, width, height);
 3163     gfs_gl_init_gl ();
 3164     glMatrixMode (GL_PROJECTION);
 3165     glLoadIdentity ();
 3166     GtkTreeModel * list = 
 3167       gtk_tree_view_get_model (GTK_TREE_VIEW (lookup_widget (view, "gl_list")));
 3168     GList * symmetries = get_symmetries (list);
 3169     max = gfs_gl_domain_extent (g_object_get_data (G_OBJECT (glarea), "sim"), symmetries);
 3170     g_list_free (symmetries);
 3171     gluPerspective (info->fov, width/(float)height, 1., 1. + 2.*max);
 3172     glMatrixMode (GL_MODELVIEW);
 3173       
 3174     glClearColor (info->bg.r, info->bg.g, info->bg.b, 1);
 3175     glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 3176 
 3177     glLoadIdentity ();
 3178     glTranslatef (info->tx, info->ty, - (1. + max));
 3179     gfs_gl_build_rotmatrix (m, info->quat);
 3180     glMultMatrixf (&m[0][0]);
 3181     glScalef (info->sx, info->sy, info->sz);
 3182 
 3183     gfk_gl_view_draw (view, GFSGL_PPM_OFFSCREEN);
 3184 
 3185     glFlush ();
 3186     gdk_gl_drawable_gl_end (gldrawable);
 3187 
 3188     pixbuf = gdk_pixbuf_get_from_drawable (NULL, GDK_DRAWABLE (pixmap), NULL, 
 3189                            0, 0, 0, 0, -1, -1);
 3190 
 3191     if (pixbuf == NULL)
 3192       g_warning ("Cannot get pixbuf\n");
 3193     else {
 3194       write_ppm_pixbuf (pixbuf, fp);
 3195       written = TRUE;
 3196       g_object_unref (G_OBJECT (pixbuf));
 3197     }
 3198     gdk_gl_context_destroy (glcontext);
 3199     info->do_init = TRUE; /* this is necessary to reinitialise textures for fonts */
 3200       }
 3201       gdk_pixmap_unset_gl_capability (pixmap);
 3202       g_object_unref (G_OBJECT (pixmap));
 3203     }
 3204     if (written)
 3205       break;
 3206   }
 3207   case GFSGL_PPM_SCREEN:
 3208     write_ppm (glarea->allocation.width, glarea->allocation.height, fp);
 3209     break;
 3210   case GFSGL_GNUPLOT: case GFSGL_OBJ: case GFSGL_KML: {
 3211     guint buffsize = 0;
 3212     gboolean done = FALSE;
 3213     gfloat base_res = viewp->base_res;
 3214     viewp->base_res = 0.;
 3215     while (!done) {
 3216       GfsGlFeedback * f;
 3217 
 3218       buffsize += 2048*2048;
 3219       f = gfs_gl_feedback_begin (buffsize);
 3220       gfk_gl_view_draw (view, GFSGL_SCREEN);
 3221       done = gfs_gl_feedback_end (f, g_object_get_data (G_OBJECT (glarea), "sim"), fp, p->format);
 3222     }
 3223     viewp->base_res = base_res;
 3224     break;
 3225   }
 3226   default: {
 3227     GLint buffsize = 0, state = GL2PS_OVERFLOW;
 3228 
 3229     while (state == GL2PS_OVERFLOW) {
 3230       buffsize += 2048*2048;
 3231       gl2psBeginPage ("", "GfsView",
 3232               NULL,
 3233               p->format, p->sort, p->options, 
 3234               GL_RGBA, 0, NULL, 
 3235               0, 0, 0,
 3236               buffsize, fp, fname);
 3237       GtkWidget * glarea = g_object_get_data (G_OBJECT (view), "glarea");
 3238       GfsGlViewParams * v = g_object_get_data (G_OBJECT (glarea), "GfsGlViewParams");
 3239       v->lw = p->lw;
 3240       gfk_gl_view_draw (view, p->format);
 3241       state = gl2psEndPage();
 3242     }
 3243   }
 3244   }
 3245 }
 3246 
 3247 static void visible_toggled (GtkCellRendererToggle * cellrenderertoggle,
 3248                  gchar * path_string,
 3249                  GtkTreeModel * model)
 3250 {
 3251   GtkTreeIter iter;
 3252   gboolean visible;
 3253   GfkGl * gl;
 3254 
 3255   g_assert (gtk_tree_model_get_iter_from_string (model, &iter, path_string));
 3256   gtk_tree_model_get (model, &iter, GL_COLUMN, &gl, VISIBLE_COLUMN, &visible, -1);
 3257   gtk_list_store_set (GTK_LIST_STORE (model), &iter, VISIBLE_COLUMN, !visible, -1);
 3258   gfk_gl_expose (gl);
 3259 }
 3260 
 3261 static gboolean tree_selection_func (GtkTreeSelection * select,
 3262                      GtkTreeModel * model,
 3263                      GtkTreePath * path,
 3264                      gboolean path_currently_selected,
 3265                      gpointer data)
 3266 {
 3267   if (path_currently_selected)
 3268     return TRUE;
 3269   else {
 3270     GtkTreeIter iter;
 3271     gboolean visible;
 3272 
 3273     g_assert (gtk_tree_model_get_iter (model, &iter, path));
 3274     gtk_tree_model_get (model, &iter, VISIBLE_COLUMN, &visible, -1);
 3275     if (visible && gtk_tree_selection_get_selected (select, &model, &iter))
 3276       gtk_list_store_set (GTK_LIST_STORE (model), &iter, SELECTED_COLUMN, FALSE, -1);
 3277     return visible;
 3278   }
 3279 }
 3280 
 3281 static void tree_selection_changed_cb (GtkTreeSelection * selection, GObject * list)
 3282 {
 3283   GtkTreeIter iter;
 3284   GtkTreeModel * model;
 3285   
 3286   if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
 3287     GfkGl * gl, * former = g_object_get_data (list, "former");
 3288 
 3289     gtk_tree_model_get (model, &iter, GL_COLUMN, &gl, -1);
 3290     gtk_list_store_set (GTK_LIST_STORE (model), &iter, SELECTED_COLUMN, TRUE, -1);
 3291     if (former && former != gl) {
 3292       gint x, y;
 3293       gtk_window_get_position (GTK_WINDOW (former->params), &x, &y);
 3294       gtk_window_move (GTK_WINDOW (gl->params), x, y);
 3295       gtk_widget_show (gl->params);
 3296       gtk_widget_hide (former->params);
 3297       g_object_set_data (list, "former", gl);
 3298     }
 3299     gtk_widget_set_sensitive (lookup_widget (GTK_WIDGET (list), "properties1"), TRUE);
 3300     gtk_widget_set_sensitive (lookup_widget (GTK_WIDGET (list), "delete3"), TRUE);
 3301   }
 3302 }
 3303 
 3304 static gchar * format_glob (GSList * list)
 3305 {
 3306   gchar * s = g_strdup ("");
 3307 
 3308   while (list) {
 3309     GfsFormat * f = list->data;
 3310     gchar * s2 = NULL;
 3311 
 3312     switch (f->t) {
 3313     case GFS_NONE_FORMAT:
 3314       s2 = g_strconcat (s, f->s, NULL);
 3315       break;
 3316     case GFS_ITER_FORMAT: case GFS_TIME_FORMAT: 
 3317       s2 = g_strconcat (s, "*", NULL);
 3318       break;
 3319     default:
 3320       g_assert_not_reached ();
 3321     }
 3322     g_free (s);
 3323     s = s2;
 3324     list = list->next;
 3325   }
 3326 
 3327   return s;
 3328 }
 3329 
 3330 static void free_glob (glob_t * files)
 3331 {
 3332   guint i;
 3333 
 3334   for (i = 0; i < files->gl_pathc; i++)
 3335     g_free (files->gl_pathv[i]);
 3336   g_free (files->gl_pathv);
 3337   g_free (files);
 3338 }
 3339 
 3340 static void read_formats (const gchar * fname,
 3341               GtkWidget * view)
 3342 {
 3343   GtkWindow * parent = GTK_WINDOW (view);
 3344   gboolean dynamic = FALSE, parallel = FALSE;
 3345   GSList * formats = gfs_format_new (fname, NULL, &dynamic, &parallel);
 3346 
 3347   if (formats == NULL || parallel) {
 3348     GtkWidget * msg = gtk_message_dialog_new (parent,
 3349                           GTK_DIALOG_DESTROY_WITH_PARENT,
 3350                           GTK_MESSAGE_WARNING,
 3351                           GTK_BUTTONS_CLOSE,
 3352                           "Cannot open file `%s`",
 3353                           fname);
 3354     gtk_dialog_run (GTK_DIALOG (msg));
 3355     gtk_widget_destroy (msg);
 3356   }
 3357   else {
 3358     gchar * g = format_glob (formats);
 3359     glob_t * files;
 3360 
 3361     files = g_malloc (sizeof (glob_t));
 3362     glob (g, GLOB_NOSORT, NULL, files);
 3363     if (!files->gl_pathc) {
 3364       GtkWidget * msg = gtk_message_dialog_new (parent,
 3365                         GTK_DIALOG_DESTROY_WITH_PARENT,
 3366                         GTK_MESSAGE_WARNING,
 3367                         GTK_BUTTONS_CLOSE,
 3368                         "Pattern `%s` did not match any files",
 3369                         fname);
 3370       gtk_dialog_run (GTK_DIALOG (msg));
 3371       gtk_widget_destroy (msg);
 3372     }
 3373     else {
 3374       GtkWidget * play;
 3375       guint i, j;
 3376 
 3377       for (i = 0; i < files->gl_pathc - 1; i++)
 3378     for (j = 0; j < files->gl_pathc - 1 - i; j++)
 3379       if (gfs_format_time_value (formats, files->gl_pathv[j + 1]) < 
 3380           gfs_format_time_value (formats, files->gl_pathv[j])) {
 3381         gchar * tmp = files->gl_pathv[j];
 3382         files->gl_pathv[j] = files->gl_pathv[j + 1];
 3383         files->gl_pathv[j + 1] = tmp;
 3384       }
 3385 
 3386       i = 0;
 3387       while (i < files->gl_pathc && 
 3388          gfs_format_time_value (formats, files->gl_pathv[i]) < G_MAXDOUBLE)
 3389     i++;
 3390       j = i;
 3391       while (j < files->gl_pathc) g_free (files->gl_pathv[j++]);
 3392       files->gl_pathc = i;
 3393       if (files->gl_pathc == 0) {
 3394     GtkWidget * msg = gtk_message_dialog_new (parent,
 3395                           GTK_DIALOG_DESTROY_WITH_PARENT,
 3396                           GTK_MESSAGE_WARNING,
 3397                           GTK_BUTTONS_CLOSE,
 3398                           "Pattern `%s` did not match any files",
 3399                           fname);
 3400     gtk_dialog_run (GTK_DIALOG (msg));
 3401     gtk_widget_destroy (msg);
 3402     free_glob (files);
 3403     gfs_format_destroy (formats);
 3404     return;
 3405       }
 3406       
 3407       files->gl_offs = 0;
 3408 
 3409       play = create_play ();
 3410       g_object_set_data_full (G_OBJECT (play), "glob_t", files, (GDestroyNotify) free_glob);
 3411       g_object_set_data (G_OBJECT (play), "parent", parent);
 3412       gtk_window_set_transient_for (GTK_WINDOW (play), parent);
 3413       gtk_window_set_position (GTK_WINDOW (play), GTK_WIN_POS_CENTER_ON_PARENT);
 3414       gtk_widget_show (play);
 3415 
 3416       gfk_gl_simulation_read (files->gl_pathv[0], view, TRUE);
 3417     }
 3418     g_free (g);
 3419   }
 3420   gfs_format_destroy (formats);
 3421 }
 3422 
 3423 static GfsSimulation * gl_simulation_read (GtsFile * fp,
 3424                        const gchar * fname,
 3425                        GtkWindow * parent)
 3426 {
 3427   GfsSimulation * sim;
 3428 
 3429   if ((sim = gfs_simulation_read (fp)) == NULL) {
 3430     gchar * basename = g_path_get_basename (fname);
 3431     GtkWidget * msg = gtk_message_dialog_new (parent,
 3432                           GTK_DIALOG_DESTROY_WITH_PARENT,
 3433                           GTK_MESSAGE_WARNING,
 3434                           GTK_BUTTONS_CLOSE,
 3435                           "File `%s' is not a valid Gerris file\n"
 3436                           "%s:%d:%d: %s\n",
 3437                           basename, basename,
 3438                           fp->line, fp->pos, fp->error);
 3439     g_free (basename);
 3440     gtk_dialog_run (GTK_DIALOG (msg));
 3441     gtk_widget_destroy (msg);
 3442     return NULL;
 3443   }
 3444   else
 3445     gfs_simulation_init (sim);
 3446   return sim;
 3447 }
 3448 
 3449 gboolean gfk_gl_view_read_parameters (GtkWidget * view, GtsFile * fp, gboolean discard)
 3450 {
 3451   GfkGl * gl = NULL;
 3452   GtkWidget * pref, * glarea, * list;
 3453   GfsGlViewParams * params;
 3454   GfsSimulation * sim;
 3455 
 3456   g_return_val_if_fail (view != NULL, FALSE);
 3457   g_return_val_if_fail (fp != NULL, FALSE);
 3458 
 3459   pref = lookup_widget (view, "preferences");
 3460   glarea = lookup_widget (view, "glarea");
 3461   params = g_object_get_data (G_OBJECT (glarea), "GfsGlViewParams");
 3462   list = lookup_widget (view, "gl_list");
 3463   sim = g_object_get_data (G_OBJECT (glarea), "sim");
 3464 
 3465   while (fp->type == GTS_STRING) {
 3466     if (!strcmp (fp->token->str, "View")) {
 3467       GfsGlViewParams p = *params;
 3468 
 3469       gfs_gl_view_params_read (&p, fp);
 3470       if (!discard) {
 3471     GtkWidget * bgcolor = lookup_widget (pref, "bgcolor");
 3472     GtkWidget * colorsel = g_object_get_data (G_OBJECT (bgcolor), "colorsel");
 3473     GdkColor c;
 3474 
 3475     *params = p;
 3476     c.red = params->bg.r*65535; c.green = params->bg.g*65535; c.blue = params->bg.b*65535;
 3477     gtk_widget_modify_bg (bgcolor, GTK_STATE_NORMAL, &c);
 3478     gtk_widget_modify_bg (bgcolor, GTK_STATE_PRELIGHT, &c);
 3479     gtk_widget_modify_bg (bgcolor, GTK_STATE_ACTIVE, &c);
 3480     gtk_color_selection_set_current_color (GTK_COLOR_SELECTION (lookup_widget 
 3481                                     (colorsel, "colorselection2")), 
 3482                            &c);
 3483     gtk_spin_button_set_value (lookup_widget_params (pref, "resolution"), params->base_res);
 3484     gtk_spin_button_set_value (lookup_widget_params (pref, "lc"), params->lc);
 3485     gtk_spin_button_set_value (lookup_widget_params (pref, "reactivity"), params->reactivity);
 3486     gtk_spin_button_set_value (lookup_widget_params (pref, "sx"), params->sx);
 3487     gtk_spin_button_set_value (lookup_widget_params (pref, "sy"), params->sy);
 3488     gtk_spin_button_set_value (lookup_widget_params (pref, "sz"), params->sz);
 3489       }
 3490     }
 3491     else {
 3492       if (discard) {
 3493     GfsGl * g;
 3494 
 3495     if ((g = gfs_gl_new_from_file (fp)))
 3496       gts_object_destroy (GTS_OBJECT (g));
 3497     else
 3498       break;
 3499       }
 3500       else {
 3501     GtsObjectClass * klass = gts_object_class_from_name (fp->token->str);
 3502     GtsObject * o;
 3503       
 3504     if (klass == NULL) {
 3505       gchar * ename = g_strconcat ("GfkGl", fp->token->str, NULL);
 3506       klass = gts_object_class_from_name (ename);
 3507       g_free (ename);
 3508     }
 3509     if (klass == NULL || !gts_object_class_is_from_class (klass, gfk_gl_class ()))
 3510       break;
 3511     gl = gfk_gl_new (GFK_GL_CLASS (klass), glarea, list);
 3512     o = GTS_OBJECT (gl);
 3513     (* klass->read) (&o, fp);
 3514     if (fp->type == GTS_ERROR) {
 3515       gts_object_destroy (o);
 3516       return FALSE;
 3517     }
 3518     if (sim)
 3519       gfk_gl_set_simulation (gl, sim);
 3520     gl_add_gl (glarea, list, gl);
 3521       }
 3522     }
 3523     if (fp->type == GTS_ERROR)
 3524       return FALSE;
 3525     gts_file_next_token (fp);
 3526   }
 3527   if (gl != NULL)
 3528     gfk_gl_expose (gl);
 3529   return TRUE;
 3530 }
 3531 
 3532 GfsSimulation * gfk_gl_simulation_read (const gchar * fname,
 3533                     GtkWidget * view,
 3534                     gboolean set)
 3535 {
 3536   GtsFile * fp;
 3537   GfsSimulation * sim = NULL;
 3538   FILE * fptr;
 3539 
 3540   g_return_val_if_fail (fname != NULL, NULL);
 3541   g_return_val_if_fail (view != NULL, NULL);
 3542 
 3543   if (g_file_test (fname, G_FILE_TEST_IS_REGULAR))
 3544     fptr = gfs_gl_popen (fname);
 3545   else {
 3546     read_formats (fname, view);
 3547     return NULL;
 3548   }
 3549   if (fptr == NULL)
 3550     return NULL;
 3551   fp = gts_file_new (fptr);
 3552   while (fp->type == GTS_STRING || fp->type == GTS_INT) {
 3553     if (!gfk_gl_view_read_parameters (view, fp, FALSE)) {
 3554       gchar * basename = g_path_get_basename (fname);
 3555       GtkWidget * msg = gtk_message_dialog_new (GTK_WINDOW (view),
 3556                         GTK_DIALOG_DESTROY_WITH_PARENT,
 3557                         GTK_MESSAGE_WARNING,
 3558                         GTK_BUTTONS_CLOSE,
 3559                         "File `%s' is not a valid GfsView file\n"
 3560                         "%s:%d:%d: %s\n",
 3561                         basename, basename,
 3562                         fp->line, fp->pos, fp->error);
 3563       g_free (basename);
 3564       gtk_dialog_run (GTK_DIALOG (msg));
 3565       gtk_widget_destroy (msg);
 3566       gts_file_destroy (fp);
 3567       pclose (fptr);
 3568       return sim;
 3569     }
 3570     if (fp->type == GTS_INT || !strcmp (fp->token->str, "GModule")) {
 3571       GfsSimulation * sim1 = gl_simulation_read (fp, fname, GTK_WINDOW (view));
 3572       if (sim1) {
 3573     if (sim && !set)
 3574       gts_object_destroy (GTS_OBJECT (sim));
 3575     sim = sim1;
 3576     if (set)
 3577       gfk_gl_view_set_simulation (view, sim, fname);
 3578       }
 3579     }
 3580   }
 3581   gts_file_destroy (fp);
 3582   pclose (fptr);
 3583   return sim;
 3584 }
 3585 
 3586 static gint filew_ok (GtkWidget * widget, GtkWidget * filew)
 3587 {
 3588   GtkWidget * view = g_object_get_data (G_OBJECT (filew), "view");
 3589   const gchar * fname = gtk_file_selection_get_filename (GTK_FILE_SELECTION (filew));
 3590 
 3591   gtk_widget_hide (filew);
 3592   gfk_gl_simulation_read (fname, view, TRUE);
 3593   return TRUE;
 3594 }
 3595 
 3596 static void substract (FttCell * from, FttCell * cell, gpointer * data)
 3597 {
 3598   GfsDomain * domain = data[0];
 3599   GSList * i = data[1], * j = domain->variables;
 3600 
 3601   while (j) {
 3602     GfsVariable * v = j->data;
 3603     if (v->name) {
 3604       gint index = GPOINTER_TO_INT (i->data);
 3605       if (index >= 0)
 3606     GFS_VALUE (from, v) -= GFS_VALUEI (cell, index);
 3607       i = i->next;
 3608     }
 3609     j = j->next;
 3610   }
 3611 }
 3612 
 3613 static void outside (FttCell * cell, gpointer * data)
 3614 {
 3615   GfsDomain * domain = data[0];
 3616   GSList * i = data[1], * j = domain->variables;
 3617   
 3618   while (j) {
 3619     GfsVariable * v = j->data;
 3620     if (v->name) {
 3621       gint index = GPOINTER_TO_INT (i->data);
 3622       if (index >= 0)
 3623     GFS_VALUE (cell, v) = 0.;
 3624       i = i->next;
 3625     }
 3626     j = j->next;
 3627   }
 3628 }
 3629 
 3630 static gint filews_ok (GtkWidget * widget, GtkWidget * filew)
 3631 {
 3632   GtkWidget * view = g_object_get_data (G_OBJECT (filew), "view");
 3633   const gchar * fname = gtk_file_selection_get_filename (GTK_FILE_SELECTION (filew));
 3634   GfsSimulation * sim;
 3635   gpointer data[2];
 3636 
 3637   gtk_widget_hide (filew);
 3638   if ((sim = gfk_gl_simulation_read (fname, view, FALSE)) != NULL) {
 3639     GtkWidget * glarea = lookup_widget (view, "glarea");
 3640     GfsDomain * domain = g_object_get_data (G_OBJECT (glarea), "sim");
 3641     GSList * i = domain->variables;
 3642     gchar * missing = g_strdup (""), * title, * basename, * s;
 3643     GSList * indices = NULL;
 3644 
 3645     while (i) {
 3646       GfsVariable * v = i->data;
 3647       if (v->name) {
 3648     GfsVariable * v1 = gfs_variable_from_name (GFS_DOMAIN (sim)->variables, v->name);
 3649     
 3650     if (v1 != NULL)
 3651       indices = g_slist_append (indices, GINT_TO_POINTER ((gint) v1->i));
 3652     else {
 3653       gchar * s = missing;
 3654       missing = s[0] == '\0' ? g_strdup (v->name) : g_strjoin (",", missing, v->name, NULL);
 3655       g_free (s);
 3656       indices = g_slist_append (indices, GINT_TO_POINTER (-1));
 3657     }
 3658       }
 3659       i = i->next;
 3660     }
 3661 
 3662     if (missing[0] != '\0') {
 3663       GtkWidget * msg = gtk_message_dialog_new (GTK_WINDOW (view),
 3664                         GTK_DIALOG_DESTROY_WITH_PARENT,
 3665                         GTK_MESSAGE_WARNING,
 3666                         GTK_BUTTONS_CLOSE,
 3667                         "The following variables are absent\n"
 3668                         "from the simulation file:\n%s",
 3669                         missing);
 3670       gtk_dialog_run (GTK_DIALOG (msg));
 3671       gtk_widget_destroy (msg);
 3672     }
 3673     g_free (missing);
 3674 
 3675     gfs_domain_cell_traverse (GFS_DOMAIN (sim),
 3676                   FTT_POST_ORDER, FTT_TRAVERSE_NON_LEAFS, -1,
 3677                   (FttCellTraverseFunc) gfs_cell_coarse_init, sim);
 3678     data[0] = domain;
 3679     data[1] = indices;
 3680     gfs_domain_combine_traverse (domain, GFS_DOMAIN (sim),
 3681                  (FttCellCombineTraverseFunc) substract, data,
 3682                  (FttCellTraverseFunc) outside, data);
 3683     gfs_domain_cell_traverse (domain,
 3684                   FTT_POST_ORDER, FTT_TRAVERSE_NON_LEAFS, -1,
 3685                   (FttCellTraverseFunc) gfs_cell_coarse_init, domain);
 3686     i = domain->variables;
 3687     while (i) {
 3688       gfs_domain_bc (domain, FTT_TRAVERSE_ALL, -1, i->data);
 3689       i = i->next;
 3690     }
 3691 
 3692     g_slist_free (indices);
 3693     gts_object_destroy (GTS_OBJECT (sim));
 3694 
 3695     basename = g_path_get_basename (fname);
 3696     title = g_strjoin (" - ", gtk_window_get_title (GTK_WINDOW (view)), basename, NULL);
 3697     if ((s = strstr (title, "GfsView: ")))
 3698       s += strlen ("GfsView: ");
 3699     else
 3700       s = title;
 3701     gfk_gl_view_set_simulation (view, GFS_SIMULATION (domain), s);
 3702     g_free (title);
 3703     g_free (basename);
 3704   }
 3705   return TRUE;
 3706 }
 3707 
 3708 G_LOCK_DEFINE (scripting_pending);
 3709 
 3710 gboolean gfk_receive_scripting_message (gpointer data)
 3711 {
 3712   GfkScriptingMessage * msg = data;
 3713   switch (msg->event) {
 3714   case GFS_SAVE_EVENT: case GFS_APPEND_EVENT: {
 3715     GfsGl2PSParams * p = msg->data;
 3716     gfs_gl2ps (p, p->fp, "", msg->view);
 3717     if (p->fp == stdout || p->fp == stderr || (msg->event == GFS_APPEND_EVENT))
 3718       fflush (p->fp);
 3719     else
 3720       fclose (p->fp);
 3721     break;
 3722   }
 3723   case GFS_ECHO_EVENT:
 3724     puts (msg->data);
 3725     fflush (stdout);
 3726     break;
 3727   }
 3728 
 3729   g_free (msg->data);
 3730   g_free (msg);
 3731   G_UNLOCK (scripting_pending);
 3732   return FALSE;
 3733 }
 3734 
 3735 static void on_gl_list_destroy (GtkTreeView * tree)
 3736 {
 3737   GtkTreeModel * model = gtk_tree_view_get_model (tree);
 3738   GtkTreeIter iter;
 3739   gboolean valid;
 3740   
 3741   valid = gtk_tree_model_get_iter_first (model, &iter);
 3742   while (valid) {
 3743     GfkGl * gl;
 3744 
 3745     gtk_tree_model_get (model, &iter, GL_COLUMN, &gl, -1);
 3746     gts_object_destroy (GTS_OBJECT (gl));
 3747     valid = gtk_tree_model_iter_next (model, &iter);
 3748   }
 3749 }
 3750 
 3751 void gfk_gl_view_set_scripting (GtkWidget * view, gboolean active)
 3752 {
 3753   g_return_if_fail (view != NULL);
 3754 
 3755   GtkWidget * pref = lookup_widget (view, "preferences");
 3756   if (active) {
 3757     gtk_widget_set_sensitive (lookup_widget (pref, "scripting_label"), TRUE);
 3758     gtk_widget_set_sensitive (lookup_widget (pref, "scripting_on"), TRUE);
 3759     gtk_widget_set_sensitive (lookup_widget (pref, "scripting_off"), TRUE);
 3760     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (pref, "scripting_on")), TRUE);
 3761   }
 3762   else {
 3763     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (pref, "scripting_off")), TRUE);
 3764     gtk_widget_set_sensitive (lookup_widget (pref, "scripting_label"), FALSE);
 3765     gtk_widget_set_sensitive (lookup_widget (pref, "scripting_on"), FALSE);
 3766     gtk_widget_set_sensitive (lookup_widget (pref, "scripting_off"), FALSE);
 3767   }
 3768   G_LOCK (gfk_gl_scripting);
 3769   gfk_gl_scripting = active;
 3770   G_UNLOCK (gfk_gl_scripting);
 3771 }
 3772 
 3773 GtkWidget * gfk_gl_view (GtkWidget * glarea)
 3774 {
 3775   GtkWidget * view, * tree, * pref, * colorsel, * colorb, * gl2ps, * about, * filew, * size;
 3776   GtkListStore * store;
 3777   GtkTreeViewColumn * column;
 3778   GtkToolbar * toolbar;
 3779   GtkTreeSelection * select;
 3780   GtkCellRenderer * renderer;
 3781   GfsGlViewParams * p;
 3782   GfsGl2PSParams * q;
 3783   gdouble * ratio;
 3784   GdkColor c;
 3785 
 3786   GtkMenu * vector, * scalar, * mesh, * special;
 3787 
 3788   g_return_val_if_fail (glarea != NULL, NULL);
 3789 
 3790   p = gfs_gl_view_params_new ();
 3791   g_object_set_data_full (G_OBJECT (glarea), "GfsGlViewParams", p, g_free);
 3792 
 3793   view = create_view ();
 3794   gtk_container_add (GTK_CONTAINER (lookup_widget (view, "frame1")), glarea);
 3795   g_object_set_data (G_OBJECT (view), "glarea", glarea);
 3796 
 3797   tree = lookup_widget (view, "gl_list");
 3798   g_signal_connect (G_OBJECT (tree), "destroy", (GCallback) on_gl_list_destroy, NULL);
 3799   store = gtk_list_store_new (N_COLUMNS,
 3800                   G_TYPE_BOOLEAN,
 3801                   GDK_TYPE_PIXBUF,
 3802                   G_TYPE_STRING,
 3803                   G_TYPE_POINTER,
 3804                   G_TYPE_BOOLEAN);
 3805   g_object_set_data (G_OBJECT (glarea), "list", store);
 3806   gtk_tree_view_set_model (GTK_TREE_VIEW (tree), GTK_TREE_MODEL (store));
 3807   select = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
 3808   gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE);
 3809   g_signal_connect (tree, "row-activated", (GCallback) on_properties1_activate, NULL);
 3810   gtk_tree_selection_set_select_function (select, tree_selection_func, NULL, NULL);
 3811   g_signal_connect (G_OBJECT (select), "changed", G_CALLBACK (tree_selection_changed_cb), tree);
 3812 
 3813   renderer = gtk_cell_renderer_pixbuf_new ();
 3814   column = gtk_tree_view_column_new_with_attributes ("Icon", renderer,
 3815                              "pixbuf", ICON_COLUMN, 
 3816                              NULL);
 3817   gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
 3818 
 3819   renderer = gtk_cell_renderer_toggle_new ();
 3820   g_signal_connect (G_OBJECT (renderer), "toggled", G_CALLBACK (visible_toggled), store);
 3821   column = gtk_tree_view_column_new_with_attributes ("Visible", renderer,
 3822                              "active", VISIBLE_COLUMN,
 3823                              NULL);
 3824   gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
 3825 
 3826   renderer = gtk_cell_renderer_text_new ();
 3827   column = gtk_tree_view_column_new_with_attributes ("Properties", renderer,
 3828                              "text", PROPERTIES_COLUMN, 
 3829                              NULL);
 3830   gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
 3831 
 3832   vector = GTK_MENU (gtk_menu_new ());
 3833   gtk_menu_item_set_submenu (GTK_MENU_ITEM (lookup_widget (view, "vector1")), 
 3834                  GTK_WIDGET (vector));
 3835   scalar = GTK_MENU (gtk_menu_new ());
 3836   gtk_menu_item_set_submenu (GTK_MENU_ITEM (lookup_widget (view, "scalar1")), 
 3837                  GTK_WIDGET (scalar));
 3838   mesh = GTK_MENU (gtk_menu_new ());
 3839   gtk_menu_item_set_submenu (GTK_MENU_ITEM (lookup_widget (view, "mesh1")), 
 3840                  GTK_WIDGET (mesh));
 3841   special = GTK_MENU (gtk_menu_new ());
 3842   gtk_menu_item_set_submenu (GTK_MENU_ITEM (lookup_widget (view, "special1")), 
 3843                  GTK_WIDGET (special));
 3844 
 3845   toolbar = GTK_TOOLBAR (lookup_widget (view, "toolbar"));
 3846   gfk_gl_tool_append (gfk_gl_vectors_class (), toolbar, vector, tree, glarea);
 3847   gfk_gl_tool_append (gfk_gl_streamlines_class (), toolbar, vector, tree, glarea);
 3848   gfk_gl_tool_append (gfk_gl_squares_class (), toolbar, scalar, tree, glarea);
 3849   gfk_gl_tool_append (gfk_gl_linear_class (), toolbar, scalar, tree, glarea);
 3850   gfk_gl_tool_append (gfk_gl_isoline_class (), toolbar, scalar, tree, glarea);
 3851   gfk_gl_tool_append (gfk_gl_cells_class (), toolbar, mesh, tree, glarea);
 3852   gfk_gl_tool_append (gfk_gl_solid_class (), toolbar, mesh, tree, glarea);
 3853   gfk_gl_tool_append (gfk_gl_vof_class (), toolbar, scalar, tree, glarea);
 3854 
 3855   gfk_gl_menu_append (gfk_gl_boundaries_class (), mesh, tree, glarea);
 3856   gfk_gl_menu_append (gfk_gl_levels_class (), mesh, tree, glarea);
 3857   gfk_gl_menu_append (gfk_gl_fractions_class (), mesh, tree, glarea);
 3858   gfk_gl_menu_append (gfk_gl_symmetry_class (), mesh, tree, glarea);
 3859   gfk_gl_menu_append (gfk_gl_periodic_class (), mesh, tree, glarea);
 3860   gfk_gl_menu_append (gfk_gl_height_class (), scalar, tree, glarea);
 3861   gfk_gl_menu_append (gfk_gl_location_class (), special, tree, glarea);
 3862   gfk_gl_menu_append (gfk_gl_ellipses_class (), special, tree, glarea);
 3863   gfk_gl_menu_append (gfk_gl_info_class (), special, tree, glarea);
 3864   gfk_gl_menu_append (gfk_gl_label_class (), special, tree, glarea);
 3865   gfk_gl_menu_append (gfk_gl_locate_class (), special, tree, glarea);
 3866   gfk_gl_menu_append (gfk_gl_clip_plane_class (), special, tree, glarea);
 3867   gfk_gl_menu_append (gfk_gl_pipes_class (), special, tree, glarea);
 3868 
 3869 #if (!FTT_2D)
 3870   gfk_gl_tool_append (gfk_gl_isosurface_class (), toolbar, scalar, tree, glarea);
 3871   gfk_gl_menu_append (gfk_gl_cut_plane_class (), special, tree, glarea);
 3872 #endif /* 3D */
 3873 
 3874   about = create_about ();
 3875   gtk_window_set_transient_for (GTK_WINDOW (about), GTK_WINDOW (view));
 3876   gtk_window_set_destroy_with_parent (GTK_WINDOW (about), TRUE);
 3877   gtk_window_set_position (GTK_WINDOW (about), GTK_WIN_POS_CENTER_ON_PARENT);
 3878   g_signal_connect (G_OBJECT (about), "delete_event", 
 3879             G_CALLBACK (gtk_widget_hide_on_delete), NULL);
 3880   g_object_set_data (G_OBJECT (view), "about", about);
 3881 
 3882   filew = gtk_file_selection_new ("Select a Gerris simulation");
 3883   gtk_window_set_transient_for (GTK_WINDOW (filew), GTK_WINDOW (view));
 3884   gtk_window_set_destroy_with_parent (GTK_WINDOW (filew), TRUE);
 3885   gtk_window_set_position (GTK_WINDOW (filew), GTK_WIN_POS_CENTER_ON_PARENT);
 3886   g_signal_connect (G_OBJECT (filew), "delete_event", 
 3887             G_CALLBACK (gtk_widget_hide_on_delete), NULL);
 3888   g_object_set_data (G_OBJECT (filew), "view", view);
 3889   g_signal_connect (G_OBJECT (GTK_FILE_SELECTION (filew)->ok_button), "clicked",
 3890             G_CALLBACK (filew_ok), filew);
 3891   g_signal_connect_swapped (G_OBJECT (GTK_FILE_SELECTION (filew)->cancel_button), "clicked", 
 3892                 G_CALLBACK (gtk_widget_hide), filew);
 3893   g_object_set_data (G_OBJECT (view), "filew", filew);
 3894 
 3895   filew = gtk_file_selection_new ("Select a Gerris simulation");
 3896   gtk_window_set_transient_for (GTK_WINDOW (filew), GTK_WINDOW (view));
 3897   gtk_window_set_destroy_with_parent (GTK_WINDOW (filew), TRUE);
 3898   gtk_window_set_position (GTK_WINDOW (filew), GTK_WIN_POS_CENTER_ON_PARENT);
 3899   g_signal_connect (G_OBJECT (filew), "delete_event", 
 3900             G_CALLBACK (gtk_widget_hide_on_delete), NULL);
 3901   g_object_set_data (G_OBJECT (filew), "view", view);
 3902   g_signal_connect (G_OBJECT (GTK_FILE_SELECTION (filew)->ok_button), "clicked",
 3903             G_CALLBACK (filews_ok), filew);
 3904   g_signal_connect_swapped (G_OBJECT (GTK_FILE_SELECTION (filew)->cancel_button), "clicked", 
 3905                 G_CALLBACK (gtk_widget_hide), filew);
 3906   g_object_set_data (G_OBJECT (view), "filews", filew);
 3907 
 3908   pref = create_preferences ();
 3909   gtk_window_set_transient_for (GTK_WINDOW (pref), GTK_WINDOW (view));
 3910   gtk_window_set_destroy_with_parent (GTK_WINDOW (pref), TRUE);
 3911   gtk_window_set_position (GTK_WINDOW (pref), GTK_WIN_POS_CENTER_ON_PARENT);
 3912   g_signal_connect (G_OBJECT (pref), "delete_event", G_CALLBACK (gtk_widget_hide_on_delete), NULL);
 3913   g_object_set_data (G_OBJECT (view), "preferences", pref);
 3914   g_object_set_data (G_OBJECT (pref), "view", view);
 3915   gfk_gl_view_set_scripting (view, FALSE);
 3916 #if FTT_2D
 3917   gtk_widget_hide (lookup_widget (pref, "sz_label"));
 3918   gtk_widget_hide (lookup_widget (pref, "sz"));
 3919 #endif /* 2D */
 3920 
 3921   colorsel = create_bg_color_selector ();
 3922   gtk_window_set_transient_for (GTK_WINDOW (colorsel), GTK_WINDOW (pref));
 3923   gtk_window_set_destroy_with_parent (GTK_WINDOW (colorsel), TRUE);
 3924   gtk_window_set_position (GTK_WINDOW (colorsel), GTK_WIN_POS_CENTER_ON_PARENT);
 3925   g_signal_connect (G_OBJECT (colorsel), "delete_event", 
 3926             G_CALLBACK (gtk_widget_hide_on_delete), NULL);
 3927   c.red = p->bg.r*65535; c.green = p->bg.g*65535; c.blue = p->bg.b*65535;
 3928   gtk_color_selection_set_current_color (GTK_COLOR_SELECTION (lookup_widget (colorsel,
 3929                                          "colorselection2")), 
 3930                      &c);
 3931 
 3932   colorb = lookup_widget_params (pref, "bgcolor");
 3933   g_object_set_data (G_OBJECT (colorb), "colorsel", colorsel);
 3934   gtk_widget_modify_bg (colorb, GTK_STATE_NORMAL, &c);
 3935   gtk_widget_modify_bg (colorb, GTK_STATE_PRELIGHT, &c);
 3936   gtk_widget_modify_bg (colorb, GTK_STATE_ACTIVE, &c);
 3937   g_object_set_data (G_OBJECT (colorsel), "bgcolor", colorb);
 3938 
 3939   gl2ps = create_gl2ps ();
 3940   gtk_window_set_transient_for (GTK_WINDOW (gl2ps), GTK_WINDOW (view));
 3941   gtk_window_set_destroy_with_parent (GTK_WINDOW (gl2ps), TRUE);
 3942   gtk_window_set_position (GTK_WINDOW (gl2ps), GTK_WIN_POS_CENTER_ON_PARENT);
 3943   g_signal_connect (G_OBJECT (gl2ps), "delete_event",
 3944             G_CALLBACK (gtk_widget_hide_on_delete), NULL);
 3945   g_object_set_data (G_OBJECT (view), "gl2ps", gl2ps);
 3946   g_object_set_data (G_OBJECT (gl2ps), "view", view);
 3947 
 3948   size = lookup_widget (gl2ps, "size");
 3949   gtk_widget_set_sensitive (size, FALSE);
 3950   g_signal_connect (G_OBJECT (glarea), "expose_event", G_CALLBACK (gl2ps_update_ppm_size), size);
 3951   ratio = g_malloc (sizeof (gdouble));
 3952   g_object_set_data_full (G_OBJECT (size), "ratio", ratio, g_free);
 3953   
 3954   q = g_malloc (sizeof (GfsGl2PSParams));
 3955   q->width = q->height = 0;
 3956   q->format = GFSGL_PPM;
 3957   q->lw = 1.;
 3958   gl2ps_ppm_set_sensitive (gl2ps, FALSE, TRUE);
 3959   q->sort = GL2PS_SIMPLE_SORT;
 3960   gtk_option_menu_set_history (GTK_OPTION_MENU (lookup_widget (gl2ps, "sort")), 1);
 3961   q->options = (GL2PS_SIMPLE_LINE_OFFSET |
 3962         GL2PS_SILENT |
 3963         GL2PS_BEST_ROOT |
 3964         GL2PS_OCCLUSION_CULL |
 3965         GL2PS_USE_CURRENT_VIEWPORT |
 3966         GL2PS_TIGHT_BOUNDING_BOX);
 3967   g_object_set_data_full (G_OBJECT (gl2ps), "GfsGl2PSParams", q, g_free);
 3968 
 3969   gtk_widget_set_sensitive (lookup_widget (view, "toolbar"), FALSE);
 3970   gtk_widget_set_sensitive (lookup_widget (view, "objects1"), FALSE);
 3971   gtk_widget_set_sensitive (lookup_widget (view, "save1"), FALSE);
 3972   gtk_widget_set_sensitive (lookup_widget (view, "edit1"), FALSE);
 3973   gtk_widget_set_sensitive (lookup_widget (view, "properties1"), FALSE);
 3974   gtk_widget_set_sensitive (lookup_widget (view, "delete3"), FALSE);
 3975   gtk_widget_set_sensitive (lookup_widget (view, "view1"), FALSE);
 3976   gtk_widget_set_sensitive (lookup_widget (view, "tools1"), FALSE);
 3977   gtk_widget_set_sensitive (lookup_widget (view, "gl_list"), FALSE);
 3978 
 3979   GtkListStore * completion = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
 3980   g_object_set_data_full (G_OBJECT (view), "completion",
 3981               g_object_ref (G_OBJECT (completion)), (GDestroyNotify) g_object_unref);
 3982 
 3983   return view;
 3984 }
 3985 
 3986 void gfk_gl_view_set_simulation (GtkWidget * view, GfsSimulation * sim, const gchar * fname)
 3987 {
 3988   GtkWidget * glarea;
 3989   GfsSimulation * prev;
 3990   gchar * basename, * s;
 3991   static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
 3992   GtkTreeModel * list;
 3993   GtkTreeIter iter;
 3994   gboolean valid;
 3995 
 3996   g_return_if_fail (view != NULL);
 3997   g_return_if_fail (sim != NULL);
 3998   g_return_if_fail (fname != NULL);
 3999 
 4000   g_static_mutex_lock (&mutex);
 4001 
 4002   glarea = lookup_widget (view, "glarea");
 4003   prev = g_object_get_data (G_OBJECT (glarea), "sim");
 4004   g_object_set_data (G_OBJECT (glarea), "sim", sim);
 4005   list = gtk_tree_view_get_model (GTK_TREE_VIEW (lookup_widget (view, "gl_list")));
 4006   valid = gtk_tree_model_get_iter_first (list, &iter);
 4007   while (valid) {
 4008     GfkGl * gl;
 4009     
 4010     gtk_tree_model_get (list, &iter, GL_COLUMN, &gl, -1);
 4011     gfk_gl_set_simulation (gl, sim);
 4012     valid = gtk_tree_model_iter_next (list, &iter);
 4013   }
 4014 
 4015   /* Remove obsolete variables from completion */
 4016   list = GTK_TREE_MODEL (lookup_widget (view, "completion"));
 4017   valid = gtk_tree_model_get_iter_first (list, &iter);
 4018   while (valid) {
 4019     gchar * name;
 4020     gpointer p;
 4021     gtk_tree_model_get (list, &iter, 
 4022             COMPLETION_NAME_COLUMN, &name,
 4023             COMPLETION_POINTER_COLUMN, &p, 
 4024             -1);
 4025     if (p) {
 4026       p = gfs_variable_from_name (GFS_DOMAIN (sim)->variables, name);
 4027       if (p) {
 4028     gtk_list_store_set (GTK_LIST_STORE (list), &iter,
 4029                 COMPLETION_NAME_COLUMN, GFS_VARIABLE (p)->name,
 4030                 COMPLETION_DESCRIPTION_COLUMN, GFS_VARIABLE (p)->description,
 4031                 COMPLETION_POINTER_COLUMN, p,
 4032                 -1);
 4033     gfs_object_simulation_set (p, NULL);
 4034     valid = gtk_tree_model_iter_next (list, &iter);
 4035       }
 4036       else if ((p = gfs_derived_variable_from_name (GFS_DOMAIN (sim)->derived_variables, name))) {
 4037     gtk_list_store_set (GTK_LIST_STORE (list), &iter,
 4038                 COMPLETION_NAME_COLUMN, GFS_DERIVED_VARIABLE (p)->name,
 4039                 COMPLETION_DESCRIPTION_COLUMN, GFS_DERIVED_VARIABLE (p)->description,
 4040                 COMPLETION_POINTER_COLUMN, p,
 4041                 -1);
 4042         gfs_object_simulation_set (p, NULL);
 4043     valid = gtk_tree_model_iter_next (list, &iter);
 4044       }
 4045       else
 4046     valid = gtk_list_store_remove (GTK_LIST_STORE (list), &iter);
 4047     }
 4048     else
 4049       valid = gtk_tree_model_iter_next (list, &iter);
 4050   }
 4051 
 4052   /* Add new variables to completion*/
 4053   GSList * i = GFS_DOMAIN (sim)->variables;
 4054   while (i) {
 4055     GfsVariable * v = i->data;
 4056     if (v->name) {
 4057       if (gfs_object_simulation (v)) {
 4058     gtk_list_store_append (GTK_LIST_STORE (list), &iter);
 4059     gtk_list_store_set (GTK_LIST_STORE (list), &iter, 
 4060                 COMPLETION_NAME_COLUMN, v->name,
 4061                 COMPLETION_DESCRIPTION_COLUMN, v->description,
 4062                 COMPLETION_POINTER_COLUMN, v,
 4063                 -1);
 4064       }
 4065       else
 4066     gfs_object_simulation_set (v, sim);
 4067     }
 4068     i = i->next;
 4069   }
 4070 
 4071   /* Add new derived variables to completion */
 4072   i = GFS_DOMAIN (sim)->derived_variables;
 4073   while (i) {
 4074     GfsDerivedVariable * v = i->data;
 4075     if (v->name) {
 4076       if (gfs_object_simulation (v)) {
 4077     gtk_list_store_append (GTK_LIST_STORE (list), &iter);
 4078     gtk_list_store_set (GTK_LIST_STORE (list), &iter, 
 4079                 COMPLETION_NAME_COLUMN, v->name,
 4080                 COMPLETION_DESCRIPTION_COLUMN, v->description,
 4081                 COMPLETION_POINTER_COLUMN, v,
 4082                 -1);
 4083       }
 4084       else
 4085     gfs_object_simulation_set (v, sim);
 4086     }
 4087     i = i->next;
 4088   }
 4089 
 4090   if (prev == NULL) {
 4091     gtk_widget_set_sensitive (lookup_widget (view, "toolbar"), TRUE);
 4092     gtk_widget_set_sensitive (lookup_widget (view, "objects1"), TRUE);
 4093     gtk_widget_set_sensitive (lookup_widget (view, "tools1"), TRUE);
 4094     gtk_widget_set_sensitive (lookup_widget (view, "gl_list"), TRUE);
 4095   }
 4096   else if (prev != sim)
 4097     gts_object_destroy (GTS_OBJECT (prev));
 4098   
 4099   basename = g_path_get_basename (fname);
 4100   s = g_strdup_printf ("GfsView: %s: t = %g", basename, sim->time.t);
 4101   gtk_window_set_title (GTK_WINDOW (view), s);
 4102   g_free (s);
 4103   g_free (basename);
 4104 
 4105   g_static_mutex_unlock (&mutex);
 4106 }
 4107 
 4108 void gfk_gl_view_draw (GtkWidget * view, guint format)
 4109 {
 4110   GtkWidget * glarea;
 4111   GfsGlViewParams * p;
 4112   GtkTreeModel * list;
 4113   GtkTreeIter iter;
 4114   gboolean valid;
 4115   guint size = 0;
 4116   GTimer * timer;
 4117 
 4118   g_return_if_fail (view != NULL);
 4119 
 4120   timer = g_timer_new ();
 4121   g_timer_start (timer);
 4122 
 4123   glarea = g_object_get_data (G_OBJECT (view), "glarea");
 4124   p = g_object_get_data (G_OBJECT (glarea), "GfsGlViewParams");
 4125 
 4126   if (p->motion) {
 4127     if (p->timing > p->reactivity) {
 4128       if (p->res == 0.) p->res = 1.;
 4129       p->res *= exp (ceil (log (p->timing/p->reactivity)/log(4.))*log (2.));
 4130       if (p->res > 50.)
 4131     p->res = 50.;
 4132     }
 4133   }
 4134   else
 4135     p->res = p->base_res;
 4136 
 4137   list = gtk_tree_view_get_model (GTK_TREE_VIEW (lookup_widget (view, "gl_list")));
 4138   GList * symmetries = get_symmetries (list);
 4139   GfsFrustum frustum;
 4140   gfs_gl_get_frustum (p, symmetries, &frustum);
 4141   GLuint display_list = glGenLists (1);
 4142   GList * gl_list = NULL;
 4143   valid = gtk_tree_model_get_iter_first (list, &iter);
 4144   while (valid) {
 4145     gboolean visible;
 4146     GfkGl * gl;
 4147 
 4148     gtk_tree_model_get (list, &iter, GL_COLUMN, &gl, VISIBLE_COLUMN, &visible, -1);
 4149     if (GFK_IS_GL_CLIP_PLANE (gl)) {
 4150       gfs_gl_clip_plane_disable (GFS_GL_CLIP_PLANE (gl->gl));
 4151       GFS_GL_CLIP_PLANE (gl->gl)->disabled = !visible;
 4152     }
 4153     else if (visible || !GFS_IS_GL_CUT_PLANE (gl->gl))
 4154       gl_list = g_list_append (gl_list, gl->gl);
 4155     valid = gtk_tree_model_iter_next (list, &iter);
 4156   }
 4157 
 4158   glNewList (display_list, GL_COMPILE);
 4159   GList * i = gl_list;
 4160   while (i) {
 4161     GfsGl * gl = i->data;
 4162     if (GFS_IS_GL_CUT_PLANE (gl)) {
 4163       GFS_GL_CUT_PLANE (gl)->list = gl_list;
 4164       gl->format = format;
 4165       gfs_gl_draw (gl, &frustum);
 4166       if (gl->size > size)
 4167     size = gl->size;
 4168       GFS_GL_CUT_PLANE (gl)->list = NULL;
 4169     }
 4170     i = i->next;
 4171   }
 4172   g_list_free (gl_list);
 4173 
 4174   GSList * clip = NULL;
 4175   gboolean firstclip = TRUE;
 4176   valid = gtk_tree_model_get_iter_first (list, &iter);
 4177   while (valid) {
 4178     gboolean visible;
 4179     GfkGl * gl;
 4180 
 4181     gtk_tree_model_get (list, &iter, GL_COLUMN, &gl, VISIBLE_COLUMN, &visible, -1);
 4182     if (visible) {
 4183       gl->gl->format = format;
 4184       if (GFK_IS_GL_CLIP_PLANE (gl)) {
 4185     if (firstclip) {
 4186       g_slist_foreach (clip, (GFunc) gfs_gl_clip_plane_disable, NULL);
 4187       g_slist_free (clip); clip = NULL;
 4188       firstclip = FALSE;      
 4189     }
 4190     gfs_gl_draw (gl->gl, &frustum);
 4191     clip = g_slist_prepend (clip, gl->gl);
 4192       }
 4193       else {
 4194     gfs_gl_draw (gl->gl, &frustum);
 4195     if (gl->gl->size > size)
 4196       size = gl->gl->size;
 4197     firstclip = TRUE;
 4198       }
 4199     }
 4200     else if (!GFK_IS_GL_CLIP_PLANE (gl))
 4201       firstclip = TRUE;
 4202     valid = gtk_tree_model_iter_next (list, &iter);
 4203   }
 4204   g_slist_free (clip);
 4205   glEndList();
 4206 
 4207   gfs_gl_symmetry_apply (symmetries, display_list);
 4208   gfs_gl_frustum_free (&frustum);
 4209   g_list_free (symmetries);
 4210   glDeleteLists (display_list, 1);  
 4211 
 4212   g_timer_stop (timer);
 4213   p->timing = g_timer_elapsed (timer, NULL);
 4214   g_timer_destroy (timer);
 4215 
 4216   if (size > 0) {
 4217     GtkStatusbar * status = GTK_STATUSBAR (lookup_widget (view, "statusbar1"));
 4218     guint id = gtk_statusbar_get_context_id (status, "glarea");
 4219     gchar * s = g_strdup_printf (" %d items, %g s", size, p->timing);
 4220 
 4221     gtk_statusbar_pop (status, id);
 4222     gtk_statusbar_push (status, id, s);
 4223     g_free (s);
 4224   }
 4225 }
 4226 
 4227 void gfk_gl_view_pick (GtkWidget * view, GfsGlRay * ray, gboolean motion)
 4228 {
 4229   GtkWidget * tree;
 4230   GtkTreeModel * list;
 4231   GtkTreeSelection * select;
 4232   GtkTreeIter iter, picked;
 4233   gboolean valid;
 4234   GfkGl * glmin = NULL;
 4235   gdouble zmin = G_MAXDOUBLE/2.;
 4236 
 4237   GtkStatusbar * status;
 4238   guint id;
 4239 
 4240   g_return_if_fail (view != NULL);
 4241   g_return_if_fail (ray != NULL);
 4242 
 4243   tree = lookup_widget (view, "gl_list");
 4244   list = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
 4245   select = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
 4246   valid = gtk_tree_model_get_iter_first (list, &iter);
 4247   while (valid) {
 4248     gboolean visible;
 4249     GfkGl * gl;
 4250 
 4251     gtk_tree_model_get (list, &iter, GL_COLUMN, &gl, VISIBLE_COLUMN, &visible, -1);
 4252     if (visible && GFS_GL_CLASS (GTS_OBJECT (gl->gl)->klass)->pick) {
 4253       gdouble z = (* GFS_GL_CLASS (GTS_OBJECT (gl->gl)->klass)->pick) (gl->gl, ray);
 4254 
 4255       if (z < zmin || (z == zmin && gtk_tree_selection_iter_is_selected (select, &iter))) {
 4256     glmin = gl;
 4257     zmin = z;
 4258     picked = iter;
 4259       }
 4260     }
 4261     valid = gtk_tree_model_iter_next (list, &iter);
 4262   }
 4263 
 4264   status = GTK_STATUSBAR (lookup_widget (view, "statusbar1"));
 4265   id = gtk_statusbar_get_context_id (status, "pick");
 4266   gtk_statusbar_pop (status, id);
 4267   if (glmin) {
 4268     gtk_tree_selection_select_iter (select, &picked);
 4269     g_assert (GFK_GL_CLASS (GTS_OBJECT (glmin)->klass)->pickinfo);
 4270     gtk_statusbar_push (status, id, 
 4271             (* GFK_GL_CLASS (GTS_OBJECT (glmin)->klass)->pickinfo) (glmin, motion));
 4272   }
 4273 }
 4274 
 4275 void gfk_gl_view_clear (GtkWidget * view)
 4276 {
 4277   GtkWidget * list, * glarea;
 4278   GtkTreeModel * model;
 4279 
 4280   g_return_if_fail (view != NULL);
 4281 
 4282   list = lookup_widget (view, "gl_list");
 4283   glarea = lookup_widget (view, "glarea");
 4284   model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
 4285 
 4286   on_gl_list_destroy (GTK_TREE_VIEW (list));
 4287   gtk_list_store_clear (GTK_LIST_STORE (model));
 4288   g_object_set_data (G_OBJECT (list), "former", NULL);
 4289 
 4290   gtk_widget_set_sensitive (lookup_widget (glarea, "save1"), FALSE);
 4291   gtk_widget_set_sensitive (lookup_widget (glarea, "edit1"), FALSE);
 4292   gtk_widget_set_sensitive (lookup_widget (glarea, "properties1"), FALSE);
 4293   gtk_widget_set_sensitive (lookup_widget (glarea, "delete3"), FALSE);
 4294   gtk_widget_set_sensitive (lookup_widget (glarea, "view1"), FALSE);
 4295 
 4296   gdk_window_invalidate_rect (glarea->window, &glarea->allocation, FALSE);
 4297 }