/****************************************************************************
  
  GLUI User Interface Toolkit
  ---------------------------

     glui_scrollbar.cpp - GLUI_Scrollbar class

          --------------------------------------------------

  Copyright (c) 2004 John Kew, 1998 Paul Rademacher

  This program is freely distributable without licensing fees and is
  provided without guarantee or warrantee expressed or implied. This
  program is -not- in the public domain.

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

#include "glui_internal_control.h"
#include <cmath>
#include <cassert>

/*static int __debug=0;              */

#define  GLUI_SCROLL_GROWTH_STEPS         800
#define  GLUI_SCROLL_MIN_GROWTH_STEPS     100
#define  GLUI_SCROLL_CALLBACK_INTERVAL    1    /* Execute the user's callback every this many clicks */

enum {
  GLUI_SCROLL_ARROW_UP,
  GLUI_SCROLL_ARROW_DOWN,
  GLUI_SCROLL_ARROW_LEFT,
  GLUI_SCROLL_ARROW_RIGHT
};
  

/****************************** GLUI_Scrollbar::GLUI_Scrollbar() **********/
// Constructor, no live var
GLUI_Scrollbar::GLUI_Scrollbar( GLUI_Node *parent,
                                const char *name, 
                                int horz_vert,
                                int data_type,
                                int id, GLUI_CB callback
                                /*,GLUI_Control *object
                                ,GLUI_InterObject_CB obj_cb*/
                                )
{
  common_construct(parent, name, horz_vert, data_type, NULL, id, callback/*, object, obj_cb*/);
}

/****************************** GLUI_Scrollbar::GLUI_Scrollbar() **********/
// Constructor, int live var
GLUI_Scrollbar::GLUI_Scrollbar( GLUI_Node *parent, const char *name, 
                                int horz_vert,
                                int *live_var,
                                int id, GLUI_CB callback
                                /*,GLUI_Control *object
                                ,GLUI_InterObject_CB obj_cb*/
                                )
{
  common_construct(parent, name, horz_vert, GLUI_SCROLL_INT, live_var, id, callback/*, object, obj_cb*/);
}

/****************************** GLUI_Scrollbar::GLUI_Scrollbar() **********/
// Constructor, float live var
GLUI_Scrollbar::GLUI_Scrollbar( GLUI_Node *parent, const char *name,
                                int horz_vert,
                                float *live_var,
                                int id, GLUI_CB callback
                                /*,GLUI_Control *object
                                ,GLUI_InterObject_CB obj_cb*/
                                )
{
  common_construct(parent, name, horz_vert, GLUI_SCROLL_FLOAT, live_var, id, callback/*, object, obj_cb*/);
}

/****************************** GLUI_Scrollbar::common_init() **********/
void GLUI_Scrollbar::common_init(void)
{
   horizontal	= true;
   h		= GLUI_SCROLL_ARROW_HEIGHT;
   w		= GLUI_TEXTBOX_WIDTH;
   alignment	= GLUI_ALIGN_CENTER;
   x_off	= 0;
   y_off_top	= 0;
   y_off_bot	= 0;
   can_activate = true;
   state	= GLUI_SCROLL_STATE_NONE;
   growth_exp	= GLUI_SCROLL_DEFAULT_GROWTH_EXP;
   callback_count = 0;
   first_callback = true;
   user_speed	= 1.0;
   float_min	= 0.0;
   float_max	= 0.0;
   int_min	= 0;
   int_max	= 0;
   associated_object = NULL;
   last_update_time=0;
   velocity_limit=50.0; /* Change value by at most 50 per second */
   box_length	      = 0;
   box_start_position = 0;
   box_end_position   = 0;
   track_length       = 0;
}

/****************************** GLUI_Scrollbar::common_construct() **********/
void GLUI_Scrollbar::common_construct(
  GLUI_Node *parent,
  const char *name, 
  int horz_vert,
  int data_type,
  void *data,
  int id, GLUI_CB callback
  /*,GLUI_Control *object,
  GLUI_InterObject_CB obj_cb*/
  )
{
  common_init();

  // make sure limits are wide enough to hold live value
  if (data_type==GLUI_SCROLL_FLOAT) {
    float lo = 0.0f, hi=1.0f;
    if (data) {
      float d = *(float*)(data);
      lo = MIN(lo, d);
      hi = MAX(hi, d);
    }
    this->set_float_limits(lo,hi);
    this->set_float_val(lo);
    this->live_type = GLUI_LIVE_FLOAT;
  } else {
    int lo = 0, hi=100;
    if (data) {
      int d = *(int*)(data);
      lo = MIN(lo, d);
      hi = MAX(hi, d);
    }
    this->set_int_limits(lo,hi);
    this->set_int_val(0);
    this->live_type = GLUI_LIVE_INT;
  }
  this->data_type = data_type;
  this->set_ptr_val( data );
  this->set_name(name);
  this->user_id = id;
  this->callback    = callback;
  //this->associated_object = object;
  //this->object_cb = obj_cb;
  this->horizontal=(horz_vert==GLUI_SCROLL_HORIZONTAL);
  if (this->horizontal) {
    this->h = GLUI_SCROLL_ARROW_HEIGHT;
    this->w = GLUI_TEXTBOX_WIDTH;
  } else {
    this->h = GLUI_TEXTBOX_HEIGHT;
    this->w = GLUI_SCROLL_ARROW_WIDTH;
  }
  parent->add_control( this );
  this->init_live();
}
 
/****************************** GLUI_Scrollbar::mouse_down_handler() **********/

int    GLUI_Scrollbar::mouse_down_handler( int local_x, int local_y )
{
  last_update_time=GLUI_Time()-1.0;
  this->state = find_arrow( local_x, local_y );
  GLUI_Master.glui_setIdleFuncIfNecessary();

  /*  printf( "spinner: mouse down  : %d/%d   arrow:%d\n", local_x, local_y,
      find_arrow( local_x, local_y ));
      */

  if ( state != GLUI_SCROLL_STATE_UP AND state != GLUI_SCROLL_STATE_DOWN)
    return true;

  reset_growth();

  /*** ints and floats behave a bit differently.  When you click on
    an int spinner, you expect the value to immediately go up by 1, whereas
    for a float it'll go up only by a fractional amount.  Therefore, we
    go ahead and increment by one for int spinners ***/
#if 1
  if ( data_type == GLUI_SCROLL_INT ) {
    // Allow for possibility of reversed limits
    int lo = MIN(int_min,int_max);
    int hi = MAX(int_min,int_max);
    int increase = int_min < int_max ? 1 : -1;
    int new_val = int_val;
    if ( state == GLUI_SCROLL_STATE_UP ) {
      new_val += increase;
    } else if ( state == GLUI_SCROLL_STATE_DOWN ) {
      new_val -= increase;
    }
    if (new_val >= lo && new_val <= hi && new_val!=int_val) {
      set_int_val(new_val);
      do_callbacks();
    }
  }
#endif
  do_click();  
  redraw();
  
  return false;
}


/******************************** GLUI_Scrollbar::mouse_up_handler() **********/

int    GLUI_Scrollbar::mouse_up_handler( int local_x, int local_y, bool inside )
{
  state = GLUI_SCROLL_STATE_NONE;
  GLUI_Master.glui_setIdleFuncIfNecessary();

  /*  printf("spinner: mouse up  : %d/%d    inside: %d\n",local_x,local_y,inside);              */

  /*glutSetCursor( GLUT_CURSOR_INHERIT );              */
  glutSetCursor( GLUT_CURSOR_LEFT_ARROW );

  redraw();

  /*  do_callbacks(); --- stub               */
  /*  if ( callback )               */
  /*  callback( this->user_id );              */
  
  return false;
}


/***************************** GLUI_Scrollbar::mouse_held_down_handler() ******/

int    GLUI_Scrollbar::mouse_held_down_handler( int local_x, int local_y,
                                                bool new_inside)
{
  int new_state;
  if ( state == GLUI_SCROLL_STATE_NONE )
    return false;
  
  /*  printf("spinner: mouse held: %d/%d    inside: %d\n",local_x,local_y,
      new_inside);
  */

  if ( state == GLUI_SCROLL_STATE_SCROLL) {   /* dragging? */
    do_drag( local_x-x_abs, local_y-y_abs );
  }
  else {                                      /* not dragging */
    new_state = find_arrow( local_x, local_y );

    if ( new_state == state ) {
      /** Still in same arrow **/
      do_click();
    }
  }
  redraw();

  return false;
}


/****************************** GLUI_Scrollbar::key_handler() **********/

int    GLUI_Scrollbar::key_handler( unsigned char key,int modifiers )
{
  return true;
}


/****************************** GLUI_Scrollbar::draw() **********/

void    GLUI_Scrollbar::draw( int x, int y )
{
  GLUI_DRAWINGSENTINAL_IDIOM

  if ( horizontal ) {
    draw_scroll_arrow(GLUI_SCROLL_ARROW_LEFT,  0, 0);
    draw_scroll_arrow(GLUI_SCROLL_ARROW_RIGHT, w-GLUI_SCROLL_ARROW_WIDTH, 0);
  } else {
    draw_scroll_arrow(GLUI_SCROLL_ARROW_UP,  0, 0);
    draw_scroll_arrow(GLUI_SCROLL_ARROW_DOWN, 0, h-GLUI_SCROLL_ARROW_HEIGHT);
  }
  draw_scroll();
}


/****************************** GLUI_Scrollbar::draw_scroll_arrow() **********/

void GLUI_Scrollbar::draw_scroll_arrow(int arrowtype, int x, int y)
{
  float offset=0;
  float L=3.5f,HC=7.f,R=10.5f;
  float T=4.5f,VC=8.f,B=11.5;
  const float verts[][6]={
    { L,10.5f,     R, 10.5f,      HC, 6.5f }, // up arrow
    { L,6.5f,      R, 6.5f,       HC,10.5f }, // down arrow
    { R-2,T,       R-2, B,        L+1,  VC   }, // left arrow
    { L+2,T,       L+2, B,        R-1,  VC   }  // right arrow
  };

  const float *tri = NULL;

  switch (arrowtype)
  {
    case GLUI_SCROLL_ARROW_UP:
      tri = verts[0];
      if (state & GLUI_SCROLL_STATE_UP) offset = 1;
      break;

    case GLUI_SCROLL_ARROW_DOWN:
      tri = verts[1];
      if (state & GLUI_SCROLL_STATE_DOWN) offset = 1;
      break;

    case GLUI_SCROLL_ARROW_LEFT:
      tri = verts[2];
      if (state & GLUI_SCROLL_STATE_DOWN) offset = 1;
      break;

    case GLUI_SCROLL_ARROW_RIGHT:
      tri = verts[3];
      if (state & GLUI_SCROLL_STATE_UP) offset = 1;
      break;

    default:
      return; /* tri is NULL */
  }

  glColor3ubv(&glui->bkgd_color.r);
  glRecti(x,y,x+GLUI_SCROLL_ARROW_WIDTH,y+GLUI_SCROLL_ARROW_HEIGHT);
  if (!offset) {
    glui->draw_raised_box(x,y+1,GLUI_SCROLL_ARROW_WIDTH-1,GLUI_SCROLL_ARROW_HEIGHT-1);
  } else {
    glColor3ub(128,128,128);
    glBegin(GL_LINE_LOOP);
    int x2=x+GLUI_SCROLL_ARROW_WIDTH, y2=y+GLUI_SCROLL_ARROW_HEIGHT;
    glVertex2i(x ,y); 
    glVertex2i(x2,y);
    glVertex2i(x2,y2); 
    glVertex2i(x ,y2);
    glEnd();
  }
  
  GLubyte black[]={0,0,0};
  GLubyte white[]={255,255,255};
  GLubyte  gray[]={128,128,128};
  GLubyte *color=black;
  if (!enabled) {
    offset = 1;
    color = white;
  }
  glTranslatef(x+offset,y+offset,0);
  glColor3ubv(color);
  glBegin(GL_TRIANGLES);
  glVertex2fv(tri); glVertex2fv(tri+2), glVertex2fv(tri+4);
  glEnd();
  glTranslatef(-(x+offset),-(y+offset),0);
  
  if (!enabled) { // once more!
    glTranslatef(x,y,0);
    glColor3ubv(gray);
    glBegin(GL_TRIANGLES);
    glVertex2fv(tri); glVertex2fv(tri+2), glVertex2fv(tri+4);
    glEnd();
    glTranslatef(-x,-y,0);
  }
}


void GLUI_Scrollbar::draw_scroll() {
  update_scroll_parameters();

  // Draw track using a checkerboard background
  const unsigned char scroll_bg[] = {
    0xD4, 0xD0, 0xC8, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xD4, 0xD0, 0xC8
  };
  glColor3f( 1.0, 1.0, 1.0 );
  glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
  glEnable( GL_TEXTURE_2D);
  glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
  glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE,
  	  scroll_bg);
  
  float y0 = horizontal? 0 : GLUI_SCROLL_ARROW_HEIGHT;
  float y1 = horizontal? h : h-GLUI_SCROLL_ARROW_HEIGHT;
  float x0 = horizontal? GLUI_SCROLL_ARROW_WIDTH   : 0;
  float x1 = horizontal? w-GLUI_SCROLL_ARROW_WIDTH : w;
  x0-=0.5; y0+=0.5;
  x1-=0.5; y1+=0.5;
  float dy = y1-y0;
  float dx = x1-x0;
  glBegin(GL_QUADS);
  glTexCoord2f(0,     0);        glVertex2f(x0,y0);
  glTexCoord2f(dx*0.5f,0);       glVertex2f(x1,y0);
  glTexCoord2f(dx*0.5f,dy*0.5f); glVertex2f(x1,y1);
  glTexCoord2f(0,      dy*0.5f); glVertex2f(x0,y1);
  glEnd();
  glDisable(GL_TEXTURE_2D);

  // Draw scroll box
  int box = box_start_position;
  if (horizontal) {
    box += GLUI_SCROLL_ARROW_WIDTH;
    draw_scroll_box(box,1,box_length,h);
  } else {
    box += GLUI_SCROLL_ARROW_HEIGHT+1;
    draw_scroll_box(0,box,w,box_length);
  }
}

/****************************** GLUI_Scrollbar::draw_scroll_box() **********/

void GLUI_Scrollbar::draw_scroll_box(int x, int y, int w, int h)
{
  if (!enabled) return;
  glColor3ubv(&glui->bkgd_color.r);
  glRecti(x,y,x+w,y+h);
  glui->draw_raised_box(x,y, w-1, h-1);
  
  if (active) {
    glEnable( GL_LINE_STIPPLE );
    glLineStipple( 1, 0x5555 );
    glColor3f( 0., 0., 0. );
    glBegin(GL_LINE_LOOP);
    int x1 = x+2, y1 = y+2, x2 = x+w-4, y2 = y+h-4;
    glVertex2i(x1,y1);
    glVertex2i(x2,y1);
    glVertex2i(x2,y2);
    glVertex2i(x1,y2);
    glEnd();
    glDisable( GL_LINE_STIPPLE );
  }
}



/**************************** update_scroll_parameters ***********/

void GLUI_Scrollbar::update_scroll_parameters() {
  track_length = horizontal? 
    this->w-GLUI_SCROLL_ARROW_WIDTH*2 :
    this->h-GLUI_SCROLL_ARROW_HEIGHT*2;
  if (data_type==GLUI_SCROLL_INT) 
  {
    if (int_max==int_min) 
      box_length=track_length;
    else {
      const int MIN_TAB = GLUI_SCROLL_BOX_STD_HEIGHT;
      //box_length = int(track_length/float(visible_range));
      //if (box_length < MIN_TAB)
        box_length = MIN_TAB;
    }
    float pixels_per_unit = (track_length-box_length)/float(int_max-int_min);
    if (horizontal)
      box_start_position = int((int_val-int_min)*pixels_per_unit);
    else 
      box_start_position = int((int_max-int_val)*pixels_per_unit);
    box_end_position = box_start_position+box_length;
  }
  else if (data_type==GLUI_SCROLL_FLOAT) 
  {
    if (float_max==float_min) 
      box_length=track_length;
    else {
      box_length = GLUI_SCROLL_BOX_STD_HEIGHT;
    }
    float pixels_per_unit = (track_length-box_length)/float(float_max-float_min);
    if (horizontal)
      box_start_position = int((float_val-float_min)*pixels_per_unit);
    else 
      box_start_position = int((float_max-float_val)*pixels_per_unit);
    box_end_position = box_start_position+box_length;
  }
}


/********************************* GLUI_Scrollbar::special_handler() **********/

int    GLUI_Scrollbar::special_handler( int key,int modifiers )
{
  if ( !horizontal && key == GLUT_KEY_UP ) {
    mouse_down_handler( x_abs + w - GLUI_SCROLL_ARROW_WIDTH + 1,
      y_abs + 1 );
    mouse_up_handler( x_abs + w - GLUI_SCROLL_ARROW_WIDTH + 1,
      y_abs + 1, true );
  }
  else if ( !horizontal && key == GLUT_KEY_DOWN ) {
    mouse_down_handler(x_abs + w - GLUI_SCROLL_ARROW_WIDTH + 1,
      y_abs+1+GLUI_SCROLL_ARROW_HEIGHT);
    mouse_up_handler( x_abs + w - GLUI_SCROLL_ARROW_WIDTH + 1,
      y_abs+1 +GLUI_SCROLL_ARROW_HEIGHT,
      true );
  }
  if ( horizontal && key == GLUT_KEY_LEFT ) {
    mouse_down_handler( x_abs + 1,y_abs + 1 );
    mouse_up_handler( x_abs + 1,   y_abs + 1, true );
  }
  else if ( horizontal && key == GLUT_KEY_RIGHT ) {
    mouse_down_handler(x_abs + w - GLUI_SCROLL_ARROW_WIDTH + 1,
      y_abs+1);
    mouse_up_handler( x_abs + w - GLUI_SCROLL_ARROW_WIDTH + 1,
      y_abs+1,
      true );
  }
  else if ( key == GLUT_KEY_HOME ) {  /** Set value to limit top - 
                                          or increment by 10 **/
  }
  else if ( key == GLUT_KEY_END ) {  
  }

  return true;
}


/************************************ GLUI_Scrollbar::update_size() **********/

void   GLUI_Scrollbar::update_size( void )
{
  if (horizontal) {
    h = GLUI_SCROLL_ARROW_HEIGHT;
    if (associated_object) {
      this->w = ((GLUI_Control *)associated_object)->w;
    }
  }
  else {
    w = GLUI_SCROLL_ARROW_WIDTH;
    if (associated_object) {
      this->h = ((GLUI_Control *)associated_object)->h;
    }
  }
}
 

/************************************ GLUI_Scrollbar::find_arrow() ************/

int    GLUI_Scrollbar::find_arrow( int local_x, int local_y )
{

  local_x = local_x-x_abs; 
  local_y = local_y-y_abs;

  if (horizontal) 
  {
    if ( local_y >=  h-GLUI_SCROLL_ARROW_HEIGHT-3 && local_y <= h) 
    {
      update_scroll_parameters();
      if ( local_x >= 0 AND local_x <= (GLUI_SCROLL_ARROW_WIDTH+box_start_position) )
      {
        return GLUI_SCROLL_STATE_DOWN;
      }
      if ( local_x >= (GLUI_SCROLL_ARROW_WIDTH+box_end_position)
           AND local_x <= (w+GLUI_SCROLL_ARROW_WIDTH) ) 
      {
        return GLUI_SCROLL_STATE_UP;
      }
      return GLUI_SCROLL_STATE_SCROLL;
    }
  }
  else 
  {
    if ( local_x >=  w-GLUI_SCROLL_ARROW_WIDTH-3 && local_x <= w) 
    {
      update_scroll_parameters();
      if ( local_y >= 0 AND local_y <= (GLUI_SCROLL_ARROW_HEIGHT+box_start_position) )
      {
        return GLUI_SCROLL_STATE_UP;
      }
      if ( local_y >= (GLUI_SCROLL_ARROW_HEIGHT+box_end_position)
           AND local_y <= (h+GLUI_SCROLL_ARROW_HEIGHT) )
      {
        return GLUI_SCROLL_STATE_DOWN;
      }
      return GLUI_SCROLL_STATE_SCROLL;
    }
  }

  return GLUI_SCROLL_STATE_NONE;
}

/***************************************** GLUI_Scrollbar::do_click() **********/

void    GLUI_Scrollbar::do_click( void )
{
  int    direction = 0;

  if ( state == GLUI_SCROLL_STATE_UP )
    direction = +1;
  else if ( state == GLUI_SCROLL_STATE_DOWN )
    direction = -1;

  if (data_type==GLUI_SCROLL_INT&&int_min>int_max) direction*=-1;
  if (data_type==GLUI_SCROLL_FLOAT&&float_min>float_max) direction*=-1;

  increase_growth();

  float modifier_factor = 1.0;
  float incr = growth * modifier_factor * user_speed ;
  
  double frame_time=GLUI_Time()-last_update_time;
  double frame_limit=velocity_limit*frame_time;
  if (incr>frame_limit) incr=frame_limit; /* don't scroll faster than limit */
  last_update_time=GLUI_Time();

  float new_val = float_val;

  new_val += direction * incr;
  if (1 || data_type==GLUI_SCROLL_FLOAT) set_float_val(new_val);
  if (0 && data_type==GLUI_SCROLL_INT) set_int_val((int)new_val);
  //printf("do_click: incr %f  val=%f  float_val=%f\n",incr,new_val,float_val);

  /*** Now update live variable and do callback.  We don't want
    to do the callback on each iteration of this function, just on every 
    i^th iteration, where i is given by GLUI_SCROLL_CALLBACK_INTERVAL ****/
  callback_count++;
  if ( (callback_count % GLUI_SCROLL_CALLBACK_INTERVAL ) == 0 )
    do_callbacks();

}


/***************************************** GLUI_Scrollbar::do_drag() **********/

void    GLUI_Scrollbar::do_drag( int x, int y )
{
  int   direction = 0;
  float incr, modifier_factor;
  /* int delta_x;              */
  int new_int_val = int_val;
  float new_float_val = float_val;

  int free_len = track_length-box_length;
  if (free_len == 0) return;

  modifier_factor = 1.0;
  if ( state == GLUI_SCROLL_STATE_SCROLL) {
    update_scroll_parameters();

    int hbox = box_length/2;
    if (horizontal) {
      int track_v = x-GLUI_SCROLL_ARROW_WIDTH;
      new_int_val = int_min + (track_v-hbox)*(int_max-int_min)/free_len;
      new_float_val = float_min + (track_v-hbox)*(float_max-float_min)/float(free_len); 
    } else {
      int track_v = y-GLUI_SCROLL_ARROW_HEIGHT;
      new_int_val = int_max - (track_v-hbox)*(int_max-int_min)/free_len;
      new_float_val = float_max - (track_v-hbox)*(float_max-float_min)/float(free_len); 
    }
  }
  else {
    if ( state == GLUI_SCROLL_STATE_UP )
      direction = +1;
    else if ( state == GLUI_SCROLL_STATE_DOWN )
      direction = -1;
    incr = growth * direction * modifier_factor * user_speed;
    new_int_val += direction;
    new_float_val += direction * (float_max-float_min)/free_len;
  }
  last_y = y;
  last_x = x;

  /*** Now update live variable and do callback.  We don't want
    to do the callback on each iteration of this function, just on every 
    i^th iteration, where i is given by GLUI_SCROLL_CALLBACK_INTERVAL ****/
  if(data_type==GLUI_SCROLL_INT)
    set_int_val(new_int_val);
  else if (data_type==GLUI_SCROLL_FLOAT)
    set_float_val(new_float_val);

  callback_count++;
  if ( (callback_count % GLUI_SCROLL_CALLBACK_INTERVAL ) == 0 )
    do_callbacks();
}


/***************************************** GLUI_Scrollbar::needs_idle() ******/

bool GLUI_Scrollbar::needs_idle( void ) const
{
  if  (state == GLUI_SCROLL_STATE_UP OR state == GLUI_SCROLL_STATE_DOWN ) {
    return true;
  }
  else {
    return false;
  }
}

/***************************************** GLUI_Scrollbar::idle() **********/

void    GLUI_Scrollbar::idle( void )
{
  if ( NOT needs_idle() )
    return;
  else
    do_click();
}


/************************************ GLUI_Scrollbar::do_callbacks() **********/

void    GLUI_Scrollbar::do_callbacks( void )
{

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

  if ( NOT first_callback ) {
    if ( data_type == GLUI_SCROLL_INT AND int_val == last_int_val ) {
      return;
    }
    if ( data_type == GLUI_SPINNER_FLOAT AND float_val == last_float_val ) {
      return;
    }
  }

  if (associated_object == NULL) {
    this->execute_callback();
  }
  else  {                      // Use internal Callbacks
    if (object_cb) {
      //object_cb(associated_object, int_val);
      object_cb(this);
    }
  }
  last_int_val   = int_val;
  last_float_val = float_val;
  first_callback = false;
}


/********************************** GLUI_Scrollbar::set_float_val() ************/

void   GLUI_Scrollbar::set_float_val( float new_val )
{
  // Allow for the possibility that the limits are reversed
  float hi = MAX(float_min,float_max);
  float lo = MIN(float_min,float_max);
  if (new_val > hi)
    new_val = hi;
  if (new_val < lo)
    new_val = lo;
  last_float_val = float_val;
  float_val = new_val;
  int_val = (int)new_val;

  redraw();

  /*** Now update the live variable ***/
  output_live(true);
}


/********************************** GLUI_Scrollbar::set_int_val() ************/

void   GLUI_Scrollbar::set_int_val( int new_val )
{
  // Allow for the possibility that the limits are reversed
  int hi = MAX(int_min,int_max);
  int lo = MIN(int_min,int_max);
  if (new_val > hi)
    new_val = hi;
  if (new_val < lo)
    new_val = lo;
  last_int_val = int_val;
  float_val = int_val = new_val;

  redraw();

  /*** Now update the live variable ***/
  output_live(true);
}

/*********************************** GLUI_Scrollbar::set_float_limits() *********/

void   GLUI_Scrollbar::set_float_limits( float low, float high, int limit_type )
{
  if (limit_type != GLUI_LIMIT_CLAMP) {
    // error!
  }
  float_min = low;
  float_max = high;
  // Allow for possiblitly of reversed limits
  float lo = MIN(low,high);
  float hi = MAX(low,high);
  if (float_val<lo) set_float_val(lo);
  if (float_val>hi) set_float_val(hi);
}
 

/*********************************** GLUI_Scrollbar::set_int_limits() *********/

void   GLUI_Scrollbar::set_int_limits( int low, int high, int limit_type )
{
  if (limit_type != GLUI_LIMIT_CLAMP) {
    // error!
  }
  int_min = low;
  int_max = high;    
  // Allow for possiblitly of reversed limits
  int lo = MIN(low,high);
  int hi = MAX(low,high);
  if (int_val<lo) set_int_val(lo);
  if (int_val>hi) set_int_val(hi);
  float_min = low;
  float_max = high;
}
 

/*********************************** GLUI_Scrollbar::reset_growth() *************/

void    GLUI_Scrollbar::reset_growth( void )
{
  growth = fabs(float_max - float_min) / float(GLUI_SCROLL_GROWTH_STEPS);
  if (data_type == GLUI_SCROLL_INT && growth<1) growth=1;
}


/******************************* GLUI_Scrollbar::increase_growth() *************/

void    GLUI_Scrollbar::increase_growth( void )
{
  float range=0;
  if (data_type==GLUI_SCROLL_FLOAT)
    range = fabs(float_max-float_min);
  else 
    range = fabs(float(int_max-int_min));
  if ( growth < (range / float(GLUI_SCROLL_MIN_GROWTH_STEPS)) )
    growth *= growth_exp;
  return;
}










