text: /****************************************************************************
    
    GLUI User Interface Toolkit
    ---------------------------
  
       glui_control.cpp - top-level GLUI_Control class
  
  
            --------------------------------------------------
  
    Copyright (c) 1998 Paul Rademacher
  
    WWW:    http://sourceforge.net/projects/glui/
    Forums: http://sourceforge.net/forum/?group_id=92496
  
    This software is provided 'as-is', without any express or implied 
    warranty. In no event will the authors be held liable for any damages 
    arising from the use of this software. 
  
    Permission is granted to anyone to use this software for any purpose, 
    including commercial applications, and to alter it and redistribute it 
    freely, subject to the following restrictions: 
  
    1. The origin of this software must not be misrepresented; you must not 
    claim that you wrote the original software. If you use this software 
    in a product, an acknowledgment in the product documentation would be 
    appreciated but is not required. 
    2. Altered source versions must be plainly marked as such, and must not be 
    misrepresented as being the original software. 
    3. This notice may not be removed or altered from any source distribution. 
  
  *****************************************************************************/
  
  #include "glui_internal_control.h"
  
  int _glui_draw_border_only = 0;
  
  /*************************** Drawing Utility routines *********************/
  
  /* Redraw this control. */
  void	      GLUI_Control::redraw(void) {
      if (glui==NULL || hidden) return;
      if (glui->should_redraw_now(this))
        translate_and_draw_front();
  }
  
  /** Redraw everybody in our window. */
  void	     GLUI_Control::redraw_window(void) {
    if (glui==NULL || hidden) return;
    if ( glui->get_glut_window_id() == -1 ) return;
    int orig = set_to_glut_window();
    glutPostRedisplay();
    restore_window(orig);
  }
  
  
  
  /* GLUI_Control::translate_and_draw_front() ********/
  
  void GLUI_Control::translate_and_draw_front()
  {
    GLUI_DRAWINGSENTINAL_IDIOM
  
    glMatrixMode( GL_MODELVIEW );
    glPushMatrix();
    translate_to_origin();
    draw(0,0);
    glPopMatrix();
  }
  
  
  /********** GLUI_Control::set_to_bkgd_color() ********/
  
  void GLUI_Control::set_to_bkgd_color( void )
  {
    if ( NOT glui )
      return;
  
    glColor3ubv( glui->bkgd_color );
  }
  
  /******** GLUI_Control::draw_box_inwards_outline() ********/
  
  void GLUI_Control::draw_box_inwards_outline( int x_min, int x_max, int y_min, int y_max )
  {
    glBegin( GL_LINES );
    glColor3f( .5, .5, .5 );
    glVertex2i( x_min, y_min );     glVertex2i( x_max, y_min );
    glVertex2i( x_min, y_min );     glVertex2i( x_min, y_max );     
  
    glColor3f( 1., 1., 1. );
    glVertex2i( x_min, y_max );     glVertex2i( x_max, y_max );
    glVertex2i( x_max, y_max );     glVertex2i( x_max, y_min );
  
    if ( enabled )
      glColor3f( 0., 0., 0. );
    else
      glColor3f( .25, .25, .25 );
  
    glVertex2i( x_min+1, y_min+1 );     glVertex2i( x_max-1, y_min+1 );
    glVertex2i( x_min+1, y_min+1 );     glVertex2i( x_min+1, y_max-1 );
  
    glColor3f( .75, .75, .75 );
    glVertex2i( x_min+1, y_max-1 );     glVertex2i( x_max-1, y_max-1 );
    glVertex2i( x_max-1, y_max-1 );     glVertex2i( x_max-1, y_min+1 );
    glEnd();  
  }
  
  
  /******* GLUI_Control::draw_box() **********/
  
  void GLUI_Control::draw_box( int x_min, int x_max, int y_min, int y_max, float r, float g, float b)
  {
    if ( r == 1.0 AND g == 1.0 AND b == 1.0 AND NOT enabled AND glui ) {
      draw_bkgd_box( x_min, x_max, y_min, y_max );
      return;
    }
  
    glColor3f( r, g, b );
    glBegin( GL_QUADS );
    glVertex2i( x_min, y_min );       glVertex2i( x_max, y_min );
    glVertex2i( x_max, y_max );       glVertex2i( x_min, y_max );
    glEnd();
  }
  
  
  /******* GLUI_Control::draw_bkgd_box() **********/
  
  void GLUI_Control::draw_bkgd_box( int x_min, int x_max, int y_min, int y_max )
  {
    set_to_bkgd_color();
  
    glBegin( GL_QUADS );
    glVertex2i( x_min, y_min );       glVertex2i( x_max, y_min );
    glVertex2i( x_max, y_max );       glVertex2i( x_min, y_max );
    glEnd();
  }
  
  
  /**** GLUI_Control::draw_active_area() ********/
  
  void GLUI_Control::draw_active_box( int x_min, int x_max, int y_min, int y_max )
  {
    GLUI_DRAWINGSENTINAL_IDIOM
  
    if ( active ) {
      glEnable( GL_LINE_STIPPLE );
      glLineStipple( 1, 0x5555 );
      glColor3f( 0., 0., 0. );
    } else {
      set_to_bkgd_color();
    }
  
    glBegin( GL_LINE_LOOP );
    glVertex2i(x_min, y_min);      glVertex2i( x_max, y_min );
    glVertex2i(x_max, y_max);      glVertex2i( x_min, y_max );
    glEnd();
    
    glDisable( GL_LINE_STIPPLE );
  }
  
  
  /**** GLUI_Control::draw_emboss_box() ********/
  
  void   GLUI_Control::draw_emboss_box(int x_min,int x_max,int y_min,int y_max)
  {
    glLineWidth( 1.0 );
    glColor3f( 1.0, 1.0, 1.0 );
  
    glBegin( GL_LINE_LOOP );
    glVertex2i( x_min, y_min );    glVertex2i( x_max, y_min );
    glVertex2i( x_max, y_max );    glVertex2i( x_min, y_max );
    glEnd();
   
    glBegin( GL_LINE_LOOP );
    glVertex2i( x_min+1, y_min+1 );    glVertex2i( x_max-1, y_min+1 );
    glVertex2i( x_max-1, y_max-1 );    glVertex2i( x_min+1, y_max-1 );
    glEnd();
    
    glColor3f( .5, .5, .5 );
    glBegin( GL_LINE_LOOP );
    glVertex2i( x_min, y_min );
    glVertex2i( x_max-1, y_min );
    glVertex2i( x_max-1, y_max-1 );
    glVertex2i( x_min, y_max-1 );
    glEnd();
  }
  
  
  
  /******* GLUT_Control::draw_recursive() **********/
  
  void GLUI_Control::draw_recursive( int x, int y )
  {
    GLUI_Control *node;
  
    /*  printf( "%s %d\n", this->name.c_str(), this->hidden );*/
    if ( NOT can_draw() )
      return;
  
    /*if ( 1 ) {  --  Debugging to check control width  
      glColor3f( 1.0, 0.0, 0.0 );
      glBegin( GL_LINES );
      glVertex2i( x_abs, y_abs );00
      glVertex2i( x_abs+w, y_abs );
  
      glEnd();
      }*/
  
    glMatrixMode( GL_MODELVIEW );
    glPushMatrix();
  
    glTranslatef( (float) this->x_abs + .5, 
  		(float) this->y_abs + .5, 
  		0.0 );
  
    if ( NOT _glui_draw_border_only ) {
      if ( NOT strcmp( name.c_str(), "Rollout" ) ) {
      }
  
      this->draw( this->x_off, this->y_off_top );
    } 
    else 
    {
      if ( dynamic_cast<GLUI_Column*>(this) ) {
        /*   printf( "%s w/h:   %d/%d\n", (char*) name, w, h );              */
        /*w = 2;              */
      }
  
      /* The following draws the area of each control              */
      glColor3f( 1.0, 0.0, 0.0 );
      glBegin( GL_LINE_LOOP );
      glVertex2i( 0, 0 ); glVertex2i( w, 0 );
      glVertex2i( w, h ); glVertex2i( 0, h );
      glEnd();
    }
    glPopMatrix();
    
    node = (GLUI_Control*) first_child();
    while( node ) {
      node->draw_recursive( node->x_abs, node->y_abs );
      node = (GLUI_Control*) node->next();
    }
  }
  
  
  /****** GLUI_Control::set_to_glut_window() *********/
  /*  Sets the current window to the glut window associated with this control  */
  
  int GLUI_Control::set_to_glut_window()
  {
    int orig_window;
  
    if ( NOT glui) 
      return 1;
  
    orig_window = glutGetWindow();
  
    glutSetWindow( glui->get_glut_window_id());
  
    return orig_window;
  }
  
  
  /********** GLUI_Control::restore_window() *********/
  
  void GLUI_Control::restore_window(int orig)
  {
    if ( orig > 0 )
      glutSetWindow( orig );
  }
  
  
  
  /****************************** Text ***************************/
  
  /*************** GLUI_Control::set_font() **********/
  
  void GLUI_Control::set_font(void *new_font)
  {
    font = new_font;
    redraw();
  }
  
  
  /********** GLUI_Control::draw_string() ************/
  
  void GLUI_Control::draw_string( const char *text )
  {
    _glutBitmapString( get_font(), text );
  }
  
  
  /**************** GLUI_Control::draw_char() ********/
  
  void GLUI_Control::draw_char(char c)
  {
    glutBitmapCharacter( get_font(), c );
  }
  
  
  /*********** GLUI_Control::string_width() **********/
  
  int GLUI_Control::string_width(const char *text)
  {
    return _glutBitmapWidthString( get_font(), text );
  }
  
  
  /************* GLUI_Control::char_width() **********/
  
  int GLUI_Control::char_width(char c)
  { /* Hash table for faster character width lookups - JVK 
         Speeds up the textbox a little bit.
    */
    int hash_index = c % CHAR_WIDTH_HASH_SIZE;
    if (char_widths[hash_index][0] != c) {
      char_widths[hash_index][0] = c;
      char_widths[hash_index][1] = glutBitmapWidth( get_font(), c );
    }
    return char_widths[hash_index][1];
  }
  
  
  /*************** GLUI_Control::get_font() **********/
  
  void *GLUI_Control::get_font( void )
  {
    /*** Does this control have its own font? ***/
    if ( this->font != NULL )
      return this->font;
    
    /*** Does the parent glui have a font? ***/
    if ( glui )
      return glui->font;
  
    /*** Return the default font ***/
    return GLUT_BITMAP_HELVETICA_12;
  }
  
  
  /************* GLUI_Control::draw_name() ***********/
  /* This draws the name of the control as either black (if enabled), or       */
  /* embossed if disabled.                                                     */
  
  void GLUI_Control::draw_name(int x, int y)
  {
    if ( NOT can_draw() )
      return;
  
    if ( enabled )
    {
      set_to_bkgd_color();
      glRasterPos2i(x+1, y+1);
      draw_string(name);
      glColor3b( 0, 0, 0 );
      glRasterPos2i(x, y);
      draw_string(name);
    }
    else
    {   /* Control is disabled - emboss the string */
      glColor3f( 1.0f, 1.0f, 1.0f );
      glRasterPos2i(x+1, y+1);
      draw_string(name);
      glColor3f( .4f, .4f, .4f );
      glRasterPos2i(x, y);
      draw_string(name);
    }
  }
  
  
  /**************************** Layout and Packing *********************/
  
  /****** GLUI_Control::align() **************/
  
  void GLUI_Control::align()
  {
    int  col_x, col_y, col_w, col_h, col_x_off, col_y_off;
    int  orig_x_abs;
  
    orig_x_abs = x_abs;
  
    /* Fix alignment bug relating to columns              */
    /*return;              */
  
    if ( NOT parent() )
      return;  /* Clearly this shouldn't happen, though */
  
    get_this_column_dims(&col_x, &col_y, &col_w, &col_h, 
  		       &col_x_off, &col_y_off);
  
    if ( dynamic_cast<GLUI_Column*>(this) ) {
      /*		if ( this->prev() != NULL ) {
  		((GLUI_Control*)prev())->get_this_column_dims(&col_x, &col_y, &col_w, &col_h, 
  		&col_x_off, &col_y_off);
  		
  		x_abs = col_x + col_w;
  		}
  		else {
  		x_abs = ((GLUI_Control*)parent())->x_abs;
  		}
  		*/
      return;
    }
  
    if ( alignment == GLUI_ALIGN_LEFT ) {
      x_abs = col_x + col_x_off;
    }
    else if ( alignment == GLUI_ALIGN_RIGHT ) {
      x_abs = col_x + col_w - col_x_off - this->w;
    }
    else if ( alignment == GLUI_ALIGN_CENTER ) {
      x_abs = col_x + (col_w - this->w) / 2;
    }
  
    if ( this->is_container ) {
      /***   Shift all child columns   ***/
      int delta = x_abs - orig_x_abs;
  
      GLUI_Control *node;
  		
      node = (GLUI_Control*) this->first_child();
      while( node != NULL ) {
        if ( dynamic_cast<GLUI_Column*>(node) ) { 
  	node->x_abs += delta;
        } 
  
        node = (GLUI_Control*) node->next();
      }
    }
  
  }
  
  
  /************** GLUI_Control::pack() ************/
  /* Recalculate positions and offsets  */
  
  void GLUI_Control::pack_old(int x, int y)
  {
    GLUI_Control  *node;
    int            max_w, curr_y, curr_x, max_y;
    int            x_in = x, y_in =y;
    int            x_margin, y_margin_top, y_margin_bot;
    int            y_top_column;
    int            column_x;
    GLUI_Column   *curr_column = NULL;
    this->update_size();
    x_margin     = this->x_off;
    y_margin_top = this->y_off_top;
    y_margin_bot = this->y_off_bot;
    this->x_abs = x_in;
    this->y_abs = y_in;
    max_w  = -1;
    max_y  = -1;
    curr_x = this->x_abs + x_margin;
    curr_y = this->y_abs + y_margin_top;
    /*** Record start of this set of columns ***/
    y_top_column = curr_y;
    column_x     = 0;
    if ( this == glui->main_panel ) {
      x=x;
    }
    /*** Iterate over children, packing them first ***/
    node = (GLUI_Control*) this->first_child();
    while( node != NULL ) {
      if ( dynamic_cast<GLUI_Panel*>(node) && !node->collapsible) { 
        /* Pad some space above fixed size panels */
        curr_y += GLUI_ITEMSPACING;
      } 
      else if ( dynamic_cast<GLUI_Column*>(node)) {
        curr_column = (GLUI_Column*) node;
        if ( 1 ) {
  	column_x += max_w + 2 * x_margin;
  	curr_x  += max_w + 2 * x_margin;
        }
  /*
        else {
  	column_x += max_w + 0 * x_margin;
  	curr_x  += max_w + 0 * x_margin;
        }
   */
        /*node->pack( curr_x, curr_y );              */
        node->x_abs = curr_x;
        node->y_abs = y_top_column;
        node->w     = 2;
        node->h     = curr_y - y_top_column;
        curr_x  += x_margin * 3 + 40;
        curr_y  = y_top_column;
        max_w = 0;
        node = (GLUI_Control*) node->next();
        continue;
      }
      node->pack( curr_x, curr_y );
      if ( dynamic_cast<GLUI_Panel*>(node) && !node->collapsible)  
        /* Pad some space below fixed size panels */
        curr_y += GLUI_ITEMSPACING;
      curr_y  += node->h;
      if ( node->w > max_w ) {
        max_w = node->w;
        if ( curr_column != NULL )
  	curr_column->w = max_w;
      }
      node = (GLUI_Control*) node->next();
      if ( node ) {
        curr_y += GLUI_ITEMSPACING;
      }
      if ( curr_y > max_y )
        max_y = curr_y;
    }
    if ( this->is_container ) {
      max_y += y_margin_bot;  /*** Add bottom border inside box */
      if ( this->first_child() ) {
        if ( dynamic_cast<GLUI_Rollout*>(this) ) {	
  	/**  We don't want the rollout to shrink in width when it's
  	  closed **/
  	this->w = MAX(this->w, column_x + max_w + 2 * x_margin );
        }
        else {
  	this->w        = column_x + max_w + 2 * x_margin;
        }
        this->h        = (max_y - y_in);
      }
      else  {            /* An empty container, so just assign default w & h */
        this->w        = GLUI_DEFAULT_CONTROL_WIDTH;
        this->h        = GLUI_DEFAULT_CONTROL_HEIGHT;
      }
      /** Expand panel if necessary (e.g., to include all the text in 
        a panel label) **/
      this->update_size();   
    }
  }
  
  /*** GLUI_Control::get_this_column_dims() **********/
  /* Gets the x,y,w,h,and x/y offsets of the column to which a control belongs */
  
  void    GLUI_Control::get_this_column_dims( int *col_x, int *col_y, 
  					    int *col_w, int *col_h, 
  					    int *col_x_off, int *col_y_off )
  {
    GLUI_Control *node, *parent_ptr;
    int           parent_h, parent_y_abs;
  
    parent_ptr = (GLUI_Control*) parent();
  
    if ( parent_ptr==NULL )
      return;
  
    parent_h     = parent_ptr->h;
    parent_y_abs = parent_ptr->y_abs;
    
    if ( dynamic_cast<GLUI_Panel*>(parent_ptr) AND
         parent_ptr->int_val == GLUI_PANEL_EMBOSSED AND
         parent_ptr->name != "" ) {
      parent_h -= GLUI_PANEL_EMBOSS_TOP;
      parent_y_abs += GLUI_PANEL_EMBOSS_TOP;
    }
  /*
    if ( 0 ) {
      GLUI_Node *first, *last, *curr;
  
      
      first = this;
      while (first->prev() AND !dynamic_cast<GLUI_Column*>(first->prev()) ) 
        first = first->prev();
  
      
      last = this;
      while ( last->next() AND !dynamic_cast<GLUI_Column*>(first->next()) )
        last = last->next();
  
      curr = first;
      int max_w = -1;
      do {
        if ( ((GLUI_Control*)curr)->w > max_w )
  	max_w = ((GLUI_Control*)curr)->w;
  
        if ( curr == last )
  	break;
  
        curr = curr->next();
      } while( curr != NULL );
  
      *col_x     = ((GLUI_Control*)first)->x_abs;
      *col_y     = ((GLUI_Control*)first)->y_abs;
      *col_w     = max_w;
      if ( parent() ) {
        *col_h     = ((GLUI_Control*)parent())->h;
        *col_x_off = ((GLUI_Control*)parent())->x_off;
      }
      else {
        *col_h = 10;
        *col_x_off = 0;
      }
      *col_y_off = 0;
  
      return;
    }
  */
    if ( 1 ) {    /* IS THIS WRONG? */
      /*** Look for preceding column ***/
      node = (GLUI_Control*) this->prev();
      while( node ) {
        if ( dynamic_cast<GLUI_Column*>(node) ) {
  	*col_x     = node->x_abs;
  	*col_y     = parent_y_abs;
  	*col_w     = node->w;
  	*col_h     = parent_h;
  	*col_x_off = node->x_off;
  	*col_y_off = 0;
  
  	return;
        }
  
        node = (GLUI_Control*) node->prev();
      }
  
      /*** Nope, Look for next column ***/
      node = (GLUI_Control*) this->next();
      while( node ) {
        if ( dynamic_cast<GLUI_Column*>(node) ) {
  	*col_x     = parent_ptr->x_abs;
  	*col_y     = parent_y_abs;
  	*col_w     = node->x_abs - parent_ptr->x_abs;
  	*col_h     = parent_h;
  	*col_x_off = node->x_off;
  	*col_y_off = 0;
  
  	return;
        }
  
        node = (GLUI_Control*) node->next();
      }
  
      /***   This is single-column panel, so return panel dims   ***/
      *col_x     = parent_ptr->x_abs;
      *col_y     = parent_y_abs;
      *col_w     = parent_ptr->w;
      *col_h     = parent_h;
      *col_x_off = parent_ptr->x_off;
      *col_y_off = 0;
    }
  }
  
  
  void    GLUI_Control::pack( int x, int y )
  {
    GLUI_Control  *node;
    int            max_w, curr_y, curr_x, max_y;
    int            x_in = x, y_in =y;
    int            x_margin, y_margin_top, y_margin_bot;
    int            y_top_column;
    int            column_x;
    GLUI_Column   *curr_column = NULL;
  
    this->update_size();
  
    x_margin     = this->x_off;
    y_margin_top = this->y_off_top;
    y_margin_bot = this->y_off_bot;
    
    this->x_abs = x_in;
    this->y_abs = y_in;
  
    max_w  = 0;
    max_y  = 0;
    curr_x = this->x_abs + x_margin;
    curr_y = this->y_abs + y_margin_top;
  
    /*** Record start of this set of columns ***/
  
    y_top_column = curr_y;
    column_x     = curr_x;
  
    /*** Iterate over children, packing them first ***/
  
    node = (GLUI_Control*) this->first_child();
    while( node != NULL ) {
      if ( dynamic_cast<GLUI_Panel*>(node) && !node->collapsible) { 
        /* Pad some space above fixed-size panels */
        curr_y += GLUI_ITEMSPACING;
      } 
      else if ( dynamic_cast<GLUI_Column*>(node) ) {
        curr_column = (GLUI_Column*) node;
        curr_x   += max_w + 1 * x_margin;
        column_x  = curr_x;
  
        node->x_abs = curr_x;
        node->y_abs = y_top_column;
        node->w     = 2;
        node->h     = curr_y - y_top_column;
  
        curr_x  += x_margin * 1;
        curr_y  = y_top_column;
        max_w = 0;
  
        node = (GLUI_Control*) node->next();
        continue;
      }
  		
      node->pack( curr_x, curr_y );
  
      if ( dynamic_cast<GLUI_Panel*>(node)  && !node->collapsible)
        /* Pad some space below fixed-size panels */
        curr_y += GLUI_ITEMSPACING;
      
      curr_y  += node->h;
  
      if ( node->w > max_w ) {
        max_w = node->w;
        if ( curr_column != NULL )
  	curr_column->w = max_w + x_margin;
      }
  
      if ( curr_y > max_y ) {
        max_y = curr_y;
        if ( curr_column != NULL )
  	curr_column->h = max_y - y_top_column;
      }
  
      node = (GLUI_Control*) node->next();
      
      if ( node ) {
        curr_y += GLUI_ITEMSPACING;
      }
  
    }
  
    if ( this->is_container ) {
      max_y += y_margin_bot;  /*** Add bottom border inside box */
  
      if ( this->first_child() ) {
        this->w        = column_x + max_w + 2 * x_margin - x_in;
        this->h        = (max_y - y_in);
      }
      else  {            /* An empty container, so just assign default w & h */
        if ( !dynamic_cast<GLUI_Rollout*>(this) &&
             !dynamic_cast<GLUI_Tree*>(this) ) {
  	this->w        = GLUI_DEFAULT_CONTROL_WIDTH;
  	this->h        = GLUI_DEFAULT_CONTROL_HEIGHT;
        }
      }
  
      /** Expand panel if necessary (e.g., to include all the text in 
        a panel label) **/
      this->update_size();   
  
  
      /*** Now we step through the GLUI_Columns, setting the 'h'  ***/
      node = (GLUI_Control*) this->first_child();
      while( node != NULL ) {
        if ( dynamic_cast<GLUI_Column*>(node) ) {
  	node->h = this->h - y_margin_bot - y_margin_top;
        }
  
        node = (GLUI_Control*) node->next();
      }
    }
  }
  
  
  
  /******************************** Live Variables **************************/
  /*********** GLUI_Control::sync_live() ************/
  /* Reads live variable and sets control to its current value                */
  /* This function is recursive, and operates on control's children           */
  
  void GLUI_Control::sync_live(int recurse, int draw_it)
  {
    GLUI_Node *node;
    int        sync_it=true;
    int        i;
    float      *fp;
    bool       changed = false;
  
    /*** If this is currently active control, and mouse button is down,
      don't sync ***/
    if ( glui )
    {
      if ( this == glui->active_control AND glui->mouse_button_down )
        sync_it = false;
  
      /*** Actually, just disable syncing if button is down ***/
      /*** Nope, go ahead and sync if mouse is down - this allows syncing in
        callbacks ***/
  /*
      if ( 0 ) {
        if ( glui->mouse_button_down ) {
  	
  	return;
        }
      }
   */
    }
  
    /***  If this control has a live variable, we check its current value
      against the stored value in the control  ***/
  
    if ( ptr_val != NULL ) {
      if ( live_type == GLUI_LIVE_NONE OR NOT sync_it ) {
      }
      else if ( live_type == GLUI_LIVE_INT ) {
        if ( *((int*)ptr_val) != last_live_int ) {
          set_int_val( *((int*)ptr_val) );
          last_live_int = *((int*)ptr_val);
          changed = true;
        }
      }   
      else if ( live_type == GLUI_LIVE_FLOAT ) {
        if ( *((float*)ptr_val) != last_live_float ) {
          set_float_val( *((float*)ptr_val) );
          last_live_float = *((float*)ptr_val);
          changed = true;
        }
      } 
      else if ( live_type == GLUI_LIVE_TEXT ) {
        if ( last_live_text.compare((const char*)ptr_val) != 0 ) {
          set_text( (char*) ptr_val );
          last_live_text = (const char*)ptr_val;
          changed = true;
        }
      } 
      else if ( live_type == GLUI_LIVE_STRING ) {
        if ( last_live_text.compare(((std::string*) ptr_val)->c_str()) != 0 ) {
          set_text( ((std::string*) ptr_val)->c_str());
          last_live_text = *((std::string*) ptr_val);
          changed = true;
        }
      } 
      else if ( live_type == GLUI_LIVE_FLOAT_ARRAY ) { 
        /***  Step through the arrays, and see if they're the same  ***/
        
        fp = (float*) ptr_val;
        for ( i=0; i<float_array_size; i++ ) {
          if ( *fp != last_live_float_array[i] ) {
            changed = true;
            break;
          }
  		
          fp++;
        }
        
        if ( changed == true) {
          fp = (float*) ptr_val;
          set_float_array_val( fp );
          for ( i=0; i<float_array_size; i++ ) {
            last_live_float_array[i] = *fp;
            fp++;
          }
        }
      }
      else if ( live_type == GLUI_LIVE_DOUBLE ) {
      }
    }
  
    /***  If this control is changed and we're supposed to be drawing, then
      draw it now    ***/
    if ( changed == true AND draw_it ) {
      redraw();
    }
  
    if ( recurse ) {
      /*** Now recursively output live vars for all children ***/
      node = this->first_child();
      while( node ) {
        ((GLUI_Control*) node)->sync_live(true, true);
        node = node->next();
      }
  
      if ( collapsible == true AND is_open == false ) {
        /** Here we have a collapsed control (e.g., a rollout that is closed **/
        /** We need to go in and sync all the collapsed controls inside      **/
         
        node = this->collapsed_node.first_child();
        while( node ) {
  	((GLUI_Control*) node)->sync_live(true, false);
  	node = node->next();
        }
      }
    }
  }
  
  
  /********** GLUI_Control::output_live() ************/
  /* Writes current value of control to live variable.                         */
  
  void GLUI_Control::output_live(int update_main_gfx)
  {
    int    i;
    float *fp;
  
    if ( ptr_val == NULL )
      return;
     
    if ( NOT live_inited ) 
      return;
     
    if ( live_type == GLUI_LIVE_NONE ) {
    }
    else if ( live_type == GLUI_LIVE_INT ) {
      *((int*)ptr_val) = int_val;
      last_live_int    = int_val;
    } 
    else if ( live_type == GLUI_LIVE_FLOAT ) {
      *((float*)ptr_val) = float_val;
      last_live_float    = float_val;
    } 
    else if ( live_type == GLUI_LIVE_TEXT ) {
      strncpy( (char*) ptr_val, text.c_str(), text.length()+1);
      last_live_text =  text;
    } 
    else if ( live_type == GLUI_LIVE_STRING ) {
      (*(std::string*)ptr_val)= text.c_str();
      last_live_text =  text;
    } 
    else if ( live_type == GLUI_LIVE_FLOAT_ARRAY ) {
      fp = (float*) ptr_val;
  
      for( i=0; i<float_array_size; i++ ) {
        *fp                      = float_array_val[i];
        last_live_float_array[i] = float_array_val[i];
        fp++;
      }
    }
    else if ( live_type == GLUI_LIVE_DOUBLE ) {
    }
  
    /** Update the main gfx window? **/
    if ( update_main_gfx AND this->glui != NULL ) {
      this->glui->post_update_main_gfx();
    }
  }
  
  
  /****** GLUI_Control::execute_callback() **********/
  
  void GLUI_Control::execute_callback()
  {
    int old_window;
    
    old_window = glutGetWindow();
  
    if ( glui AND glui->main_gfx_window_id != -1 ) 
      glutSetWindow( glui->main_gfx_window_id );
  
    this->callback( this );
  //  if ( this->callback ) 
  //    this->callback( this->user_id );
  
    glutSetWindow( old_window );
  }
  
  
  /************** GLUI_Control::init_live() **********/
  /* Reads in  value of a live variable.  Called once, when ctrl is created   */
  
  void GLUI_Control::init_live()
  {
    int    i;
    float *fp;
  
    if ( ptr_val == NULL )
      return;
  
    if ( live_type == GLUI_LIVE_NONE ) {
    }
    else if ( live_type == GLUI_LIVE_INT ) {
      set_int_val( *((int*)ptr_val) );
      last_live_int = *((int*)ptr_val);
    } 
    else if ( live_type == GLUI_LIVE_FLOAT ) {
      set_float_val( *((float*)ptr_val) );
      last_live_float = *((float*)ptr_val);
    } 
    else if ( live_type == GLUI_LIVE_TEXT ) {
      set_text( (const char*) ptr_val );
      last_live_text = (const char*) ptr_val;
    }
    else if ( live_type == GLUI_LIVE_STRING ) {
      set_text( ((std::string*) ptr_val)->c_str() );
      last_live_text = ((std::string*) ptr_val)->c_str();
    }
    else if ( live_type == GLUI_LIVE_FLOAT_ARRAY ) {
      set_float_array_val( (float*) ptr_val );
  
      fp = (float*) ptr_val;
  
      for( i=0; i<float_array_size; i++ ) {
        last_live_float_array[i] = *fp;
        fp++;
      }
  
    }
    else if ( live_type == GLUI_LIVE_DOUBLE ) {
    }
  
    live_inited = true;
  }
  
  /***** GLUI_Control::set_float_array_val() ********/
  
  void  GLUI_Control::set_float_array_val( float *array_ptr )
  {
    int i;
  
    if ( array_ptr == NULL )
      return;
  
    for( i=0; i<float_array_size; i++ ) {
      float_array_val[i] = array_ptr[i];
    }
  
    /*** Output the live var, without updating the main gfx window ***/
    output_live(false);
  }
  
  
  /***** GLUI_Control::get_float_array_val() ********/
  
  void  GLUI_Control::get_float_array_val( float *array_ptr )
  {
    int i;
  
    if ( array_ptr == NULL )
      return;
  
    for( i=0; i<float_array_size; i++ ) {
      array_ptr[i] = float_array_val[i];
    }
  }
  
  
  
  /**************************** Little Utility Routines ********************/
  
  /**** GLUI_Control::set_name() ********************/
  
  void   GLUI_Control::set_name( const char *str )
  {
    name = str;
    redraw(); 
  }
  
  /**** GLUI_Control::enable() ****************/
  
  void GLUI_Control::enable()
  {
    GLUI_Control *node;
  
    enabled = true;
     
    if ( NOT glui )
      return;
  
    redraw();
  
    /*** Now recursively enable all buttons below it ***/
    node = (GLUI_Control*) first_child();
    while(node)
    {
      node->enable();
      node = (GLUI_Control*) node->next();
    }
  }
  
  
  /***** GLUI_Control::disable() ****************/
  
  void GLUI_Control::disable()
  {
    GLUI_Control *node;
  
    enabled = false;
    
    if ( NOT glui )
      return;
  
    if ( glui->active_control == this )
      glui->deactivate_current_control();
    redraw();
  
    /*** Now recursively disable all buttons below it ***/
    node = (GLUI_Control*) first_child();
    while(node) {
      node->disable();
      node = (GLUI_Control*) node->next();
    }
  }
  
  /******* GLUI_Control::set_w() **************/
  
  void GLUI_Control::set_w(int new_w)
  {
    w = new_w;
    update_size();  /* Make sure control is big enough to fit text */
    if (glui) glui->refresh();
  }
  
  
  /**** GLUI_Control::set_h() **************/
  
  void GLUI_Control::set_h(int new_h)
  {
    h = new_h;
    update_size();  /* Make sure control is big enough to fit text */
    if (glui) glui->refresh();
  }
  
  
  /***** GLUI_Control::set_alignment() ******/
  
  void GLUI_Control::set_alignment(int new_align)
  {
    alignment = new_align;
  
    if ( glui )
    {
      glui->align_controls(this);
      redraw_window();
    }
  }
  
  
  /***** GLUI_Control::needs_idle() *********/
  /* This method gets overloaded by specific classes, e.g. Spinner.            */
  /* It returns whether or not a control needs to receive an idle event or not */
  /* For example, a spinner only needs idle events when the user is holding    */
  /* the mouse down in one of the arrows.  Otherwise, don't waste cycles       */
  /* and OpenGL context switching by calling its idle.                         */
  
  bool GLUI_Control::needs_idle() const
  { 
    return false; 
  }
  
  
  /********* GLUI_Control::~GLUI_Control() **********/
  
  GLUI_Control::~GLUI_Control()
  {
    GLUI_Control *item = (GLUI_Control*) this->first_child();
  
    while (item) 
    {
      GLUI_Control *tmp = item;
      item = (GLUI_Control*) item->next();
      delete tmp;
    }
  }
  
  /********* GLUI_Control::hide_internal() ********/
  /** Sets hidden==true for this  control and all its siblings.             */
  /**  If recurse is true, we go to children as well                       */
  
  void         GLUI_Control::hide_internal( int recurse )
  {
    GLUI_Node *node;
  
    node = (GLUI_Node *) this;
    while( node != NULL ) {
      ((GLUI_Control*)node)->hidden = true;
  
      if ( recurse AND node->first_child() != NULL )  
        ((GLUI_Control*) node->first_child())->hide_internal(true);
        
      node = node->next();
    }
  
    node = this->collapsed_node.first_child();
    while( node != NULL ) {
      ((GLUI_Control*)node)->hidden = true;
  
      if ( recurse AND node->first_child() != NULL )  
        ((GLUI_Control*) node->first_child())->hide_internal(true);
        
      node = node->next();
    }
  }
  
  
  /********* GLUI_Control::unhide_internal() ********/
  /** Sets hidden==false for this  control and all its siblings.             */
  /**  If recurse is true, we go to children as well                       */
  
  void         GLUI_Control::unhide_internal( int recurse )
  {
    GLUI_Node *node;
  
    node = (GLUI_Node *) this;
    while( node != NULL ) {
      /*    printf( "unhide: %s [%d]\n", ((GLUI_Control*)node)->name.c_str(), 
  	    ((GLUI_Control*)node)->hidden );*/
      ((GLUI_Control*)node)->hidden = false;
  
      if ( recurse AND node->first_child() != NULL )  
        ((GLUI_Control*) node->first_child())->unhide_internal(true);
        
      node = node->next();
    }
  
    node = this->collapsed_node.first_child();
    while( node != NULL ) {
      ((GLUI_Control*)node)->hidden = false;
  
      if ( recurse AND node->first_child() != NULL )  
        ((GLUI_Control*) node->first_child())->unhide_internal(true);
        
      node = node->next();
    }
  }
  