/****************************************************************************
  
  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 library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*****************************************************************************/

#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;

  glColor3ub( glui->bkgd_color.r, glui->bkgd_color.g, glui->bkgd_color.b );
}

/******** 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;

    /**   Look for first control in this column   **/
    first = this;
    while (first->prev() AND !dynamic_cast<GLUI_Column*>(first->prev()) ) 
      first = first->prev();

    /**   Look for last control in this column    **/
    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 ) { /* THIS CODE BELOW SHOULD NOT BE EXECUTED */
      if ( glui->mouse_button_down ) {
	/* printf( "Can't sync\n" );              */
	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_DOUBLE ) {
      if ( *((double*)ptr_val) != last_live_double ) {
        set_double_val( *((double*)ptr_val) );
        last_live_double = *((double*)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_DOUBLE ) {
    *((double*)ptr_val) = double_val;
    last_live_double    = double_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++;
    }
  }

  /** 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_DOUBLE ) {
    set_double_val( *((double*)ptr_val) );
    last_live_double = *((double*)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();
  }
}
