| /**************************************************************************** | |
| GLUI User Interface Toolkit | |
| --------------------------- | |
| glui_spinner.cpp - GLUI_Spinner class | |
| notes: | |
| spinner does not explicitly keep track of the current value - this is all | |
| handled by the underlying edittext control | |
| -> thus, spinner->sync_live() has no meaning, nor spinner->output_live | |
| -> BUT, edittext will alter this spinner's float_val and int_val, | |
| so that spinner->get/set will work | |
| FIXME: there's a heck of a lot of duplication between this and glui_scrollbar.cpp. | |
| (OSL, 2006/06) | |
| -------------------------------------------------- | |
| 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" | |
| #include <cmath> | |
| #include <cassert> | |
| /*static int __debug=0; */ | |
| #define GLUI_SPINNER_GROWTH_STEPS 800 | |
| #define GLUI_SPINNER_MIN_GROWTH_STEPS 100 | |
| #define GLUI_SPINNER_CALLBACK_INTERVAL 1 | |
| /****************************** spinner_edittext_callback() ******************/ | |
| /* This function is not used anymore. It has been replaced by directly */ | |
| /* Including an optional pointer to a spinner from an edittext box */ | |
| void spinner_edittext_callback( int id ) | |
| { | |
| GLUI_Spinner *spinner; | |
| putchar( '.' ); flushout; | |
| spinner = (GLUI_Spinner*) id; | |
| if ( NOT spinner ) | |
| return; | |
| spinner->do_callbacks(); | |
| } | |
| /****************************** GLUI_Spinner::GLUI_Spinner() ****************/ | |
| GLUI_Spinner::GLUI_Spinner( GLUI_Node* parent, const char *name, | |
| int data_type, int id, GLUI_CB callback ) | |
| { | |
| common_construct(parent, name, data_type, NULL, id, callback); | |
| } | |
| /****************************** GLUI_Spinner::GLUI_Spinner() ****************/ | |
| GLUI_Spinner::GLUI_Spinner( GLUI_Node* parent, const char *name, | |
| int *live_var, int id, GLUI_CB callback ) | |
| { | |
| common_construct(parent, name, GLUI_SPINNER_INT, live_var, id, callback); | |
| } | |
| /****************************** GLUI_Spinner::GLUI_Spinner() ****************/ | |
| GLUI_Spinner::GLUI_Spinner( GLUI_Node* parent, const char *name, | |
| float *live_var, int id, GLUI_CB callback ) | |
| { | |
| common_construct(parent, name, GLUI_SPINNER_FLOAT, live_var, id, callback); | |
| } | |
| /****************************** GLUI_Spinner::GLUI_Spinner() ****************/ | |
| GLUI_Spinner::GLUI_Spinner( GLUI_Node *parent, const char *name, | |
| int data_t, void *live_var, | |
| int id, GLUI_CB callback ) | |
| { | |
| common_construct(parent, name, data_t, live_var, id, callback); | |
| } | |
| /****************************** GLUI_Spinner::common_construct() ************/ | |
| void GLUI_Spinner::common_construct( GLUI_Node* parent, const char *name, | |
| int data_t, void *data, | |
| int id, GLUI_CB cb ) | |
| { | |
| common_init(); | |
| if ( NOT strcmp( name, "Spinner Test" )) | |
| id=id; | |
| int text_type; | |
| if ( data_t == GLUI_SPINNER_INT ) { | |
| text_type = GLUI_EDITTEXT_INT; | |
| } | |
| else if ( data_t == GLUI_SPINNER_FLOAT ) { | |
| text_type = GLUI_EDITTEXT_FLOAT; | |
| } | |
| else { | |
| assert(0); /* Did not pass in a valid data type */ | |
| } | |
| user_id = id; | |
| data_type = data_t; | |
| callback = cb; | |
| set_name( name ); | |
| //glui = parent->get_glui(); | |
| parent->add_control( this ); | |
| GLUI_EditText *txt = | |
| new GLUI_EditText( this, name, text_type, data, id, cb); | |
| edittext = txt; /* Link the edittext to the spinner */ | |
| /* control->ptr_val = data; */ | |
| edittext->spinner = this; /* Link the spinner to the edittext */ | |
| } | |
| /****************************** GLUI_Spinner::mouse_down_handler() **********/ | |
| int GLUI_Spinner::mouse_down_handler( int local_x, int local_y ) | |
| { | |
| 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_SPINNER_STATE_UP AND state != GLUI_SPINNER_STATE_DOWN ) | |
| return true; | |
| reset_growth(); | |
| redraw(); | |
| /*** 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 ( data_type == GLUI_SPINNER_INT ) { | |
| if ( state == GLUI_SPINNER_STATE_UP ) | |
| edittext->set_float_val( edittext->float_val + 1.0 ); | |
| else if ( state == GLUI_SPINNER_STATE_DOWN ) | |
| edittext->set_float_val( edittext->float_val - .9 ); | |
| } | |
| do_click(); | |
| return false; | |
| } | |
| /******************************** GLUI_Spinner::mouse_up_handler() **********/ | |
| int GLUI_Spinner::mouse_up_handler( int local_x, int local_y, bool inside ) | |
| { | |
| state = GLUI_SPINNER_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_Spinner::mouse_held_down_handler() ******/ | |
| int GLUI_Spinner::mouse_held_down_handler( int local_x, int local_y, | |
| bool new_inside) | |
| { | |
| int new_state; | |
| if ( state == GLUI_SPINNER_STATE_NONE ) | |
| return false; | |
| /* printf("spinner: mouse held: %d/%d inside: %d\n",local_x,local_y, | |
| new_inside); | |
| */ | |
| if ( state == GLUI_SPINNER_STATE_BOTH ) { /* dragging? */ | |
| do_drag( local_x, local_y ); | |
| } | |
| else { /* not dragging */ | |
| new_state = find_arrow( local_x, local_y ); | |
| if ( new_state == state ) { | |
| /** Still in same arrow **/ | |
| do_click(); | |
| } | |
| else { | |
| if ( new_inside OR 1) { | |
| /** The state changed, but we're still inside - that | |
| means we moved off the arrow: begin dragging **/ | |
| state = GLUI_SPINNER_STATE_BOTH; | |
| } | |
| else { | |
| /*** Here check y of mouse position to determine whether to | |
| drag ***/ | |
| /* ... */ | |
| } | |
| } | |
| /*** We switched to up/down dragging ***/ | |
| if ( state == GLUI_SPINNER_STATE_BOTH ) { | |
| glutSetCursor( GLUT_CURSOR_UP_DOWN ); | |
| last_x = local_x; | |
| last_y = local_y; | |
| /** If the spinner has limits, we reset the growth value, since | |
| reset_growth() will compute a new growth value for dragging | |
| vs. clicking. If the spinner has no limits, then we just let the | |
| growth remain at whatever the user has incremented it up to **/ | |
| if ( edittext->has_limits != GLUI_LIMIT_NONE ) | |
| reset_growth(); | |
| } | |
| redraw(); | |
| } | |
| return false; | |
| } | |
| /****************************** GLUI_Spinner::key_handler() **********/ | |
| int GLUI_Spinner::key_handler( unsigned char key,int modifiers ) | |
| { | |
| return true; | |
| } | |
| /****************************** GLUI_Spinner::draw() **********/ | |
| void GLUI_Spinner::draw( int x, int y ) | |
| { | |
| GLUI_DRAWINGSENTINAL_IDIOM | |
| if ( enabled ) { | |
| /*** Draw the up arrow either pressed or unrpessed ***/ | |
| if ( state == GLUI_SPINNER_STATE_UP OR state == GLUI_SPINNER_STATE_BOTH ) | |
| glui->std_bitmaps.draw( GLUI_STDBITMAP_SPINNER_UP_ON, | |
| w-GLUI_SPINNER_ARROW_WIDTH-1, | |
| GLUI_SPINNER_ARROW_Y); | |
| else | |
| glui->std_bitmaps.draw( GLUI_STDBITMAP_SPINNER_UP_OFF, | |
| w-GLUI_SPINNER_ARROW_WIDTH-1, | |
| GLUI_SPINNER_ARROW_Y); | |
| /*** Draw the down arrow either pressed or unrpessed ***/ | |
| if (state == GLUI_SPINNER_STATE_DOWN OR state == GLUI_SPINNER_STATE_BOTH) | |
| glui->std_bitmaps.draw( GLUI_STDBITMAP_SPINNER_DOWN_ON, | |
| w-GLUI_SPINNER_ARROW_WIDTH-1, | |
| GLUI_SPINNER_ARROW_HEIGHT+GLUI_SPINNER_ARROW_Y); | |
| else | |
| glui->std_bitmaps.draw( GLUI_STDBITMAP_SPINNER_DOWN_OFF, | |
| w-GLUI_SPINNER_ARROW_WIDTH-1, | |
| GLUI_SPINNER_ARROW_HEIGHT+GLUI_SPINNER_ARROW_Y); | |
| } | |
| else { /**** The spinner is disabled ****/ | |
| glui->std_bitmaps.draw( GLUI_STDBITMAP_SPINNER_UP_DIS, | |
| w-GLUI_SPINNER_ARROW_WIDTH-1, | |
| GLUI_SPINNER_ARROW_Y); | |
| glui->std_bitmaps.draw( GLUI_STDBITMAP_SPINNER_DOWN_DIS, | |
| w-GLUI_SPINNER_ARROW_WIDTH-1, | |
| GLUI_SPINNER_ARROW_HEIGHT+GLUI_SPINNER_ARROW_Y); | |
| } | |
| if ( active ) { | |
| glColor3ub( 0, 0, 0 ); | |
| glEnable( GL_LINE_STIPPLE ); | |
| glLineStipple( 1, 0x5555 ); | |
| } | |
| else { | |
| glColor3ub( glui->bkgd_color.r,glui->bkgd_color.g,glui->bkgd_color.b ); | |
| } | |
| glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); | |
| glDisable( GL_CULL_FACE ); | |
| glBegin( GL_QUADS ); | |
| glVertex2i( w-GLUI_SPINNER_ARROW_WIDTH-2, 0 ); | |
| glVertex2i( w, 0 ); | |
| glVertex2i( w, h ); | |
| glVertex2i( w-GLUI_SPINNER_ARROW_WIDTH-2, h ); | |
| glEnd(); | |
| glDisable( GL_LINE_STIPPLE ); | |
| glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); | |
| } | |
| /********************************* GLUI_Spinner::special_handler() **********/ | |
| int GLUI_Spinner::special_handler( int key,int modifiers ) | |
| { | |
| if ( key == GLUT_KEY_UP ) { /** Simulate a click in the up arrow **/ | |
| mouse_down_handler( x_abs + w - GLUI_SPINNER_ARROW_WIDTH + 1, | |
| y_abs + GLUI_SPINNER_ARROW_Y+1 ); | |
| mouse_up_handler( x_abs + w - GLUI_SPINNER_ARROW_WIDTH + 1, | |
| y_abs + GLUI_SPINNER_ARROW_Y+1, true ); | |
| } | |
| else if ( key == GLUT_KEY_DOWN ) { /** Simulate a click in the up arrow **/ | |
| mouse_down_handler(x_abs + w - GLUI_SPINNER_ARROW_WIDTH + 1, | |
| y_abs+GLUI_SPINNER_ARROW_Y+1+GLUI_SPINNER_ARROW_HEIGHT); | |
| mouse_up_handler( x_abs + w - GLUI_SPINNER_ARROW_WIDTH + 1, | |
| y_abs+GLUI_SPINNER_ARROW_Y+1 +GLUI_SPINNER_ARROW_HEIGHT, | |
| 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_Spinner::set_float_val() ************/ | |
| void GLUI_Spinner::set_float_val( float new_val ) | |
| { | |
| if ( NOT edittext ) | |
| return; | |
| edittext->set_float_val( new_val ); | |
| } | |
| /********************************** GLUI_Spinner::set_int_val() ************/ | |
| void GLUI_Spinner::set_int_val( int new_val ) | |
| { | |
| if ( NOT edittext ) | |
| return; | |
| edittext->set_int_val( new_val ); | |
| } | |
| /************************************ GLUI_Spinner::update_size() **********/ | |
| void GLUI_Spinner::update_size( void ) | |
| { | |
| if (!edittext) return; | |
| /*edittext->w = this->w - GLUI_SPINNER_ARROW_WIDTH-3; */ | |
| this->w = edittext->w + GLUI_SPINNER_ARROW_WIDTH + 3; | |
| } | |
| /************************************ GLUI_Spinner::find_arrow() ************/ | |
| int GLUI_Spinner::find_arrow( int local_x, int local_y ) | |
| { | |
| local_x -= x_abs; | |
| local_y -= y_abs; | |
| if ( local_x >= (w - GLUI_SPINNER_ARROW_WIDTH) AND | |
| local_x <= w ) { | |
| if ( local_y >= GLUI_SPINNER_ARROW_Y AND | |
| local_y <= (GLUI_SPINNER_ARROW_Y+GLUI_SPINNER_ARROW_HEIGHT) ) | |
| return GLUI_SPINNER_STATE_UP; | |
| if ( local_y >= GLUI_SPINNER_ARROW_Y+GLUI_SPINNER_ARROW_HEIGHT AND | |
| local_y <= (GLUI_SPINNER_ARROW_Y+GLUI_SPINNER_ARROW_HEIGHT*2) ) | |
| return GLUI_SPINNER_STATE_DOWN; | |
| } | |
| return GLUI_SPINNER_STATE_NONE; | |
| } | |
| /***************************************** GLUI_Spinner::do_click() **********/ | |
| void GLUI_Spinner::do_click( void ) | |
| { | |
| int direction = 0; | |
| float incr; | |
| float modifier_factor; | |
| if ( state == GLUI_SPINNER_STATE_UP ) | |
| direction = +1; | |
| else if ( state == GLUI_SPINNER_STATE_DOWN ) | |
| direction = -1; | |
| increase_growth(); | |
| modifier_factor = 1.0; | |
| if ( glui ) { | |
| if ( glui->curr_modifiers & GLUT_ACTIVE_SHIFT ) | |
| modifier_factor = 100.0f; | |
| else if ( glui->curr_modifiers & GLUT_ACTIVE_CTRL ) | |
| modifier_factor = .01f; | |
| } | |
| if ( this->data_type == GLUI_SPINNER_FLOAT OR 1) { | |
| incr = growth * direction * modifier_factor * user_speed; | |
| edittext->set_float_val( edittext->float_val + incr ); | |
| /** Remember, edittext mirrors the float and int values ***/ | |
| } | |
| /*** 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_SPINNER_CALLBACK_INTERVAL ****/ | |
| callback_count++; | |
| if ( (callback_count % GLUI_SPINNER_CALLBACK_INTERVAL ) == 0 ) | |
| do_callbacks(); | |
| } | |
| /***************************************** GLUI_Spinner::do_drag() **********/ | |
| void GLUI_Spinner::do_drag( int x, int y ) | |
| { | |
| int delta_y; | |
| float incr, modifier_factor; | |
| /* int delta_x; */ | |
| modifier_factor = 1.0f; | |
| if ( glui ) { | |
| if ( glui->curr_modifiers & GLUT_ACTIVE_SHIFT ) | |
| modifier_factor = 100.0f; | |
| else if ( glui->curr_modifiers & GLUT_ACTIVE_CTRL ) | |
| modifier_factor = .01f; | |
| } | |
| /* delta_x = x - last_x; */ | |
| delta_y = -(y - last_y); | |
| if ( this->data_type == GLUI_SPINNER_FLOAT OR 1 ) { | |
| incr = growth * delta_y * modifier_factor * user_speed; | |
| edittext->set_float_val( edittext->float_val + incr ); | |
| /** Remember, edittext mirrors the float and int values ***/ | |
| } | |
| last_x = x; | |
| last_y = y; | |
| /*** 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_SPINNER_CALLBACK_INTERVAL ****/ | |
| callback_count++; | |
| if ( (callback_count % GLUI_SPINNER_CALLBACK_INTERVAL ) == 0 ) | |
| do_callbacks(); | |
| } | |
| /***************************************** GLUI_Spinner::needs_idle() ******/ | |
| bool GLUI_Spinner::needs_idle( void ) const | |
| { | |
| if (state == GLUI_SPINNER_STATE_UP OR state == GLUI_SPINNER_STATE_DOWN ) { | |
| return true; | |
| } | |
| else { | |
| return false; | |
| } | |
| } | |
| /***************************************** GLUI_Spinner::idle() **********/ | |
| void GLUI_Spinner::idle( void ) | |
| { | |
| if ( NOT needs_idle() ) | |
| return; | |
| else | |
| do_click(); | |
| } | |
| /************************************ GLUI_Spinner::do_callbacks() **********/ | |
| void GLUI_Spinner::do_callbacks( void ) | |
| { | |
| /*** This is not necessary, b/c edittext automatically updates us ***/ | |
| if ( NOT edittext ) | |
| return; | |
| this->float_val = edittext->float_val; | |
| this->int_val = edittext->int_val; | |
| /* *******************************************/ | |
| if ( NOT first_callback ) { | |
| if ( data_type == GLUI_SPINNER_INT AND int_val == last_int_val ) { | |
| return; | |
| } | |
| if ( data_type == GLUI_SPINNER_FLOAT AND float_val == last_float_val ) { | |
| return; | |
| } | |
| } | |
| this->execute_callback(); | |
| last_int_val = int_val; | |
| last_float_val = float_val; | |
| first_callback = false; | |
| } | |
| /********************************* GLUI_Spinner::set_float_limits() *********/ | |
| void GLUI_Spinner::set_float_limits( float low, float high, int limit_type ) | |
| { | |
| if ( NOT edittext ) | |
| return; | |
| edittext->set_float_limits( low, high, limit_type ); | |
| } | |
| /*********************************** GLUI_Spinner::set_int_limits() *********/ | |
| void GLUI_Spinner::set_int_limits( int low, int high, int limit_type ) | |
| { | |
| if ( NOT edittext ) | |
| return; | |
| edittext->set_int_limits( low, high, limit_type ); | |
| } | |
| /*********************************** GLUI_Spinner:reset_growth() *************/ | |
| void GLUI_Spinner::reset_growth( void ) | |
| { | |
| float lo, hi; | |
| if ( edittext->has_limits == GLUI_LIMIT_NONE ) { | |
| if ( data_type == GLUI_SPINNER_FLOAT ) | |
| growth = sqrt(ABS(edittext->float_val)) * .05f; | |
| else if ( data_type == GLUI_SPINNER_INT ) | |
| growth = .4f; | |
| } | |
| else { | |
| if ( data_type == GLUI_SPINNER_FLOAT ) { | |
| lo = edittext->float_low; | |
| hi = edittext->float_high; | |
| growth = (hi-lo) / GLUI_SPINNER_GROWTH_STEPS; | |
| } | |
| else if ( data_type == GLUI_SPINNER_INT ) { | |
| lo = (float) edittext->int_low; | |
| hi = (float) edittext->int_high; | |
| growth = (hi-lo) / GLUI_SPINNER_GROWTH_STEPS; | |
| } | |
| } | |
| if ( growth == 0.0f ) | |
| growth = .001f; | |
| } | |
| /******************************* GLUI_Spinner:increase_growth() *************/ | |
| void GLUI_Spinner::increase_growth( void ) | |
| { | |
| float hi = 0.0,lo = 0.0; | |
| if ( data_type == GLUI_SPINNER_FLOAT ) { | |
| lo = edittext->float_low; | |
| hi = edittext->float_high; | |
| } | |
| else if ( data_type == GLUI_SPINNER_INT ) { | |
| lo = (float) edittext->int_low; | |
| hi = (float) edittext->int_high; | |
| } | |
| if ( growth < (hi-lo) / GLUI_SPINNER_MIN_GROWTH_STEPS ) | |
| growth *= growth_exp; | |
| /* printf( "growth: %f\n", growth ); */ | |
| } | |
| /*************************************** GLUI_Spinner:get_text() *************/ | |
| const char *GLUI_Spinner::get_text( void ) | |
| { | |
| if (edittext) | |
| return edittext->text.c_str(); | |
| else | |
| return ""; | |
| } | |
| /********************************** GLUI_Spinner:get_float_val() *************/ | |
| float GLUI_Spinner::get_float_val( void ) | |
| { | |
| if (edittext) | |
| return edittext->float_val; | |
| else | |
| return 0.0f; | |
| } | |
| /********************************** GLUI_Spinner:get_int_val() *************/ | |
| int GLUI_Spinner::get_int_val( void ) | |
| { | |
| if (edittext) | |
| return edittext->int_val; | |
| else | |
| return 0; | |
| } | |