Imported Upstream version 2.81
[platform/upstream/libbullet.git] / Extras / glui / glui_edittext.cpp
1 /****************************************************************************
2   
3   GLUI User Interface Toolkit
4   ---------------------------
5
6      glui_edittext.cpp - GLUI_EditText control class
7
8
9           --------------------------------------------------
10
11   Copyright (c) 1998 Paul Rademacher
12
13   WWW:    http://sourceforge.net/projects/glui/
14   Forums: http://sourceforge.net/forum/?group_id=92496
15
16   This library is free software; you can redistribute it and/or
17   modify it under the terms of the GNU Lesser General Public
18   License as published by the Free Software Foundation; either
19   version 2.1 of the License, or (at your option) any later version.
20
21   This library is distributed in the hope that it will be useful,
22   but WITHOUT ANY WARRANTY; without even the implied warranty of
23   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24   Lesser General Public License for more details.
25
26   You should have received a copy of the GNU Lesser General Public
27   License along with this library; if not, write to the Free Software
28   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
29
30 *****************************************************************************/
31
32 #include "glui_internal_control.h"
33 #include <cassert>
34
35 /****************************** GLUI_EditText::GLUI_EditText() **********/
36
37 GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name,
38                              int data_type, void *live_var,
39                              int id, GLUI_CB callback )
40 {
41   if (data_type == GLUI_EDITTEXT_TEXT) {
42     live_type = GLUI_LIVE_TEXT;
43   }
44   else if (data_type == GLUI_EDITTEXT_STRING) {
45    data_type = GLUI_EDITTEXT_TEXT;  // EDITTEXT_STRING doesn't really exist.
46                                      // Except as a signal to make a string.
47                                      // It's a backwards-compat hack.
48    live_type = GLUI_LIVE_STRING;
49   }
50   else if (data_type == GLUI_EDITTEXT_INT) {
51     live_type = GLUI_LIVE_INT;
52   }
53   else if (data_type == GLUI_EDITTEXT_FLOAT) {
54     live_type = GLUI_LIVE_FLOAT;
55   }
56   common_construct( parent, name, data_type, live_type, live_var, id, callback );
57 }
58
59 /****************************** GLUI_EditText::GLUI_EditText() **********/
60
61 GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name,
62                               int text_type, int id, GLUI_CB callback )
63 {
64   common_construct( parent, name, text_type, GLUI_LIVE_NONE, 0, id, callback);
65 }
66
67 /****************************** GLUI_EditText::GLUI_EditText() **********/
68
69 GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name,
70                               int *live_var,
71                               int id, GLUI_CB callback )
72 {
73   common_construct( parent, name, GLUI_EDITTEXT_INT, GLUI_LIVE_INT, live_var, id, callback);
74 }
75
76 /****************************** GLUI_EditText::GLUI_EditText() **********/
77
78 GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name,
79                               float *live_var,
80                               int id, GLUI_CB callback )
81 {
82   common_construct( parent, name, GLUI_EDITTEXT_FLOAT, GLUI_LIVE_FLOAT, live_var, id, callback);
83 }
84
85 /****************************** GLUI_EditText::GLUI_EditText() **********/
86
87 GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name, 
88                               char *live_var,
89                               int id, GLUI_CB callback )
90 {
91   common_construct( parent, name, GLUI_EDITTEXT_TEXT, GLUI_LIVE_TEXT, live_var, id, callback);
92 }
93
94 /****************************** GLUI_EditText::GLUI_EditText() **********/
95
96 GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name, 
97                               std::string &live_var,
98                               int id, GLUI_CB callback )
99 {
100   common_construct( parent, name, GLUI_EDITTEXT_TEXT, GLUI_LIVE_STRING, &live_var, id, callback);
101 }
102
103 /****************************** GLUI_EditText::common_construct() **********/
104
105 void GLUI_EditText::common_construct( GLUI_Node *parent, const char *name, 
106                                       int data_t, int live_t, void *data, int id, 
107                                       GLUI_CB cb )
108 {
109   common_init();
110   set_name( name );
111     
112   live_type   = live_t;
113   data_type   = data_t;
114   ptr_val     = data;
115   user_id     = id;
116   callback    = cb;
117     
118
119   if ( live_type == GLUI_LIVE_INT) {
120     if ( data == NULL )
121       set_int_val(int_val);   /** Set to some default, in case of no live var **/
122   }
123   else if ( live_type == GLUI_LIVE_FLOAT ) {
124     num_periods = 1;
125     if ( data == NULL )
126       set_float_val(float_val);   /** Set to some default, in case of no live var **/
127   }
128
129   parent->add_control( this );
130
131   init_live();
132 }
133
134 /****************************** GLUI_EditText::mouse_down_handler() **********/
135
136 int    GLUI_EditText::mouse_down_handler( int local_x, int local_y )
137 {
138   int tmp_insertion_pt;
139
140   if ( debug )    dump( stdout, "-> MOUSE DOWN" );
141
142   tmp_insertion_pt = find_insertion_pt( local_x, local_y );  
143   if ( tmp_insertion_pt == -1 ) {
144     if ( glui )
145       glui->deactivate_current_control(  );
146     return false;
147   }
148
149   insertion_pt = tmp_insertion_pt;
150
151   sel_start = sel_end = insertion_pt;
152
153   if ( can_draw())
154     update_and_draw_text();
155
156   if ( debug )    dump( stdout, "<- MOUSE UP" );
157
158   return true;
159 }
160
161
162 /******************************** GLUI_EditText::mouse_up_handler() **********/
163
164 int    GLUI_EditText::mouse_up_handler( int local_x, int local_y, bool inside )
165 {
166   return false;
167 }
168
169
170 /***************************** GLUI_EditText::mouse_held_down_handler() ******/
171
172 int    GLUI_EditText::mouse_held_down_handler( int local_x, int local_y,
173                                                bool new_inside)
174 {
175   int tmp_pt;
176
177   if ( NOT new_inside ) 
178     return false;
179
180   if ( debug )    dump( stdout, "-> HELD DOWN" );
181   
182   tmp_pt = find_insertion_pt( local_x, local_y );
183   
184   if ( tmp_pt == -1 AND sel_end != 0 ) {    /* moved mouse past left edge */
185     special_handler( GLUT_KEY_LEFT, GLUT_ACTIVE_SHIFT );
186   }
187   else if ( tmp_pt == substring_end+1 AND sel_end != (int) text.length()) {    
188     /* moved mouse past right edge */
189     special_handler( GLUT_KEY_RIGHT, GLUT_ACTIVE_SHIFT );    
190   }
191   else if ( tmp_pt != -1 AND tmp_pt != sel_end ) {
192     sel_end = insertion_pt = tmp_pt;
193     
194     update_and_draw_text();
195   }
196
197   if ( debug )
198     dump( stdout, "<- HELD DOWN" );
199
200   return false;
201 }
202
203
204 /****************************** GLUI_EditText::key_handler() **********/
205
206 int    GLUI_EditText::key_handler( unsigned char key,int modifiers )
207 {
208   int i, regular_key;
209   /* int has_selection;              */
210
211   if ( NOT glui )
212     return false;
213
214   if ( debug )
215     dump( stdout, "-> KEY HANDLER" );
216
217   regular_key = false;
218   bool ctrl_down = (modifiers & GLUT_ACTIVE_CTRL)!=0;
219   /*  has_selection = (sel_start != sel_end);              */
220
221   if ( key == CTRL('m') ) {           /* RETURN */
222     /*    glui->deactivate_current_control();              */
223     deactivate();  /** Force callbacks, etc **/
224     activate(GLUI_ACTIVATE_TAB);     /** Reselect all text **/
225     redraw();
226     return true;
227   }
228   else if ( key  == CTRL('[')) {         /* ESCAPE */
229     glui->deactivate_current_control();
230     return true;
231   }
232   else if ( (key == 127 AND !ctrl_down) OR  /* FORWARD DELETE */
233             ( key == CTRL('d') AND modifiers == GLUT_ACTIVE_CTRL) ) 
234   {
235     if ( sel_start == sel_end ) {   /* no selection */
236       if ( insertion_pt < (int)text.length() ) {
237         /*** See if we're deleting a period in a float data-type box ***/
238         if ( data_type == GLUI_EDITTEXT_FLOAT AND text[insertion_pt]=='.' )
239           num_periods--;
240
241         /*** Shift over string first ***/
242         text.erase(insertion_pt,1);
243       }
244     }
245     else {                         /* There is a selection */
246       clear_substring( MIN(sel_start,sel_end), MAX(sel_start,sel_end ));
247       insertion_pt = MIN(sel_start,sel_end);
248       sel_start = sel_end = insertion_pt;
249     }
250   }
251   else if ( ((key == 127) AND ctrl_down) OR   // Delete word forward
252             ((key == 'd') AND (modifiers == GLUT_ACTIVE_ALT)) )
253   {
254     if ( sel_start == sel_end ) {   /* no selection */
255       sel_start = insertion_pt;
256       sel_end = find_word_break( insertion_pt, +1 );
257     }
258
259     clear_substring( MIN(sel_start,sel_end), MAX(sel_start,sel_end ));
260     insertion_pt = MIN(sel_start,sel_end);
261     sel_start = sel_end = insertion_pt;
262   }
263   else if ( key == CTRL('h') ) {       /* BACKSPACE */
264     if ( sel_start == sel_end ) {   /* no selection */
265       if ( insertion_pt > 0 ) {
266         /*** See if we're deleting a period in a float data-type box ***/
267         if ( data_type == GLUI_EDITTEXT_FLOAT AND text[insertion_pt-1]=='.' )
268           num_periods--;
269
270         /*** Shift over string first ***/
271         insertion_pt--;
272         text.erase(insertion_pt,1);
273       }
274     }
275     else {                         /* There is a selection */
276       clear_substring( MIN(sel_start,sel_end), MAX(sel_start,sel_end ));
277       insertion_pt = MIN(sel_start,sel_end);
278       sel_start = sel_end = insertion_pt;
279     }
280   }
281   else if ( modifiers == GLUT_ACTIVE_CTRL )  /* CTRL ONLY */ 
282   {
283     /* Ctrl-key bindings */
284     if ( key == CTRL('a') ) {
285       return special_handler( GLUT_KEY_HOME, 0 );
286     }
287     else if ( key == CTRL('e') ) {
288       return special_handler( GLUT_KEY_END, 0 );
289     }
290     else if ( key == CTRL('b') ) {
291       return special_handler( GLUT_KEY_LEFT, 0 );
292     }
293     else if ( key == CTRL('f') ) {
294       return special_handler( GLUT_KEY_RIGHT, 0 );
295     }
296     else if ( key == CTRL('p') ) {
297       return special_handler( GLUT_KEY_UP, 0 );
298     }
299     else if ( key == CTRL('n') ) {
300       return special_handler( GLUT_KEY_DOWN, 0 );
301     }
302     else if ( key == CTRL('u') ) { /* ERASE LINE */
303       insertion_pt = 0;  
304       text.erase(0,text.length());
305       sel_start = sel_end = 0;
306     }
307     else if ( key == CTRL('k') ) { /* KILL TO END OF LINE */
308       sel_start = sel_end = insertion_pt;
309       text.erase(insertion_pt,GLUI_String::npos);
310     }
311   }
312   else if ( modifiers == GLUT_ACTIVE_ALT ) /* ALT ONLY */
313   {
314     if ( key == 'b' ) { // Backward word
315       return special_handler ( GLUT_KEY_LEFT, GLUT_ACTIVE_CTRL );
316     }
317     if ( key == 'f' ) { // Forward word
318       return special_handler ( GLUT_KEY_RIGHT, GLUT_ACTIVE_CTRL );
319     }
320   }
321   else if ( (modifiers & GLUT_ACTIVE_CTRL) OR
322             (modifiers & GLUT_ACTIVE_ALT) ) 
323   {
324     /** ignore other keys with modifiers */
325     return true;
326   }
327   else { /* Regular key */    
328     regular_key = true;
329
330     /** Check if we only accept numbers **/
331     if (data_type == GLUI_EDITTEXT_FLOAT ) {
332       if ( (key < '0' OR key > '9') AND key != '.' AND key != '-' )
333         return true;
334
335       if ( key == '-' ) { /* User typed a '-' */
336
337         /* If user has first character selected, then '-' is allowed */
338         if ( NOT ( MIN(sel_start,sel_end) == 0 AND
339                    MAX(sel_start,sel_end) > 0 ) ) {
340
341           /* User does not have 1st char selected */
342           if (insertion_pt != 0 OR text[0] == '-' ) {
343             return true; /* Can only place negative at beginning of text,
344                             and only one of them */
345           }
346         }
347       }
348
349       if ( key == '.' ) {
350         /*printf( "PERIOD: %d\n", num_periods );              */
351
352         if ( num_periods > 0 ) {
353           /** We're trying to type a period, but the text already contains
354           a period.  Check whether the period is contained within
355           is current selection (thus it will be safely replaced) **/
356
357           int period_found = false; 
358           if ( sel_start != sel_end ) {
359             for( i=MIN(sel_end,sel_start); i<MAX(sel_start,sel_end); i++ ) {
360               /*  printf( "%c ", text[i] );              */
361               if ( text[i] == '.' ) {
362                 period_found = true;
363                 break;
364               }
365             }
366           }
367
368           /* printf( "found: %d    num: %d\n", period_found, num_periods );              */
369
370           if ( NOT period_found )
371             return true;
372         }
373       }
374     } 
375     else if (data_type == GLUI_EDITTEXT_INT)    
376     {
377       if ( (key < '0' OR key > '9') AND key != '-' )
378         return true;
379
380       if ( key == '-' ) { /* User typed a '-' */
381
382         /* If user has first character selected, then '-' is allowed */
383         if ( NOT ( MIN(sel_start,sel_end) == 0 AND
384           MAX(sel_start,sel_end) > 0 ) ) {
385
386             /* User does not have 1st char selected */
387             if (insertion_pt != 0 OR text[0] == '-' ) {
388               return true; /* Can only place negative at beginning of text,
389                            and only one of them */
390             }
391           }
392       }
393     }
394
395     /** This is just to get rid of warnings - the flag regular_key is 
396       set if the key was not a backspace, return, whatever.  But I
397       believe if we're here, we know it was a regular key anyway */
398     if ( regular_key ) {
399     }
400
401     /**** If there's a current selection, erase it ******/
402     if ( sel_start != sel_end ) {
403       clear_substring( MIN(sel_start,sel_end), MAX(sel_start,sel_end ));
404       insertion_pt = MIN(sel_start,sel_end);
405       sel_start = sel_end = insertion_pt;
406     }
407
408     /******** We insert the character into the string ***/
409
410     text.insert(insertion_pt,1,key);
411
412     /******** Move the insertion point and substring_end one over ******/
413     insertion_pt++;
414     substring_end++;
415
416     sel_start = sel_end = insertion_pt;
417   }
418
419   /******** Now redraw text ***********/
420   /* Hack to prevent text box from being cleared first **/  
421   /**  int substring_change =  update_substring_bounds();
422   draw_text_only = 
423   (NOT substring_change AND NOT has_selection AND regular_key ); 
424   */
425
426   draw_text_only = false;  /** Well, hack is not yet working **/
427   update_and_draw_text();
428   draw_text_only = false;
429
430
431   if ( debug )
432     dump( stdout, "<- KEY HANDLER" );
433
434   /*** Now look to see if this string has a period ***/
435   num_periods = 0;
436   for( i=0; i<(int)text.length(); i++ )
437     if ( text[i] == '.' )
438       num_periods++;
439
440   return true;
441 }
442
443
444 /****************************** GLUI_EditText::activate() **********/
445
446 void    GLUI_EditText::activate( int how )
447 {
448   if ( debug )
449     dump( stdout, "-> ACTIVATE" );
450
451   active = true;
452
453   if ( how == GLUI_ACTIVATE_MOUSE )
454     return;  /* Don't select everything if activated with mouse */
455
456   orig_text = text;
457
458   sel_start    = 0;
459   sel_end      = (int)text.length();
460   insertion_pt = 0;
461
462   if ( debug )
463     dump( stdout, "<- ACTIVATE" );
464 }
465
466
467 /****************************** GLUI_EditText::deactivate() **********/
468
469 void    GLUI_EditText::deactivate( void )
470 {
471   int    new_int_val;
472   float  new_float_val;
473
474   active = false;
475
476   if ( NOT glui )
477     return;
478
479   if ( debug )
480     dump( stdout, "-> DISACTIVATE" );
481
482   sel_start = sel_end = insertion_pt = -1; 
483
484   /***** Retrieve the current value from the text *****/
485   /***** The live variable will be updated by set_text() ****/
486   if ( data_type == GLUI_EDITTEXT_FLOAT ) {
487     if ( text.length() == 0 ) /* zero-length string - make it "0.0" */
488       text = "0.0";
489
490     new_float_val = atof( text.c_str() );
491
492     set_float_val( new_float_val );
493   }
494   else if ( data_type == GLUI_EDITTEXT_INT ) {
495     if ( text.length() == 0 ) /* zero-length string - make it "0" */
496       text = "0";
497
498     new_int_val = atoi( text.c_str() );
499
500     set_int_val( new_int_val );
501   }
502   else 
503     if ( data_type == GLUI_EDITTEXT_TEXT ) {
504       set_text(text); /* This will force callbacks and gfx refresh */
505     }
506
507   update_substring_bounds();
508
509   /******** redraw text without insertion point ***********/
510   redraw();
511
512   /***** Now do callbacks if value changed ******/
513   if ( orig_text != text ) {
514     this->execute_callback();
515     
516     if ( 0 ) {
517       /* THE CODE BELOW IS FROM WHEN SPINNER ALSO MAINTAINED CALLBACKS    */
518       if ( spinner == NULL ) {   /** Are we independent of a spinner?  **/  
519         if ( callback ) {
520           callback( this );              
521         }              
522       }              
523       else {                      /* We're attached to a spinner */              
524         spinner->do_callbacks();  /* Let the spinner do the callback stuff */  
525       }              
526     }
527   }
528
529   if ( debug )
530     dump( stdout, "<- DISACTIVATE" );
531 }
532
533 /****************************** GLUI_EditText::draw() **********/
534
535 void    GLUI_EditText::draw( int x, int y )
536 {
537   GLUI_DRAWINGSENTINAL_IDIOM
538   int name_x;
539
540   name_x = MAX(text_x_offset - string_width(this->name) - 3,0);
541   draw_name( name_x , 13);
542
543   glBegin( GL_LINES );
544   glColor3f( .5, .5, .5 );
545   glVertex2i( text_x_offset, 0 );     glVertex2i( w, 0 );
546   glVertex2i( text_x_offset, 0 );     glVertex2i( text_x_offset, h );     
547
548   glColor3f( 1., 1., 1. );
549   glVertex2i( text_x_offset, h );     glVertex2i( w, h );
550   glVertex2i( w, h );                 glVertex2i( w, 0 );
551
552   if ( enabled )
553     glColor3f( 0., 0., 0. );
554   else
555     glColor3f( .25, .25, .25 );
556   glVertex2i( text_x_offset+1, 1 );     glVertex2i( w-1, 1 );
557   glVertex2i( text_x_offset+1, 1 );     glVertex2i( text_x_offset+1, h-1 );
558
559   glColor3f( .75, .75, .75 );
560   glVertex2i( text_x_offset+1, h-1 );     glVertex2i( w-1, h-1 );
561   glVertex2i( w-1, h-1 );                 glVertex2i( w-1, 1 );
562   glEnd();
563
564   /** Find where to draw the text **/
565   update_substring_bounds();
566   draw_text(0,0);
567   
568   draw_insertion_pt();
569 }
570
571
572
573 /************************** GLUI_EditText::update_substring_bounds() *********/
574
575 int    GLUI_EditText::update_substring_bounds( void )
576 {
577   int box_width;
578   int text_len = (int)text.length();
579   int old_start, old_end;
580
581   old_start = substring_start;
582   old_end = substring_end;
583
584   /*** Calculate the width of the usable area of the edit box ***/
585   box_width = MAX( this->w - this->text_x_offset 
586                    - 4     /*  2 * the two-line box border */ 
587                    - 2 * GLUI_EDITTEXT_BOXINNERMARGINX, 0 );
588
589   CLAMP( substring_end, 0, MAX(text_len-1,0) );
590   CLAMP( substring_start, 0, MAX(text_len-1,0) );
591
592   if ( debug )    dump( stdout, "-> UPDATE SS" );
593
594   if ( insertion_pt >= 0 AND 
595        insertion_pt < substring_start ) {   /* cursor moved left */
596     substring_start = insertion_pt;
597
598     while ( substring_width( substring_start, substring_end ) > box_width )
599       substring_end--;
600   }
601   else if ( insertion_pt > substring_end ) {  /* cursor moved right */
602     substring_end = insertion_pt-1;
603
604     while ( substring_width( substring_start, substring_end ) > box_width )
605       substring_start++;
606   }
607   else {   /* cursor is within old substring bounds */
608     if ( last_insertion_pt > insertion_pt ) {  /* cursor moved left */
609     }
610     else {
611       while ( substring_width( substring_start, substring_end ) > box_width )
612         substring_end--;
613
614       while(substring_end < text_len-1 
615             AND substring_width( substring_start, substring_end ) <= box_width)
616         substring_end++;
617     }
618   }
619
620   while ( substring_width( substring_start, substring_end ) > box_width )
621     substring_end--;
622
623   last_insertion_pt = insertion_pt;
624
625   /*** No selection if not enabled ***/
626   if ( NOT enabled ) {
627     sel_start = sel_end = 0;
628   }
629
630   if ( debug )    dump( stdout, "<- UPDATE SS" );
631
632   if ( substring_start == old_start AND substring_end == old_end )
633     return false;  /*** bounds did not change ***/
634   else 
635     return true;   /*** bounds did change ***/
636 }
637
638
639 /********************************* GLUI_EditText::update_x_offsets() *********/
640
641 void    GLUI_EditText::update_x_offsets( void )
642 {
643 }
644  
645
646 /********************************* GLUI_EditText::draw_text() ****************/
647
648 void    GLUI_EditText::draw_text( int x, int y )
649 {
650   GLUI_DRAWINGSENTINAL_IDIOM
651   int text_x, i, sel_lo, sel_hi;
652
653   if ( debug )    dump( stdout, "-> DRAW_TEXT" );
654
655   if ( NOT draw_text_only ) {
656     if ( enabled )
657       glColor3f( 1., 1., 1. );
658     else
659       set_to_bkgd_color();
660     glDisable( GL_CULL_FACE );
661     glBegin( GL_QUADS );
662     glVertex2i( text_x_offset+2, 2 );     glVertex2i( w-2, 2 );
663     glVertex2i( w-2, h-2 );               glVertex2i( text_x_offset+2, h-2 );
664     glEnd();
665   }
666
667   /** Find where to draw the text **/
668
669   text_x = text_x_offset + 2 + GLUI_EDITTEXT_BOXINNERMARGINX;
670
671   /*printf( "text_x: %d      substr_width: %d     start/end: %d/%d\n",
672     text_x,     substring_width( substring_start, substring_end ),
673     substring_start, substring_end );
674     */
675   /** Find lower and upper selection bounds **/
676   sel_lo = MIN(sel_start, sel_end );
677   sel_hi = MAX(sel_start, sel_end );
678
679   int sel_x_start, sel_x_end, delta;
680
681   /** Draw selection area dark **/
682   if ( sel_start != sel_end ) {
683     sel_x_start = text_x;
684     sel_x_end   = text_x;
685     for( i=substring_start; i<=substring_end; i++ ) {
686       delta = char_width( text[i] );
687
688       if ( i < sel_lo ) {
689         sel_x_start += delta;
690         sel_x_end   += delta;
691       }
692       else if ( i < sel_hi ) {
693         sel_x_end   += delta;
694       }
695     }
696
697     glColor3f( 0.0f, 0.0f, .6f );
698     glBegin( GL_QUADS );
699     glVertex2i( sel_x_start, 2 );    glVertex2i( sel_x_end, 2 );
700     glVertex2i( sel_x_end, h-2 );    glVertex2i( sel_x_start, h-2 );
701     glEnd();
702   }
703    
704
705   if ( sel_start == sel_end ) {   /* No current selection */
706     if ( enabled )
707       glColor3b( 0, 0, 0 );
708     else
709       glColor3b( 32, 32, 32 );
710       
711     glRasterPos2i( text_x, 13);
712     for( i=substring_start; i<=substring_end; i++ ) {
713       glutBitmapCharacter( get_font(), this->text[i] );
714     }
715   }
716   else {                          /* There is a selection */
717     int x = text_x;
718     for( i=substring_start; i<=substring_end; i++ ) {
719       if ( IN_BOUNDS( i, sel_lo, sel_hi-1)) { /* This character is selected */
720         glColor3f( 1., 1., 1. );
721         glRasterPos2i( x, 13);
722         glutBitmapCharacter( get_font(), this->text[i] );
723       }
724       else {
725         glColor3f( 0., 0., 0. );
726         glRasterPos2i( x, 13);
727         glutBitmapCharacter( get_font(), this->text[i] );
728       }
729       
730       x += char_width( text[i] );
731     }
732   }
733
734   if ( debug )    dump( stdout, "<- DRAW_TEXT" );  
735 }
736
737
738 /******************************** GLUI_EditText::find_insertion_pt() *********/
739 /* This function returns the character numer *before which* the insertion    */
740 /* point goes                                                                */
741
742 int  GLUI_EditText::find_insertion_pt( int x, int y )
743 {
744   int curr_x, i;
745
746   /*** See if we clicked outside box ***/
747   if ( x < this->x_abs + text_x_offset )
748     return -1;
749
750   /* We move from right to left, looking to see if the mouse was clicked
751      to the right of the ith character */
752
753   curr_x = this->x_abs + text_x_offset 
754     + substring_width( substring_start, substring_end )
755     + 2                             /* The edittext box has a 2-pixel margin */
756     + GLUI_EDITTEXT_BOXINNERMARGINX;   /** plus this many pixels blank space
757                                          between the text and the box       **/
758
759   /*** See if we clicked in an empty box ***/
760   if ( (int) text.length() == 0 ) 
761     return 0;
762
763   /** find mouse click in text **/
764   for( i=substring_end; i>=substring_start; i-- ) {
765     curr_x -= char_width( text[i] );
766
767     if ( x > curr_x ) {
768       /*      printf( "-> %d\n", i );              */
769       
770       return i+1;
771     }
772   }
773
774   return 0;
775
776   /* Well, the mouse wasn't after any of the characters...see if it's
777      before the beginning of the substring */
778   if ( 0 ) {
779     if ( x > (x_abs + text_x_offset + 2 ) )
780       return substring_start;
781     
782     return -1; /* Nothing found */
783   }
784 }
785
786
787 /******************************** GLUI_EditText::draw_insertion_pt() *********/
788
789 void     GLUI_EditText::draw_insertion_pt( void )
790 {
791   int curr_x, i;
792
793   if ( NOT can_draw() )
794     return;
795
796   /*** Don't draw insertion pt if control is disabled ***/
797   if ( NOT enabled )
798     return;
799
800   if ( debug )    dump( stdout, "-> DRAW_INS_PT" );
801
802   if ( sel_start != sel_end OR insertion_pt < 0 ) {
803     return;  /* Don't draw insertion point if there is a current selection */
804   }
805
806   /*    printf( "insertion pt: %d\n", insertion_pt );              */
807
808   curr_x = this->x_abs + text_x_offset 
809     + substring_width( substring_start, substring_end )
810     + 2                             /* The edittext box has a 2-pixel margin */
811     + GLUI_EDITTEXT_BOXINNERMARGINX;   /** plus this many pixels blank space
812                                          between the text and the box       **/
813
814   for( i=substring_end; i>=insertion_pt; i-- ) {
815     curr_x -= char_width( text[i] ); 
816   }  
817
818   glColor3f( 0.0, 0.0, 0.0 );
819   glBegin( GL_LINE_LOOP );
820   /***
821     glVertex2i( curr_x, y_abs + 4 );
822     glVertex2i( curr_x, y_abs + 4 );
823     glVertex2i( curr_x, y_abs + h - 3 );
824     glVertex2i( curr_x, y_abs + h - 3 );
825     ***/
826   curr_x -= x_abs;
827   glVertex2i( curr_x, 0 + 4 );
828   glVertex2i( curr_x, 0 + 4 );
829   glVertex2i( curr_x, 0 + h - 3 );
830   glVertex2i( curr_x, 0 + h - 3 );
831   glEnd();
832
833   if ( debug )    dump( stdout, "-> DRAW_INS_PT" );
834 }
835
836
837
838 /******************************** GLUI_EditText::substring_width() *********/
839
840 int  GLUI_EditText::substring_width( int start, int end )
841 {
842   int i, width;
843
844   width = 0;
845
846   for( i=start; i<=end; i++ )
847     width += char_width( text[i] ); 
848
849   return width;
850 }
851  
852
853 /***************************** GLUI_EditText::update_and_draw_text() ********/
854
855 void   GLUI_EditText::update_and_draw_text( void )
856 {
857   if ( NOT can_draw() )
858     return;
859
860   update_substring_bounds();
861   /*  printf( "ss: %d/%d\n", substring_start, substring_end );                  */
862
863   redraw();
864 }
865
866
867 /********************************* GLUI_EditText::special_handler() **********/
868
869 int    GLUI_EditText::special_handler( int key,int modifiers )
870 {
871   if ( NOT glui )
872     return false;
873   
874   if ( debug )
875     printf( "SPECIAL:%d - mod:%d   subs:%d/%d  ins:%d  sel:%d/%d\n", 
876             key, modifiers, substring_start, substring_end,insertion_pt,
877             sel_start, sel_end );        
878
879   if ( key == GLUT_KEY_LEFT ) {
880     if ( (modifiers & GLUT_ACTIVE_CTRL) != 0 ) {
881       insertion_pt = find_word_break( insertion_pt, -1 );
882     }
883     else {
884       insertion_pt--;
885     }
886   }
887   else if ( key == GLUT_KEY_RIGHT ) {
888     if ( (modifiers & GLUT_ACTIVE_CTRL) != 0 ) {
889       insertion_pt = find_word_break( insertion_pt, +1 );
890     }
891     else {
892       insertion_pt++;
893     }
894   }
895   else if ( key == GLUT_KEY_HOME ) {
896     insertion_pt = 0;
897   }
898   else if ( key == GLUT_KEY_END ) {
899     insertion_pt = (int) text.length();
900   }
901
902   /*** Update selection if shift key is down ***/
903   if ( (modifiers & GLUT_ACTIVE_SHIFT ) != 0 )
904     sel_end = insertion_pt;
905   else 
906     sel_start = sel_end = insertion_pt;
907   
908
909   CLAMP( insertion_pt, 0, (int) text.length()); /* Make sure insertion_pt 
910                                                                                       is in bounds */
911   CLAMP( sel_start, 0, (int) text.length()); /* Make sure insertion_pt 
912                                                                                     is in bounds */
913   CLAMP( sel_end, 0, (int) text.length()); /* Make sure insertion_pt 
914                                                                              is in bounds */
915                                               
916   /******** Now redraw text ***********/
917   if ( can_draw())
918     update_and_draw_text();
919
920   return true;
921 }
922
923
924 /****************************** GLUI_EditText::find_word_break() **********/
925 /* It looks either left or right (depending on value of 'direction'       */
926 /* for the beginning of the next 'word', where word are characters        */
927 /* separated by one of the following tokens:  " :-.,"                     */
928 /* If there is no next word in the specified direction, this returns      */
929 /* the beginning of 'text', or the very end.                              */
930
931 int    GLUI_EditText::find_word_break( int start, int direction )
932 {
933   int    i, j;
934   const char *breaks = " :-.,";
935   int     num_break_chars = (int)strlen(breaks), text_len = (int)text.length();
936   int     new_pt;
937
938   /** If we're moving left, we have to start two back, in case we're either
939   already at the beginning of a word, or on a separating token.  
940   Otherwise, this function would just return the word we're already at **/
941   if ( direction == -1 ) {
942     start -= 2;
943   }
944
945   /***** Iterate over text in the specified direction *****/
946   for ( i=start; i >= 0 AND i < text_len; i += direction ) {
947
948     /** For each character in text, iterate over list of separating tokens **/
949     for( j=0; j<num_break_chars; j++ ) {
950       if ( text[i] == breaks[j] ) {
951
952         /** character 'i' is a separating token, so we return i+1 **/
953         new_pt = i + 1;
954
955         CLAMP( new_pt, 0, text_len );
956
957         return new_pt;
958       }
959     }
960   }
961
962   if ( direction > 0 )  /* Return the end of string */
963     return text_len;
964   else                  /* Return the beginning of the text */
965     return 0;
966 }
967
968
969 /********************************** GLUI_EditText::clear_substring() ********/
970
971 void   GLUI_EditText::clear_substring( int start, int end )
972 {
973   int i;
974
975   /*
976   printf( "clearing: %d-%d   '", start,end);
977   for(i=start;i<end;i++ )
978   putchar(text[i]);
979   printf( "'\n" ); flushout;
980   */
981   /*** See if we're deleting a period in a float data-type box ***/
982   if ( data_type == GLUI_EDITTEXT_FLOAT ) {
983     for( i=start; i<end; i++ )
984       if ( text[i] == '.' )
985         num_periods = 0;
986   }
987
988   text.erase(start,end-start);
989 }
990
991
992
993 /************************************ GLUI_EditText::update_size() **********/
994
995 void   GLUI_EditText::update_size( void )
996 {
997   int text_size, delta;
998
999   if ( NOT glui )
1000     return;
1001
1002   text_size = string_width( name );
1003
1004   delta = 0;
1005   if ( text_x_offset < text_size +2 )
1006     delta = text_size+2-text_x_offset;
1007
1008   text_x_offset += delta;
1009   /*  w += delta;              */
1010
1011   if ( data_type == GLUI_EDITTEXT_TEXT OR 
1012        data_type == GLUI_EDITTEXT_FLOAT) {
1013     if ( w < text_x_offset + GLUI_EDITTEXT_MIN_TEXT_WIDTH )
1014       w = text_x_offset + GLUI_EDITTEXT_MIN_TEXT_WIDTH;
1015   }
1016   else if ( data_type == GLUI_EDITTEXT_INT ) {
1017     if ( w < text_x_offset + GLUI_EDITTEXT_MIN_INT_WIDTH )
1018       w = text_x_offset + GLUI_EDITTEXT_MIN_INT_WIDTH;
1019   }
1020 }
1021
1022
1023 /****************************** GLUI_EditText::set_text() **********/
1024
1025 void    GLUI_EditText::set_text( const char *new_text )
1026 {
1027   text=new_text;
1028   substring_start = 0;
1029   substring_end   = (int) text.length() - 1;
1030   insertion_pt    = -1;
1031   sel_start       = 0;
1032   sel_end         = 0;
1033
1034   if ( can_draw() )
1035     update_and_draw_text();
1036
1037   /** Update the spinner, if we have one **/
1038   if ( spinner ) {
1039     spinner->float_val = this->float_val;
1040     spinner->int_val   = this->int_val;
1041   }
1042
1043   /*** Now update the live variable ***/
1044   output_live(true);
1045 }
1046
1047
1048 /******************************* GLUI_EditText::set_float_val() ************/
1049
1050 void   GLUI_EditText::set_float_val( float new_val )
1051 {
1052   if ( has_limits == GLUI_LIMIT_CLAMP ) {
1053     /*** Clamp the new value to the existing limits ***/
1054
1055     CLAMP( new_val, float_low, float_high );
1056   } 
1057   else if ( has_limits == GLUI_LIMIT_WRAP ) {
1058     /*** Clamp the value cyclically to the limits - that is, if the
1059       value exceeds the max, set it the the minimum, and conversely ***/
1060
1061     if ( new_val < float_low )
1062       new_val = float_high;
1063     if ( new_val > float_high )
1064       new_val = float_low;
1065   }
1066
1067   float_val = new_val;
1068   int_val   = (int) new_val;  /* Mirror the value as an int, too */
1069   
1070   set_numeric_text();
1071 }
1072
1073
1074 /********************************** GLUI_EditText::set_int_val() ************/
1075
1076 void   GLUI_EditText::set_int_val( int new_val )
1077 {
1078   if ( has_limits == GLUI_LIMIT_CLAMP ) {
1079     /*** Clamp the new value to the existing limits ***/
1080
1081     CLAMP( new_val, int_low, int_high );
1082   }
1083   else if ( has_limits == GLUI_LIMIT_WRAP ) {
1084     /*** Clamp the value cyclically to the limits - that is, if the
1085       value exceeds the max, set it the the minimum, and conversely ***/
1086
1087     if ( new_val < int_low )
1088       new_val = int_high;
1089     if ( new_val > int_high )
1090       new_val = int_low;
1091   }
1092
1093   int_val   = new_val;
1094   float_val = (float) new_val;   /* We mirror the value as a float, too */
1095
1096   set_numeric_text();
1097 }
1098
1099
1100 /********************************* GLUI_EditText::set_float_limits() *********/
1101
1102 void GLUI_EditText::set_float_limits( float low, float high, int limit_type )
1103 {
1104   has_limits  = limit_type;
1105   float_low   = low;
1106   float_high  = high;
1107   
1108   if ( NOT IN_BOUNDS( float_val, float_low, float_high ))
1109     set_float_val( float_low );
1110
1111   int_low     = (int) float_low;
1112   int_high    = (int) float_high;
1113 }
1114
1115
1116 /*********************************** GLUI_EditText::set_int_limits() *********/
1117
1118 void   GLUI_EditText::set_int_limits( int low, int high, int limit_type )
1119 {
1120   has_limits  = limit_type;
1121   int_low     = low;
1122   int_high    = high;
1123
1124   if ( NOT IN_BOUNDS( int_val, int_low, int_high ))
1125     set_int_val( int_low );
1126
1127   float_low   = (float) int_low;
1128   float_high  = (float) int_high;
1129 }
1130
1131
1132 /************************************ GLUI_EditText::set_numeric_text() ******/
1133
1134 void    GLUI_EditText::set_numeric_text( void )
1135 {
1136   char buf_num[200];
1137   int  i, text_len;
1138
1139   if ( data_type == GLUI_EDITTEXT_FLOAT ) {
1140     sprintf( buf_num, "%#g", float_val );
1141
1142     num_periods = 0;
1143     text_len = (int) strlen(buf_num);
1144     for ( i=0; i<text_len; i++ )
1145       if ( buf_num[i] == '.' )
1146         num_periods++;
1147
1148     /* Now remove trailing zeros */
1149     if ( num_periods > 0 ) {
1150       text_len = (int) strlen(buf_num);
1151       for ( i=text_len-1; i>0; i-- ) {
1152         if ( buf_num[i] == '0' AND buf_num[i-1] != '.' )
1153           buf_num[i] = '\0';
1154         else 
1155           break;
1156       }
1157     }
1158     set_text( buf_num );
1159   }
1160   else {
1161     sprintf( buf_num, "%d", int_val );
1162     set_text( buf_num );
1163   }
1164     
1165 }
1166
1167
1168 /*************************************** GLUI_EditText::dump() **************/
1169
1170 void   GLUI_EditText::dump( FILE *out, const char *name )
1171 {
1172   fprintf( out, 
1173            "%s (edittext@%p):  ins_pt:%d  subs:%d/%d  sel:%d/%d   len:%d\n",
1174            name, this, 
1175            insertion_pt,
1176            substring_start,
1177            substring_end,
1178            sel_start,
1179            sel_end,
1180            (int) text.length());
1181 }
1182
1183
1184 /**************************************** GLUI_EditText::mouse_over() ********/
1185
1186 int    GLUI_EditText::mouse_over( int state, int x, int y )
1187 {
1188   if ( state ) {
1189     /*  curr_cursor = GLUT_CURSOR_TEXT;              */
1190     glutSetCursor( GLUT_CURSOR_TEXT );
1191   }
1192   else {
1193     /*    printf( "OUT\n" );              */
1194     glutSetCursor( GLUT_CURSOR_LEFT_ARROW );
1195   }
1196
1197   return true;
1198 }