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 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.
32
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.
37
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
41
42 *****************************************************************************/
43
44 #include "glui_internal_control.h"
45 #include <cmath>
46 #include <cassert>
47
48 /*static int __debug=0; */
49
50 #define GLUI_SPINNER_GROWTH_STEPS 800
51 #define GLUI_SPINNER_MIN_GROWTH_STEPS 100
52 #define GLUI_SPINNER_CALLBACK_INTERVAL 1
53
54
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 */
58
spinner_edittext_callback(int id)59 void spinner_edittext_callback( int id )
60 {
61 GLUI_Spinner *spinner;
62
63 putchar( '.' ); flushout;
64
65 spinner = (GLUI_Spinner*) id;
66
67 if ( NOT spinner )
68 return;
69
70 spinner->do_callbacks();
71 }
72
73
74 /****************************** GLUI_Spinner::GLUI_Spinner() ****************/
75
GLUI_Spinner(GLUI_Node * parent,const char * name,int data_type,int id,GLUI_CB callback)76 GLUI_Spinner::GLUI_Spinner( GLUI_Node* parent, const char *name,
77 int data_type, int id, GLUI_CB callback )
78 {
79 common_construct(parent, name, data_type, NULL, id, callback);
80 }
81
82 /****************************** GLUI_Spinner::GLUI_Spinner() ****************/
83
GLUI_Spinner(GLUI_Node * parent,const char * name,int * live_var,int id,GLUI_CB callback)84 GLUI_Spinner::GLUI_Spinner( GLUI_Node* parent, const char *name,
85 int *live_var, int id, GLUI_CB callback )
86 {
87 common_construct(parent, name, GLUI_SPINNER_INT, live_var, id, callback);
88 }
89
90 /****************************** GLUI_Spinner::GLUI_Spinner() ****************/
91
GLUI_Spinner(GLUI_Node * parent,const char * name,float * live_var,int id,GLUI_CB callback)92 GLUI_Spinner::GLUI_Spinner( GLUI_Node* parent, const char *name,
93 float *live_var, int id, GLUI_CB callback )
94 {
95 common_construct(parent, name, GLUI_SPINNER_FLOAT, live_var, id, callback);
96 }
97
98 /****************************** GLUI_Spinner::GLUI_Spinner() ****************/
99
GLUI_Spinner(GLUI_Node * parent,const char * name,int data_t,void * live_var,int id,GLUI_CB callback)100 GLUI_Spinner::GLUI_Spinner( GLUI_Node *parent, const char *name,
101 int data_t, void *live_var,
102 int id, GLUI_CB callback )
103 {
104 common_construct(parent, name, data_t, live_var, id, callback);
105 }
106
107 /****************************** GLUI_Spinner::common_construct() ************/
108
common_construct(GLUI_Node * parent,const char * name,int data_t,void * data,int id,GLUI_CB cb)109 void GLUI_Spinner::common_construct( GLUI_Node* parent, const char *name,
110 int data_t, void *data,
111 int id, GLUI_CB cb )
112 {
113 common_init();
114
115 if ( NOT strcmp( name, "Spinner Test" ))
116 id=id;
117
118 int text_type;
119 if ( data_t == GLUI_SPINNER_INT ) {
120 text_type = GLUI_EDITTEXT_INT;
121 }
122 else if ( data_t == GLUI_SPINNER_FLOAT ) {
123 text_type = GLUI_EDITTEXT_FLOAT;
124 }
125 else {
126 assert(0); /* Did not pass in a valid data type */
127 }
128
129 user_id = id;
130 data_type = data_t;
131 callback = cb;
132 set_name( name );
133 //glui = parent->get_glui();
134
135 parent->add_control( this );
136
137 GLUI_EditText *txt =
138 new GLUI_EditText( this, name, text_type, data, id, cb);
139
140 edittext = txt; /* Link the edittext to the spinner */
141 /* control->ptr_val = data; */
142
143 edittext->spinner = this; /* Link the spinner to the edittext */
144
145 }
146
147 /****************************** GLUI_Spinner::mouse_down_handler() **********/
148
mouse_down_handler(int local_x,int local_y)149 int GLUI_Spinner::mouse_down_handler( int local_x, int local_y )
150 {
151 this->state = find_arrow( local_x, local_y );
152 GLUI_Master.glui_setIdleFuncIfNecessary();
153
154 /* printf( "spinner: mouse down : %d/%d arrow:%d\n", local_x, local_y,
155 find_arrow( local_x, local_y ));
156 */
157
158 if ( state != GLUI_SPINNER_STATE_UP AND state != GLUI_SPINNER_STATE_DOWN )
159 return true;
160
161 reset_growth();
162 redraw();
163
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 );
173 }
174
175 do_click();
176
177 return false;
178 }
179
180
181 /******************************** GLUI_Spinner::mouse_up_handler() **********/
182
mouse_up_handler(int local_x,int local_y,bool inside)183 int GLUI_Spinner::mouse_up_handler( int local_x, int local_y, bool inside )
184 {
185 state = GLUI_SPINNER_STATE_NONE;
186 GLUI_Master.glui_setIdleFuncIfNecessary();
187
188 /* printf("spinner: mouse up : %d/%d inside: %d\n",local_x,local_y,inside); */
189
190 /*glutSetCursor( GLUT_CURSOR_INHERIT ); */
191 glutSetCursor( GLUT_CURSOR_LEFT_ARROW );
192 redraw();
193
194 /* do_callbacks(); --- stub */
195 /* if ( callback ) */
196 /* callback( this->user_id ); */
197
198 return false;
199 }
200
201
202 /***************************** GLUI_Spinner::mouse_held_down_handler() ******/
203
mouse_held_down_handler(int local_x,int local_y,bool new_inside)204 int GLUI_Spinner::mouse_held_down_handler( int local_x, int local_y,
205 bool new_inside)
206 {
207 int new_state;
208
209 if ( state == GLUI_SPINNER_STATE_NONE )
210 return false;
211
212 /* printf("spinner: mouse held: %d/%d inside: %d\n",local_x,local_y,
213 new_inside);
214 */
215
216 if ( state == GLUI_SPINNER_STATE_BOTH ) { /* dragging? */
217 do_drag( local_x, local_y );
218 }
219 else { /* not dragging */
220 new_state = find_arrow( local_x, local_y );
221
222 if ( new_state == state ) {
223 /** Still in same arrow **/
224 do_click();
225 }
226 else {
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;
231 }
232 else {
233 /*** Here check y of mouse position to determine whether to
234 drag ***/
235
236 /* ... */
237 }
238 }
239
240 /*** We switched to up/down dragging ***/
241 if ( state == GLUI_SPINNER_STATE_BOTH ) {
242 glutSetCursor( GLUT_CURSOR_UP_DOWN );
243 last_x = local_x;
244 last_y = local_y;
245
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 )
251 reset_growth();
252 }
253
254 redraw();
255 }
256
257 return false;
258 }
259
260
261 /****************************** GLUI_Spinner::key_handler() **********/
262
key_handler(unsigned char key,int modifiers)263 int GLUI_Spinner::key_handler( unsigned char key,int modifiers )
264 {
265
266
267 return true;
268 }
269
270
271 /****************************** GLUI_Spinner::draw() **********/
272
draw(int x,int y)273 void GLUI_Spinner::draw( int x, int y )
274 {
275 GLUI_DRAWINGSENTINAL_IDIOM
276
277 if ( enabled ) {
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);
283 else
284 glui->std_bitmaps.draw( GLUI_STDBITMAP_SPINNER_UP_OFF,
285 w-GLUI_SPINNER_ARROW_WIDTH-1,
286 GLUI_SPINNER_ARROW_Y);
287
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);
293 else
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);
297 }
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);
305 }
306
307 if ( active ) {
308 glColor3ub( 0, 0, 0 );
309 glEnable( GL_LINE_STIPPLE );
310 glLineStipple( 1, 0x5555 );
311 }
312 else {
313 glColor3ub( glui->bkgd_color.r,glui->bkgd_color.g,glui->bkgd_color.b );
314 }
315
316 glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
317 glDisable( GL_CULL_FACE );
318 glBegin( GL_QUADS );
319 glVertex2i( w-GLUI_SPINNER_ARROW_WIDTH-2, 0 );
320 glVertex2i( w, 0 );
321 glVertex2i( w, h );
322 glVertex2i( w-GLUI_SPINNER_ARROW_WIDTH-2, h );
323 glEnd();
324 glDisable( GL_LINE_STIPPLE );
325 glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
326 }
327
328
329 /********************************* GLUI_Spinner::special_handler() **********/
330
special_handler(int key,int modifiers)331 int GLUI_Spinner::special_handler( int key,int modifiers )
332 {
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 );
338 }
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,
344 true );
345 }
346 else if ( key == GLUT_KEY_HOME ) { /** Set value to limit top -
347 or increment by 10 **/
348 }
349 else if ( key == GLUT_KEY_END ) {
350 }
351
352 return true;
353 }
354
355
356 /******************************* GLUI_Spinner::set_float_val() ************/
357
set_float_val(float new_val)358 void GLUI_Spinner::set_float_val( float new_val )
359 {
360 if ( NOT edittext )
361 return;
362
363 edittext->set_float_val( new_val );
364 }
365
366
367 /********************************** GLUI_Spinner::set_int_val() ************/
368
set_int_val(int new_val)369 void GLUI_Spinner::set_int_val( int new_val )
370 {
371 if ( NOT edittext )
372 return;
373
374 edittext->set_int_val( new_val );
375 }
376
377
378 /************************************ GLUI_Spinner::update_size() **********/
379
update_size(void)380 void GLUI_Spinner::update_size( void )
381 {
382 if (!edittext) return;
383 /*edittext->w = this->w - GLUI_SPINNER_ARROW_WIDTH-3; */
384 this->w = edittext->w + GLUI_SPINNER_ARROW_WIDTH + 3;
385 }
386
387
388 /************************************ GLUI_Spinner::find_arrow() ************/
389
find_arrow(int local_x,int local_y)390 int GLUI_Spinner::find_arrow( int local_x, int local_y )
391 {
392 local_x -= x_abs;
393 local_y -= y_abs;
394
395 if ( local_x >= (w - GLUI_SPINNER_ARROW_WIDTH) AND
396 local_x <= w ) {
397
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;
401
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;
405
406 }
407
408 return GLUI_SPINNER_STATE_NONE;
409 }
410
411
412 /***************************************** GLUI_Spinner::do_click() **********/
413
do_click(void)414 void GLUI_Spinner::do_click( void )
415 {
416 int direction = 0;
417 float incr;
418 float modifier_factor;
419
420 if ( state == GLUI_SPINNER_STATE_UP )
421 direction = +1;
422 else if ( state == GLUI_SPINNER_STATE_DOWN )
423 direction = -1;
424
425 increase_growth();
426
427 modifier_factor = 1.0;
428 if ( glui ) {
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;
433 }
434
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 ***/
439 }
440
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 ****/
444 callback_count++;
445 if ( (callback_count % GLUI_SPINNER_CALLBACK_INTERVAL ) == 0 )
446 do_callbacks();
447 }
448
449
450 /***************************************** GLUI_Spinner::do_drag() **********/
451
do_drag(int x,int y)452 void GLUI_Spinner::do_drag( int x, int y )
453 {
454 int delta_y;
455 float incr, modifier_factor;
456 /* int delta_x; */
457
458 modifier_factor = 1.0f;
459 if ( glui ) {
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;
464 }
465
466 /* delta_x = x - last_x; */
467 delta_y = -(y - last_y);
468
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 ***/
473 }
474
475 last_x = x;
476 last_y = y;
477
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 ****/
481
482 callback_count++;
483 if ( (callback_count % GLUI_SPINNER_CALLBACK_INTERVAL ) == 0 )
484 do_callbacks();
485 }
486
487
488 /***************************************** GLUI_Spinner::needs_idle() ******/
489
needs_idle(void) const490 bool GLUI_Spinner::needs_idle( void ) const
491 {
492 if (state == GLUI_SPINNER_STATE_UP OR state == GLUI_SPINNER_STATE_DOWN ) {
493 return true;
494 }
495 else {
496 return false;
497 }
498 }
499
500 /***************************************** GLUI_Spinner::idle() **********/
501
idle(void)502 void GLUI_Spinner::idle( void )
503 {
504 if ( NOT needs_idle() )
505 return;
506 else
507 do_click();
508 }
509
510
511 /************************************ GLUI_Spinner::do_callbacks() **********/
512
do_callbacks(void)513 void GLUI_Spinner::do_callbacks( void )
514 {
515 /*** This is not necessary, b/c edittext automatically updates us ***/
516 if ( NOT edittext )
517 return;
518 this->float_val = edittext->float_val;
519 this->int_val = edittext->int_val;
520 /* *******************************************/
521
522 if ( NOT first_callback ) {
523 if ( data_type == GLUI_SPINNER_INT AND int_val == last_int_val ) {
524 return;
525 }
526
527 if ( data_type == GLUI_SPINNER_FLOAT AND float_val == last_float_val ) {
528 return;
529 }
530 }
531
532 this->execute_callback();
533
534 last_int_val = int_val;
535 last_float_val = float_val;
536 first_callback = false;
537 }
538
539
540 /********************************* GLUI_Spinner::set_float_limits() *********/
541
set_float_limits(float low,float high,int limit_type)542 void GLUI_Spinner::set_float_limits( float low, float high, int limit_type )
543 {
544 if ( NOT edittext )
545 return;
546
547 edittext->set_float_limits( low, high, limit_type );
548 }
549
550
551 /*********************************** GLUI_Spinner::set_int_limits() *********/
552
set_int_limits(int low,int high,int limit_type)553 void GLUI_Spinner::set_int_limits( int low, int high, int limit_type )
554 {
555 if ( NOT edittext )
556 return;
557
558 edittext->set_int_limits( low, high, limit_type );
559 }
560
561
562 /*********************************** GLUI_Spinner:reset_growth() *************/
563
reset_growth(void)564 void GLUI_Spinner::reset_growth( void )
565 {
566 float lo, hi;
567
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 )
572 growth = .4f;
573 }
574 else {
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;
579 }
580 else if ( data_type == GLUI_SPINNER_INT ) {
581 lo = (float) edittext->int_low;
582 hi = (float) edittext->int_high;
583
584 growth = (hi-lo) / GLUI_SPINNER_GROWTH_STEPS;
585 }
586 }
587
588 if ( growth == 0.0f )
589 growth = .001f;
590 }
591
592
593 /******************************* GLUI_Spinner:increase_growth() *************/
594
increase_growth(void)595 void GLUI_Spinner::increase_growth( void )
596 {
597 float hi = 0.0,lo = 0.0;
598
599 if ( data_type == GLUI_SPINNER_FLOAT ) {
600 lo = edittext->float_low;
601 hi = edittext->float_high;
602 }
603 else if ( data_type == GLUI_SPINNER_INT ) {
604 lo = (float) edittext->int_low;
605 hi = (float) edittext->int_high;
606 }
607
608 if ( growth < (hi-lo) / GLUI_SPINNER_MIN_GROWTH_STEPS )
609 growth *= growth_exp;
610
611 /* printf( "growth: %f\n", growth ); */
612 }
613
614
615 /*************************************** GLUI_Spinner:get_text() *************/
616
get_text(void)617 const char *GLUI_Spinner::get_text( void )
618 {
619 if (edittext)
620 return edittext->text.c_str();
621 else
622 return "";
623 }
624
625
626 /********************************** GLUI_Spinner:get_float_val() *************/
627
get_float_val(void)628 float GLUI_Spinner::get_float_val( void )
629 {
630 if (edittext)
631 return edittext->float_val;
632 else
633 return 0.0f;
634 }
635
636
637 /********************************** GLUI_Spinner:get_int_val() *************/
638
get_int_val(void)639 int GLUI_Spinner::get_int_val( void )
640 {
641 if (edittext)
642 return edittext->int_val;
643 else
644 return 0;
645 }
646
647
648