1 /****************************************************************************
2
3 GLUI User Interface Toolkit
4 ---------------------------
5
6 glui_spinner.cpp - GLUI_Spinner class
7
8
9 notes:
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
15
16
17 FIXME: there's a heck of a lot of duplication between this and glui_scrollbar.cpp.
18 (OSL, 2006/06)
19
20
21 --------------------------------------------------
22
23 Copyright (c) 1998 Paul Rademacher
24
25 WWW: http://sourceforge.net/projects/glui/
26 Forums: http://sourceforge.net/forum/?group_id=92496
27
28 This software is provided 'as-is', without any express or implied
29 warranty. In no event will the authors be held liable for any damages
30 arising from the use of this software.
31
32 Permission is granted to anyone to use this software for any purpose,
33 including commercial applications, and to alter it and redistribute it
34 freely, subject to the following restrictions:
35
36 1. The origin of this software must not be misrepresented; you must not
37 claim that you wrote the original software. If you use this software
38 in a product, an acknowledgment in the product documentation would be
39 appreciated but is not required.
40 2. Altered source versions must be plainly marked as such, and must not be
41 misrepresented as being the original software.
42 3. This notice may not be removed or altered from any source distribution.
43
44 *****************************************************************************/
45
46 #include "glui_internal_control.h"
47 #include <cmath>
48 #include <cassert>
49
50 /*static int __debug=0; */
51
52 #define GLUI_SPINNER_GROWTH_STEPS 800
53 #define GLUI_SPINNER_MIN_GROWTH_STEPS 100
54 #define GLUI_SPINNER_CALLBACK_INTERVAL 1
55
56
57 /****************************** GLUI_Spinner::GLUI_Spinner() ****************/
58
GLUI_Spinner(GLUI_Node * parent,const char * name,int data_type,int id,GLUI_CB callback)59 GLUI_Spinner::GLUI_Spinner( GLUI_Node* parent, const char *name,
60 int data_type, int id, GLUI_CB callback )
61 {
62 common_construct(parent, name, data_type, NULL, id, callback);
63 }
64
65 /****************************** GLUI_Spinner::GLUI_Spinner() ****************/
66
GLUI_Spinner(GLUI_Node * parent,const char * name,int * live_var,int id,GLUI_CB callback)67 GLUI_Spinner::GLUI_Spinner( GLUI_Node* parent, const char *name,
68 int *live_var, int id, GLUI_CB callback )
69 {
70 common_construct(parent, name, GLUI_SPINNER_INT, live_var, id, callback);
71 }
72
73 /****************************** GLUI_Spinner::GLUI_Spinner() ****************/
74
GLUI_Spinner(GLUI_Node * parent,const char * name,float * live_var,int id,GLUI_CB callback)75 GLUI_Spinner::GLUI_Spinner( GLUI_Node* parent, const char *name,
76 float *live_var, int id, GLUI_CB callback )
77 {
78 common_construct(parent, name, GLUI_SPINNER_FLOAT, live_var, id, callback);
79 }
80
81 /****************************** GLUI_Spinner::GLUI_Spinner() ****************/
82
GLUI_Spinner(GLUI_Node * parent,const char * name,int data_t,void * live_var,int id,GLUI_CB callback)83 GLUI_Spinner::GLUI_Spinner( GLUI_Node *parent, const char *name,
84 int data_t, void *live_var,
85 int id, GLUI_CB callback )
86 {
87 common_construct(parent, name, data_t, live_var, id, callback);
88 }
89
90 /****************************** GLUI_Spinner::common_construct() ************/
91
common_construct(GLUI_Node * parent,const char * name,int data_t,void * data,int id,GLUI_CB cb)92 void GLUI_Spinner::common_construct( GLUI_Node* parent, const char *name,
93 int data_t, void *data,
94 int id, GLUI_CB cb )
95 {
96 common_init();
97
98 if ( NOT strcmp( name, "Spinner Test" ))
99 id=id;
100
101 int text_type;
102 if ( data_t == GLUI_SPINNER_INT ) {
103 text_type = GLUI_EDITTEXT_INT;
104 }
105 else if ( data_t == GLUI_SPINNER_FLOAT ) {
106 text_type = GLUI_EDITTEXT_FLOAT;
107 }
108 else {
109 assert(0); /* Did not pass in a valid data type */
110 }
111
112 user_id = id;
113 data_type = data_t;
114 callback = cb;
115 set_name( name );
116 //glui = parent->get_glui();
117
118 parent->add_control( this );
119
120 GLUI_EditText *txt =
121 new GLUI_EditText( this, name, text_type, data, id, cb);
122
123 edittext = txt; /* Link the edittext to the spinner */
124 /* control->ptr_val = data; */
125
126 edittext->spinner = this; /* Link the spinner to the edittext */
127
128 }
129
130 /****************************** GLUI_Spinner::mouse_down_handler() **********/
131
mouse_down_handler(int local_x,int local_y)132 int GLUI_Spinner::mouse_down_handler( int local_x, int local_y )
133 {
134 this->state = find_arrow( local_x, local_y );
135 GLUI_Master.glui_setIdleFuncIfNecessary();
136
137 /* printf( "spinner: mouse down : %d/%d arrow:%d\n", local_x, local_y,
138 find_arrow( local_x, local_y ));
139 */
140
141 if ( state != GLUI_SPINNER_STATE_UP AND state != GLUI_SPINNER_STATE_DOWN )
142 return true;
143
144 reset_growth();
145 redraw();
146
147 /*** ints and floats behave a bit differently. When you click on
148 an int spinner, you expect the value to immediately go up by 1, whereas
149 for a float it'll go up only by a fractional amount. Therefore, we
150 go ahead and increment by one for int spinners ***/
151 if ( data_type == GLUI_SPINNER_INT ) {
152 if ( state == GLUI_SPINNER_STATE_UP )
153 edittext->set_float_val( edittext->float_val + 1.0 );
154 else if ( state == GLUI_SPINNER_STATE_DOWN )
155 edittext->set_float_val( edittext->float_val - .9 );
156 }
157
158 do_click();
159
160 return false;
161 }
162
163
164 /******************************** GLUI_Spinner::mouse_up_handler() **********/
165
mouse_up_handler(int local_x,int local_y,bool inside)166 int GLUI_Spinner::mouse_up_handler( int local_x, int local_y, bool inside )
167 {
168 state = GLUI_SPINNER_STATE_NONE;
169 GLUI_Master.glui_setIdleFuncIfNecessary();
170
171 /* printf("spinner: mouse up : %d/%d inside: %d\n",local_x,local_y,inside); */
172
173 /*glutSetCursor( GLUT_CURSOR_INHERIT ); */
174 glutSetCursor( GLUT_CURSOR_LEFT_ARROW );
175 redraw();
176
177 /* do_callbacks(); --- stub */
178 /* if ( callback ) */
179 /* callback( this->user_id ); */
180
181 return false;
182 }
183
184
185 /***************************** GLUI_Spinner::mouse_held_down_handler() ******/
186
mouse_held_down_handler(int local_x,int local_y,bool new_inside)187 int GLUI_Spinner::mouse_held_down_handler( int local_x, int local_y,
188 bool new_inside)
189 {
190 int new_state;
191
192 if ( state == GLUI_SPINNER_STATE_NONE )
193 return false;
194
195 /* printf("spinner: mouse held: %d/%d inside: %d\n",local_x,local_y,
196 new_inside);
197 */
198
199 if ( state == GLUI_SPINNER_STATE_BOTH ) { /* dragging? */
200 do_drag( local_x, local_y );
201 }
202 else { /* not dragging */
203 new_state = find_arrow( local_x, local_y );
204
205 if ( new_state == state ) {
206 /** Still in same arrow **/
207 do_click();
208 }
209 else {
210 if ( new_inside OR 1) {
211 /** The state changed, but we're still inside - that
212 means we moved off the arrow: begin dragging **/
213 state = GLUI_SPINNER_STATE_BOTH;
214 }
215 else {
216 /*** Here check y of mouse position to determine whether to
217 drag ***/
218
219 /* ... */
220 }
221 }
222
223 /*** We switched to up/down dragging ***/
224 if ( state == GLUI_SPINNER_STATE_BOTH ) {
225 glutSetCursor( GLUT_CURSOR_UP_DOWN );
226 last_x = local_x;
227 last_y = local_y;
228
229 /** If the spinner has limits, we reset the growth value, since
230 reset_growth() will compute a new growth value for dragging
231 vs. clicking. If the spinner has no limits, then we just let the
232 growth remain at whatever the user has incremented it up to **/
233 if ( edittext->has_limits != GLUI_LIMIT_NONE )
234 reset_growth();
235 }
236
237 redraw();
238 }
239
240 return false;
241 }
242
243
244 /****************************** GLUI_Spinner::key_handler() **********/
245
key_handler(unsigned char key,int modifiers)246 int GLUI_Spinner::key_handler( unsigned char key,int modifiers )
247 {
248
249
250 return true;
251 }
252
253
254 /****************************** GLUI_Spinner::draw() **********/
255
draw(int x,int y)256 void GLUI_Spinner::draw( int x, int y )
257 {
258 GLUI_DRAWINGSENTINAL_IDIOM
259
260 if ( enabled ) {
261 /*** Draw the up arrow either pressed or unrpessed ***/
262 if ( state == GLUI_SPINNER_STATE_UP OR state == GLUI_SPINNER_STATE_BOTH )
263 glui->std_bitmaps.draw( GLUI_STDBITMAP_SPINNER_UP_ON,
264 w-GLUI_SPINNER_ARROW_WIDTH-1,
265 GLUI_SPINNER_ARROW_Y);
266 else
267 glui->std_bitmaps.draw( GLUI_STDBITMAP_SPINNER_UP_OFF,
268 w-GLUI_SPINNER_ARROW_WIDTH-1,
269 GLUI_SPINNER_ARROW_Y);
270
271 /*** Draw the down arrow either pressed or unrpessed ***/
272 if (state == GLUI_SPINNER_STATE_DOWN OR state == GLUI_SPINNER_STATE_BOTH)
273 glui->std_bitmaps.draw( GLUI_STDBITMAP_SPINNER_DOWN_ON,
274 w-GLUI_SPINNER_ARROW_WIDTH-1,
275 GLUI_SPINNER_ARROW_HEIGHT+GLUI_SPINNER_ARROW_Y);
276 else
277 glui->std_bitmaps.draw( GLUI_STDBITMAP_SPINNER_DOWN_OFF,
278 w-GLUI_SPINNER_ARROW_WIDTH-1,
279 GLUI_SPINNER_ARROW_HEIGHT+GLUI_SPINNER_ARROW_Y);
280 }
281 else { /**** The spinner is disabled ****/
282 glui->std_bitmaps.draw( GLUI_STDBITMAP_SPINNER_UP_DIS,
283 w-GLUI_SPINNER_ARROW_WIDTH-1,
284 GLUI_SPINNER_ARROW_Y);
285 glui->std_bitmaps.draw( GLUI_STDBITMAP_SPINNER_DOWN_DIS,
286 w-GLUI_SPINNER_ARROW_WIDTH-1,
287 GLUI_SPINNER_ARROW_HEIGHT+GLUI_SPINNER_ARROW_Y);
288 }
289
290 if ( active ) {
291 glColor3ub( 0, 0, 0 );
292 glEnable( GL_LINE_STIPPLE );
293 glLineStipple( 1, 0x5555 );
294 }
295 else {
296 glColor3ubv( glui->bkgd_color );
297 }
298
299 glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
300 glDisable( GL_CULL_FACE );
301 glBegin( GL_QUADS );
302 glVertex2i( w-GLUI_SPINNER_ARROW_WIDTH-2, 0 );
303 glVertex2i( w, 0 );
304 glVertex2i( w, h );
305 glVertex2i( w-GLUI_SPINNER_ARROW_WIDTH-2, h );
306 glEnd();
307 glDisable( GL_LINE_STIPPLE );
308 glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
309 }
310
311
312 /********************************* GLUI_Spinner::special_handler() **********/
313
special_handler(int key,int modifiers)314 int GLUI_Spinner::special_handler( int key,int modifiers )
315 {
316 if ( key == GLUT_KEY_UP ) { /** Simulate a click in the up arrow **/
317 mouse_down_handler( x_abs + w - GLUI_SPINNER_ARROW_WIDTH + 1,
318 y_abs + GLUI_SPINNER_ARROW_Y+1 );
319 mouse_up_handler( x_abs + w - GLUI_SPINNER_ARROW_WIDTH + 1,
320 y_abs + GLUI_SPINNER_ARROW_Y+1, true );
321 }
322 else if ( key == GLUT_KEY_DOWN ) { /** Simulate a click in the up arrow **/
323 mouse_down_handler(x_abs + w - GLUI_SPINNER_ARROW_WIDTH + 1,
324 y_abs+GLUI_SPINNER_ARROW_Y+1+GLUI_SPINNER_ARROW_HEIGHT);
325 mouse_up_handler( x_abs + w - GLUI_SPINNER_ARROW_WIDTH + 1,
326 y_abs+GLUI_SPINNER_ARROW_Y+1 +GLUI_SPINNER_ARROW_HEIGHT,
327 true );
328 }
329 else if ( key == GLUT_KEY_HOME ) { /** Set value to limit top -
330 or increment by 10 **/
331 }
332 else if ( key == GLUT_KEY_END ) {
333 }
334
335 return true;
336 }
337
338
339 /******************************* GLUI_Spinner::set_float_val() ************/
340
set_float_val(float new_val)341 void GLUI_Spinner::set_float_val( float new_val )
342 {
343 if ( NOT edittext )
344 return;
345
346 edittext->set_float_val( new_val );
347 }
348
349
350 /********************************** GLUI_Spinner::set_int_val() ************/
351
set_int_val(int new_val)352 void GLUI_Spinner::set_int_val( int new_val )
353 {
354 if ( NOT edittext )
355 return;
356
357 edittext->set_int_val( new_val );
358 }
359
360
361 /************************************ GLUI_Spinner::update_size() **********/
362
update_size(void)363 void GLUI_Spinner::update_size( void )
364 {
365 if (!edittext) return;
366 /*edittext->w = this->w - GLUI_SPINNER_ARROW_WIDTH-3; */
367 this->w = edittext->w + GLUI_SPINNER_ARROW_WIDTH + 3;
368 }
369
370
371 /************************************ GLUI_Spinner::find_arrow() ************/
372
find_arrow(int local_x,int local_y)373 int GLUI_Spinner::find_arrow( int local_x, int local_y )
374 {
375 local_x -= x_abs;
376 local_y -= y_abs;
377
378 if ( local_x >= (w - GLUI_SPINNER_ARROW_WIDTH) AND
379 local_x <= w ) {
380
381 if ( local_y >= GLUI_SPINNER_ARROW_Y AND
382 local_y <= (GLUI_SPINNER_ARROW_Y+GLUI_SPINNER_ARROW_HEIGHT) )
383 return GLUI_SPINNER_STATE_UP;
384
385 if ( local_y >= GLUI_SPINNER_ARROW_Y+GLUI_SPINNER_ARROW_HEIGHT AND
386 local_y <= (GLUI_SPINNER_ARROW_Y+GLUI_SPINNER_ARROW_HEIGHT*2) )
387 return GLUI_SPINNER_STATE_DOWN;
388
389 }
390
391 return GLUI_SPINNER_STATE_NONE;
392 }
393
394
395 /***************************************** GLUI_Spinner::do_click() **********/
396
do_click(void)397 void GLUI_Spinner::do_click( void )
398 {
399 int direction = 0;
400 float incr;
401 float modifier_factor;
402
403 if ( state == GLUI_SPINNER_STATE_UP )
404 direction = +1;
405 else if ( state == GLUI_SPINNER_STATE_DOWN )
406 direction = -1;
407
408 increase_growth();
409
410 modifier_factor = 1.0;
411 if ( glui ) {
412 if ( glui->curr_modifiers & GLUT_ACTIVE_SHIFT )
413 modifier_factor = 100.0f;
414 else if ( glui->curr_modifiers & GLUT_ACTIVE_CTRL )
415 modifier_factor = .01f;
416 }
417
418 if ( this->data_type == GLUI_SPINNER_FLOAT OR 1) {
419 incr = growth * direction * modifier_factor * user_speed;
420 edittext->set_float_val( edittext->float_val + incr );
421 /** Remember, edittext mirrors the float and int values ***/
422 }
423
424 /*** Now update live variable and do callback. We don't want
425 to do the callback on each iteration of this function, just on every
426 i^th iteration, where i is given by GLUI_SPINNER_CALLBACK_INTERVAL ****/
427 callback_count++;
428 if ( (callback_count % GLUI_SPINNER_CALLBACK_INTERVAL ) == 0 )
429 do_callbacks();
430 }
431
432
433 /***************************************** GLUI_Spinner::do_drag() **********/
434
do_drag(int x,int y)435 void GLUI_Spinner::do_drag( int x, int y )
436 {
437 int delta_y;
438 float incr, modifier_factor;
439 /* int delta_x; */
440
441 modifier_factor = 1.0f;
442 if ( glui ) {
443 if ( glui->curr_modifiers & GLUT_ACTIVE_SHIFT )
444 modifier_factor = 100.0f;
445 else if ( glui->curr_modifiers & GLUT_ACTIVE_CTRL )
446 modifier_factor = .01f;
447 }
448
449 /* delta_x = x - last_x; */
450 delta_y = -(y - last_y);
451
452 if ( this->data_type == GLUI_SPINNER_FLOAT OR 1 ) {
453 incr = growth * delta_y * modifier_factor * user_speed;
454 edittext->set_float_val( edittext->float_val + incr );
455 /** Remember, edittext mirrors the float and int values ***/
456 }
457
458 last_x = x;
459 last_y = y;
460
461 /*** Now update live variable and do callback. We don't want
462 to do the callback on each iteration of this function, just on every
463 i^th iteration, where i is given by GLUI_SPINNER_CALLBACK_INTERVAL ****/
464
465 callback_count++;
466 if ( (callback_count % GLUI_SPINNER_CALLBACK_INTERVAL ) == 0 )
467 do_callbacks();
468 }
469
470
471 /***************************************** GLUI_Spinner::needs_idle() ******/
472
needs_idle(void) const473 bool GLUI_Spinner::needs_idle( void ) const
474 {
475 if (state == GLUI_SPINNER_STATE_UP OR state == GLUI_SPINNER_STATE_DOWN ) {
476 return true;
477 }
478 else {
479 return false;
480 }
481 }
482
483 /***************************************** GLUI_Spinner::idle() **********/
484
idle(void)485 void GLUI_Spinner::idle( void )
486 {
487 if ( NOT needs_idle() )
488 return;
489 else
490 do_click();
491 }
492
493
494 /************************************ GLUI_Spinner::do_callbacks() **********/
495
do_callbacks(void)496 void GLUI_Spinner::do_callbacks( void )
497 {
498 /*** This is not necessary, b/c edittext automatically updates us ***/
499 if ( NOT edittext )
500 return;
501 this->float_val = edittext->float_val;
502 this->int_val = edittext->int_val;
503 /* *******************************************/
504
505 if ( NOT first_callback ) {
506 if ( data_type == GLUI_SPINNER_INT AND int_val == last_int_val ) {
507 return;
508 }
509
510 if ( data_type == GLUI_SPINNER_FLOAT AND float_val == last_float_val ) {
511 return;
512 }
513 }
514
515 this->execute_callback();
516
517 last_int_val = int_val;
518 last_float_val = float_val;
519 first_callback = false;
520 }
521
522
523 /********************************* GLUI_Spinner::set_float_limits() *********/
524
set_float_limits(float low,float high,int limit_type)525 void GLUI_Spinner::set_float_limits( float low, float high, int limit_type )
526 {
527 if ( NOT edittext )
528 return;
529
530 edittext->set_float_limits( low, high, limit_type );
531 }
532
533
534 /*********************************** GLUI_Spinner::set_int_limits() *********/
535
set_int_limits(int low,int high,int limit_type)536 void GLUI_Spinner::set_int_limits( int low, int high, int limit_type )
537 {
538 if ( NOT edittext )
539 return;
540
541 edittext->set_int_limits( low, high, limit_type );
542 }
543
544
545 /*********************************** GLUI_Spinner:reset_growth() *************/
546
reset_growth(void)547 void GLUI_Spinner::reset_growth( void )
548 {
549 float lo, hi;
550
551 if ( edittext->has_limits == GLUI_LIMIT_NONE ) {
552 if ( data_type == GLUI_SPINNER_FLOAT )
553 growth = sqrt(ABS(edittext->float_val)) * .05f;
554 else if ( data_type == GLUI_SPINNER_INT )
555 growth = .4f;
556 }
557 else {
558 if ( data_type == GLUI_SPINNER_FLOAT ) {
559 lo = edittext->float_low;
560 hi = edittext->float_high;
561 growth = (hi-lo) / GLUI_SPINNER_GROWTH_STEPS;
562 }
563 else if ( data_type == GLUI_SPINNER_INT ) {
564 lo = (float) edittext->int_low;
565 hi = (float) edittext->int_high;
566
567 growth = (hi-lo) / GLUI_SPINNER_GROWTH_STEPS;
568 }
569 }
570
571 if ( growth == 0.0f )
572 growth = .001f;
573 }
574
575
576 /******************************* GLUI_Spinner:increase_growth() *************/
577
increase_growth(void)578 void GLUI_Spinner::increase_growth( void )
579 {
580 float hi = 0.0,lo = 0.0;
581
582 if ( data_type == GLUI_SPINNER_FLOAT ) {
583 lo = edittext->float_low;
584 hi = edittext->float_high;
585 }
586 else if ( data_type == GLUI_SPINNER_INT ) {
587 lo = (float) edittext->int_low;
588 hi = (float) edittext->int_high;
589 }
590
591 if ( growth < (hi-lo) / GLUI_SPINNER_MIN_GROWTH_STEPS )
592 growth *= growth_exp;
593
594 /* printf( "growth: %f\n", growth ); */
595 }
596
597
598 /*************************************** GLUI_Spinner:get_text() *************/
599
get_text(void)600 const char *GLUI_Spinner::get_text( void )
601 {
602 if (edittext)
603 return edittext->text.c_str();
604 else
605 return "";
606 }
607
608
609 /********************************** GLUI_Spinner:get_float_val() *************/
610
get_float_val(void)611 float GLUI_Spinner::get_float_val( void )
612 {
613 if (edittext)
614 return edittext->float_val;
615 else
616 return 0.0f;
617 }
618
619
620 /********************************** GLUI_Spinner:get_int_val() *************/
621
get_int_val(void)622 int GLUI_Spinner::get_int_val( void )
623 {
624 if (edittext)
625 return edittext->int_val;
626 else
627 return 0;
628 }
629
630
631