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