/**************************************************************************** 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 -------------------------------------------------- Copyright (c) 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.h" #include "stdinc.h" #include /*static int __debug=0; */ #define GLUI_SPINNER_GROWTH_STEPS 800 #define GLUI_SPINNER_MIN_GROWTH_STEPS 100 #define GLUI_SPINNER_CALLBACK_INTERVAL 1 /****************************** 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 ); /* 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(); if ( can_draw() ) draw_arrows(); /*** 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, int inside ) { state = GLUI_SPINNER_STATE_NONE; /* printf("spinner: mouse up : %d/%d inside: %d\n",local_x,local_y,inside); */ /*glutSetCursor( GLUT_CURSOR_INHERIT ); */ glutSetCursor( GLUT_CURSOR_LEFT_ARROW ); draw_arrows(); /* 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, int 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(); } if ( can_draw() ) draw_arrows(); } 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 ) { int orig; if ( NOT can_draw() ) return; orig = set_to_glut_window(); 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 ); restore_window( orig ); } /********************************* 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 ) { /*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::draw_arrows() **********/ void GLUI_Spinner::draw_arrows( void ) { if ( NOT can_draw() ) return; translate_and_draw_front(); } /***************************************** 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() ******/ int GLUI_Spinner::needs_idle( void ) { 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() *************/ char *GLUI_Spinner::get_text( void ) { if (edittext) return edittext->text; 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; }