1 /****************************************************************************
3 GLUI User Interface Toolkit
4 ---------------------------
6 glui_scrollbar.cpp - GLUI_Scrollbar class
8 --------------------------------------------------
10 Copyright (c) 2004 John Kew, 1998 Paul Rademacher
12 This program is freely distributable without licensing fees and is
13 provided without guarantee or warrantee expressed or implied. This
14 program is -not- in the public domain.
16 *****************************************************************************/
18 #include "glui_internal_control.h"
22 /*static int __debug=0; */
24 #define GLUI_SCROLL_GROWTH_STEPS 800
25 #define GLUI_SCROLL_MIN_GROWTH_STEPS 100
26 #define GLUI_SCROLL_CALLBACK_INTERVAL 1 /* Execute the user's callback every this many clicks */
30 GLUI_SCROLL_ARROW_DOWN,
31 GLUI_SCROLL_ARROW_LEFT,
32 GLUI_SCROLL_ARROW_RIGHT
36 /****************************** GLUI_Scrollbar::GLUI_Scrollbar() **********/
37 // Constructor, no live var
38 GLUI_Scrollbar::GLUI_Scrollbar( GLUI_Node *parent,
42 int id, GLUI_CB callback
43 /*,GLUI_Control *object
44 ,GLUI_InterObject_CB obj_cb*/
47 common_construct(parent, name, horz_vert, data_type, NULL, id, callback/*, object, obj_cb*/);
50 /****************************** GLUI_Scrollbar::GLUI_Scrollbar() **********/
51 // Constructor, int live var
52 GLUI_Scrollbar::GLUI_Scrollbar( GLUI_Node *parent, const char *name,
55 int id, GLUI_CB callback
56 /*,GLUI_Control *object
57 ,GLUI_InterObject_CB obj_cb*/
60 common_construct(parent, name, horz_vert, GLUI_SCROLL_INT, live_var, id, callback/*, object, obj_cb*/);
63 /****************************** GLUI_Scrollbar::GLUI_Scrollbar() **********/
64 // Constructor, float live var
65 GLUI_Scrollbar::GLUI_Scrollbar( GLUI_Node *parent, const char *name,
68 int id, GLUI_CB callback
69 /*,GLUI_Control *object
70 ,GLUI_InterObject_CB obj_cb*/
73 common_construct(parent, name, horz_vert, GLUI_SCROLL_FLOAT, live_var, id, callback/*, object, obj_cb*/);
76 /****************************** GLUI_Scrollbar::common_init() **********/
77 void GLUI_Scrollbar::common_init(void)
80 h = GLUI_SCROLL_ARROW_HEIGHT;
81 w = GLUI_TEXTBOX_WIDTH;
82 alignment = GLUI_ALIGN_CENTER;
87 state = GLUI_SCROLL_STATE_NONE;
88 growth_exp = GLUI_SCROLL_DEFAULT_GROWTH_EXP;
90 first_callback = true;
96 associated_object = NULL;
98 velocity_limit=50.0; /* Change value by at most 50 per second */
100 box_start_position = 0;
101 box_end_position = 0;
105 /****************************** GLUI_Scrollbar::common_construct() **********/
106 void GLUI_Scrollbar::common_construct(
112 int id, GLUI_CB callback
113 /*,GLUI_Control *object,
114 GLUI_InterObject_CB obj_cb*/
119 // make sure limits are wide enough to hold live value
120 if (data_type==GLUI_SCROLL_FLOAT) {
121 float lo = 0.0f, hi=1.0f;
123 float d = *(float*)(data);
127 this->set_float_limits(lo,hi);
128 this->set_float_val(lo);
129 this->live_type = GLUI_LIVE_FLOAT;
133 int d = *(int*)(data);
137 this->set_int_limits(lo,hi);
138 this->set_int_val(0);
139 this->live_type = GLUI_LIVE_INT;
141 this->data_type = data_type;
142 this->set_ptr_val( data );
143 this->set_name(name);
145 this->callback = callback;
146 //this->associated_object = object;
147 //this->object_cb = obj_cb;
148 this->horizontal=(horz_vert==GLUI_SCROLL_HORIZONTAL);
149 if (this->horizontal) {
150 this->h = GLUI_SCROLL_ARROW_HEIGHT;
151 this->w = GLUI_TEXTBOX_WIDTH;
153 this->h = GLUI_TEXTBOX_HEIGHT;
154 this->w = GLUI_SCROLL_ARROW_WIDTH;
156 parent->add_control( this );
160 /****************************** GLUI_Scrollbar::mouse_down_handler() **********/
162 int GLUI_Scrollbar::mouse_down_handler( int local_x, int local_y )
164 last_update_time=GLUI_Time()-1.0;
165 this->state = find_arrow( local_x, local_y );
166 GLUI_Master.glui_setIdleFuncIfNecessary();
168 /* printf( "spinner: mouse down : %d/%d arrow:%d\n", local_x, local_y,
169 find_arrow( local_x, local_y ));
172 if ( state != GLUI_SCROLL_STATE_UP AND state != GLUI_SCROLL_STATE_DOWN)
177 /*** ints and floats behave a bit differently. When you click on
178 an int spinner, you expect the value to immediately go up by 1, whereas
179 for a float it'll go up only by a fractional amount. Therefore, we
180 go ahead and increment by one for int spinners ***/
182 if ( data_type == GLUI_SCROLL_INT ) {
183 // Allow for possibility of reversed limits
184 int lo = MIN(int_min,int_max);
185 int hi = MAX(int_min,int_max);
186 int increase = int_min < int_max ? 1 : -1;
187 int new_val = int_val;
188 if ( state == GLUI_SCROLL_STATE_UP ) {
190 } else if ( state == GLUI_SCROLL_STATE_DOWN ) {
193 if (new_val >= lo && new_val <= hi && new_val!=int_val) {
194 set_int_val(new_val);
206 /******************************** GLUI_Scrollbar::mouse_up_handler() **********/
208 int GLUI_Scrollbar::mouse_up_handler( int local_x, int local_y, bool inside )
210 state = GLUI_SCROLL_STATE_NONE;
211 GLUI_Master.glui_setIdleFuncIfNecessary();
213 /* printf("spinner: mouse up : %d/%d inside: %d\n",local_x,local_y,inside); */
215 /*glutSetCursor( GLUT_CURSOR_INHERIT ); */
216 glutSetCursor( GLUT_CURSOR_LEFT_ARROW );
220 /* do_callbacks(); --- stub */
221 /* if ( callback ) */
222 /* callback( this->user_id ); */
228 /***************************** GLUI_Scrollbar::mouse_held_down_handler() ******/
230 int GLUI_Scrollbar::mouse_held_down_handler( int local_x, int local_y,
234 if ( state == GLUI_SCROLL_STATE_NONE )
237 /* printf("spinner: mouse held: %d/%d inside: %d\n",local_x,local_y,
241 if ( state == GLUI_SCROLL_STATE_SCROLL) { /* dragging? */
242 do_drag( local_x-x_abs, local_y-y_abs );
244 else { /* not dragging */
245 new_state = find_arrow( local_x, local_y );
247 if ( new_state == state ) {
248 /** Still in same arrow **/
258 /****************************** GLUI_Scrollbar::key_handler() **********/
260 int GLUI_Scrollbar::key_handler( unsigned char key,int modifiers )
266 /****************************** GLUI_Scrollbar::draw() **********/
268 void GLUI_Scrollbar::draw( int x, int y )
270 GLUI_DRAWINGSENTINAL_IDIOM
273 draw_scroll_arrow(GLUI_SCROLL_ARROW_LEFT, 0, 0);
274 draw_scroll_arrow(GLUI_SCROLL_ARROW_RIGHT, w-GLUI_SCROLL_ARROW_WIDTH, 0);
276 draw_scroll_arrow(GLUI_SCROLL_ARROW_UP, 0, 0);
277 draw_scroll_arrow(GLUI_SCROLL_ARROW_DOWN, 0, h-GLUI_SCROLL_ARROW_HEIGHT);
283 /****************************** GLUI_Scrollbar::draw_scroll_arrow() **********/
285 void GLUI_Scrollbar::draw_scroll_arrow(int arrowtype, int x, int y)
288 float L=3.5f,HC=7.f,R=10.5f;
289 float T=4.5f,VC=8.f,B=11.5;
290 const float verts[][6]={
291 { L,10.5f, R, 10.5f, HC, 6.5f }, // up arrow
292 { L,6.5f, R, 6.5f, HC,10.5f }, // down arrow
293 { R-2,T, R-2, B, L+1, VC }, // left arrow
294 { L+2,T, L+2, B, R-1, VC } // right arrow
297 const float *tri = NULL;
301 case GLUI_SCROLL_ARROW_UP:
303 if (state & GLUI_SCROLL_STATE_UP) offset = 1;
306 case GLUI_SCROLL_ARROW_DOWN:
308 if (state & GLUI_SCROLL_STATE_DOWN) offset = 1;
311 case GLUI_SCROLL_ARROW_LEFT:
313 if (state & GLUI_SCROLL_STATE_DOWN) offset = 1;
316 case GLUI_SCROLL_ARROW_RIGHT:
318 if (state & GLUI_SCROLL_STATE_UP) offset = 1;
322 return; /* tri is NULL */
325 glColor3ubv(&glui->bkgd_color.r);
326 glRecti(x,y,x+GLUI_SCROLL_ARROW_WIDTH,y+GLUI_SCROLL_ARROW_HEIGHT);
328 glui->draw_raised_box(x,y+1,GLUI_SCROLL_ARROW_WIDTH-1,GLUI_SCROLL_ARROW_HEIGHT-1);
330 glColor3ub(128,128,128);
331 glBegin(GL_LINE_LOOP);
332 int x2=x+GLUI_SCROLL_ARROW_WIDTH, y2=y+GLUI_SCROLL_ARROW_HEIGHT;
340 GLubyte black[]={0,0,0};
341 GLubyte white[]={255,255,255};
342 GLubyte gray[]={128,128,128};
343 GLubyte *color=black;
348 glTranslatef(x+offset,y+offset,0);
350 glBegin(GL_TRIANGLES);
351 glVertex2fv(tri); glVertex2fv(tri+2), glVertex2fv(tri+4);
353 glTranslatef(-(x+offset),-(y+offset),0);
355 if (!enabled) { // once more!
358 glBegin(GL_TRIANGLES);
359 glVertex2fv(tri); glVertex2fv(tri+2), glVertex2fv(tri+4);
361 glTranslatef(-x,-y,0);
366 void GLUI_Scrollbar::draw_scroll() {
367 update_scroll_parameters();
369 // Draw track using a checkerboard background
370 const unsigned char scroll_bg[] = {
371 0xD4, 0xD0, 0xC8, 0xFF, 0xFF, 0xFF,
372 0xFF, 0xFF, 0xFF, 0xD4, 0xD0, 0xC8
374 glColor3f( 1.0, 1.0, 1.0 );
375 glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
376 glEnable( GL_TEXTURE_2D);
377 glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
378 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
379 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
380 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
381 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
382 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE,
385 float y0 = horizontal? 0 : GLUI_SCROLL_ARROW_HEIGHT;
386 float y1 = horizontal? h : h-GLUI_SCROLL_ARROW_HEIGHT;
387 float x0 = horizontal? GLUI_SCROLL_ARROW_WIDTH : 0;
388 float x1 = horizontal? w-GLUI_SCROLL_ARROW_WIDTH : w;
394 glTexCoord2f(0, 0); glVertex2f(x0,y0);
395 glTexCoord2f(dx*0.5f,0); glVertex2f(x1,y0);
396 glTexCoord2f(dx*0.5f,dy*0.5f); glVertex2f(x1,y1);
397 glTexCoord2f(0, dy*0.5f); glVertex2f(x0,y1);
399 glDisable(GL_TEXTURE_2D);
402 int box = box_start_position;
404 box += GLUI_SCROLL_ARROW_WIDTH;
405 draw_scroll_box(box,1,box_length,h);
407 box += GLUI_SCROLL_ARROW_HEIGHT+1;
408 draw_scroll_box(0,box,w,box_length);
412 /****************************** GLUI_Scrollbar::draw_scroll_box() **********/
414 void GLUI_Scrollbar::draw_scroll_box(int x, int y, int w, int h)
416 if (!enabled) return;
417 glColor3ubv(&glui->bkgd_color.r);
418 glRecti(x,y,x+w,y+h);
419 glui->draw_raised_box(x,y, w-1, h-1);
422 glEnable( GL_LINE_STIPPLE );
423 glLineStipple( 1, 0x5555 );
424 glColor3f( 0., 0., 0. );
425 glBegin(GL_LINE_LOOP);
426 int x1 = x+2, y1 = y+2, x2 = x+w-4, y2 = y+h-4;
432 glDisable( GL_LINE_STIPPLE );
438 /**************************** update_scroll_parameters ***********/
440 void GLUI_Scrollbar::update_scroll_parameters() {
441 track_length = horizontal?
442 this->w-GLUI_SCROLL_ARROW_WIDTH*2 :
443 this->h-GLUI_SCROLL_ARROW_HEIGHT*2;
444 if (data_type==GLUI_SCROLL_INT)
446 if (int_max==int_min)
447 box_length=track_length;
449 const int MIN_TAB = GLUI_SCROLL_BOX_STD_HEIGHT;
450 //box_length = int(track_length/float(visible_range));
451 //if (box_length < MIN_TAB)
452 box_length = MIN_TAB;
454 float pixels_per_unit = (track_length-box_length)/float(int_max-int_min);
456 box_start_position = int((int_val-int_min)*pixels_per_unit);
458 box_start_position = int((int_max-int_val)*pixels_per_unit);
459 box_end_position = box_start_position+box_length;
461 else if (data_type==GLUI_SCROLL_FLOAT)
463 if (float_max==float_min)
464 box_length=track_length;
466 box_length = GLUI_SCROLL_BOX_STD_HEIGHT;
468 float pixels_per_unit = (track_length-box_length)/float(float_max-float_min);
470 box_start_position = int((float_val-float_min)*pixels_per_unit);
472 box_start_position = int((float_max-float_val)*pixels_per_unit);
473 box_end_position = box_start_position+box_length;
478 /********************************* GLUI_Scrollbar::special_handler() **********/
480 int GLUI_Scrollbar::special_handler( int key,int modifiers )
482 if ( !horizontal && key == GLUT_KEY_UP ) {
483 mouse_down_handler( x_abs + w - GLUI_SCROLL_ARROW_WIDTH + 1,
485 mouse_up_handler( x_abs + w - GLUI_SCROLL_ARROW_WIDTH + 1,
488 else if ( !horizontal && key == GLUT_KEY_DOWN ) {
489 mouse_down_handler(x_abs + w - GLUI_SCROLL_ARROW_WIDTH + 1,
490 y_abs+1+GLUI_SCROLL_ARROW_HEIGHT);
491 mouse_up_handler( x_abs + w - GLUI_SCROLL_ARROW_WIDTH + 1,
492 y_abs+1 +GLUI_SCROLL_ARROW_HEIGHT,
495 if ( horizontal && key == GLUT_KEY_LEFT ) {
496 mouse_down_handler( x_abs + 1,y_abs + 1 );
497 mouse_up_handler( x_abs + 1, y_abs + 1, true );
499 else if ( horizontal && key == GLUT_KEY_RIGHT ) {
500 mouse_down_handler(x_abs + w - GLUI_SCROLL_ARROW_WIDTH + 1,
502 mouse_up_handler( x_abs + w - GLUI_SCROLL_ARROW_WIDTH + 1,
506 else if ( key == GLUT_KEY_HOME ) { /** Set value to limit top -
507 or increment by 10 **/
509 else if ( key == GLUT_KEY_END ) {
516 /************************************ GLUI_Scrollbar::update_size() **********/
518 void GLUI_Scrollbar::update_size( void )
521 h = GLUI_SCROLL_ARROW_HEIGHT;
522 if (associated_object) {
523 this->w = ((GLUI_Control *)associated_object)->w;
527 w = GLUI_SCROLL_ARROW_WIDTH;
528 if (associated_object) {
529 this->h = ((GLUI_Control *)associated_object)->h;
535 /************************************ GLUI_Scrollbar::find_arrow() ************/
537 int GLUI_Scrollbar::find_arrow( int local_x, int local_y )
540 local_x = local_x-x_abs;
541 local_y = local_y-y_abs;
545 if ( local_y >= h-GLUI_SCROLL_ARROW_HEIGHT-3 && local_y <= h)
547 update_scroll_parameters();
548 if ( local_x >= 0 AND local_x <= (GLUI_SCROLL_ARROW_WIDTH+box_start_position) )
550 return GLUI_SCROLL_STATE_DOWN;
552 if ( local_x >= (GLUI_SCROLL_ARROW_WIDTH+box_end_position)
553 AND local_x <= (w+GLUI_SCROLL_ARROW_WIDTH) )
555 return GLUI_SCROLL_STATE_UP;
557 return GLUI_SCROLL_STATE_SCROLL;
562 if ( local_x >= w-GLUI_SCROLL_ARROW_WIDTH-3 && local_x <= w)
564 update_scroll_parameters();
565 if ( local_y >= 0 AND local_y <= (GLUI_SCROLL_ARROW_HEIGHT+box_start_position) )
567 return GLUI_SCROLL_STATE_UP;
569 if ( local_y >= (GLUI_SCROLL_ARROW_HEIGHT+box_end_position)
570 AND local_y <= (h+GLUI_SCROLL_ARROW_HEIGHT) )
572 return GLUI_SCROLL_STATE_DOWN;
574 return GLUI_SCROLL_STATE_SCROLL;
578 return GLUI_SCROLL_STATE_NONE;
581 /***************************************** GLUI_Scrollbar::do_click() **********/
583 void GLUI_Scrollbar::do_click( void )
587 if ( state == GLUI_SCROLL_STATE_UP )
589 else if ( state == GLUI_SCROLL_STATE_DOWN )
592 if (data_type==GLUI_SCROLL_INT&&int_min>int_max) direction*=-1;
593 if (data_type==GLUI_SCROLL_FLOAT&&float_min>float_max) direction*=-1;
597 float modifier_factor = 1.0;
598 float incr = growth * modifier_factor * user_speed ;
600 double frame_time=GLUI_Time()-last_update_time;
601 double frame_limit=velocity_limit*frame_time;
602 if (incr>frame_limit) incr=frame_limit; /* don't scroll faster than limit */
603 last_update_time=GLUI_Time();
605 float new_val = float_val;
607 new_val += direction * incr;
608 if (1 || data_type==GLUI_SCROLL_FLOAT) set_float_val(new_val);
609 if (0 && data_type==GLUI_SCROLL_INT) set_int_val((int)new_val);
610 //printf("do_click: incr %f val=%f float_val=%f\n",incr,new_val,float_val);
612 /*** Now update live variable and do callback. We don't want
613 to do the callback on each iteration of this function, just on every
614 i^th iteration, where i is given by GLUI_SCROLL_CALLBACK_INTERVAL ****/
616 if ( (callback_count % GLUI_SCROLL_CALLBACK_INTERVAL ) == 0 )
622 /***************************************** GLUI_Scrollbar::do_drag() **********/
624 void GLUI_Scrollbar::do_drag( int x, int y )
627 float incr, modifier_factor;
629 int new_int_val = int_val;
630 float new_float_val = float_val;
632 int free_len = track_length-box_length;
633 if (free_len == 0) return;
635 modifier_factor = 1.0;
636 if ( state == GLUI_SCROLL_STATE_SCROLL) {
637 update_scroll_parameters();
639 int hbox = box_length/2;
641 int track_v = x-GLUI_SCROLL_ARROW_WIDTH;
642 new_int_val = int_min + (track_v-hbox)*(int_max-int_min)/free_len;
643 new_float_val = float_min + (track_v-hbox)*(float_max-float_min)/float(free_len);
645 int track_v = y-GLUI_SCROLL_ARROW_HEIGHT;
646 new_int_val = int_max - (track_v-hbox)*(int_max-int_min)/free_len;
647 new_float_val = float_max - (track_v-hbox)*(float_max-float_min)/float(free_len);
651 if ( state == GLUI_SCROLL_STATE_UP )
653 else if ( state == GLUI_SCROLL_STATE_DOWN )
655 incr = growth * direction * modifier_factor * user_speed;
656 new_int_val += direction;
657 new_float_val += direction * (float_max-float_min)/free_len;
662 /*** Now update live variable and do callback. We don't want
663 to do the callback on each iteration of this function, just on every
664 i^th iteration, where i is given by GLUI_SCROLL_CALLBACK_INTERVAL ****/
665 if(data_type==GLUI_SCROLL_INT)
666 set_int_val(new_int_val);
667 else if (data_type==GLUI_SCROLL_FLOAT)
668 set_float_val(new_float_val);
671 if ( (callback_count % GLUI_SCROLL_CALLBACK_INTERVAL ) == 0 )
676 /***************************************** GLUI_Scrollbar::needs_idle() ******/
678 bool GLUI_Scrollbar::needs_idle( void ) const
680 if (state == GLUI_SCROLL_STATE_UP OR state == GLUI_SCROLL_STATE_DOWN ) {
688 /***************************************** GLUI_Scrollbar::idle() **********/
690 void GLUI_Scrollbar::idle( void )
692 if ( NOT needs_idle() )
699 /************************************ GLUI_Scrollbar::do_callbacks() **********/
701 void GLUI_Scrollbar::do_callbacks( void )
704 /* *******************************************/
706 if ( NOT first_callback ) {
707 if ( data_type == GLUI_SCROLL_INT AND int_val == last_int_val ) {
710 if ( data_type == GLUI_SPINNER_FLOAT AND float_val == last_float_val ) {
715 if (associated_object == NULL) {
716 this->execute_callback();
718 else { // Use internal Callbacks
720 //object_cb(associated_object, int_val);
724 last_int_val = int_val;
725 last_float_val = float_val;
726 first_callback = false;
730 /********************************** GLUI_Scrollbar::set_float_val() ************/
732 void GLUI_Scrollbar::set_float_val( float new_val )
734 // Allow for the possibility that the limits are reversed
735 float hi = MAX(float_min,float_max);
736 float lo = MIN(float_min,float_max);
741 last_float_val = float_val;
743 int_val = (int)new_val;
747 /*** Now update the live variable ***/
752 /********************************** GLUI_Scrollbar::set_int_val() ************/
754 void GLUI_Scrollbar::set_int_val( int new_val )
756 // Allow for the possibility that the limits are reversed
757 int hi = MAX(int_min,int_max);
758 int lo = MIN(int_min,int_max);
763 last_int_val = int_val;
764 float_val = int_val = new_val;
768 /*** Now update the live variable ***/
772 /*********************************** GLUI_Scrollbar::set_float_limits() *********/
774 void GLUI_Scrollbar::set_float_limits( float low, float high, int limit_type )
776 if (limit_type != GLUI_LIMIT_CLAMP) {
781 // Allow for possiblitly of reversed limits
782 float lo = MIN(low,high);
783 float hi = MAX(low,high);
784 if (float_val<lo) set_float_val(lo);
785 if (float_val>hi) set_float_val(hi);
789 /*********************************** GLUI_Scrollbar::set_int_limits() *********/
791 void GLUI_Scrollbar::set_int_limits( int low, int high, int limit_type )
793 if (limit_type != GLUI_LIMIT_CLAMP) {
798 // Allow for possiblitly of reversed limits
799 int lo = MIN(low,high);
800 int hi = MAX(low,high);
801 if (int_val<lo) set_int_val(lo);
802 if (int_val>hi) set_int_val(hi);
808 /*********************************** GLUI_Scrollbar::reset_growth() *************/
810 void GLUI_Scrollbar::reset_growth( void )
812 growth = fabs(float_max - float_min) / float(GLUI_SCROLL_GROWTH_STEPS);
813 if (data_type == GLUI_SCROLL_INT && growth<1) growth=1;
817 /******************************* GLUI_Scrollbar::increase_growth() *************/
819 void GLUI_Scrollbar::increase_growth( void )
822 if (data_type==GLUI_SCROLL_FLOAT)
823 range = fabs(float_max-float_min);
825 range = fabs(float(int_max-int_min));
826 if ( growth < (range / float(GLUI_SCROLL_MIN_GROWTH_STEPS)) )
827 growth *= growth_exp;