New directory tree, with preprocessor/ inside interpretor/.
[Faustine.git] / interpretor / preprocessor / faust-0.9.47mr3 / architecture / gui / faustgtk.h
diff --git a/interpretor/preprocessor/faust-0.9.47mr3/architecture/gui/faustgtk.h b/interpretor/preprocessor/faust-0.9.47mr3/architecture/gui/faustgtk.h
new file mode 100644 (file)
index 0000000..68e6055
--- /dev/null
@@ -0,0 +1,1550 @@
+#ifndef FAUST_GTKUI_H
+#define FAUST_GTKUI_H
+
+#include "GUI.h"
+
+/******************************************************************************
+*******************************************************************************
+
+                                GRAPHIC USER INTERFACE
+                                  gtk interface
+
+*******************************************************************************
+*******************************************************************************/
+#include <string>
+#include <set>
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <assert.h>
+
+using namespace std;
+
+#define max(x,y) (((x)>(y)) ? (x) : (y))
+#define min(x,y) (((x)<(y)) ? (x) : (y))
+
+#define stackSize 256
+
+// Insertion modes
+
+#define kSingleMode 0
+#define kBoxMode 1
+#define kTabMode 2
+
+//------------ calculate needed precision
+static int precision(double n)
+{
+       if (n < 0.009999) return 3;
+       else if (n < 0.099999) return 2;
+       else if (n < 0.999999) return 1;
+       else return 0;
+}
+
+namespace gtk_knob
+{
+
+class GtkKnob
+{
+private:
+       double start_x, start_y, max_value;
+public:
+       GtkRange parent;
+       int last_quadrant;
+       GtkKnob();
+       ~GtkKnob();
+       GtkWidget *gtk_knob_new_with_adjustment(GtkAdjustment *_adjustment);
+       
+};
+
+#define GTK_TYPE_KNOB          (gtk_knob_get_type())
+#define GTK_KNOB(obj)          (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_KNOB, GtkKnob))
+#define GTK_IS_KNOB(obj)       (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_KNOB))
+#define GTK_KNOB_CLASS(klass)  (G_TYPE_CHECK_CLASS_CAST ((klass),  GTK_TYPE_KNOB, GtkKnobClass))
+#define GTK_IS_KNOB_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((klass),  GTK_TYPE_KNOB))
+
+GtkKnob::GtkKnob()
+// GtkKnob constructor
+{
+
+}
+
+GtkKnob::~GtkKnob()
+{
+       // Nothing specific to do...
+}
+
+struct GtkKnobClass {
+       GtkRangeClass parent_class;
+       int knob_x;
+       int knob_y;
+       int knob_step;
+       int button_is;
+
+};
+
+//------forward declaration
+GType gtk_knob_get_type ();
+
+/****************************************************************
+ ** calculate the knop pointer with dead zone
+ */
+
+const double scale_zero = 20 * (M_PI/180); // defines "dead zone" for knobs
+
+static void knob_expose(GtkWidget *widget, int knob_x, int knob_y, GdkEventExpose *event, int arc_offset)
+{
+       /** check resize **/
+       int grow;
+       if(widget->allocation.width > widget->allocation.height) {
+               grow = widget->allocation.height;
+       } else {
+               grow =  widget->allocation.width;
+       }
+       knob_x = grow-4;
+       knob_y = grow-4;
+       /** get values for the knob **/
+       GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
+       int knobx = (widget->allocation.x+2 + (widget->allocation.width-4 - knob_x) * 0.5);
+       int knoby = (widget->allocation.y+2 + (widget->allocation.height-4 - knob_y) * 0.5);
+       int knobx1 = (widget->allocation.x+2 + (widget->allocation.width-4)* 0.5);
+       int knoby1 = (widget->allocation.y+2 + (widget->allocation.height-4) * 0.5);
+       double knobstate = (adj->value - adj->lower) / (adj->upper - adj->lower);
+       double angle = scale_zero + knobstate * 2 * (M_PI - scale_zero);
+       double knobstate1 = (0. - adj->lower) / (adj->upper - adj->lower);
+       double pointer_off = knob_x/6;
+       double radius = min(knob_x-pointer_off, knob_y-pointer_off) / 2;
+       double lengh_x = (knobx+radius+pointer_off/2) - radius * sin(angle);
+       double lengh_y = (knoby+radius+pointer_off/2) + radius * cos(angle);
+       double radius1 = min(knob_x, knob_y) / 2 ;
+
+       /** get widget forground color convert to cairo **/
+       GtkStyle *style = gtk_widget_get_style (widget);
+       double r = min(0.6,style->fg[gtk_widget_get_state(widget)].red/65535.0),
+                  g = min(0.6,style->fg[gtk_widget_get_state(widget)].green/65535.0),
+                  b = min(0.6,style->fg[gtk_widget_get_state(widget)].blue/65535.0);
+
+       /** paint focus **/
+       if (GTK_WIDGET_HAS_FOCUS(widget)== TRUE) {
+               gtk_paint_focus(widget->style, widget->window, GTK_STATE_NORMAL, NULL, widget, NULL,
+                               knobx-2, knoby-2, knob_x+4, knob_y+4);
+       }
+       /** create clowing knobs with cairo **/
+       cairo_t *cr = gdk_cairo_create(GDK_DRAWABLE(widget->window));
+       GdkRegion *region;
+       region = gdk_region_rectangle (&widget->allocation);
+       gdk_region_intersect (region, event->region);
+       gdk_cairo_region (cr, region);
+       cairo_clip (cr);
+       
+       cairo_arc(cr,knobx1+arc_offset, knoby1+arc_offset, knob_x/2.1, 0, 2 * M_PI );
+       cairo_pattern_t*pat =
+               cairo_pattern_create_radial (knobx1+arc_offset-knob_x/6,knoby1+arc_offset-knob_x/6, 1,knobx1+arc_offset,knoby1+arc_offset,knob_x/2.1 );
+       if(adj->lower<0 && adj->value>0.) {
+               cairo_pattern_add_color_stop_rgb (pat, 0, r+0.4, g+0.4 + knobstate-knobstate1, b+0.4);
+               cairo_pattern_add_color_stop_rgb (pat, 0.7, r+0.15, g+0.15 + (knobstate-knobstate1)*0.5, b+0.15);
+               cairo_pattern_add_color_stop_rgb (pat, 1, r, g, b);
+       } else if(adj->lower<0 && adj->value<=0.) {
+               cairo_pattern_add_color_stop_rgb (pat, 0, r+0.4 +knobstate1- knobstate, g+0.4, b+0.4);
+               cairo_pattern_add_color_stop_rgb (pat, 0.7, r+0.15 +(knobstate1- knobstate)*0.5, g+0.15, b+0.15);
+               cairo_pattern_add_color_stop_rgb (pat, 1, r, g, b);
+       } else {
+               cairo_pattern_add_color_stop_rgb (pat, 0, r+0.4, g+0.4 +knobstate, b+0.4);
+               cairo_pattern_add_color_stop_rgb (pat, 0.7, r+0.15, g+0.15 + knobstate*0.5, b+0.15);
+               cairo_pattern_add_color_stop_rgb (pat, 1, r, g, b);
+       }
+       cairo_set_source (cr, pat);
+       cairo_fill_preserve (cr);
+       gdk_cairo_set_source_color(cr, gtk_widget_get_style (widget)->fg);
+       cairo_set_line_width(cr, 2.0);
+       cairo_stroke(cr);
+
+       /** create a rotating pointer on the kob**/
+       cairo_set_source_rgb(cr,  0.1, 0.1, 0.1);
+       cairo_set_line_width(cr,max(3, min(7, knob_x/15)));
+       cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); 
+       cairo_set_line_join(cr, CAIRO_LINE_JOIN_BEVEL);
+       cairo_move_to(cr, knobx+radius1, knoby+radius1);
+       cairo_line_to(cr,lengh_x,lengh_y);
+       cairo_stroke(cr);
+       cairo_set_source_rgb(cr,  0.9, 0.9, 0.9);
+       cairo_set_line_width(cr,min(5, max(1,knob_x/30)));
+       cairo_move_to(cr, knobx+radius1, knoby+radius1);
+       cairo_line_to(cr,lengh_x,lengh_y);
+       cairo_stroke(cr);
+       cairo_pattern_destroy (pat);
+       gdk_region_destroy (region);
+       cairo_destroy(cr);
+}
+
+/****************************************************************
+ ** general expose events for all "knob" controllers
+ */
+
+//----------- draw the Knob when moved
+static gboolean gtk_knob_expose (GtkWidget *widget, GdkEventExpose *event)
+{
+       g_assert(GTK_IS_KNOB(widget));
+       GtkKnobClass *klass =  GTK_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget));
+       knob_expose(widget, klass->knob_x, klass->knob_y, event, 0);
+       return TRUE;
+}
+
+/****************************************************************
+ ** set initial size for GdkDrawable per type
+ */
+
+static void gtk_knob_size_request (GtkWidget *widget, GtkRequisition *requisition)
+{
+       g_assert(GTK_IS_KNOB(widget));
+       GtkKnobClass *klass =  GTK_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget));
+       requisition->width = klass->knob_x;
+       requisition->height = klass->knob_y;
+}
+
+/****************************************************************
+ ** set value from key bindings
+ */
+
+static void gtk_knob_set_value (GtkWidget *widget, int dir_down)
+{
+       g_assert(GTK_IS_KNOB(widget));
+
+       GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
+
+       int oldstep = (int)(0.5f + (adj->value - adj->lower) / adj->step_increment);
+       int step;
+       int nsteps = (int)(0.5f + (adj->upper - adj->lower) / adj->step_increment);
+       if (dir_down)
+               step = oldstep - 1;
+       else
+               step = oldstep + 1;
+       float value = adj->lower + step * double(adj->upper - adj->lower) / nsteps;
+       gtk_widget_grab_focus(widget);
+       gtk_range_set_value(GTK_RANGE(widget), value);
+}
+
+/****************************************************************
+ ** keyboard bindings
+ */
+
+static gboolean gtk_knob_key_press (GtkWidget *widget, GdkEventKey *event)
+{
+       g_assert(GTK_IS_KNOB(widget));
+
+       GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
+       switch (event->keyval) {
+       case GDK_Home:
+               gtk_range_set_value(GTK_RANGE(widget), adj->lower);
+               return TRUE;
+       case GDK_End:
+               gtk_range_set_value(GTK_RANGE(widget), adj->upper);
+               return TRUE;
+       case GDK_Up:
+               gtk_knob_set_value(widget, 0);
+               return TRUE;
+       case GDK_Right:
+               gtk_knob_set_value(widget, 0);
+               return TRUE;
+       case GDK_Down:
+               gtk_knob_set_value(widget, 1);
+               return TRUE;
+       case GDK_Left:
+               gtk_knob_set_value(widget, 1);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+/****************************************************************
+ ** alternative (radial) knob motion mode (ctrl + mouse pressed)
+ */
+
+static void knob_pointer_event(GtkWidget *widget, gdouble x, gdouble y, int knob_x, int knob_y,
+                               bool drag, int state)
+{
+       static double last_y = 2e20;
+       GtkKnob *knob = GTK_KNOB(widget);
+       GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
+       double radius =  min(knob_x, knob_y) / 2;
+       int  knobx = (widget->allocation.width - knob_x) / 2;
+       int  knoby = (widget->allocation.height - knob_y) / 2;
+       double posx = (knobx + radius) - x; // x axis right -> left
+       double posy = (knoby + radius) - y; // y axis top -> bottom
+       double value;
+       if (!drag) {
+               if (state & GDK_CONTROL_MASK) {
+                       last_y = 2e20;
+                       return;
+               } else {
+                       last_y = posy;
+               }
+       }
+       if (last_y < 1e20) { // in drag started with Control Key
+               const double scaling = 0.005;
+               double scal = (state & GDK_SHIFT_MASK ? scaling*0.1 : scaling);
+               value = (last_y - posy) * scal;
+               last_y = posy;
+               gtk_range_set_value(GTK_RANGE(widget), adj->value - value * (adj->upper - adj->lower));
+               return;
+       }
+
+       double angle = atan2(-posx, posy) + M_PI; // clockwise, zero at 6 o'clock, 0 .. 2*M_PI
+       if (drag) {
+               // block "forbidden zone" and direct moves between quadrant 1 and 4
+               int quadrant = 1 + int(angle/M_PI_2);
+               if (knob->last_quadrant == 1 && (quadrant == 3 || quadrant == 4)) {
+                       angle = scale_zero;
+               } else if (knob->last_quadrant == 4 && (quadrant == 1 || quadrant == 2)) {
+                       angle = 2*M_PI - scale_zero;
+               } else {
+                       if (angle < scale_zero) {
+                               angle = scale_zero;
+                       } else if (angle > 2*M_PI - scale_zero) {
+                               angle = 2*M_PI - scale_zero;
+                       }
+                       knob->last_quadrant = quadrant;
+               }
+       } else {
+               if (angle < scale_zero) {
+                       angle = scale_zero;
+               } else if (angle > 2*M_PI - scale_zero) {
+                       angle = 2*M_PI - scale_zero;
+               }
+               knob->last_quadrant = 0;
+       }
+       angle = (angle - scale_zero) / (2 * (M_PI-scale_zero)); // normalize to 0..1
+       gtk_range_set_value(GTK_RANGE(widget), adj->lower + angle * (adj->upper - adj->lower));
+}
+
+/****************************************************************
+ ** mouse button pressed set value
+ */
+
+static gboolean gtk_knob_button_press (GtkWidget *widget, GdkEventButton *event)
+{
+       g_assert(GTK_IS_KNOB(widget));
+       
+       GtkKnobClass *klass =  GTK_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget));
+       
+
+       switch (event->button) {
+       case 1:  // left button
+               gtk_widget_grab_focus(widget);
+               gtk_widget_grab_default (widget);
+               gtk_grab_add(widget);
+               klass->button_is = 1;
+               knob_pointer_event(widget, event->x, event->y, klass->knob_x, klass->knob_y,
+                                                  false, event->state);
+               break;
+       case 2: //wheel
+               klass->button_is = 2;
+               break;
+       case 3:  // right button 
+               klass->button_is = 3;
+               break;
+       default: // do nothing
+               break;
+       }
+       return TRUE;
+}
+
+/****************************************************************
+ ** mouse button release
+ */
+
+static gboolean gtk_knob_button_release (GtkWidget *widget, GdkEventButton *event)
+{
+       g_assert(GTK_IS_KNOB(widget));
+       GTK_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget))->button_is = 0;
+       if (GTK_WIDGET_HAS_GRAB(widget))
+               gtk_grab_remove(widget);
+       return FALSE;
+}
+
+/****************************************************************
+ ** set the value from mouse movement
+ */
+
+static gboolean gtk_knob_pointer_motion (GtkWidget *widget, GdkEventMotion *event)
+{
+       g_assert(GTK_IS_KNOB(widget));
+       GtkKnobClass *klass =  GTK_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget));
+       
+       gdk_event_request_motions (event);
+       
+       if (GTK_WIDGET_HAS_GRAB(widget)) {
+               knob_pointer_event(widget, event->x, event->y, klass->knob_x, klass->knob_y,
+                                                  true, event->state);
+       }
+       return FALSE;
+}
+
+/****************************************************************
+ ** set value from mouseweel
+ */
+
+static gboolean gtk_knob_scroll (GtkWidget *widget, GdkEventScroll *event)
+{
+       usleep(5000);
+       gtk_knob_set_value(widget, event->direction);
+       return FALSE;
+}
+
+/****************************************************************
+ ** init the GtkKnobClass
+ */
+
+static void gtk_knob_class_init (GtkKnobClass *klass)
+{
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+
+       /** set here the sizes and steps for the used knob **/
+//--------- small knob size and steps
+       
+       klass->knob_x = 30;
+       klass->knob_y = 30;
+       klass->knob_step = 86;
+
+//--------- event button
+       klass->button_is = 0;
+
+//--------- connect the events with funktions
+       widget_class->expose_event = gtk_knob_expose;
+       widget_class->size_request = gtk_knob_size_request;
+       widget_class->button_press_event = gtk_knob_button_press;
+       widget_class->button_release_event = gtk_knob_button_release;
+       widget_class->motion_notify_event = gtk_knob_pointer_motion;
+       widget_class->key_press_event = gtk_knob_key_press;
+       widget_class->scroll_event = gtk_knob_scroll;
+}
+
+/****************************************************************
+ ** init the Knob type/size
+ */
+
+static void gtk_knob_init (GtkKnob *knob)
+{
+       GtkWidget *widget = GTK_WIDGET(knob);
+       GtkKnobClass *klass =  GTK_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget));
+
+       GTK_WIDGET_SET_FLAGS (GTK_WIDGET(knob), GTK_CAN_FOCUS);
+       GTK_WIDGET_SET_FLAGS (GTK_WIDGET(knob), GTK_CAN_DEFAULT);
+
+       widget->requisition.width = klass->knob_x;
+       widget->requisition.height = klass->knob_y;
+
+}
+
+/****************************************************************
+ ** redraw when value changed
+ */
+
+static gboolean gtk_knob_value_changed(gpointer obj)
+{
+       GtkWidget *widget = (GtkWidget *)obj;
+       gtk_widget_queue_draw(widget);
+       return FALSE;
+}
+
+/****************************************************************
+ ** create small knob
+ */
+
+GtkWidget *GtkKnob::gtk_knob_new_with_adjustment(GtkAdjustment *_adjustment)
+{
+       GtkWidget *widget = GTK_WIDGET( g_object_new (GTK_TYPE_KNOB, NULL ));
+       GtkKnob *knob = GTK_KNOB(widget);
+       knob->last_quadrant = 0;
+       if (widget) {
+               gtk_range_set_adjustment(GTK_RANGE(widget), _adjustment);
+               g_signal_connect(GTK_OBJECT(widget), "value-changed",
+                                G_CALLBACK(gtk_knob_value_changed), widget);
+       }
+       return widget;
+}
+
+/****************************************************************
+ ** get the Knob type
+ */
+
+GType gtk_knob_get_type (void)
+{
+       static GType kn_type = 0;
+       if (!kn_type) {
+               static const GTypeInfo kn_info = {
+                       sizeof(GtkKnobClass), NULL,  NULL, (GClassInitFunc)gtk_knob_class_init, NULL, NULL, sizeof (GtkKnob), 0, (GInstanceInitFunc)gtk_knob_init
+               };
+               kn_type = g_type_register_static(GTK_TYPE_RANGE,  "GtkKnob", &kn_info, (GTypeFlags)0);
+       }
+       return kn_type;
+}
+}/* end of gtk_knob namespace */
+
+gtk_knob::GtkKnob myGtkKnob;
+
+/**
+ * rmWhiteSpaces(): Remove the leading and trailing white spaces of a string
+ * (but not those in the middle of the string)
+ */
+static string rmWhiteSpaces(const string& s)
+{
+    size_t i = s.find_first_not_of(" \t");
+    size_t j = s.find_last_not_of(" \t");
+
+    if (i != string::npos & j != string::npos) {
+        return s.substr(i, 1+j-i);
+    } else {
+        return "";
+    }
+}
+
+
+/**
+ * Extracts metdata from a label : 'vol [unit: dB]' -> 'vol' + metadata
+ */
+static void extractMetadata(const string& fulllabel, string& label, map<string, string>& metadata)
+{
+    enum {kLabel, kEscape1, kEscape2, kEscape3, kKey, kValue};
+    int state = kLabel; int deep = 0;
+    string key, value;
+
+    for (unsigned int i=0; i < fulllabel.size(); i++) {
+        char c = fulllabel[i];
+        switch (state) {
+            case kLabel :
+                assert (deep == 0);
+                switch (c) {
+                    case '\\' : state = kEscape1; break;
+                    case '[' : state = kKey; deep++; break;
+                    default : label += c;
+                }
+                break;
+
+            case kEscape1 :
+                label += c;
+                state = kLabel;
+                break;
+
+            case kEscape2 :
+                key += c;
+                state = kKey;
+                break;
+
+            case kEscape3 :
+                value += c;
+                state = kValue;
+                break;
+
+            case kKey :
+                assert (deep > 0);
+                switch (c) {
+                    case '\\' :  state = kEscape2;
+                                break;
+
+                    case '[' :  deep++;
+                                key += c;
+                                break;
+
+                    case ':' :  if (deep == 1) {
+                                    state = kValue;
+                                } else {
+                                    key += c;
+                                }
+                                break;
+                    case ']' :  deep--;
+                                if (deep < 1) {
+                                    metadata[rmWhiteSpaces(key)] = "";
+                                    state = kLabel;
+                                    key="";
+                                    value="";
+                                } else {
+                                    key += c;
+                                }
+                                break;
+                    default :   key += c;
+                }
+                break;
+
+            case kValue :
+                assert (deep > 0);
+                switch (c) {
+                    case '\\' : state = kEscape3;
+                                break;
+
+                    case '[' :  deep++;
+                                value += c;
+                                break;
+
+                    case ']' :  deep--;
+                                if (deep < 1) {
+                                    metadata[rmWhiteSpaces(key)]=rmWhiteSpaces(value);
+                                    state = kLabel;
+                                    key="";
+                                    value="";
+                                } else {
+                                    value += c;
+                                }
+                                break;
+                    default :   value += c;
+                }
+                break;
+
+            default :
+                cerr << "ERROR unrecognized state " << state << endl;
+        }
+    }
+    label = rmWhiteSpaces(label);
+}
+
+
+class GTKUI : public GUI
+{
+ private :
+    static bool                         fInitialized;
+    static map<float*, float>           fGuiSize;       // map widget zone with widget size coef
+    static map<float*, string>          fTooltip;       // map widget zone with tooltip strings
+    static set<float*>                  fKnobSet;       // set of widget zone to be knobs
+       string                                                          gGroupTooltip;
+    
+    bool isKnob(float* zone){return fKnobSet.count(zone) > 0;}
+    
+ protected :
+    GtkWidget*  fWindow;
+    int         fTop;
+    GtkWidget*  fBox[stackSize];
+    int         fMode[stackSize];
+    bool        fStopped;
+
+    GtkWidget* addWidget(const char* label, GtkWidget* w);
+    virtual void pushBox(int mode, GtkWidget* w);
+
+        
+ public :
+    static const gboolean expand = TRUE;
+    static const gboolean fill = TRUE;
+    static const gboolean homogene = FALSE;
+         
+    GTKUI(char * name, int* pargc, char*** pargv);
+
+    // -- Labels and metadata
+
+    virtual void declare (float* zone, const char* key, const char* value);
+    virtual int  checkLabelOptions (GtkWidget* widget, const string& fullLabel, string& simplifiedLabel);
+    virtual void checkForTooltip (float* zone, GtkWidget* widget);
+    
+    // -- layout groups
+    
+    virtual void openFrameBox(const char* label);   
+    virtual void openTabBox(const char* label = "");
+    virtual void openHorizontalBox(const char* label = "");
+    virtual void openVerticalBox(const char* label = "");
+
+    // -- extra widget's layouts
+
+    virtual void openDialogBox(const char* label, float* zone);
+    virtual void openEventBox(const char* label = "");
+    virtual void openHandleBox(const char* label = "");
+    virtual void openExpanderBox(const char* label, float* zone);
+    
+    virtual void closeBox();
+    virtual void adjustStack(int n);
+
+    // -- active widgets
+    
+    virtual void addButton(const char* label, float* zone);
+    virtual void addToggleButton(const char* label, float* zone);
+    virtual void addCheckButton(const char* label, float* zone);
+    virtual void addVerticalSlider(const char* label, float* zone, float init, float min, float max, float step);   
+    virtual void addHorizontalSlider(const char* label, float* zone, float init, float min, float max, float step); 
+    virtual void addKnob(const char* label, float* zone, float init, float min, float max, float step);
+    virtual void addNumEntry(const char* label, float* zone, float init, float min, float max, float step);
+    
+    // -- passive display widgets
+    
+    virtual void addNumDisplay(const char* label, float* zone, int precision);
+    virtual void addTextDisplay(const char* label, float* zone, const char* names[], float min, float max);
+    virtual void addHorizontalBargraph(const char* label, float* zone, float min, float max);
+    virtual void addVerticalBargraph(const char* label, float* zone, float min, float max);
+    
+    virtual void show();
+    virtual void run();
+    
+};
+
+
+
+/******************************************************************************
+*******************************************************************************
+
+                                GRAPHIC USER INTERFACE (v2)
+                                  gtk implementation
+
+*******************************************************************************
+*******************************************************************************/
+
+// global static fields
+
+bool                        GTKUI::fInitialized = false;
+map<float*, float>          GTKUI::fGuiSize;
+map<float*, string>         GTKUI::fTooltip;
+set<float*>                 GTKUI::fKnobSet;       // set of widget zone to be knobs
+
+/**
+* Format tooltip string by replacing some white spaces by 
+* return characters so that line width doesn't exceed n.
+* Limitation : long words exceeding n are not cut 
+*/
+static string formatTooltip(int n, const string& tt)
+{
+       string  ss = tt;        // ss string we are going to format
+       int             lws = 0;        // last white space encountered
+       int     lri = 0;        // last return inserted
+       for (int i=0; i<tt.size(); i++) {
+               if (tt[i] == ' ') lws = i;
+               if (((i-lri) >= n) && (lws > lri)) {
+                       // insert return here
+                       ss[lws] = '\n';
+                       lri = lws;
+               }
+       }
+       cout << ss;
+       return ss;
+}
+
+
+
+static gint delete_event( GtkWidget *widget, GdkEvent *event, gpointer data )
+{
+    return FALSE; 
+}
+
+static void destroy_event( GtkWidget *widget, gpointer data )
+{
+    gtk_main_quit ();
+}
+
+         
+GTKUI::GTKUI(char * name, int* pargc, char*** pargv) 
+{
+    if (!fInitialized) {
+        gtk_init(pargc, pargv);
+        fInitialized = true;
+    }
+    
+    fWindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+    //gtk_container_set_border_width (GTK_CONTAINER (fWindow), 10);
+    gtk_window_set_title (GTK_WINDOW (fWindow), name);
+    gtk_signal_connect (GTK_OBJECT (fWindow), "delete_event", GTK_SIGNAL_FUNC (delete_event), NULL);
+    gtk_signal_connect (GTK_OBJECT (fWindow), "destroy", GTK_SIGNAL_FUNC (destroy_event), NULL);
+
+    fTop = 0;
+    fBox[fTop] = gtk_vbox_new (homogene, 4);
+    fMode[fTop] = kBoxMode;
+    gtk_container_add (GTK_CONTAINER (fWindow), fBox[fTop]);
+    fStopped = false;
+}
+
+// empilement des boites
+
+void GTKUI::pushBox(int mode, GtkWidget* w)
+{
+    ++fTop;
+    assert(fTop < stackSize);
+    fMode[fTop]     = mode;
+    fBox[fTop]      = w;
+}
+
+
+/**
+ * Remove n levels from the stack S before the top level
+ * adjustStack(n): S -> S' with S' = S(0),S(n+1),S(n+2),...
+ */
+void GTKUI::adjustStack(int n)
+{
+    if (n > 0) {
+        assert(fTop >= n);
+
+        fTop -= n; 
+        fMode[fTop] = fMode[fTop+n];
+        fBox[fTop]  = fBox[fTop+n];
+    }
+}
+
+void GTKUI::closeBox()
+{
+    --fTop;
+    assert(fTop >= 0);
+}
+
+
+/**
+ * Analyses the widget zone metadata declarations and takes
+ * appropriate actions 
+ */
+void GTKUI::declare(float* zone, const char* key, const char* value)
+{
+       if (zone == 0) {
+               // special zone 0 means group metadata
+               if (strcmp(key,"tooltip")==0) {
+                       // only group tooltip are currently implemented
+                       gGroupTooltip = formatTooltip(30, value);
+               }
+       } else {
+               if (strcmp(key,"size")==0) {
+                       fGuiSize[zone]=atof(value);
+               }
+               else if (strcmp(key,"tooltip")==0) {
+                       fTooltip[zone] = formatTooltip(30,value) ;
+               }
+               else if (strcmp(key,"style")==0) {
+                       if (strcmp(value,"knob") == 0) {
+                               fKnobSet.insert(zone);
+                       }
+               }
+       }
+}
+        
+        
+
+/**
+ * Analyses a full label and activates the relevant options. returns a simplified
+ * label (without options) and an amount of stack adjustement (in case additional
+ * containers were pushed on the stack). 
+ */
+
+int GTKUI::checkLabelOptions(GtkWidget* widget, const string& fullLabel, string& simplifiedLabel)
+{   
+    map<string, string> metadata;
+    extractMetadata(fullLabel, simplifiedLabel, metadata);
+
+    if (metadata.count("tooltip")) {
+        gtk_tooltips_set_tip (gtk_tooltips_new (), widget, metadata["tooltip"].c_str(), NULL);
+    }
+    if (metadata["option"] == "detachable") {
+        openHandleBox(simplifiedLabel.c_str());
+        return 1;
+    }
+
+       //---------------------
+       if (gGroupTooltip != string()) {
+               gtk_tooltips_set_tip (gtk_tooltips_new (), widget, gGroupTooltip.c_str(), NULL);
+               gGroupTooltip = string();
+       }
+       
+       //----------------------
+       
+    // no adjustement of the stack needed
+    return 0;
+}
+
+/**
+ * Check if a tooltip is associated to a zone and add it to the corresponding widget
+ */
+void GTKUI::checkForTooltip(float* zone, GtkWidget* widget)
+{
+    if (fTooltip.count(zone)) {
+        gtk_tooltips_set_tip (gtk_tooltips_new (), widget, fTooltip[zone].c_str(), NULL);
+    }
+}
+
+
+// les differentes boites
+
+void GTKUI::openFrameBox(const char* label)
+{
+    GtkWidget * box = gtk_frame_new (label);
+    //gtk_container_set_border_width (GTK_CONTAINER (box), 10);
+            
+    pushBox(kSingleMode, addWidget(label, box));
+}
+
+
+void GTKUI::openTabBox(const char* fullLabel)
+{
+    string  label;
+    GtkWidget* widget = gtk_notebook_new();
+
+    int     adjust = checkLabelOptions(widget, fullLabel, label);
+
+    pushBox(kTabMode, addWidget(label.c_str(), widget));
+
+    // adjust stack because otherwise Handlebox will remain open
+    adjustStack(adjust);
+}
+
+
+void GTKUI::openHorizontalBox(const char* fullLabel)
+{   
+    string   label;
+    GtkWidget* box = gtk_hbox_new (homogene, 4);
+    int     adjust = checkLabelOptions(box, fullLabel, label);
+
+    gtk_container_set_border_width (GTK_CONTAINER (box), 10);
+            
+    if (fMode[fTop] != kTabMode && label[0] != 0) {
+        GtkWidget * frame = addWidget(label.c_str(), gtk_frame_new (label.c_str()));
+        gtk_container_add (GTK_CONTAINER(frame), box);
+        gtk_widget_show(box);
+        pushBox(kBoxMode, box);
+    } else {
+        pushBox(kBoxMode, addWidget(label.c_str(), box));
+    }
+
+    // adjust stack because otherwise Handlebox will remain open
+    adjustStack(adjust);
+}
+
+
+void GTKUI::openVerticalBox(const char* fullLabel)
+{
+    string  label;
+    GtkWidget * box = gtk_vbox_new (homogene, 4);
+    int      adjust = checkLabelOptions(box, fullLabel, label);
+
+    gtk_container_set_border_width (GTK_CONTAINER (box), 10);
+            
+    if (fMode[fTop] != kTabMode && label[0] != 0) {
+        GtkWidget * frame = addWidget(label.c_str(), gtk_frame_new (label.c_str()));
+        gtk_container_add (GTK_CONTAINER(frame), box);
+        gtk_widget_show(box);
+        pushBox(kBoxMode, box);
+    } else {
+        pushBox(kBoxMode, addWidget(label.c_str(), box));
+    }
+
+    // adjust stack because otherwise Handlebox will remain open
+    adjustStack(adjust);
+}
+
+
+void GTKUI::openHandleBox(const char* label)
+{
+    GtkWidget * box = gtk_hbox_new (homogene, 4);
+    gtk_container_set_border_width (GTK_CONTAINER (box), 2);
+    if (fMode[fTop] != kTabMode && label[0] != 0)
+    {
+        GtkWidget * frame = addWidget(label, gtk_handle_box_new ());
+        gtk_container_add (GTK_CONTAINER(frame), box);
+        gtk_widget_show(box);
+        pushBox(kBoxMode, box);
+    }
+    else
+    {
+        pushBox(kBoxMode, addWidget(label, box));
+    }
+}
+
+
+void GTKUI::openEventBox(const char* label)
+{
+    GtkWidget * box = gtk_hbox_new (homogene, 4);
+    gtk_container_set_border_width (GTK_CONTAINER (box), 2);
+    if (fMode[fTop] != kTabMode && label[0] != 0)
+    {
+        GtkWidget * frame = addWidget(label, gtk_event_box_new ());
+        gtk_container_add (GTK_CONTAINER(frame), box);
+        gtk_widget_show(box);
+        pushBox(kBoxMode, box);
+    }
+    else
+    {
+        pushBox(kBoxMode, addWidget(label, box));
+    }
+}
+
+
+struct uiExpanderBox : public uiItem
+{
+    GtkExpander* fButton;
+    uiExpanderBox(GUI* ui, float* zone, GtkExpander* b) : uiItem(ui, zone), fButton(b) {}
+    static void expanded (GtkWidget *widget, gpointer data)
+    {
+        float   v = gtk_expander_get_expanded  (GTK_EXPANDER(widget));
+        if (v == 1.000000)
+        {
+            v = 0;
+        }
+        else v = 1;
+        ((uiItem*)data)->modifyZone(v);
+    }
+
+    virtual void reflectZone()
+    {
+        float   v = *fZone;
+        fCache = v;
+        gtk_expander_set_expanded(GTK_EXPANDER(fButton), v);
+    }
+};
+
+void GTKUI::openExpanderBox(const char* label, float* zone)
+{
+    *zone = 0.0;
+    GtkWidget * box = gtk_hbox_new (homogene, 4);
+    gtk_container_set_border_width (GTK_CONTAINER (box), 2);
+    if (fMode[fTop] != kTabMode && label[0] != 0)
+    {
+        GtkWidget * frame = addWidget(label, gtk_expander_new (label));
+        gtk_container_add (GTK_CONTAINER(frame), box);
+        uiExpanderBox* c = new uiExpanderBox(this, zone, GTK_EXPANDER(frame));
+        gtk_signal_connect (GTK_OBJECT (frame), "activate", GTK_SIGNAL_FUNC (uiExpanderBox::expanded), (gpointer)c);
+        gtk_widget_show(box);
+        pushBox(kBoxMode, box);
+    }
+    else
+    {
+        pushBox(kBoxMode, addWidget(label, box));
+    }
+}
+
+
+
+GtkWidget* GTKUI::addWidget(const char* label, GtkWidget* w)
+{ 
+    switch (fMode[fTop]) {
+        case kSingleMode    : gtk_container_add (GTK_CONTAINER(fBox[fTop]), w);                             break;
+        case kBoxMode       : gtk_box_pack_start (GTK_BOX(fBox[fTop]), w, expand, fill, 0);                 break;
+        case kTabMode       : gtk_notebook_append_page (GTK_NOTEBOOK(fBox[fTop]), w, gtk_label_new(label)); break;
+    }
+    gtk_widget_show (w);
+    return w;
+}
+
+// --------------------------- Press button ---------------------------
+
+struct uiButton : public uiItem
+{
+    GtkButton*  fButton;
+    
+    uiButton (GUI* ui, float* zone, GtkButton* b) : uiItem(ui, zone), fButton(b) {}
+    
+    static void pressed( GtkWidget *widget, gpointer   data )
+    {
+        uiItem* c = (uiItem*) data;
+        c->modifyZone(1.0);
+    }
+
+    static void released( GtkWidget *widget, gpointer   data )
+    {
+        uiItem* c = (uiItem*) data;
+        c->modifyZone(0.0);
+    }
+
+    virtual void reflectZone()  
+    { 
+        float   v = *fZone;
+        fCache = v; 
+        if (v > 0.0) gtk_button_pressed(fButton); else gtk_button_released(fButton);
+    }
+};
+
+void GTKUI::addButton(const char* label, float* zone)
+{
+    *zone = 0.0;
+    GtkWidget*  button = gtk_button_new_with_label (label);
+    addWidget(label, button);
+    
+    uiButton* c = new uiButton(this, zone, GTK_BUTTON(button));
+    
+    gtk_signal_connect (GTK_OBJECT (button), "pressed", GTK_SIGNAL_FUNC (uiButton::pressed), (gpointer) c);
+    gtk_signal_connect (GTK_OBJECT (button), "released", GTK_SIGNAL_FUNC (uiButton::released), (gpointer) c);
+
+    checkForTooltip(zone, button);
+}
+
+// ---------------------------  Toggle Buttons ---------------------------
+
+struct uiToggleButton : public uiItem
+{
+    GtkToggleButton* fButton;
+    
+    uiToggleButton(GUI* ui, float* zone, GtkToggleButton* b) : uiItem(ui, zone), fButton(b) {}
+    
+    static void toggled (GtkWidget *widget, gpointer data)
+    {
+        float   v = (GTK_TOGGLE_BUTTON (widget)->active) ? 1.0 : 0.0; 
+        ((uiItem*)data)->modifyZone(v);
+    }
+
+    virtual void reflectZone()  
+    { 
+        float   v = *fZone;
+        fCache = v; 
+        gtk_toggle_button_set_active(fButton, v > 0.0); 
+    }
+};
+
+void GTKUI::addToggleButton(const char* label, float* zone)
+{
+    *zone = 0.0;
+    GtkWidget*  button = gtk_toggle_button_new_with_label (label);
+    addWidget(label, button);
+    
+    uiToggleButton* c = new uiToggleButton(this, zone, GTK_TOGGLE_BUTTON(button));
+    gtk_signal_connect (GTK_OBJECT (button), "toggled", GTK_SIGNAL_FUNC (uiToggleButton::toggled), (gpointer) c);
+
+    checkForTooltip(zone, button);
+}
+
+
+
+void show_dialog(GtkWidget *widget, gpointer data)
+{
+    if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget)) == TRUE)
+    {
+        gtk_widget_show(GTK_WIDGET(data));
+        gint root_x, root_y;
+        gtk_window_get_position (GTK_WINDOW(data), &root_x, &root_y);
+        root_y -= 120;
+        gtk_window_move(GTK_WINDOW(data), root_x, root_y);
+    }
+    else gtk_widget_hide(GTK_WIDGET(data));
+}
+
+static gboolean deleteevent( GtkWidget *widget, gpointer   data )
+{
+return TRUE;
+} 
+
+void GTKUI::openDialogBox(const char* label, float* zone)
+{
+    // create toplevel window and set properties
+    GtkWidget * dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_decorated(GTK_WINDOW(dialog), TRUE);
+    gtk_window_set_deletable(GTK_WINDOW(dialog), FALSE);
+    gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
+    gtk_window_set_gravity(GTK_WINDOW(dialog), GDK_GRAVITY_SOUTH);
+    gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(fWindow));
+    gtk_window_set_position (GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
+    gtk_window_set_keep_below (GTK_WINDOW(dialog), FALSE);
+    gtk_window_set_title (GTK_WINDOW (dialog), label);
+    g_signal_connect (G_OBJECT (dialog), "delete_event", G_CALLBACK (deleteevent), NULL); 
+    gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
+
+    GtkWidget * box = gtk_hbox_new (homogene, 4);
+    *zone = 0.0;
+    GtkWidget*  button = gtk_toggle_button_new ();
+    gtk_signal_connect (GTK_OBJECT (button), "toggled", GTK_SIGNAL_FUNC (show_dialog), (gpointer) dialog);
+    gtk_container_add (GTK_CONTAINER(fBox[fTop]), button);
+    gtk_container_add (GTK_CONTAINER(dialog), box);
+    gtk_widget_show (button);
+    gtk_widget_show(box);
+    pushBox(kBoxMode, box);
+}
+
+
+
+
+// ---------------------------  Check Button ---------------------------
+
+struct uiCheckButton : public uiItem
+{
+    GtkToggleButton* fButton;
+    
+    uiCheckButton(GUI* ui, float* zone, GtkToggleButton* b) : uiItem(ui, zone), fButton(b) {}
+    
+    static void toggled (GtkWidget *widget, gpointer data)
+    {
+        float   v = (GTK_TOGGLE_BUTTON (widget)->active) ? 1.0 : 0.0; 
+        ((uiItem*)data)->modifyZone(v);
+    }
+
+    virtual void reflectZone()  
+    { 
+        float   v = *fZone;
+        fCache = v; 
+        gtk_toggle_button_set_active(fButton, v > 0.0); 
+    }
+};
+
+void GTKUI::addCheckButton(const char* label, float* zone)
+{
+    *zone = 0.0;
+    GtkWidget*  button = gtk_check_button_new_with_label (label);
+    addWidget(label, button);
+    
+    uiCheckButton* c = new uiCheckButton(this, zone, GTK_TOGGLE_BUTTON(button));
+    gtk_signal_connect (GTK_OBJECT (button), "toggled", GTK_SIGNAL_FUNC(uiCheckButton::toggled), (gpointer) c);
+
+    checkForTooltip(zone, button);
+}
+
+
+// ---------------------------  Adjustmenty based widgets ---------------------------
+
+struct uiAdjustment : public uiItem
+{
+    GtkAdjustment* fAdj;
+    
+    uiAdjustment(GUI* ui, float* zone, GtkAdjustment* adj) : uiItem(ui, zone), fAdj(adj) {}
+    
+    static void changed (GtkWidget *widget, gpointer data)
+    {
+        float   v = GTK_ADJUSTMENT (widget)->value; 
+        ((uiItem*)data)->modifyZone(v);
+    }
+
+    virtual void reflectZone()  
+    { 
+        float   v = *fZone;
+        fCache = v; 
+        gtk_adjustment_set_value(fAdj, v);  
+    }
+};
+
+// --------------------------- format knob value display ---------------------------
+
+struct uiValueDisplay : public uiItem
+{
+       GtkLabel* fLabel;
+       int     fPrecision ;
+
+       uiValueDisplay(GUI* ui, float* zone, GtkLabel* label, int precision)
+               : uiItem(ui, zone), fLabel(label), fPrecision(precision) {}
+
+       virtual void reflectZone()
+               {
+                       float v = *fZone;
+                       fCache = v;
+                       char s[64];
+                       if (fPrecision <= 0)
+                               snprintf(s, 63, "%d", int(v));
+
+                       else if (fPrecision > 3)
+                               snprintf(s, 63, "%f", v);
+
+                       else if (fPrecision == 1)
+                       {
+                               const char* format[] = {"%.1f", "%.2f", "%.3f"};
+                               snprintf(s, 63, format[1-1], v);
+                       }
+                       else if (fPrecision == 2)
+                       {
+                               const char* format[] = {"%.1f", "%.2f", "%.3f"};
+                               snprintf(s, 63, format[2-1], v);
+                       }
+                       else
+                       {
+                               const char* format[] = {"%.1f", "%.2f", "%.3f"};
+                               snprintf(s, 63, format[3-1], v);
+                       }
+                       gtk_label_set_text(fLabel, s);
+               }
+};
+
+// ------------------------------- Knob -----------------------------------------
+
+void GTKUI::addKnob(const char* label, float* zone, float init, float min, float max, float step)
+{
+       *zone = init;
+    GtkObject* adj = gtk_adjustment_new(init, min, max, step, 10*step, 0);
+    
+    uiAdjustment* c = new uiAdjustment(this, zone, GTK_ADJUSTMENT(adj));
+
+    gtk_signal_connect (GTK_OBJECT (adj), "value-changed", GTK_SIGNAL_FUNC (uiAdjustment::changed), (gpointer) c);
+    
+       GtkWidget* slider = gtk_vbox_new (FALSE, 0);
+       GtkWidget* fil = gtk_vbox_new (FALSE, 0);
+       GtkWidget* rei = gtk_vbox_new (FALSE, 0);
+       GtkWidget* re =myGtkKnob.gtk_knob_new_with_adjustment(GTK_ADJUSTMENT(adj));
+       GtkWidget* lw = gtk_label_new("");
+       new uiValueDisplay(this, zone, GTK_LABEL(lw),precision(step));
+       gtk_container_add (GTK_CONTAINER(rei), re);
+       if(fGuiSize[zone]) {
+               float size = 30 * fGuiSize[zone];
+               gtk_widget_set_size_request(rei, size, size );
+               gtk_box_pack_start (GTK_BOX(slider), fil, TRUE, TRUE, 0);
+               gtk_box_pack_start (GTK_BOX(slider), rei, FALSE, FALSE, 0);
+       } else {
+               gtk_container_add (GTK_CONTAINER(slider), fil);
+               gtk_container_add (GTK_CONTAINER(slider), rei);
+       }
+       gtk_container_add (GTK_CONTAINER(slider), lw);
+       gtk_widget_show_all(slider);
+       
+       if (label && label[0]!=0) {
+        openFrameBox(label);
+        addWidget(label, slider);
+        closeBox();
+    } else {
+        addWidget(label, slider);
+    }
+
+    checkForTooltip(zone, slider);
+}
+
+// -------------------------- Vertical Slider -----------------------------------
+
+void GTKUI::addVerticalSlider(const char* label, float* zone, float init, float min, float max, float step)
+{
+       if (isKnob(zone)) { 
+               addKnob(label, zone, init, min, max, step);
+               return;
+       } 
+    *zone = init;
+    GtkObject* adj = gtk_adjustment_new(init, min, max, step, 10*step, 0);
+    
+    uiAdjustment* c = new uiAdjustment(this, zone, GTK_ADJUSTMENT(adj));
+
+    gtk_signal_connect (GTK_OBJECT (adj), "value-changed", GTK_SIGNAL_FUNC (uiAdjustment::changed), (gpointer) c);
+    
+       GtkWidget* slider = gtk_vscale_new (GTK_ADJUSTMENT(adj));
+       gtk_scale_set_digits(GTK_SCALE(slider), precision(step));
+       float size = 160;
+       if(fGuiSize[zone]) {
+               size = 160 * fGuiSize[zone];
+       }
+       gtk_widget_set_size_request(slider, -1, size);
+       
+    gtk_range_set_inverted (GTK_RANGE(slider), TRUE);
+    
+    if (label && label[0]!=0) {
+        openFrameBox(label);
+        addWidget(label, slider);
+        closeBox();
+    } else {
+        addWidget(label, slider);
+    }
+
+    checkForTooltip(zone, slider);
+}
+
+// -------------------------- Horizontal Slider -----------------------------------
+
+void GTKUI::addHorizontalSlider(const char* label, float* zone, float init, float min, float max, float step)
+{
+       if (isKnob(zone)) { 
+               addKnob(label, zone, init, min, max, step);
+               return;
+       } 
+    *zone = init;
+    GtkObject* adj = gtk_adjustment_new(init, min, max, step, 10*step, 0);
+    
+    uiAdjustment* c = new uiAdjustment(this, zone, GTK_ADJUSTMENT(adj));
+
+    gtk_signal_connect (GTK_OBJECT (adj), "value-changed", GTK_SIGNAL_FUNC (uiAdjustment::changed), (gpointer) c);
+    
+    GtkWidget* slider = gtk_hscale_new (GTK_ADJUSTMENT(adj));
+       gtk_scale_set_digits(GTK_SCALE(slider), precision(step));
+       float size = 160;
+       if(fGuiSize[zone]) {
+               size = 160 * fGuiSize[zone];
+       }
+       gtk_widget_set_size_request(slider, size, -1);
+    
+    if (label && label[0]!=0) {
+        openFrameBox(label);
+        addWidget(label, slider);
+        closeBox();
+    } else {
+        addWidget(label, slider);
+    }             
+
+    checkForTooltip(zone, slider);
+}
+
+
+// ------------------------------ Num Entry -----------------------------------
+
+void GTKUI::addNumEntry(const char* label, float* zone, float init, float min, float max, float step)
+{
+       if (isKnob(zone)) { 
+               addKnob(label, zone, init, min, max, step);
+               return;
+       } 
+    *zone = init;
+    GtkObject* adj = gtk_adjustment_new(init, min, max, step, 10*step, step);
+    
+    uiAdjustment* c = new uiAdjustment(this, zone, GTK_ADJUSTMENT(adj));
+
+    gtk_signal_connect (GTK_OBJECT (adj), "value-changed", GTK_SIGNAL_FUNC (uiAdjustment::changed), (gpointer) c);
+    
+    GtkWidget* spinner = gtk_spin_button_new (GTK_ADJUSTMENT(adj), 0.005, precision(step));
+
+    openFrameBox(label);
+    addWidget(label, spinner);
+    closeBox();
+
+    checkForTooltip(zone, spinner);
+}
+
+
+// ==========================   passive widgets ===============================
+
+
+// ------------------------------ Progress Bar -----------------------------------
+
+struct uiBargraph : public uiItem
+{
+    GtkProgressBar*     fProgressBar;
+    float               fMin;
+    float               fMax;
+    
+    uiBargraph(GUI* ui, float* zone, GtkProgressBar* pbar, float lo, float hi) 
+            : uiItem(ui, zone), fProgressBar(pbar), fMin(lo), fMax(hi) {}
+
+    float scale(float v)        { return (v-fMin)/(fMax-fMin); }
+    
+    virtual void reflectZone()  
+    { 
+        float   v = *fZone;
+        fCache = v; 
+        gtk_progress_bar_set_fraction(fProgressBar, scale(v));  
+    }
+};
+
+    
+
+void GTKUI::addVerticalBargraph(const char* label, float* zone, float lo, float hi)
+{
+    GtkWidget* pb = gtk_progress_bar_new();
+    gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(pb), GTK_PROGRESS_BOTTOM_TO_TOP);
+    gtk_widget_set_size_request(pb, 8, -1);
+    new uiBargraph(this, zone, GTK_PROGRESS_BAR(pb), lo, hi);
+    openFrameBox(label);
+    addWidget(label, pb);
+    closeBox();
+
+    checkForTooltip(zone, pb);
+}
+    
+
+void GTKUI::addHorizontalBargraph(const char* label, float* zone, float lo, float hi)
+{
+    GtkWidget* pb = gtk_progress_bar_new();
+    gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(pb), GTK_PROGRESS_LEFT_TO_RIGHT);
+    gtk_widget_set_size_request(pb, -1, 8);
+    new uiBargraph(this, zone, GTK_PROGRESS_BAR(pb), lo, hi);
+    openFrameBox(label);
+    addWidget(label, pb);
+    closeBox();
+
+    checkForTooltip(zone, pb);
+}
+
+
+// ------------------------------ Num Display -----------------------------------
+
+struct uiNumDisplay : public uiItem
+{
+    GtkLabel* fLabel;
+    int fPrecision;
+    
+    uiNumDisplay(GUI* ui, float* zone, GtkLabel* label, int precision) 
+            : uiItem(ui, zone), fLabel(label), fPrecision(precision) {}
+
+    virtual void reflectZone()  
+    { 
+        float   v = *fZone;
+        fCache = v;
+        char s[64]; 
+        if (fPrecision <= 0) { 
+            snprintf(s, 63, "%d", int(v)); 
+        } else if (fPrecision>3) {
+            snprintf(s, 63, "%f", v);
+        } else {
+            const char* format[] = {"%.1f", "%.2f", "%.3f"};
+            snprintf(s, 63, format[fPrecision-1], v);
+        }
+        gtk_label_set_text(fLabel, s);
+    }
+};
+    
+
+void GTKUI::addNumDisplay(const char* label, float* zone, int precision )
+{
+    GtkWidget* lw = gtk_label_new("");
+    new uiNumDisplay(this, zone, GTK_LABEL(lw), precision);
+    openFrameBox(label);
+    addWidget(label, lw);
+    closeBox();
+
+    checkForTooltip(zone, lw);
+}
+
+
+// ------------------------------ Text Display -----------------------------------
+
+struct uiTextDisplay : public uiItem
+{
+        GtkLabel*           fLabel;
+        const char**    fNames;
+        float               fMin;
+        float               fMax;
+        int                         fNum;
+        
+        
+        uiTextDisplay (GUI* ui, float* zone, GtkLabel* label, const char* names[], float lo, float hi)
+                        : uiItem(ui, zone), fLabel(label), fNames(names), fMin(lo), fMax(hi)
+        {
+                fNum = 0;
+                while (fNames[fNum] != 0) fNum++;
+    }
+
+    virtual void reflectZone()  
+    { 
+        float   v = *fZone;
+        fCache = v;
+        
+        int idx = int(fNum*(v-fMin)/(fMax-fMin));
+        
+        if      (idx < 0)       idx = 0; 
+        else if (idx >= fNum)   idx = fNum-1;
+                
+        gtk_label_set_text(fLabel, fNames[idx]); 
+    }
+};
+    
+
+void GTKUI::addTextDisplay(const char* label, float* zone, const char* names[], float lo, float hi )
+{
+    GtkWidget* lw = gtk_label_new("");
+    new uiTextDisplay (this, zone, GTK_LABEL(lw), names, lo, hi);
+    openFrameBox(label);
+    addWidget(label, lw);
+    closeBox();
+
+    checkForTooltip(zone, lw);
+}
+
+
+
+void GTKUI::show() 
+{
+    assert(fTop == 0);
+    gtk_widget_show  (fBox[0]);
+    gtk_widget_show  (fWindow);
+}
+
+
+/**
+ * Update all user items reflecting zone z
+ */
+    
+static gboolean callUpdateAllGuis(gpointer)
+{ 
+    GUI::updateAllGuis(); 
+    return TRUE;
+}
+
+
+void GTKUI::run() 
+{
+    assert(fTop == 0);
+    gtk_widget_show  (fBox[0]);
+    gtk_widget_show  (fWindow);
+    gtk_timeout_add(40, callUpdateAllGuis, 0);
+    gtk_main ();
+    stop();
+}
+
+
+
+#endif
+