| /**************************************************************************** | |
| GLUI User Interface Toolkit | |
| --------------------------- | |
| glui_edittext.cpp - GLUI_EditText control class | |
| -------------------------------------------------- | |
| Copyright (c) 1998 Paul Rademacher | |
| WWW: http://sourceforge.net/projects/glui/ | |
| Forums: http://sourceforge.net/forum/?group_id=92496 | |
| This library is free software; you can redistribute it and/or | |
| modify it under the terms of the GNU Lesser General Public | |
| License as published by the Free Software Foundation; either | |
| version 2.1 of the License, or (at your option) any later version. | |
| This library is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| Lesser General Public License for more details. | |
| You should have received a copy of the GNU Lesser General Public | |
| License along with this library; if not, write to the Free Software | |
| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| *****************************************************************************/ | |
| #include "glui_internal_control.h" | |
| #include <cassert> | |
| /****************************** GLUI_EditText::GLUI_EditText() **********/ | |
| GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name, | |
| int data_type, void *live_var, | |
| int id, GLUI_CB callback ) | |
| { | |
| if (data_type == GLUI_EDITTEXT_TEXT) { | |
| live_type = GLUI_LIVE_TEXT; | |
| } | |
| else if (data_type == GLUI_EDITTEXT_STRING) { | |
| data_type = GLUI_EDITTEXT_TEXT; // EDITTEXT_STRING doesn't really exist. | |
| // Except as a signal to make a string. | |
| // It's a backwards-compat hack. | |
| live_type = GLUI_LIVE_STRING; | |
| } | |
| else if (data_type == GLUI_EDITTEXT_INT) { | |
| live_type = GLUI_LIVE_INT; | |
| } | |
| else if (data_type == GLUI_EDITTEXT_FLOAT) { | |
| live_type = GLUI_LIVE_FLOAT; | |
| } | |
| common_construct( parent, name, data_type, live_type, live_var, id, callback ); | |
| } | |
| /****************************** GLUI_EditText::GLUI_EditText() **********/ | |
| GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name, | |
| int text_type, int id, GLUI_CB callback ) | |
| { | |
| common_construct( parent, name, text_type, GLUI_LIVE_NONE, 0, id, callback); | |
| } | |
| /****************************** GLUI_EditText::GLUI_EditText() **********/ | |
| GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name, | |
| int *live_var, | |
| int id, GLUI_CB callback ) | |
| { | |
| common_construct( parent, name, GLUI_EDITTEXT_INT, GLUI_LIVE_INT, live_var, id, callback); | |
| } | |
| /****************************** GLUI_EditText::GLUI_EditText() **********/ | |
| GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name, | |
| float *live_var, | |
| int id, GLUI_CB callback ) | |
| { | |
| common_construct( parent, name, GLUI_EDITTEXT_FLOAT, GLUI_LIVE_FLOAT, live_var, id, callback); | |
| } | |
| /****************************** GLUI_EditText::GLUI_EditText() **********/ | |
| GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name, | |
| char *live_var, | |
| int id, GLUI_CB callback ) | |
| { | |
| common_construct( parent, name, GLUI_EDITTEXT_TEXT, GLUI_LIVE_TEXT, live_var, id, callback); | |
| } | |
| /****************************** GLUI_EditText::GLUI_EditText() **********/ | |
| GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name, | |
| std::string &live_var, | |
| int id, GLUI_CB callback ) | |
| { | |
| common_construct( parent, name, GLUI_EDITTEXT_TEXT, GLUI_LIVE_STRING, &live_var, id, callback); | |
| } | |
| /****************************** GLUI_EditText::common_construct() **********/ | |
| void GLUI_EditText::common_construct( GLUI_Node *parent, const char *name, | |
| int data_t, int live_t, void *data, int id, | |
| GLUI_CB cb ) | |
| { | |
| common_init(); | |
| set_name( name ); | |
| live_type = live_t; | |
| data_type = data_t; | |
| ptr_val = data; | |
| user_id = id; | |
| callback = cb; | |
| if ( live_type == GLUI_LIVE_INT) { | |
| if ( data == NULL ) | |
| set_int_val(int_val); /** Set to some default, in case of no live var **/ | |
| } | |
| else if ( live_type == GLUI_LIVE_FLOAT ) { | |
| num_periods = 1; | |
| if ( data == NULL ) | |
| set_float_val(float_val); /** Set to some default, in case of no live var **/ | |
| } | |
| parent->add_control( this ); | |
| init_live(); | |
| } | |
| /****************************** GLUI_EditText::mouse_down_handler() **********/ | |
| int GLUI_EditText::mouse_down_handler( int local_x, int local_y ) | |
| { | |
| int tmp_insertion_pt; | |
| if ( debug ) dump( stdout, "-> MOUSE DOWN" ); | |
| tmp_insertion_pt = find_insertion_pt( local_x, local_y ); | |
| if ( tmp_insertion_pt == -1 ) { | |
| if ( glui ) | |
| glui->deactivate_current_control( ); | |
| return false; | |
| } | |
| insertion_pt = tmp_insertion_pt; | |
| sel_start = sel_end = insertion_pt; | |
| if ( can_draw()) | |
| update_and_draw_text(); | |
| if ( debug ) dump( stdout, "<- MOUSE UP" ); | |
| return true; | |
| } | |
| /******************************** GLUI_EditText::mouse_up_handler() **********/ | |
| int GLUI_EditText::mouse_up_handler( int local_x, int local_y, bool inside ) | |
| { | |
| return false; | |
| } | |
| /***************************** GLUI_EditText::mouse_held_down_handler() ******/ | |
| int GLUI_EditText::mouse_held_down_handler( int local_x, int local_y, | |
| bool new_inside) | |
| { | |
| int tmp_pt; | |
| if ( NOT new_inside ) | |
| return false; | |
| if ( debug ) dump( stdout, "-> HELD DOWN" ); | |
| tmp_pt = find_insertion_pt( local_x, local_y ); | |
| if ( tmp_pt == -1 AND sel_end != 0 ) { /* moved mouse past left edge */ | |
| special_handler( GLUT_KEY_LEFT, GLUT_ACTIVE_SHIFT ); | |
| } | |
| else if ( tmp_pt == substring_end+1 AND sel_end != (int) text.length()) { | |
| /* moved mouse past right edge */ | |
| special_handler( GLUT_KEY_RIGHT, GLUT_ACTIVE_SHIFT ); | |
| } | |
| else if ( tmp_pt != -1 AND tmp_pt != sel_end ) { | |
| sel_end = insertion_pt = tmp_pt; | |
| update_and_draw_text(); | |
| } | |
| if ( debug ) | |
| dump( stdout, "<- HELD DOWN" ); | |
| return false; | |
| } | |
| /****************************** GLUI_EditText::key_handler() **********/ | |
| int GLUI_EditText::key_handler( unsigned char key,int modifiers ) | |
| { | |
| int i, regular_key; | |
| /* int has_selection; */ | |
| if ( NOT glui ) | |
| return false; | |
| if ( debug ) | |
| dump( stdout, "-> KEY HANDLER" ); | |
| regular_key = false; | |
| bool ctrl_down = (modifiers & GLUT_ACTIVE_CTRL)!=0; | |
| /* has_selection = (sel_start != sel_end); */ | |
| if ( key == CTRL('m') ) { /* RETURN */ | |
| /* glui->deactivate_current_control(); */ | |
| deactivate(); /** Force callbacks, etc **/ | |
| activate(GLUI_ACTIVATE_TAB); /** Reselect all text **/ | |
| redraw(); | |
| return true; | |
| } | |
| else if ( key == CTRL('[')) { /* ESCAPE */ | |
| glui->deactivate_current_control(); | |
| return true; | |
| } | |
| else if ( (key == 127 AND !ctrl_down) OR /* FORWARD DELETE */ | |
| ( key == CTRL('d') AND modifiers == GLUT_ACTIVE_CTRL) ) | |
| { | |
| if ( sel_start == sel_end ) { /* no selection */ | |
| if ( insertion_pt < (int)text.length() ) { | |
| /*** See if we're deleting a period in a float data-type box ***/ | |
| if ( data_type == GLUI_EDITTEXT_FLOAT AND text[insertion_pt]=='.' ) | |
| num_periods--; | |
| /*** Shift over string first ***/ | |
| text.erase(insertion_pt,1); | |
| } | |
| } | |
| else { /* There is a selection */ | |
| clear_substring( MIN(sel_start,sel_end), MAX(sel_start,sel_end )); | |
| insertion_pt = MIN(sel_start,sel_end); | |
| sel_start = sel_end = insertion_pt; | |
| } | |
| } | |
| else if ( ((key == 127) AND ctrl_down) OR // Delete word forward | |
| ((key == 'd') AND (modifiers == GLUT_ACTIVE_ALT)) ) | |
| { | |
| if ( sel_start == sel_end ) { /* no selection */ | |
| sel_start = insertion_pt; | |
| sel_end = find_word_break( insertion_pt, +1 ); | |
| } | |
| clear_substring( MIN(sel_start,sel_end), MAX(sel_start,sel_end )); | |
| insertion_pt = MIN(sel_start,sel_end); | |
| sel_start = sel_end = insertion_pt; | |
| } | |
| else if ( key == CTRL('h') ) { /* BACKSPACE */ | |
| if ( sel_start == sel_end ) { /* no selection */ | |
| if ( insertion_pt > 0 ) { | |
| /*** See if we're deleting a period in a float data-type box ***/ | |
| if ( data_type == GLUI_EDITTEXT_FLOAT AND text[insertion_pt-1]=='.' ) | |
| num_periods--; | |
| /*** Shift over string first ***/ | |
| insertion_pt--; | |
| text.erase(insertion_pt,1); | |
| } | |
| } | |
| else { /* There is a selection */ | |
| clear_substring( MIN(sel_start,sel_end), MAX(sel_start,sel_end )); | |
| insertion_pt = MIN(sel_start,sel_end); | |
| sel_start = sel_end = insertion_pt; | |
| } | |
| } | |
| else if ( modifiers == GLUT_ACTIVE_CTRL ) /* CTRL ONLY */ | |
| { | |
| /* Ctrl-key bindings */ | |
| if ( key == CTRL('a') ) { | |
| return special_handler( GLUT_KEY_HOME, 0 ); | |
| } | |
| else if ( key == CTRL('e') ) { | |
| return special_handler( GLUT_KEY_END, 0 ); | |
| } | |
| else if ( key == CTRL('b') ) { | |
| return special_handler( GLUT_KEY_LEFT, 0 ); | |
| } | |
| else if ( key == CTRL('f') ) { | |
| return special_handler( GLUT_KEY_RIGHT, 0 ); | |
| } | |
| else if ( key == CTRL('p') ) { | |
| return special_handler( GLUT_KEY_UP, 0 ); | |
| } | |
| else if ( key == CTRL('n') ) { | |
| return special_handler( GLUT_KEY_DOWN, 0 ); | |
| } | |
| else if ( key == CTRL('u') ) { /* ERASE LINE */ | |
| insertion_pt = 0; | |
| text.erase(0,text.length()); | |
| sel_start = sel_end = 0; | |
| } | |
| else if ( key == CTRL('k') ) { /* KILL TO END OF LINE */ | |
| sel_start = sel_end = insertion_pt; | |
| text.erase(insertion_pt,GLUI_String::npos); | |
| } | |
| } | |
| else if ( modifiers == GLUT_ACTIVE_ALT ) /* ALT ONLY */ | |
| { | |
| if ( key == 'b' ) { // Backward word | |
| return special_handler ( GLUT_KEY_LEFT, GLUT_ACTIVE_CTRL ); | |
| } | |
| if ( key == 'f' ) { // Forward word | |
| return special_handler ( GLUT_KEY_RIGHT, GLUT_ACTIVE_CTRL ); | |
| } | |
| } | |
| else if ( (modifiers & GLUT_ACTIVE_CTRL) OR | |
| (modifiers & GLUT_ACTIVE_ALT) ) | |
| { | |
| /** ignore other keys with modifiers */ | |
| return true; | |
| } | |
| else { /* Regular key */ | |
| regular_key = true; | |
| /** Check if we only accept numbers **/ | |
| if (data_type == GLUI_EDITTEXT_FLOAT ) { | |
| if ( (key < '0' OR key > '9') AND key != '.' AND key != '-' ) | |
| return true; | |
| if ( key == '-' ) { /* User typed a '-' */ | |
| /* If user has first character selected, then '-' is allowed */ | |
| if ( NOT ( MIN(sel_start,sel_end) == 0 AND | |
| MAX(sel_start,sel_end) > 0 ) ) { | |
| /* User does not have 1st char selected */ | |
| if (insertion_pt != 0 OR text[0] == '-' ) { | |
| return true; /* Can only place negative at beginning of text, | |
| and only one of them */ | |
| } | |
| } | |
| } | |
| if ( key == '.' ) { | |
| /*printf( "PERIOD: %d\n", num_periods ); */ | |
| if ( num_periods > 0 ) { | |
| /** We're trying to type a period, but the text already contains | |
| a period. Check whether the period is contained within | |
| is current selection (thus it will be safely replaced) **/ | |
| int period_found = false; | |
| if ( sel_start != sel_end ) { | |
| for( i=MIN(sel_end,sel_start); i<MAX(sel_start,sel_end); i++ ) { | |
| /* printf( "%c ", text[i] ); */ | |
| if ( text[i] == '.' ) { | |
| period_found = true; | |
| break; | |
| } | |
| } | |
| } | |
| /* printf( "found: %d num: %d\n", period_found, num_periods ); */ | |
| if ( NOT period_found ) | |
| return true; | |
| } | |
| } | |
| } | |
| else if (data_type == GLUI_EDITTEXT_INT) | |
| { | |
| if ( (key < '0' OR key > '9') AND key != '-' ) | |
| return true; | |
| if ( key == '-' ) { /* User typed a '-' */ | |
| /* If user has first character selected, then '-' is allowed */ | |
| if ( NOT ( MIN(sel_start,sel_end) == 0 AND | |
| MAX(sel_start,sel_end) > 0 ) ) { | |
| /* User does not have 1st char selected */ | |
| if (insertion_pt != 0 OR text[0] == '-' ) { | |
| return true; /* Can only place negative at beginning of text, | |
| and only one of them */ | |
| } | |
| } | |
| } | |
| } | |
| /** This is just to get rid of warnings - the flag regular_key is | |
| set if the key was not a backspace, return, whatever. But I | |
| believe if we're here, we know it was a regular key anyway */ | |
| if ( regular_key ) { | |
| } | |
| /**** If there's a current selection, erase it ******/ | |
| if ( sel_start != sel_end ) { | |
| clear_substring( MIN(sel_start,sel_end), MAX(sel_start,sel_end )); | |
| insertion_pt = MIN(sel_start,sel_end); | |
| sel_start = sel_end = insertion_pt; | |
| } | |
| /******** We insert the character into the string ***/ | |
| text.insert(insertion_pt,1,key); | |
| /******** Move the insertion point and substring_end one over ******/ | |
| insertion_pt++; | |
| substring_end++; | |
| sel_start = sel_end = insertion_pt; | |
| } | |
| /******** Now redraw text ***********/ | |
| /* Hack to prevent text box from being cleared first **/ | |
| /** int substring_change = update_substring_bounds(); | |
| draw_text_only = | |
| (NOT substring_change AND NOT has_selection AND regular_key ); | |
| */ | |
| draw_text_only = false; /** Well, hack is not yet working **/ | |
| update_and_draw_text(); | |
| draw_text_only = false; | |
| if ( debug ) | |
| dump( stdout, "<- KEY HANDLER" ); | |
| /*** Now look to see if this string has a period ***/ | |
| num_periods = 0; | |
| for( i=0; i<(int)text.length(); i++ ) | |
| if ( text[i] == '.' ) | |
| num_periods++; | |
| return true; | |
| } | |
| /****************************** GLUI_EditText::activate() **********/ | |
| void GLUI_EditText::activate( int how ) | |
| { | |
| if ( debug ) | |
| dump( stdout, "-> ACTIVATE" ); | |
| active = true; | |
| if ( how == GLUI_ACTIVATE_MOUSE ) | |
| return; /* Don't select everything if activated with mouse */ | |
| orig_text = text; | |
| sel_start = 0; | |
| sel_end = (int)text.length(); | |
| insertion_pt = 0; | |
| if ( debug ) | |
| dump( stdout, "<- ACTIVATE" ); | |
| } | |
| /****************************** GLUI_EditText::deactivate() **********/ | |
| void GLUI_EditText::deactivate( void ) | |
| { | |
| int new_int_val; | |
| float new_float_val; | |
| active = false; | |
| if ( NOT glui ) | |
| return; | |
| if ( debug ) | |
| dump( stdout, "-> DISACTIVATE" ); | |
| sel_start = sel_end = insertion_pt = -1; | |
| /***** Retrieve the current value from the text *****/ | |
| /***** The live variable will be updated by set_text() ****/ | |
| if ( data_type == GLUI_EDITTEXT_FLOAT ) { | |
| if ( text.length() == 0 ) /* zero-length string - make it "0.0" */ | |
| text = "0.0"; | |
| new_float_val = atof( text.c_str() ); | |
| set_float_val( new_float_val ); | |
| } | |
| else if ( data_type == GLUI_EDITTEXT_INT ) { | |
| if ( text.length() == 0 ) /* zero-length string - make it "0" */ | |
| text = "0"; | |
| new_int_val = atoi( text.c_str() ); | |
| set_int_val( new_int_val ); | |
| } | |
| else | |
| if ( data_type == GLUI_EDITTEXT_TEXT ) { | |
| set_text(text); /* This will force callbacks and gfx refresh */ | |
| } | |
| update_substring_bounds(); | |
| /******** redraw text without insertion point ***********/ | |
| redraw(); | |
| /***** Now do callbacks if value changed ******/ | |
| if ( orig_text != text ) { | |
| this->execute_callback(); | |
| if ( 0 ) { | |
| /* THE CODE BELOW IS FROM WHEN SPINNER ALSO MAINTAINED CALLBACKS */ | |
| if ( spinner == NULL ) { /** Are we independent of a spinner? **/ | |
| if ( callback ) { | |
| callback( this ); | |
| } | |
| } | |
| else { /* We're attached to a spinner */ | |
| spinner->do_callbacks(); /* Let the spinner do the callback stuff */ | |
| } | |
| } | |
| } | |
| if ( debug ) | |
| dump( stdout, "<- DISACTIVATE" ); | |
| } | |
| /****************************** GLUI_EditText::draw() **********/ | |
| void GLUI_EditText::draw( int x, int y ) | |
| { | |
| GLUI_DRAWINGSENTINAL_IDIOM | |
| int name_x; | |
| name_x = MAX(text_x_offset - string_width(this->name) - 3,0); | |
| draw_name( name_x , 13); | |
| glBegin( GL_LINES ); | |
| glColor3f( .5, .5, .5 ); | |
| glVertex2i( text_x_offset, 0 ); glVertex2i( w, 0 ); | |
| glVertex2i( text_x_offset, 0 ); glVertex2i( text_x_offset, h ); | |
| glColor3f( 1., 1., 1. ); | |
| glVertex2i( text_x_offset, h ); glVertex2i( w, h ); | |
| glVertex2i( w, h ); glVertex2i( w, 0 ); | |
| if ( enabled ) | |
| glColor3f( 0., 0., 0. ); | |
| else | |
| glColor3f( .25, .25, .25 ); | |
| glVertex2i( text_x_offset+1, 1 ); glVertex2i( w-1, 1 ); | |
| glVertex2i( text_x_offset+1, 1 ); glVertex2i( text_x_offset+1, h-1 ); | |
| glColor3f( .75, .75, .75 ); | |
| glVertex2i( text_x_offset+1, h-1 ); glVertex2i( w-1, h-1 ); | |
| glVertex2i( w-1, h-1 ); glVertex2i( w-1, 1 ); | |
| glEnd(); | |
| /** Find where to draw the text **/ | |
| update_substring_bounds(); | |
| draw_text(0,0); | |
| draw_insertion_pt(); | |
| } | |
| /************************** GLUI_EditText::update_substring_bounds() *********/ | |
| int GLUI_EditText::update_substring_bounds( void ) | |
| { | |
| int box_width; | |
| int text_len = (int)text.length(); | |
| int old_start, old_end; | |
| old_start = substring_start; | |
| old_end = substring_end; | |
| /*** Calculate the width of the usable area of the edit box ***/ | |
| box_width = MAX( this->w - this->text_x_offset | |
| - 4 /* 2 * the two-line box border */ | |
| - 2 * GLUI_EDITTEXT_BOXINNERMARGINX, 0 ); | |
| CLAMP( substring_end, 0, MAX(text_len-1,0) ); | |
| CLAMP( substring_start, 0, MAX(text_len-1,0) ); | |
| if ( debug ) dump( stdout, "-> UPDATE SS" ); | |
| if ( insertion_pt >= 0 AND | |
| insertion_pt < substring_start ) { /* cursor moved left */ | |
| substring_start = insertion_pt; | |
| while ( substring_width( substring_start, substring_end ) > box_width ) | |
| substring_end--; | |
| } | |
| else if ( insertion_pt > substring_end ) { /* cursor moved right */ | |
| substring_end = insertion_pt-1; | |
| while ( substring_width( substring_start, substring_end ) > box_width ) | |
| substring_start++; | |
| } | |
| else { /* cursor is within old substring bounds */ | |
| if ( last_insertion_pt > insertion_pt ) { /* cursor moved left */ | |
| } | |
| else { | |
| while ( substring_width( substring_start, substring_end ) > box_width ) | |
| substring_end--; | |
| while(substring_end < text_len-1 | |
| AND substring_width( substring_start, substring_end ) <= box_width) | |
| substring_end++; | |
| } | |
| } | |
| while ( substring_width( substring_start, substring_end ) > box_width ) | |
| substring_end--; | |
| last_insertion_pt = insertion_pt; | |
| /*** No selection if not enabled ***/ | |
| if ( NOT enabled ) { | |
| sel_start = sel_end = 0; | |
| } | |
| if ( debug ) dump( stdout, "<- UPDATE SS" ); | |
| if ( substring_start == old_start AND substring_end == old_end ) | |
| return false; /*** bounds did not change ***/ | |
| else | |
| return true; /*** bounds did change ***/ | |
| } | |
| /********************************* GLUI_EditText::update_x_offsets() *********/ | |
| void GLUI_EditText::update_x_offsets( void ) | |
| { | |
| } | |
| /********************************* GLUI_EditText::draw_text() ****************/ | |
| void GLUI_EditText::draw_text( int x, int y ) | |
| { | |
| GLUI_DRAWINGSENTINAL_IDIOM | |
| int text_x, i, sel_lo, sel_hi; | |
| if ( debug ) dump( stdout, "-> DRAW_TEXT" ); | |
| if ( NOT draw_text_only ) { | |
| if ( enabled ) | |
| glColor3f( 1., 1., 1. ); | |
| else | |
| set_to_bkgd_color(); | |
| glDisable( GL_CULL_FACE ); | |
| glBegin( GL_QUADS ); | |
| glVertex2i( text_x_offset+2, 2 ); glVertex2i( w-2, 2 ); | |
| glVertex2i( w-2, h-2 ); glVertex2i( text_x_offset+2, h-2 ); | |
| glEnd(); | |
| } | |
| /** Find where to draw the text **/ | |
| text_x = text_x_offset + 2 + GLUI_EDITTEXT_BOXINNERMARGINX; | |
| /*printf( "text_x: %d substr_width: %d start/end: %d/%d\n", | |
| text_x, substring_width( substring_start, substring_end ), | |
| substring_start, substring_end ); | |
| */ | |
| /** Find lower and upper selection bounds **/ | |
| sel_lo = MIN(sel_start, sel_end ); | |
| sel_hi = MAX(sel_start, sel_end ); | |
| int sel_x_start, sel_x_end, delta; | |
| /** Draw selection area dark **/ | |
| if ( sel_start != sel_end ) { | |
| sel_x_start = text_x; | |
| sel_x_end = text_x; | |
| for( i=substring_start; i<=substring_end; i++ ) { | |
| delta = char_width( text[i] ); | |
| if ( i < sel_lo ) { | |
| sel_x_start += delta; | |
| sel_x_end += delta; | |
| } | |
| else if ( i < sel_hi ) { | |
| sel_x_end += delta; | |
| } | |
| } | |
| glColor3f( 0.0f, 0.0f, .6f ); | |
| glBegin( GL_QUADS ); | |
| glVertex2i( sel_x_start, 2 ); glVertex2i( sel_x_end, 2 ); | |
| glVertex2i( sel_x_end, h-2 ); glVertex2i( sel_x_start, h-2 ); | |
| glEnd(); | |
| } | |
| if ( sel_start == sel_end ) { /* No current selection */ | |
| if ( enabled ) | |
| glColor3b( 0, 0, 0 ); | |
| else | |
| glColor3b( 32, 32, 32 ); | |
| glRasterPos2i( text_x, 13); | |
| for( i=substring_start; i<=substring_end; i++ ) { | |
| glutBitmapCharacter( get_font(), this->text[i] ); | |
| } | |
| } | |
| else { /* There is a selection */ | |
| int x = text_x; | |
| for( i=substring_start; i<=substring_end; i++ ) { | |
| if ( IN_BOUNDS( i, sel_lo, sel_hi-1)) { /* This character is selected */ | |
| glColor3f( 1., 1., 1. ); | |
| glRasterPos2i( x, 13); | |
| glutBitmapCharacter( get_font(), this->text[i] ); | |
| } | |
| else { | |
| glColor3f( 0., 0., 0. ); | |
| glRasterPos2i( x, 13); | |
| glutBitmapCharacter( get_font(), this->text[i] ); | |
| } | |
| x += char_width( text[i] ); | |
| } | |
| } | |
| if ( debug ) dump( stdout, "<- DRAW_TEXT" ); | |
| } | |
| /******************************** GLUI_EditText::find_insertion_pt() *********/ | |
| /* This function returns the character numer *before which* the insertion */ | |
| /* point goes */ | |
| int GLUI_EditText::find_insertion_pt( int x, int y ) | |
| { | |
| int curr_x, i; | |
| /*** See if we clicked outside box ***/ | |
| if ( x < this->x_abs + text_x_offset ) | |
| return -1; | |
| /* We move from right to left, looking to see if the mouse was clicked | |
| to the right of the ith character */ | |
| curr_x = this->x_abs + text_x_offset | |
| + substring_width( substring_start, substring_end ) | |
| + 2 /* The edittext box has a 2-pixel margin */ | |
| + GLUI_EDITTEXT_BOXINNERMARGINX; /** plus this many pixels blank space | |
| between the text and the box **/ | |
| /*** See if we clicked in an empty box ***/ | |
| if ( (int) text.length() == 0 ) | |
| return 0; | |
| /** find mouse click in text **/ | |
| for( i=substring_end; i>=substring_start; i-- ) { | |
| curr_x -= char_width( text[i] ); | |
| if ( x > curr_x ) { | |
| /* printf( "-> %d\n", i ); */ | |
| return i+1; | |
| } | |
| } | |
| return 0; | |
| /* Well, the mouse wasn't after any of the characters...see if it's | |
| before the beginning of the substring */ | |
| if ( 0 ) { | |
| if ( x > (x_abs + text_x_offset + 2 ) ) | |
| return substring_start; | |
| return -1; /* Nothing found */ | |
| } | |
| } | |
| /******************************** GLUI_EditText::draw_insertion_pt() *********/ | |
| void GLUI_EditText::draw_insertion_pt( void ) | |
| { | |
| int curr_x, i; | |
| if ( NOT can_draw() ) | |
| return; | |
| /*** Don't draw insertion pt if control is disabled ***/ | |
| if ( NOT enabled ) | |
| return; | |
| if ( debug ) dump( stdout, "-> DRAW_INS_PT" ); | |
| if ( sel_start != sel_end OR insertion_pt < 0 ) { | |
| return; /* Don't draw insertion point if there is a current selection */ | |
| } | |
| /* printf( "insertion pt: %d\n", insertion_pt ); */ | |
| curr_x = this->x_abs + text_x_offset | |
| + substring_width( substring_start, substring_end ) | |
| + 2 /* The edittext box has a 2-pixel margin */ | |
| + GLUI_EDITTEXT_BOXINNERMARGINX; /** plus this many pixels blank space | |
| between the text and the box **/ | |
| for( i=substring_end; i>=insertion_pt; i-- ) { | |
| curr_x -= char_width( text[i] ); | |
| } | |
| glColor3f( 0.0, 0.0, 0.0 ); | |
| glBegin( GL_LINE_LOOP ); | |
| /*** | |
| glVertex2i( curr_x, y_abs + 4 ); | |
| glVertex2i( curr_x, y_abs + 4 ); | |
| glVertex2i( curr_x, y_abs + h - 3 ); | |
| glVertex2i( curr_x, y_abs + h - 3 ); | |
| ***/ | |
| curr_x -= x_abs; | |
| glVertex2i( curr_x, 0 + 4 ); | |
| glVertex2i( curr_x, 0 + 4 ); | |
| glVertex2i( curr_x, 0 + h - 3 ); | |
| glVertex2i( curr_x, 0 + h - 3 ); | |
| glEnd(); | |
| if ( debug ) dump( stdout, "-> DRAW_INS_PT" ); | |
| } | |
| /******************************** GLUI_EditText::substring_width() *********/ | |
| int GLUI_EditText::substring_width( int start, int end ) | |
| { | |
| int i, width; | |
| width = 0; | |
| for( i=start; i<=end; i++ ) | |
| width += char_width( text[i] ); | |
| return width; | |
| } | |
| /***************************** GLUI_EditText::update_and_draw_text() ********/ | |
| void GLUI_EditText::update_and_draw_text( void ) | |
| { | |
| if ( NOT can_draw() ) | |
| return; | |
| update_substring_bounds(); | |
| /* printf( "ss: %d/%d\n", substring_start, substring_end ); */ | |
| redraw(); | |
| } | |
| /********************************* GLUI_EditText::special_handler() **********/ | |
| int GLUI_EditText::special_handler( int key,int modifiers ) | |
| { | |
| if ( NOT glui ) | |
| return false; | |
| if ( debug ) | |
| printf( "SPECIAL:%d - mod:%d subs:%d/%d ins:%d sel:%d/%d\n", | |
| key, modifiers, substring_start, substring_end,insertion_pt, | |
| sel_start, sel_end ); | |
| if ( key == GLUT_KEY_LEFT ) { | |
| if ( (modifiers & GLUT_ACTIVE_CTRL) != 0 ) { | |
| insertion_pt = find_word_break( insertion_pt, -1 ); | |
| } | |
| else { | |
| insertion_pt--; | |
| } | |
| } | |
| else if ( key == GLUT_KEY_RIGHT ) { | |
| if ( (modifiers & GLUT_ACTIVE_CTRL) != 0 ) { | |
| insertion_pt = find_word_break( insertion_pt, +1 ); | |
| } | |
| else { | |
| insertion_pt++; | |
| } | |
| } | |
| else if ( key == GLUT_KEY_HOME ) { | |
| insertion_pt = 0; | |
| } | |
| else if ( key == GLUT_KEY_END ) { | |
| insertion_pt = (int) text.length(); | |
| } | |
| /*** Update selection if shift key is down ***/ | |
| if ( (modifiers & GLUT_ACTIVE_SHIFT ) != 0 ) | |
| sel_end = insertion_pt; | |
| else | |
| sel_start = sel_end = insertion_pt; | |
| CLAMP( insertion_pt, 0, (int) text.length()); /* Make sure insertion_pt | |
| is in bounds */ | |
| CLAMP( sel_start, 0, (int) text.length()); /* Make sure insertion_pt | |
| is in bounds */ | |
| CLAMP( sel_end, 0, (int) text.length()); /* Make sure insertion_pt | |
| is in bounds */ | |
| /******** Now redraw text ***********/ | |
| if ( can_draw()) | |
| update_and_draw_text(); | |
| return true; | |
| } | |
| /****************************** GLUI_EditText::find_word_break() **********/ | |
| /* It looks either left or right (depending on value of 'direction' */ | |
| /* for the beginning of the next 'word', where word are characters */ | |
| /* separated by one of the following tokens: " :-.," */ | |
| /* If there is no next word in the specified direction, this returns */ | |
| /* the beginning of 'text', or the very end. */ | |
| int GLUI_EditText::find_word_break( int start, int direction ) | |
| { | |
| int i, j; | |
| char *breaks = " :-.,"; | |
| int num_break_chars = (int)strlen(breaks), text_len = (int)text.length(); | |
| int new_pt; | |
| /** If we're moving left, we have to start two back, in case we're either | |
| already at the beginning of a word, or on a separating token. | |
| Otherwise, this function would just return the word we're already at **/ | |
| if ( direction == -1 ) { | |
| start -= 2; | |
| } | |
| /***** Iterate over text in the specified direction *****/ | |
| for ( i=start; i >= 0 AND i < text_len; i += direction ) { | |
| /** For each character in text, iterate over list of separating tokens **/ | |
| for( j=0; j<num_break_chars; j++ ) { | |
| if ( text[i] == breaks[j] ) { | |
| /** character 'i' is a separating token, so we return i+1 **/ | |
| new_pt = i + 1; | |
| CLAMP( new_pt, 0, text_len ); | |
| return new_pt; | |
| } | |
| } | |
| } | |
| if ( direction > 0 ) /* Return the end of string */ | |
| return text_len; | |
| else /* Return the beginning of the text */ | |
| return 0; | |
| } | |
| /********************************** GLUI_EditText::clear_substring() ********/ | |
| void GLUI_EditText::clear_substring( int start, int end ) | |
| { | |
| int i; | |
| /* | |
| printf( "clearing: %d-%d '", start,end); | |
| for(i=start;i<end;i++ ) | |
| putchar(text[i]); | |
| printf( "'\n" ); flushout; | |
| */ | |
| /*** See if we're deleting a period in a float data-type box ***/ | |
| if ( data_type == GLUI_EDITTEXT_FLOAT ) { | |
| for( i=start; i<end; i++ ) | |
| if ( text[i] == '.' ) | |
| num_periods = 0; | |
| } | |
| text.erase(start,end-start); | |
| } | |
| /************************************ GLUI_EditText::update_size() **********/ | |
| void GLUI_EditText::update_size( void ) | |
| { | |
| int text_size, delta; | |
| if ( NOT glui ) | |
| return; | |
| text_size = string_width( name ); | |
| delta = 0; | |
| if ( text_x_offset < text_size +2 ) | |
| delta = text_size+2-text_x_offset; | |
| text_x_offset += delta; | |
| /* w += delta; */ | |
| if ( data_type == GLUI_EDITTEXT_TEXT OR | |
| data_type == GLUI_EDITTEXT_FLOAT) { | |
| if ( w < text_x_offset + GLUI_EDITTEXT_MIN_TEXT_WIDTH ) | |
| w = text_x_offset + GLUI_EDITTEXT_MIN_TEXT_WIDTH; | |
| } | |
| else if ( data_type == GLUI_EDITTEXT_INT ) { | |
| if ( w < text_x_offset + GLUI_EDITTEXT_MIN_INT_WIDTH ) | |
| w = text_x_offset + GLUI_EDITTEXT_MIN_INT_WIDTH; | |
| } | |
| } | |
| /****************************** GLUI_EditText::set_text() **********/ | |
| void GLUI_EditText::set_text( const char *new_text ) | |
| { | |
| text=new_text; | |
| substring_start = 0; | |
| substring_end = (int) text.length() - 1; | |
| insertion_pt = -1; | |
| sel_start = 0; | |
| sel_end = 0; | |
| if ( can_draw() ) | |
| update_and_draw_text(); | |
| /** Update the spinner, if we have one **/ | |
| if ( spinner ) { | |
| spinner->float_val = this->float_val; | |
| spinner->int_val = this->int_val; | |
| } | |
| /*** Now update the live variable ***/ | |
| output_live(true); | |
| } | |
| /******************************* GLUI_EditText::set_float_val() ************/ | |
| void GLUI_EditText::set_float_val( float new_val ) | |
| { | |
| if ( has_limits == GLUI_LIMIT_CLAMP ) { | |
| /*** Clamp the new value to the existing limits ***/ | |
| CLAMP( new_val, float_low, float_high ); | |
| } | |
| else if ( has_limits == GLUI_LIMIT_WRAP ) { | |
| /*** Clamp the value cyclically to the limits - that is, if the | |
| value exceeds the max, set it the the minimum, and conversely ***/ | |
| if ( new_val < float_low ) | |
| new_val = float_high; | |
| if ( new_val > float_high ) | |
| new_val = float_low; | |
| } | |
| float_val = new_val; | |
| int_val = (int) new_val; /* Mirror the value as an int, too */ | |
| set_numeric_text(); | |
| } | |
| /********************************** GLUI_EditText::set_int_val() ************/ | |
| void GLUI_EditText::set_int_val( int new_val ) | |
| { | |
| if ( has_limits == GLUI_LIMIT_CLAMP ) { | |
| /*** Clamp the new value to the existing limits ***/ | |
| CLAMP( new_val, int_low, int_high ); | |
| } | |
| else if ( has_limits == GLUI_LIMIT_WRAP ) { | |
| /*** Clamp the value cyclically to the limits - that is, if the | |
| value exceeds the max, set it the the minimum, and conversely ***/ | |
| if ( new_val < int_low ) | |
| new_val = int_high; | |
| if ( new_val > int_high ) | |
| new_val = int_low; | |
| } | |
| int_val = new_val; | |
| float_val = (float) new_val; /* We mirror the value as a float, too */ | |
| set_numeric_text(); | |
| } | |
| /********************************* GLUI_EditText::set_float_limits() *********/ | |
| void GLUI_EditText::set_float_limits( float low, float high, int limit_type ) | |
| { | |
| has_limits = limit_type; | |
| float_low = low; | |
| float_high = high; | |
| if ( NOT IN_BOUNDS( float_val, float_low, float_high )) | |
| set_float_val( float_low ); | |
| int_low = (int) float_low; | |
| int_high = (int) float_high; | |
| } | |
| /*********************************** GLUI_EditText::set_int_limits() *********/ | |
| void GLUI_EditText::set_int_limits( int low, int high, int limit_type ) | |
| { | |
| has_limits = limit_type; | |
| int_low = low; | |
| int_high = high; | |
| if ( NOT IN_BOUNDS( int_val, int_low, int_high )) | |
| set_int_val( int_low ); | |
| float_low = (float) int_low; | |
| float_high = (float) int_high; | |
| } | |
| /************************************ GLUI_EditText::set_numeric_text() ******/ | |
| void GLUI_EditText::set_numeric_text( void ) | |
| { | |
| char buf_num[200]; | |
| int i, text_len; | |
| if ( data_type == GLUI_EDITTEXT_FLOAT ) { | |
| sprintf( buf_num, "%#g", float_val ); | |
| num_periods = 0; | |
| text_len = (int) strlen(buf_num); | |
| for ( i=0; i<text_len; i++ ) | |
| if ( buf_num[i] == '.' ) | |
| num_periods++; | |
| /* Now remove trailing zeros */ | |
| if ( num_periods > 0 ) { | |
| text_len = (int) strlen(buf_num); | |
| for ( i=text_len-1; i>0; i-- ) { | |
| if ( buf_num[i] == '0' AND buf_num[i-1] != '.' ) | |
| buf_num[i] = '\0'; | |
| else | |
| break; | |
| } | |
| } | |
| set_text( buf_num ); | |
| } | |
| else { | |
| sprintf( buf_num, "%d", int_val ); | |
| set_text( buf_num ); | |
| } | |
| } | |
| /*************************************** GLUI_EditText::dump() **************/ | |
| void GLUI_EditText::dump( FILE *out, const char *name ) | |
| { | |
| fprintf( out, | |
| "%s (edittext@%p): ins_pt:%d subs:%d/%d sel:%d/%d len:%d\n", | |
| name, this, | |
| insertion_pt, | |
| substring_start, | |
| substring_end, | |
| sel_start, | |
| sel_end, | |
| (int) text.length()); | |
| } | |
| /**************************************** GLUI_EditText::mouse_over() ********/ | |
| int GLUI_EditText::mouse_over( int state, int x, int y ) | |
| { | |
| if ( state ) { | |
| /* curr_cursor = GLUT_CURSOR_TEXT; */ | |
| glutSetCursor( GLUT_CURSOR_TEXT ); | |
| } | |
| else { | |
| /* printf( "OUT\n" ); */ | |
| glutSetCursor( GLUT_CURSOR_LEFT_ARROW ); | |
| } | |
| return true; | |
| } |