1
/****************************************************************************
3
GLUI User Interface Toolkit
4
---------------------------
6
glui_spinner.cpp - GLUI_Spinner class
10
spinner does not explicitly keep track of the current value - this is all
11
handled by the underlying edittext control
12
-> thus, spinner->sync_live() has no meaning, nor spinner->output_live
13
-> BUT, edittext will alter this spinner's float_val and int_val,
14
so that spinner->get/set will work
17
FIXME: there's a heck of a lot of duplication between this and glui_scrollbar.cpp.
21
--------------------------------------------------
23
Copyright (c) 1998 Paul Rademacher
25
WWW: http://sourceforge.net/projects/glui/
26
Forums: http://sourceforge.net/forum/?group_id=92496
28
This library is free software; you can redistribute it and/or
29
modify it under the terms of the GNU Lesser General Public
30
License as published by the Free Software Foundation; either
31
version 2.1 of the License, or (at your option) any later version.
33
This library is distributed in the hope that it will be useful,
34
but WITHOUT ANY WARRANTY; without even the implied warranty of
35
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
36
Lesser General Public License for more details.
38
You should have received a copy of the GNU Lesser General Public
39
License along with this library; if not, write to the Free Software
40
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
42
*****************************************************************************/
44
#include "glui_internal_control.h"
48
/*static int __debug=0; */
50
#define GLUI_SPINNER_GROWTH_STEPS 800
51
#define GLUI_SPINNER_MIN_GROWTH_STEPS 100
52
#define GLUI_SPINNER_CALLBACK_INTERVAL 1
55
/****************************** spinner_edittext_callback() ******************/
56
/* This function is not used anymore. It has been replaced by directly */
57
/* Including an optional pointer to a spinner from an edittext box */
59
void spinner_edittext_callback( int id )
61
GLUI_Spinner *spinner;
63
putchar( '.' ); flushout;
65
spinner = (GLUI_Spinner*) id;
70
spinner->do_callbacks();
74
/****************************** GLUI_Spinner::GLUI_Spinner() ****************/
76
GLUI_Spinner::GLUI_Spinner( GLUI_Node* parent, const char *name,
77
int data_type, int id, GLUI_CB callback )
79
common_construct(parent, name, data_type, NULL, id, callback);
82
/****************************** GLUI_Spinner::GLUI_Spinner() ****************/
84
GLUI_Spinner::GLUI_Spinner( GLUI_Node* parent, const char *name,
85
int *live_var, int id, GLUI_CB callback )
87
common_construct(parent, name, GLUI_SPINNER_INT, live_var, id, callback);
90
/****************************** GLUI_Spinner::GLUI_Spinner() ****************/
92
GLUI_Spinner::GLUI_Spinner( GLUI_Node* parent, const char *name,
93
float *live_var, int id, GLUI_CB callback )
95
common_construct(parent, name, GLUI_SPINNER_FLOAT, live_var, id, callback);
98
/****************************** GLUI_Spinner::GLUI_Spinner() ****************/
100
GLUI_Spinner::GLUI_Spinner( GLUI_Node *parent, const char *name,
101
int data_t, void *live_var,
102
int id, GLUI_CB callback )
104
common_construct(parent, name, data_t, live_var, id, callback);
107
/****************************** GLUI_Spinner::common_construct() ************/
109
void GLUI_Spinner::common_construct( GLUI_Node* parent, const char *name,
110
int data_t, void *data,
115
if ( NOT strcmp( name, "Spinner Test" ))
119
if ( data_t == GLUI_SPINNER_INT ) {
120
text_type = GLUI_EDITTEXT_INT;
122
else if ( data_t == GLUI_SPINNER_FLOAT ) {
123
text_type = GLUI_EDITTEXT_FLOAT;
126
assert(0); /* Did not pass in a valid data type */
133
//glui = parent->get_glui();
135
parent->add_control( this );
138
new GLUI_EditText( this, name, text_type, data, id, cb);
140
edittext = txt; /* Link the edittext to the spinner */
141
/* control->ptr_val = data; */
143
edittext->spinner = this; /* Link the spinner to the edittext */
147
/****************************** GLUI_Spinner::mouse_down_handler() **********/
149
int GLUI_Spinner::mouse_down_handler( int local_x, int local_y )
151
this->state = find_arrow( local_x, local_y );
152
GLUI_Master.glui_setIdleFuncIfNecessary();
154
/* printf( "spinner: mouse down : %d/%d arrow:%d\n", local_x, local_y,
155
find_arrow( local_x, local_y ));
158
if ( state != GLUI_SPINNER_STATE_UP AND state != GLUI_SPINNER_STATE_DOWN )
164
/*** ints and floats behave a bit differently. When you click on
165
an int spinner, you expect the value to immediately go up by 1, whereas
166
for a float it'll go up only by a fractional amount. Therefore, we
167
go ahead and increment by one for int spinners ***/
168
if ( data_type == GLUI_SPINNER_INT ) {
169
if ( state == GLUI_SPINNER_STATE_UP )
170
edittext->set_float_val( edittext->float_val + 1.0 );
171
else if ( state == GLUI_SPINNER_STATE_DOWN )
172
edittext->set_float_val( edittext->float_val - .9 );
181
/******************************** GLUI_Spinner::mouse_up_handler() **********/
183
int GLUI_Spinner::mouse_up_handler( int local_x, int local_y, bool inside )
185
state = GLUI_SPINNER_STATE_NONE;
186
GLUI_Master.glui_setIdleFuncIfNecessary();
188
/* printf("spinner: mouse up : %d/%d inside: %d\n",local_x,local_y,inside); */
190
/*glutSetCursor( GLUT_CURSOR_INHERIT ); */
191
glutSetCursor( GLUT_CURSOR_LEFT_ARROW );
194
/* do_callbacks(); --- stub */
195
/* if ( callback ) */
196
/* callback( this->user_id ); */
202
/***************************** GLUI_Spinner::mouse_held_down_handler() ******/
204
int GLUI_Spinner::mouse_held_down_handler( int local_x, int local_y,
209
if ( state == GLUI_SPINNER_STATE_NONE )
212
/* printf("spinner: mouse held: %d/%d inside: %d\n",local_x,local_y,
216
if ( state == GLUI_SPINNER_STATE_BOTH ) { /* dragging? */
217
do_drag( local_x, local_y );
219
else { /* not dragging */
220
new_state = find_arrow( local_x, local_y );
222
if ( new_state == state ) {
223
/** Still in same arrow **/
227
if ( new_inside OR 1) {
228
/** The state changed, but we're still inside - that
229
means we moved off the arrow: begin dragging **/
230
state = GLUI_SPINNER_STATE_BOTH;
233
/*** Here check y of mouse position to determine whether to
240
/*** We switched to up/down dragging ***/
241
if ( state == GLUI_SPINNER_STATE_BOTH ) {
242
glutSetCursor( GLUT_CURSOR_UP_DOWN );
246
/** If the spinner has limits, we reset the growth value, since
247
reset_growth() will compute a new growth value for dragging
248
vs. clicking. If the spinner has no limits, then we just let the
249
growth remain at whatever the user has incremented it up to **/
250
if ( edittext->has_limits != GLUI_LIMIT_NONE )
261
/****************************** GLUI_Spinner::key_handler() **********/
263
int GLUI_Spinner::key_handler( unsigned char key,int modifiers )
271
/****************************** GLUI_Spinner::draw() **********/
273
void GLUI_Spinner::draw( int x, int y )
275
GLUI_DRAWINGSENTINAL_IDIOM
278
/*** Draw the up arrow either pressed or unrpessed ***/
279
if ( state == GLUI_SPINNER_STATE_UP OR state == GLUI_SPINNER_STATE_BOTH )
280
glui->std_bitmaps.draw( GLUI_STDBITMAP_SPINNER_UP_ON,
281
w-GLUI_SPINNER_ARROW_WIDTH-1,
282
GLUI_SPINNER_ARROW_Y);
284
glui->std_bitmaps.draw( GLUI_STDBITMAP_SPINNER_UP_OFF,
285
w-GLUI_SPINNER_ARROW_WIDTH-1,
286
GLUI_SPINNER_ARROW_Y);
288
/*** Draw the down arrow either pressed or unrpessed ***/
289
if (state == GLUI_SPINNER_STATE_DOWN OR state == GLUI_SPINNER_STATE_BOTH)
290
glui->std_bitmaps.draw( GLUI_STDBITMAP_SPINNER_DOWN_ON,
291
w-GLUI_SPINNER_ARROW_WIDTH-1,
292
GLUI_SPINNER_ARROW_HEIGHT+GLUI_SPINNER_ARROW_Y);
294
glui->std_bitmaps.draw( GLUI_STDBITMAP_SPINNER_DOWN_OFF,
295
w-GLUI_SPINNER_ARROW_WIDTH-1,
296
GLUI_SPINNER_ARROW_HEIGHT+GLUI_SPINNER_ARROW_Y);
298
else { /**** The spinner is disabled ****/
299
glui->std_bitmaps.draw( GLUI_STDBITMAP_SPINNER_UP_DIS,
300
w-GLUI_SPINNER_ARROW_WIDTH-1,
301
GLUI_SPINNER_ARROW_Y);
302
glui->std_bitmaps.draw( GLUI_STDBITMAP_SPINNER_DOWN_DIS,
303
w-GLUI_SPINNER_ARROW_WIDTH-1,
304
GLUI_SPINNER_ARROW_HEIGHT+GLUI_SPINNER_ARROW_Y);
308
glColor3ub( 0, 0, 0 );
309
glEnable( GL_LINE_STIPPLE );
310
glLineStipple( 1, 0x5555 );
313
glColor3ub( glui->bkgd_color.r,glui->bkgd_color.g,glui->bkgd_color.b );
316
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
317
glDisable( GL_CULL_FACE );
319
glVertex2i( w-GLUI_SPINNER_ARROW_WIDTH-2, 0 );
322
glVertex2i( w-GLUI_SPINNER_ARROW_WIDTH-2, h );
324
glDisable( GL_LINE_STIPPLE );
325
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
329
/********************************* GLUI_Spinner::special_handler() **********/
331
int GLUI_Spinner::special_handler( int key,int modifiers )
333
if ( key == GLUT_KEY_UP ) { /** Simulate a click in the up arrow **/
334
mouse_down_handler( x_abs + w - GLUI_SPINNER_ARROW_WIDTH + 1,
335
y_abs + GLUI_SPINNER_ARROW_Y+1 );
336
mouse_up_handler( x_abs + w - GLUI_SPINNER_ARROW_WIDTH + 1,
337
y_abs + GLUI_SPINNER_ARROW_Y+1, true );
339
else if ( key == GLUT_KEY_DOWN ) { /** Simulate a click in the up arrow **/
340
mouse_down_handler(x_abs + w - GLUI_SPINNER_ARROW_WIDTH + 1,
341
y_abs+GLUI_SPINNER_ARROW_Y+1+GLUI_SPINNER_ARROW_HEIGHT);
342
mouse_up_handler( x_abs + w - GLUI_SPINNER_ARROW_WIDTH + 1,
343
y_abs+GLUI_SPINNER_ARROW_Y+1 +GLUI_SPINNER_ARROW_HEIGHT,
346
else if ( key == GLUT_KEY_HOME ) { /** Set value to limit top -
347
or increment by 10 **/
349
else if ( key == GLUT_KEY_END ) {
356
/******************************* GLUI_Spinner::set_float_val() ************/
358
void GLUI_Spinner::set_float_val( float new_val )
363
edittext->set_float_val( new_val );
367
/********************************** GLUI_Spinner::set_int_val() ************/
369
void GLUI_Spinner::set_int_val( int new_val )
374
edittext->set_int_val( new_val );
378
/************************************ GLUI_Spinner::update_size() **********/
380
void GLUI_Spinner::update_size( void )
382
if (!edittext) return;
383
/*edittext->w = this->w - GLUI_SPINNER_ARROW_WIDTH-3; */
384
this->w = edittext->w + GLUI_SPINNER_ARROW_WIDTH + 3;
388
/************************************ GLUI_Spinner::find_arrow() ************/
390
int GLUI_Spinner::find_arrow( int local_x, int local_y )
395
if ( local_x >= (w - GLUI_SPINNER_ARROW_WIDTH) AND
398
if ( local_y >= GLUI_SPINNER_ARROW_Y AND
399
local_y <= (GLUI_SPINNER_ARROW_Y+GLUI_SPINNER_ARROW_HEIGHT) )
400
return GLUI_SPINNER_STATE_UP;
402
if ( local_y >= GLUI_SPINNER_ARROW_Y+GLUI_SPINNER_ARROW_HEIGHT AND
403
local_y <= (GLUI_SPINNER_ARROW_Y+GLUI_SPINNER_ARROW_HEIGHT*2) )
404
return GLUI_SPINNER_STATE_DOWN;
408
return GLUI_SPINNER_STATE_NONE;
412
/***************************************** GLUI_Spinner::do_click() **********/
414
void GLUI_Spinner::do_click( void )
418
float modifier_factor;
420
if ( state == GLUI_SPINNER_STATE_UP )
422
else if ( state == GLUI_SPINNER_STATE_DOWN )
427
modifier_factor = 1.0;
429
if ( glui->curr_modifiers & GLUT_ACTIVE_SHIFT )
430
modifier_factor = 100.0f;
431
else if ( glui->curr_modifiers & GLUT_ACTIVE_CTRL )
432
modifier_factor = .01f;
435
if ( this->data_type == GLUI_SPINNER_FLOAT OR 1) {
436
incr = growth * direction * modifier_factor * user_speed;
437
edittext->set_float_val( edittext->float_val + incr );
438
/** Remember, edittext mirrors the float and int values ***/
441
/*** Now update live variable and do callback. We don't want
442
to do the callback on each iteration of this function, just on every
443
i^th iteration, where i is given by GLUI_SPINNER_CALLBACK_INTERVAL ****/
445
if ( (callback_count % GLUI_SPINNER_CALLBACK_INTERVAL ) == 0 )
450
/***************************************** GLUI_Spinner::do_drag() **********/
452
void GLUI_Spinner::do_drag( int x, int y )
455
float incr, modifier_factor;
458
modifier_factor = 1.0f;
460
if ( glui->curr_modifiers & GLUT_ACTIVE_SHIFT )
461
modifier_factor = 100.0f;
462
else if ( glui->curr_modifiers & GLUT_ACTIVE_CTRL )
463
modifier_factor = .01f;
466
/* delta_x = x - last_x; */
467
delta_y = -(y - last_y);
469
if ( this->data_type == GLUI_SPINNER_FLOAT OR 1 ) {
470
incr = growth * delta_y * modifier_factor * user_speed;
471
edittext->set_float_val( edittext->float_val + incr );
472
/** Remember, edittext mirrors the float and int values ***/
478
/*** Now update live variable and do callback. We don't want
479
to do the callback on each iteration of this function, just on every
480
i^th iteration, where i is given by GLUI_SPINNER_CALLBACK_INTERVAL ****/
483
if ( (callback_count % GLUI_SPINNER_CALLBACK_INTERVAL ) == 0 )
488
/***************************************** GLUI_Spinner::needs_idle() ******/
490
bool GLUI_Spinner::needs_idle( void ) const
492
if (state == GLUI_SPINNER_STATE_UP OR state == GLUI_SPINNER_STATE_DOWN ) {
500
/***************************************** GLUI_Spinner::idle() **********/
502
void GLUI_Spinner::idle( void )
504
if ( NOT needs_idle() )
511
/************************************ GLUI_Spinner::do_callbacks() **********/
513
void GLUI_Spinner::do_callbacks( void )
515
/*** This is not necessary, b/c edittext automatically updates us ***/
518
this->float_val = edittext->float_val;
519
this->int_val = edittext->int_val;
520
/* *******************************************/
522
if ( NOT first_callback ) {
523
if ( data_type == GLUI_SPINNER_INT AND int_val == last_int_val ) {
527
if ( data_type == GLUI_SPINNER_FLOAT AND float_val == last_float_val ) {
532
this->execute_callback();
534
last_int_val = int_val;
535
last_float_val = float_val;
536
first_callback = false;
540
/********************************* GLUI_Spinner::set_float_limits() *********/
542
void GLUI_Spinner::set_float_limits( float low, float high, int limit_type )
547
edittext->set_float_limits( low, high, limit_type );
551
/*********************************** GLUI_Spinner::set_int_limits() *********/
553
void GLUI_Spinner::set_int_limits( int low, int high, int limit_type )
558
edittext->set_int_limits( low, high, limit_type );
562
/*********************************** GLUI_Spinner:reset_growth() *************/
564
void GLUI_Spinner::reset_growth( void )
568
if ( edittext->has_limits == GLUI_LIMIT_NONE ) {
569
if ( data_type == GLUI_SPINNER_FLOAT )
570
growth = sqrt(ABS(edittext->float_val)) * .05f;
571
else if ( data_type == GLUI_SPINNER_INT )
575
if ( data_type == GLUI_SPINNER_FLOAT ) {
576
lo = edittext->float_low;
577
hi = edittext->float_high;
578
growth = (hi-lo) / GLUI_SPINNER_GROWTH_STEPS;
580
else if ( data_type == GLUI_SPINNER_INT ) {
581
lo = (float) edittext->int_low;
582
hi = (float) edittext->int_high;
584
growth = (hi-lo) / GLUI_SPINNER_GROWTH_STEPS;
588
if ( growth == 0.0f )
593
/******************************* GLUI_Spinner:increase_growth() *************/
595
void GLUI_Spinner::increase_growth( void )
597
float hi = 0.0,lo = 0.0;
599
if ( data_type == GLUI_SPINNER_FLOAT ) {
600
lo = edittext->float_low;
601
hi = edittext->float_high;
603
else if ( data_type == GLUI_SPINNER_INT ) {
604
lo = (float) edittext->int_low;
605
hi = (float) edittext->int_high;
608
if ( growth < (hi-lo) / GLUI_SPINNER_MIN_GROWTH_STEPS )
609
growth *= growth_exp;
611
/* printf( "growth: %f\n", growth ); */
615
/*************************************** GLUI_Spinner:get_text() *************/
617
const char *GLUI_Spinner::get_text( void )
620
return edittext->text.c_str();
626
/********************************** GLUI_Spinner:get_float_val() *************/
628
float GLUI_Spinner::get_float_val( void )
631
return edittext->float_val;
637
/********************************** GLUI_Spinner:get_int_val() *************/
639
int GLUI_Spinner::get_int_val( void )
642
return edittext->int_val;