1 /****************************************************************************
3 GLUI User Interface Toolkit
4 ---------------------------
6 glui_edittext.cpp - GLUI_EditText control class
9 --------------------------------------------------
11 Copyright (c) 1998 Paul Rademacher
13 WWW: http://sourceforge.net/projects/glui/
14 Forums: http://sourceforge.net/forum/?group_id=92496
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.
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.
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
30 *****************************************************************************/
32 #include "glui_internal_control.h"
35 /****************************** GLUI_EditText::GLUI_EditText() **********/
37 GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name,
38 int data_type, void *live_var,
39 int id, GLUI_CB callback )
41 if (data_type == GLUI_EDITTEXT_TEXT) {
42 live_type = GLUI_LIVE_TEXT;
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;
50 else if (data_type == GLUI_EDITTEXT_INT) {
51 live_type = GLUI_LIVE_INT;
53 else if (data_type == GLUI_EDITTEXT_FLOAT) {
54 live_type = GLUI_LIVE_FLOAT;
56 common_construct( parent, name, data_type, live_type, live_var, id, callback );
59 /****************************** GLUI_EditText::GLUI_EditText() **********/
61 GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name,
62 int text_type, int id, GLUI_CB callback )
64 common_construct( parent, name, text_type, GLUI_LIVE_NONE, 0, id, callback);
67 /****************************** GLUI_EditText::GLUI_EditText() **********/
69 GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name,
71 int id, GLUI_CB callback )
73 common_construct( parent, name, GLUI_EDITTEXT_INT, GLUI_LIVE_INT, live_var, id, callback);
76 /****************************** GLUI_EditText::GLUI_EditText() **********/
78 GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name,
80 int id, GLUI_CB callback )
82 common_construct( parent, name, GLUI_EDITTEXT_FLOAT, GLUI_LIVE_FLOAT, live_var, id, callback);
85 /****************************** GLUI_EditText::GLUI_EditText() **********/
87 GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name,
89 int id, GLUI_CB callback )
91 common_construct( parent, name, GLUI_EDITTEXT_TEXT, GLUI_LIVE_TEXT, live_var, id, callback);
94 /****************************** GLUI_EditText::GLUI_EditText() **********/
96 GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name,
97 std::string &live_var,
98 int id, GLUI_CB callback )
100 common_construct( parent, name, GLUI_EDITTEXT_TEXT, GLUI_LIVE_STRING, &live_var, id, callback);
103 /****************************** GLUI_EditText::common_construct() **********/
105 void GLUI_EditText::common_construct( GLUI_Node *parent, const char *name,
106 int data_t, int live_t, void *data, int id,
119 if ( live_type == GLUI_LIVE_INT) {
121 set_int_val(int_val); /** Set to some default, in case of no live var **/
123 else if ( live_type == GLUI_LIVE_FLOAT ) {
126 set_float_val(float_val); /** Set to some default, in case of no live var **/
129 parent->add_control( this );
134 /****************************** GLUI_EditText::mouse_down_handler() **********/
136 int GLUI_EditText::mouse_down_handler( int local_x, int local_y )
138 int tmp_insertion_pt;
140 if ( debug ) dump( stdout, "-> MOUSE DOWN" );
142 tmp_insertion_pt = find_insertion_pt( local_x, local_y );
143 if ( tmp_insertion_pt == -1 ) {
145 glui->deactivate_current_control( );
149 insertion_pt = tmp_insertion_pt;
151 sel_start = sel_end = insertion_pt;
154 update_and_draw_text();
156 if ( debug ) dump( stdout, "<- MOUSE UP" );
162 /******************************** GLUI_EditText::mouse_up_handler() **********/
164 int GLUI_EditText::mouse_up_handler( int local_x, int local_y, bool inside )
170 /***************************** GLUI_EditText::mouse_held_down_handler() ******/
172 int GLUI_EditText::mouse_held_down_handler( int local_x, int local_y,
177 if ( NOT new_inside )
180 if ( debug ) dump( stdout, "-> HELD DOWN" );
182 tmp_pt = find_insertion_pt( local_x, local_y );
184 if ( tmp_pt == -1 AND sel_end != 0 ) { /* moved mouse past left edge */
185 special_handler( GLUT_KEY_LEFT, GLUT_ACTIVE_SHIFT );
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 );
191 else if ( tmp_pt != -1 AND tmp_pt != sel_end ) {
192 sel_end = insertion_pt = tmp_pt;
194 update_and_draw_text();
198 dump( stdout, "<- HELD DOWN" );
204 /****************************** GLUI_EditText::key_handler() **********/
206 int GLUI_EditText::key_handler( unsigned char key,int modifiers )
209 /* int has_selection; */
215 dump( stdout, "-> KEY HANDLER" );
218 bool ctrl_down = (modifiers & GLUT_ACTIVE_CTRL)!=0;
219 /* has_selection = (sel_start != sel_end); */
221 if ( key == CTRL('m') ) { /* RETURN */
222 /* glui->deactivate_current_control(); */
223 deactivate(); /** Force callbacks, etc **/
224 activate(GLUI_ACTIVATE_TAB); /** Reselect all text **/
228 else if ( key == CTRL('[')) { /* ESCAPE */
229 glui->deactivate_current_control();
232 else if ( (key == 127 AND !ctrl_down) OR /* FORWARD DELETE */
233 ( key == CTRL('d') AND modifiers == GLUT_ACTIVE_CTRL) )
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]=='.' )
241 /*** Shift over string first ***/
242 text.erase(insertion_pt,1);
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;
251 else if ( ((key == 127) AND ctrl_down) OR // Delete word forward
252 ((key == 'd') AND (modifiers == GLUT_ACTIVE_ALT)) )
254 if ( sel_start == sel_end ) { /* no selection */
255 sel_start = insertion_pt;
256 sel_end = find_word_break( insertion_pt, +1 );
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;
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]=='.' )
270 /*** Shift over string first ***/
272 text.erase(insertion_pt,1);
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;
281 else if ( modifiers == GLUT_ACTIVE_CTRL ) /* CTRL ONLY */
283 /* Ctrl-key bindings */
284 if ( key == CTRL('a') ) {
285 return special_handler( GLUT_KEY_HOME, 0 );
287 else if ( key == CTRL('e') ) {
288 return special_handler( GLUT_KEY_END, 0 );
290 else if ( key == CTRL('b') ) {
291 return special_handler( GLUT_KEY_LEFT, 0 );
293 else if ( key == CTRL('f') ) {
294 return special_handler( GLUT_KEY_RIGHT, 0 );
296 else if ( key == CTRL('p') ) {
297 return special_handler( GLUT_KEY_UP, 0 );
299 else if ( key == CTRL('n') ) {
300 return special_handler( GLUT_KEY_DOWN, 0 );
302 else if ( key == CTRL('u') ) { /* ERASE LINE */
304 text.erase(0,text.length());
305 sel_start = sel_end = 0;
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);
312 else if ( modifiers == GLUT_ACTIVE_ALT ) /* ALT ONLY */
314 if ( key == 'b' ) { // Backward word
315 return special_handler ( GLUT_KEY_LEFT, GLUT_ACTIVE_CTRL );
317 if ( key == 'f' ) { // Forward word
318 return special_handler ( GLUT_KEY_RIGHT, GLUT_ACTIVE_CTRL );
321 else if ( (modifiers & GLUT_ACTIVE_CTRL) OR
322 (modifiers & GLUT_ACTIVE_ALT) )
324 /** ignore other keys with modifiers */
327 else { /* Regular key */
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 != '-' )
335 if ( key == '-' ) { /* User typed a '-' */
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 ) ) {
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 */
350 /*printf( "PERIOD: %d\n", num_periods ); */
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) **/
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] == '.' ) {
368 /* printf( "found: %d num: %d\n", period_found, num_periods ); */
370 if ( NOT period_found )
375 else if (data_type == GLUI_EDITTEXT_INT)
377 if ( (key < '0' OR key > '9') AND key != '-' )
380 if ( key == '-' ) { /* User typed a '-' */
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 ) ) {
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 */
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 */
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;
408 /******** We insert the character into the string ***/
410 text.insert(insertion_pt,1,key);
412 /******** Move the insertion point and substring_end one over ******/
416 sel_start = sel_end = insertion_pt;
419 /******** Now redraw text ***********/
420 /* Hack to prevent text box from being cleared first **/
421 /** int substring_change = update_substring_bounds();
423 (NOT substring_change AND NOT has_selection AND regular_key );
426 draw_text_only = false; /** Well, hack is not yet working **/
427 update_and_draw_text();
428 draw_text_only = false;
432 dump( stdout, "<- KEY HANDLER" );
434 /*** Now look to see if this string has a period ***/
436 for( i=0; i<(int)text.length(); i++ )
437 if ( text[i] == '.' )
444 /****************************** GLUI_EditText::activate() **********/
446 void GLUI_EditText::activate( int how )
449 dump( stdout, "-> ACTIVATE" );
453 if ( how == GLUI_ACTIVATE_MOUSE )
454 return; /* Don't select everything if activated with mouse */
459 sel_end = (int)text.length();
463 dump( stdout, "<- ACTIVATE" );
467 /****************************** GLUI_EditText::deactivate() **********/
469 void GLUI_EditText::deactivate( void )
480 dump( stdout, "-> DISACTIVATE" );
482 sel_start = sel_end = insertion_pt = -1;
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" */
490 new_float_val = atof( text.c_str() );
492 set_float_val( new_float_val );
494 else if ( data_type == GLUI_EDITTEXT_INT ) {
495 if ( text.length() == 0 ) /* zero-length string - make it "0" */
498 new_int_val = atoi( text.c_str() );
500 set_int_val( new_int_val );
503 if ( data_type == GLUI_EDITTEXT_TEXT ) {
504 set_text(text); /* This will force callbacks and gfx refresh */
507 update_substring_bounds();
509 /******** redraw text without insertion point ***********/
512 /***** Now do callbacks if value changed ******/
513 if ( orig_text != text ) {
514 this->execute_callback();
517 /* THE CODE BELOW IS FROM WHEN SPINNER ALSO MAINTAINED CALLBACKS */
518 if ( spinner == NULL ) { /** Are we independent of a spinner? **/
523 else { /* We're attached to a spinner */
524 spinner->do_callbacks(); /* Let the spinner do the callback stuff */
530 dump( stdout, "<- DISACTIVATE" );
533 /****************************** GLUI_EditText::draw() **********/
535 void GLUI_EditText::draw( int x, int y )
537 GLUI_DRAWINGSENTINAL_IDIOM
540 name_x = MAX(text_x_offset - string_width(this->name) - 3,0);
541 draw_name( name_x , 13);
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 );
548 glColor3f( 1., 1., 1. );
549 glVertex2i( text_x_offset, h ); glVertex2i( w, h );
550 glVertex2i( w, h ); glVertex2i( w, 0 );
553 glColor3f( 0., 0., 0. );
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 );
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 );
564 /** Find where to draw the text **/
565 update_substring_bounds();
573 /************************** GLUI_EditText::update_substring_bounds() *********/
575 int GLUI_EditText::update_substring_bounds( void )
578 int text_len = (int)text.length();
579 int old_start, old_end;
581 old_start = substring_start;
582 old_end = substring_end;
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 );
589 CLAMP( substring_end, 0, MAX(text_len-1,0) );
590 CLAMP( substring_start, 0, MAX(text_len-1,0) );
592 if ( debug ) dump( stdout, "-> UPDATE SS" );
594 if ( insertion_pt >= 0 AND
595 insertion_pt < substring_start ) { /* cursor moved left */
596 substring_start = insertion_pt;
598 while ( substring_width( substring_start, substring_end ) > box_width )
601 else if ( insertion_pt > substring_end ) { /* cursor moved right */
602 substring_end = insertion_pt-1;
604 while ( substring_width( substring_start, substring_end ) > box_width )
607 else { /* cursor is within old substring bounds */
608 if ( last_insertion_pt > insertion_pt ) { /* cursor moved left */
611 while ( substring_width( substring_start, substring_end ) > box_width )
614 while(substring_end < text_len-1
615 AND substring_width( substring_start, substring_end ) <= box_width)
620 while ( substring_width( substring_start, substring_end ) > box_width )
623 last_insertion_pt = insertion_pt;
625 /*** No selection if not enabled ***/
627 sel_start = sel_end = 0;
630 if ( debug ) dump( stdout, "<- UPDATE SS" );
632 if ( substring_start == old_start AND substring_end == old_end )
633 return false; /*** bounds did not change ***/
635 return true; /*** bounds did change ***/
639 /********************************* GLUI_EditText::update_x_offsets() *********/
641 void GLUI_EditText::update_x_offsets( void )
646 /********************************* GLUI_EditText::draw_text() ****************/
648 void GLUI_EditText::draw_text( int x, int y )
650 GLUI_DRAWINGSENTINAL_IDIOM
651 int text_x, i, sel_lo, sel_hi;
653 if ( debug ) dump( stdout, "-> DRAW_TEXT" );
655 if ( NOT draw_text_only ) {
657 glColor3f( 1., 1., 1. );
660 glDisable( GL_CULL_FACE );
662 glVertex2i( text_x_offset+2, 2 ); glVertex2i( w-2, 2 );
663 glVertex2i( w-2, h-2 ); glVertex2i( text_x_offset+2, h-2 );
667 /** Find where to draw the text **/
669 text_x = text_x_offset + 2 + GLUI_EDITTEXT_BOXINNERMARGINX;
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 );
675 /** Find lower and upper selection bounds **/
676 sel_lo = MIN(sel_start, sel_end );
677 sel_hi = MAX(sel_start, sel_end );
679 int sel_x_start, sel_x_end, delta;
681 /** Draw selection area dark **/
682 if ( sel_start != sel_end ) {
683 sel_x_start = text_x;
685 for( i=substring_start; i<=substring_end; i++ ) {
686 delta = char_width( text[i] );
689 sel_x_start += delta;
692 else if ( i < sel_hi ) {
697 glColor3f( 0.0f, 0.0f, .6f );
699 glVertex2i( sel_x_start, 2 ); glVertex2i( sel_x_end, 2 );
700 glVertex2i( sel_x_end, h-2 ); glVertex2i( sel_x_start, h-2 );
705 if ( sel_start == sel_end ) { /* No current selection */
707 glColor3b( 0, 0, 0 );
709 glColor3b( 32, 32, 32 );
711 glRasterPos2i( text_x, 13);
712 for( i=substring_start; i<=substring_end; i++ ) {
713 glutBitmapCharacter( get_font(), this->text[i] );
716 else { /* There is a selection */
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] );
725 glColor3f( 0., 0., 0. );
726 glRasterPos2i( x, 13);
727 glutBitmapCharacter( get_font(), this->text[i] );
730 x += char_width( text[i] );
734 if ( debug ) dump( stdout, "<- DRAW_TEXT" );
738 /******************************** GLUI_EditText::find_insertion_pt() *********/
739 /* This function returns the character numer *before which* the insertion */
742 int GLUI_EditText::find_insertion_pt( int x, int y )
746 /*** See if we clicked outside box ***/
747 if ( x < this->x_abs + text_x_offset )
750 /* We move from right to left, looking to see if the mouse was clicked
751 to the right of the ith character */
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 **/
759 /*** See if we clicked in an empty box ***/
760 if ( (int) text.length() == 0 )
763 /** find mouse click in text **/
764 for( i=substring_end; i>=substring_start; i-- ) {
765 curr_x -= char_width( text[i] );
768 /* printf( "-> %d\n", i ); */
776 /* Well, the mouse wasn't after any of the characters...see if it's
777 before the beginning of the substring */
779 if ( x > (x_abs + text_x_offset + 2 ) )
780 return substring_start;
782 return -1; /* Nothing found */
787 /******************************** GLUI_EditText::draw_insertion_pt() *********/
789 void GLUI_EditText::draw_insertion_pt( void )
793 if ( NOT can_draw() )
796 /*** Don't draw insertion pt if control is disabled ***/
800 if ( debug ) dump( stdout, "-> DRAW_INS_PT" );
802 if ( sel_start != sel_end OR insertion_pt < 0 ) {
803 return; /* Don't draw insertion point if there is a current selection */
806 /* printf( "insertion pt: %d\n", insertion_pt ); */
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 **/
814 for( i=substring_end; i>=insertion_pt; i-- ) {
815 curr_x -= char_width( text[i] );
818 glColor3f( 0.0, 0.0, 0.0 );
819 glBegin( GL_LINE_LOOP );
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 );
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 );
833 if ( debug ) dump( stdout, "-> DRAW_INS_PT" );
838 /******************************** GLUI_EditText::substring_width() *********/
840 int GLUI_EditText::substring_width( int start, int end )
846 for( i=start; i<=end; i++ )
847 width += char_width( text[i] );
853 /***************************** GLUI_EditText::update_and_draw_text() ********/
855 void GLUI_EditText::update_and_draw_text( void )
857 if ( NOT can_draw() )
860 update_substring_bounds();
861 /* printf( "ss: %d/%d\n", substring_start, substring_end ); */
867 /********************************* GLUI_EditText::special_handler() **********/
869 int GLUI_EditText::special_handler( int key,int modifiers )
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 );
879 if ( key == GLUT_KEY_LEFT ) {
880 if ( (modifiers & GLUT_ACTIVE_CTRL) != 0 ) {
881 insertion_pt = find_word_break( insertion_pt, -1 );
887 else if ( key == GLUT_KEY_RIGHT ) {
888 if ( (modifiers & GLUT_ACTIVE_CTRL) != 0 ) {
889 insertion_pt = find_word_break( insertion_pt, +1 );
895 else if ( key == GLUT_KEY_HOME ) {
898 else if ( key == GLUT_KEY_END ) {
899 insertion_pt = (int) text.length();
902 /*** Update selection if shift key is down ***/
903 if ( (modifiers & GLUT_ACTIVE_SHIFT ) != 0 )
904 sel_end = insertion_pt;
906 sel_start = sel_end = insertion_pt;
909 CLAMP( insertion_pt, 0, (int) text.length()); /* Make sure insertion_pt
911 CLAMP( sel_start, 0, (int) text.length()); /* Make sure insertion_pt
913 CLAMP( sel_end, 0, (int) text.length()); /* Make sure insertion_pt
916 /******** Now redraw text ***********/
918 update_and_draw_text();
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. */
931 int GLUI_EditText::find_word_break( int start, int direction )
934 const char *breaks = " :-.,";
935 int num_break_chars = (int)strlen(breaks), text_len = (int)text.length();
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 ) {
945 /***** Iterate over text in the specified direction *****/
946 for ( i=start; i >= 0 AND i < text_len; i += direction ) {
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] ) {
952 /** character 'i' is a separating token, so we return i+1 **/
955 CLAMP( new_pt, 0, text_len );
962 if ( direction > 0 ) /* Return the end of string */
964 else /* Return the beginning of the text */
969 /********************************** GLUI_EditText::clear_substring() ********/
971 void GLUI_EditText::clear_substring( int start, int end )
976 printf( "clearing: %d-%d '", start,end);
977 for(i=start;i<end;i++ )
979 printf( "'\n" ); flushout;
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] == '.' )
988 text.erase(start,end-start);
993 /************************************ GLUI_EditText::update_size() **********/
995 void GLUI_EditText::update_size( void )
997 int text_size, delta;
1002 text_size = string_width( name );
1005 if ( text_x_offset < text_size +2 )
1006 delta = text_size+2-text_x_offset;
1008 text_x_offset += delta;
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;
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;
1023 /****************************** GLUI_EditText::set_text() **********/
1025 void GLUI_EditText::set_text( const char *new_text )
1028 substring_start = 0;
1029 substring_end = (int) text.length() - 1;
1035 update_and_draw_text();
1037 /** Update the spinner, if we have one **/
1039 spinner->float_val = this->float_val;
1040 spinner->int_val = this->int_val;
1043 /*** Now update the live variable ***/
1048 /******************************* GLUI_EditText::set_float_val() ************/
1050 void GLUI_EditText::set_float_val( float new_val )
1052 if ( has_limits == GLUI_LIMIT_CLAMP ) {
1053 /*** Clamp the new value to the existing limits ***/
1055 CLAMP( new_val, float_low, float_high );
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 ***/
1061 if ( new_val < float_low )
1062 new_val = float_high;
1063 if ( new_val > float_high )
1064 new_val = float_low;
1067 float_val = new_val;
1068 int_val = (int) new_val; /* Mirror the value as an int, too */
1074 /********************************** GLUI_EditText::set_int_val() ************/
1076 void GLUI_EditText::set_int_val( int new_val )
1078 if ( has_limits == GLUI_LIMIT_CLAMP ) {
1079 /*** Clamp the new value to the existing limits ***/
1081 CLAMP( new_val, int_low, int_high );
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 ***/
1087 if ( new_val < int_low )
1089 if ( new_val > int_high )
1094 float_val = (float) new_val; /* We mirror the value as a float, too */
1100 /********************************* GLUI_EditText::set_float_limits() *********/
1102 void GLUI_EditText::set_float_limits( float low, float high, int limit_type )
1104 has_limits = limit_type;
1108 if ( NOT IN_BOUNDS( float_val, float_low, float_high ))
1109 set_float_val( float_low );
1111 int_low = (int) float_low;
1112 int_high = (int) float_high;
1116 /*********************************** GLUI_EditText::set_int_limits() *********/
1118 void GLUI_EditText::set_int_limits( int low, int high, int limit_type )
1120 has_limits = limit_type;
1124 if ( NOT IN_BOUNDS( int_val, int_low, int_high ))
1125 set_int_val( int_low );
1127 float_low = (float) int_low;
1128 float_high = (float) int_high;
1132 /************************************ GLUI_EditText::set_numeric_text() ******/
1134 void GLUI_EditText::set_numeric_text( void )
1139 if ( data_type == GLUI_EDITTEXT_FLOAT ) {
1140 sprintf( buf_num, "%#g", float_val );
1143 text_len = (int) strlen(buf_num);
1144 for ( i=0; i<text_len; i++ )
1145 if ( buf_num[i] == '.' )
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] != '.' )
1158 set_text( buf_num );
1161 sprintf( buf_num, "%d", int_val );
1162 set_text( buf_num );
1168 /*************************************** GLUI_EditText::dump() **************/
1170 void GLUI_EditText::dump( FILE *out, const char *name )
1173 "%s (edittext@%p): ins_pt:%d subs:%d/%d sel:%d/%d len:%d\n",
1180 (int) text.length());
1184 /**************************************** GLUI_EditText::mouse_over() ********/
1186 int GLUI_EditText::mouse_over( int state, int x, int y )
1189 /* curr_cursor = GLUT_CURSOR_TEXT; */
1190 glutSetCursor( GLUT_CURSOR_TEXT );
1193 /* printf( "OUT\n" ); */
1194 glutSetCursor( GLUT_CURSOR_LEFT_ARROW );