Saxum/extern/bullet/Extras/glui/glui_spinner.cpp
Fabian Klemp aeb6218d2d Renaming.
2014-10-24 11:49:46 +02:00

631 lines
17 KiB
C++

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