1 /****************************************************************************
3 GLUI User Interface Toolkit
4 ---------------------------
6 glui_textbox.cpp - GLUI_TextBox control class
9 --------------------------------------------------
11 Copyright (c) 1998 Paul Rademacher, 2004 John Kew
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"
36 static const int LINE_HEIGHT = 15;
38 /****************************** GLUI_TextBox::GLUI_TextBox() **********/
40 GLUI_TextBox::GLUI_TextBox(GLUI_Node *parent, GLUI_String &live_var,
41 bool scroll, int id, GLUI_CB callback )
43 common_construct(parent, &live_var, scroll, id, callback);
46 /****************************** GLUI_TextBox::GLUI_TextBox() **********/
48 GLUI_TextBox::GLUI_TextBox( GLUI_Node *parent, bool scroll, int id,
51 common_construct(parent, NULL, scroll, id, callback);
54 /****************************** GLUI_TextBox::common_construct() **********/
55 void GLUI_TextBox::common_construct(
56 GLUI_Node *parent, GLUI_String *data,
57 bool scroll, int id, GLUI_CB callback)
61 GLUI_Node *tb_panel = parent;
64 GLUI_Panel *p = new GLUI_Panel(parent,"",GLUI_PANEL_NONE);
70 this->live_type = GLUI_LIVE_STRING;
72 this->live_type = GLUI_LIVE_NONE;
75 this->callback = callback;
76 this->name = "textbox";
77 tb_panel->add_control( this );
79 new GLUI_Column(tb_panel, false);
81 new GLUI_Scrollbar(tb_panel,
85 scrollbar->set_object_callback(GLUI_TextBox::scrollbar_callback, this);
86 scrollbar->set_alignment(GLUI_ALIGN_LEFT);
87 // scrollbar->can_activate = false; //kills ability to mouse drag too
92 /****************************** GLUI_TextBox::mouse_down_handler() **********/
94 int GLUI_TextBox::mouse_down_handler( int local_x, int local_y )
98 if ( debug ) dump( stdout, "-> MOUSE DOWN" );
100 tmp_insertion_pt = find_insertion_pt( local_x, local_y );
101 if ( tmp_insertion_pt == -1 ) {
103 glui->deactivate_current_control( );
107 insertion_pt = tmp_insertion_pt;
109 sel_start = sel_end = insertion_pt;
111 keygoal_x = insert_x;
114 update_and_draw_text();
116 if ( debug ) dump( stdout, "<- MOUSE UP" );
122 /******************************** GLUI_TextBox::mouse_up_handler() **********/
124 int GLUI_TextBox::mouse_up_handler( int local_x, int local_y, bool inside )
130 /***************************** GLUI_TextBox::mouse_held_down_handler() ******/
132 int GLUI_TextBox::mouse_held_down_handler( int local_x, int local_y,
137 if ( NOT new_inside ) return false;
139 if ( debug ) dump( stdout, "-> HELD DOWN" );
141 tmp_pt = find_insertion_pt( local_x, local_y );
142 keygoal_x = insert_x;
144 if ( tmp_pt == -1 AND sel_end != 0 ) { /* moved mouse past left edge */
145 special_handler( GLUT_KEY_LEFT, GLUT_ACTIVE_SHIFT );
147 else if ( tmp_pt == substring_end+1 AND sel_end != (int) text.length()) {
148 /* moved mouse past right edge */
149 special_handler( GLUT_KEY_RIGHT, GLUT_ACTIVE_SHIFT );
151 else if ( tmp_pt != -1 AND tmp_pt != sel_end ) {
152 sel_end = insertion_pt = tmp_pt;
154 update_and_draw_text();
158 dump( stdout, "<- HELD DOWN" );
164 /****************************** GLUI_TextBox::key_handler() **********/
165 int GLUI_TextBox::key_handler( unsigned char key,int modifiers )
168 /* int has_selection; */
174 dump( stdout, "-> KEY HANDLER" );
177 bool ctrl_down = (modifiers & GLUT_ACTIVE_CTRL)!=0;
178 /* has_selection = (sel_start != sel_end); */
180 if ( key == CTRL('[')) { /* ESCAPE */
181 glui->deactivate_current_control();
184 else if ( (key == 127 AND !ctrl_down) OR /* FORWARD DELETE */
185 ( key == CTRL('d') AND modifiers == GLUT_ACTIVE_CTRL) )
187 if ( sel_start == sel_end ) { /* no selection */
188 if ( insertion_pt < (int)text.length() ) {
189 text.erase(insertion_pt,1);
192 else { /* There is a selection */
193 clear_substring( MIN(sel_start,sel_end), MAX(sel_start,sel_end ));
194 insertion_pt = MIN(sel_start,sel_end);
195 sel_start = sel_end = insertion_pt;
198 else if ( ((key == 127) AND ctrl_down) OR // Delete word forward
199 ((key == 'd') AND (modifiers == GLUT_ACTIVE_ALT)) )
201 if ( sel_start == sel_end ) { /* no selection */
202 sel_start = insertion_pt;
203 sel_end = find_word_break( insertion_pt, +1 );
206 clear_substring( MIN(sel_start,sel_end), MAX(sel_start,sel_end ));
207 insertion_pt = MIN(sel_start,sel_end);
208 sel_start = sel_end = insertion_pt;
210 else if ( key == CTRL('h') ) { /* BACKSPACE */
211 if ( sel_start == sel_end ) { /* no selection */
212 if ( insertion_pt > 0 ) {
214 text.erase(insertion_pt,1);
217 else { /* There is a selection */
218 clear_substring( MIN(sel_start,sel_end), MAX(sel_start,sel_end ));
219 insertion_pt = MIN(sel_start,sel_end);
220 sel_start = sel_end = insertion_pt;
223 else if ( modifiers == GLUT_ACTIVE_CTRL ) /* CTRL ONLY */
225 /* Ctrl-key bindings */
226 if ( key == CTRL('a') ) {
227 return special_handler( GLUT_KEY_HOME, 0 );
229 else if ( key == CTRL('e') ) {
230 return special_handler( GLUT_KEY_END, 0 );
232 else if ( key == CTRL('b') ) {
233 return special_handler( GLUT_KEY_LEFT, 0 );
235 else if ( key == CTRL('f') ) {
236 return special_handler( GLUT_KEY_RIGHT, 0 );
238 else if ( key == CTRL('p') ) {
239 return special_handler( GLUT_KEY_UP, 0 );
241 else if ( key == CTRL('n') ) {
242 return special_handler( GLUT_KEY_DOWN, 0 );
244 else if ( key == CTRL('u') ) { /* ERASE LINE */
246 text.erase(0,text.length());
247 sel_start = sel_end = 0;
249 else if ( key == CTRL('k') ) { /* KILL TO END OF LINE */
250 sel_start = sel_end = insertion_pt;
251 text.erase(insertion_pt,GLUI_String::npos);
254 else if ( modifiers == GLUT_ACTIVE_ALT ) /* ALT ONLY */
256 if ( key == 'b' ) { // Backward word
257 return special_handler ( GLUT_KEY_LEFT, GLUT_ACTIVE_CTRL );
259 if ( key == 'f' ) { // Forward word
260 return special_handler ( GLUT_KEY_RIGHT, GLUT_ACTIVE_CTRL );
263 else if ( (modifiers & GLUT_ACTIVE_CTRL) OR
264 (modifiers & GLUT_ACTIVE_ALT) )
266 /** ignore other keys with modifiers */
269 else { /* Regular key */
270 if ( key == 13 ) /* RETURNS are written as newlines*/
275 /** This is just to get rid of warnings - the flag regular_key is
276 set if the key was not a backspace, return, whatever. But I
277 believe if we're here, we know it was a regular key anyway */
281 /**** If there's a current selection, erase it ******/
282 if ( sel_start != sel_end ) {
283 clear_substring( MIN(sel_start,sel_end), MAX(sel_start,sel_end ));
284 insertion_pt = MIN(sel_start,sel_end);
285 sel_start = sel_end = insertion_pt;
288 /******** We insert the character into the string ***/
290 text.insert(insertion_pt,1,key);
292 /******** Move the insertion point and substring_end one over ******/
296 sel_start = sel_end = insertion_pt;
299 /******** Now redraw text ***********/
300 /* Hack to prevent text box from being cleared first **/
301 /** int substring_change = update_substring_bounds();
303 (NOT substring_change AND NOT has_selection AND regular_key );
306 draw_text_only = false; /** Well, hack is not yet working **/
307 update_and_draw_text();
308 draw_text_only = false;
312 dump( stdout, "<- KEY HANDLER" );
317 /****************************** GLUI_TextBox::enable() **********/
319 void GLUI_TextBox::enable( void )
321 GLUI_Control::enable();
325 /****************************** GLUI_TextBox::disable() **********/
327 void GLUI_TextBox::disable( void )
329 GLUI_Control::disable();
330 scrollbar->disable();
333 /****************************** GLUI_TextBox::activate() **********/
335 void GLUI_TextBox::activate( int how )
338 dump( stdout, "-> ACTIVATE" );
341 if ( how == GLUI_ACTIVATE_MOUSE )
342 return; /* Don't select everything if activated with mouse */
347 sel_end = int(text.length());
350 dump( stdout, "<- ACTIVATE" );
354 /****************************** GLUI_TextBox::deactivate() **********/
356 void GLUI_TextBox::deactivate( void )
364 dump( stdout, "-> DISACTIVATE" );
366 sel_start = sel_end = insertion_pt = -1;
368 /***** Retrieve the current value from the text *****/
369 /***** The live variable will be updated by set_text() ****/
370 set_text(text.c_str()); /* This will force callbacks and gfx refresh */
372 update_substring_bounds();
374 /******** redraw text without insertion point ***********/
377 /***** Now do callbacks if value changed ******/
378 if ( orig_text != text ) {
379 this->execute_callback();
386 dump( stdout, "<- DISACTIVATE" );
389 /****************************** GLUI_TextBox::draw() **********/
391 void GLUI_TextBox::draw( int x, int y )
393 GLUI_DRAWINGSENTINAL_IDIOM
399 /* Bevelled Border */
401 glColor3f( .5, .5, .5 );
402 glVertex2i( 0, 0 ); glVertex2i( w, 0 );
403 glVertex2i( 0, 0 ); glVertex2i( 0, h );
405 glColor3f( 1., 1., 1. );
406 glVertex2i( 0, h ); glVertex2i( w, h );
407 glVertex2i( w, h ); glVertex2i( w, 0 );
410 glColor3f( 0., 0., 0. );
412 glColor3f( .25, .25, .25 );
413 glVertex2i( 1, 1 ); glVertex2i( w-1, 1 );
414 glVertex2i( 1, 1 ); glVertex2i( 1, h-1 );
416 glColor3f( .75, .75, .75 );
417 glVertex2i( 1, h-1 ); glVertex2i( w-1, h-1 );
418 glVertex2i( w-1, h-1 ); glVertex2i( w-1, 1 );
421 /* Draw Background if enabled*/
423 glColor3f( 1., 1., 1. );
424 glDisable( GL_CULL_FACE );
426 glVertex2i( 2, 2 ); glVertex2i( w-2, 2 );
427 glVertex2i( w-2, h-2 ); glVertex2i(2, h-2 );
430 glColor3f( .8, .8, .8 );
431 glDisable( GL_CULL_FACE );
433 glVertex2i( 2, 2 ); glVertex2i( w-2, 2 );
434 glVertex2i( w-2, h-2 ); glVertex2i(2, h-2 );
438 /* Begin Drawing Lines of Text */
441 text_length = int(text.length())-1;
443 /* Figure out how wide the box is */
444 box_width = get_box_width();
446 /* Get the first line substring */
447 while (substring_width(substring_start, substring_end+1 ) < box_width &&
448 substring_end < text_length && text[substring_end+1] != '\n')
451 /* Figure out which lines are visible*/
453 visible_lines = (int)(h-20)/LINE_HEIGHT;
454 if (start_line < (curr_line-visible_lines)) {
455 for (i = 0; ((curr_line-i)*LINE_HEIGHT+20) > h; i++);
457 } else if ( start_line > curr_line) {
458 start_line = curr_line;
462 if (line && substring_end < text_length) {
463 substring_start = substring_end+1;
464 while (substring_width(substring_start, substring_end+1 ) < box_width &&
465 substring_end < text_length && text[substring_end+1] != '\n')
468 if (text[substring_end+1] == '\n') { /* Skip newline */
471 if (line < start_line || (line > curr_line && curr_line > (start_line + visible_lines))) {
475 if ((line - start_line) <= visible_lines)
476 draw_text(0,(line - start_line)*LINE_HEIGHT); /* tabs and other nasties are handled by substring_width */
478 } while (substring_end < text_length);
484 scrollbar->set_int_limits(MAX(0,num_lines/*-1*/-visible_lines),0);
486 glTranslatef(scrollbar->x_abs-x_abs, scrollbar->y_abs-y_abs,0.0);
487 scrollbar->draw_scroll();
494 /************************** GLUI_TextBox::update_substring_bounds() *********/
496 int GLUI_TextBox::update_substring_bounds( void )
499 int text_len = int(text.length());
500 int old_start, old_end;
502 old_start = substring_start;
503 old_end = substring_end;
505 /*** Calculate the width of the usable area of the edit box ***/
506 box_width = get_box_width();
508 CLAMP( substring_end, 0, MAX(text_len-1,0) );
509 CLAMP( substring_start, 0, MAX(text_len-1,0) );
511 if ( debug ) dump( stdout, "-> UPDATE SS" );
513 if ( insertion_pt >= 0 AND
514 insertion_pt < substring_start ) { /* cursor moved left */
515 substring_start = insertion_pt;
517 while ( substring_width( substring_start, substring_end ) > box_width )
520 else if ( insertion_pt > substring_end ) { /* cursor moved right */
521 substring_end = insertion_pt-1;
523 while ( substring_width( substring_start, substring_end ) > box_width )
526 else { /* cursor is within old substring bounds */
527 if ( last_insertion_pt > insertion_pt ) { /* cursor moved left */
530 while ( substring_width( substring_start, substring_end ) > box_width )
533 while(substring_width( substring_start, substring_end+1 ) <= box_width
534 AND substring_end < text_len-1 )
539 while ( substring_width( substring_start, substring_end ) > box_width )
542 last_insertion_pt = insertion_pt;
544 /*** No selection if not enabled ***/
546 sel_start = sel_end = 0;
549 if ( debug ) dump( stdout, "<- UPDATE SS" );
551 if ( substring_start == old_start AND substring_end == old_end )
552 return false; /*** bounds did not change ***/
554 return true; /*** bounds did change ***/
559 /********************************* GLUI_TextBox::update_x_offsets() *********/
561 void GLUI_TextBox::update_x_offsets( void )
566 /********************************* GLUI_TextBox::draw_text() ****************/
568 void GLUI_TextBox::draw_text( int x, int y )
570 GLUI_DRAWINGSENTINAL_IDIOM
571 int text_x, i, sel_lo, sel_hi, x_pos;
573 if ( debug ) dump( stdout, "-> DRAW_TEXT" );
575 /** Find where to draw the text **/
577 text_x = 2 + GLUI_TEXTBOX_BOXINNERMARGINX;
579 /** Find lower and upper selection bounds **/
580 sel_lo = MIN(sel_start, sel_end );
581 sel_hi = MAX(sel_start, sel_end );
583 int sel_x_start, sel_x_end, delta;
585 /** Draw selection area dark **/
586 if ( sel_start != sel_end ) {
587 sel_x_start = text_x;
590 for( i=substring_start; sel_x_end < (w - text_x) && i<=substring_end; i++ ) {
592 if (text[i] == '\t') // Character is a tab, go to next tab stop
593 while (((delta + sel_x_end) < (w - text_x)) &&
594 (delta == 0 || delta % tab_width))
597 delta = char_width( text[i] );
600 sel_x_start += delta;
603 else if ( i < sel_hi ) {
608 glColor3f( 0.0f, 0.0f, .6f );
609 glRecti(sel_x_start, y+5, sel_x_end, y+20);
613 if ( sel_start == sel_end ) { // No current selection
616 glColor3b( 0, 0, 0 );
618 glColor3b( 32, 32, 32 );
620 glRasterPos2i( text_x, y+LINE_HEIGHT);
621 for( i=substring_start; i<=substring_end; i++ ) {
622 if (this->text[i] == '\t') { // Character is a tab, go to next tab stop
623 x_pos = ((x_pos-text_x)/tab_width)*tab_width+tab_width+text_x;
624 glRasterPos2i( x_pos, y+LINE_HEIGHT); // Reposition pen after tab
626 glutBitmapCharacter( get_font(), this->text[i] );
627 x_pos += char_width( this->text[i] );
631 else { // There is a selection
633 for( i=substring_start; i<=substring_end; i++ ) {
634 if ( IN_BOUNDS( i, sel_lo, sel_hi-1)) { // This character is selected
635 glColor3f( 1., 1., 1. );
636 glRasterPos2i( x_pos, y+LINE_HEIGHT);
637 if (this->text[i] == '\t') { // Character is a tab, go to next tab stop
638 x_pos = ((x_pos-text_x)/tab_width)*tab_width+tab_width+text_x;
641 glutBitmapCharacter( get_font(), this->text[i] );
644 glColor3f( 0., 0., 0. );
645 glRasterPos2i( x_pos, y+LINE_HEIGHT);
646 if (this->text[i] == '\t') { // Character is a tab, go to next tab stop
647 x_pos = ((x_pos-text_x)/tab_width)*tab_width+tab_width+text_x;
648 glRasterPos2i( x_pos, y+LINE_HEIGHT); // Reposition pen after tab
650 glutBitmapCharacter( get_font(), this->text[i] );
653 x_pos += char_width( text[i] );
657 if ( debug ) dump( stdout, "<- DRAW_TEXT" );
661 /******************************** GLUI_TextBox::find_insertion_pt() *********/
662 /* This function returns the character number *before which* the insertion */
665 int GLUI_TextBox::find_insertion_pt( int x, int y )
667 /*** See if we clicked outside box ***/
668 if ( x < this->x_abs || y < this->y_abs)
671 /*** See if we clicked in an empty box ***/
675 /* update insert variables */
679 int text_length = int(text.length())-1;
680 int box_width = get_box_width();
686 int y_off = y - (y_abs + 2 + GLUI_TEXTBOX_BOXINNERMARGINX);
687 int x_off = x - (x_abs + 2 + GLUI_TEXTBOX_BOXINNERMARGINX);
689 /* Find the line clicked,
690 The possibility of long lines getting wrapped complicates this. */
691 while ((line-start_line+1)*LINE_HEIGHT < y_off && eol < text_length)
693 while (eol < text_length && text[eol] != '\n' &&
694 substring_width(sol, eol+1) <= box_width)
698 if (text[eol]=='\n' && eol<text_length) { eol++; }
703 // Now search to the end of this line for the closest insertion point
704 int prev_w=0,total_w=0,prev_eol=eol;
705 while (eol <= text_length
706 && (total_w=substring_width(prev_eol,eol,prev_w))< x_off
707 && (eol==text_length||text[eol]!='\n'))
713 if (total_w>=x_off) {
714 // did we go far enough? (see if click was >1/2 width of last char)
715 int decision_pt = prev_w+(total_w-prev_w)/2;
716 if (x_off>decision_pt) eol++;
721 while (eol < text_length && text[eol] != '\n' &&
722 substring_width(sol, eol+1) < box_width )
728 /* We move from right to left, looking to see if the mouse was clicked
729 to the right of the ith character */
731 int curr_x = this->x_abs
732 + substring_width( sol, eol )
733 + 2 /* The edittext box has a 2-pixel margin */
734 + GLUI_TEXTBOX_BOXINNERMARGINX; /** plus this many pixels blank space
735 between the text and the box **/
738 /** find mouse click in text **/
740 if (x_off > substring_width(sol, eol))
743 for(i = sol; i <= eol+1; i++) {
744 if (x_off <= substring_width(sol, i))
752 int GLUI_TextBox::get_box_width()
755 - 4 /* 2 * the two-line box border */
756 - 2 * GLUI_TEXTBOX_BOXINNERMARGINX, 0 );
760 /******************************** GLUI_TextBox::draw_insertion_pt() *********/
762 void GLUI_TextBox::draw_insertion_pt( void )
764 int curr_x, box_width, text_length, eol, sol, line;
766 if ( NOT can_draw() )
769 /*** Don't draw insertion pt if control is disabled ***/
773 if ( sel_start != sel_end OR insertion_pt < 0 ) {
774 return; /* Don't draw insertion point if there is a current selection */
777 if ( debug ) dump( stdout, "-> DRAW_INS_PT" );
779 /* printf( "insertion pt: %d\n", insertion_pt ); */
781 box_width = get_box_width();
783 // This function is unable to distinguish whether an insertion
784 // point on a line break should be drawn on the line before or the line after.
785 // This depends on the sequence of operations used to get there, and this
786 // function just doesn't have that information. If curr_line were kept up
787 // to date elsewhere that could be used here to disambiguate, but arrow keys
788 // and such do not update it.
792 text_length = int(text.length())-1;
794 //while (eol < text_length && text[eol] != '\n'
795 // && substring_width(sol, eol + 1) < box_width )
798 while (eol < insertion_pt && eol <= text_length)
800 if (text[eol] == '\n' || substring_width(sol, eol + 1) >= box_width)
803 if (text[eol]=='\n'||eol!=insertion_pt
804 ||(eol==insertion_pt && eol>0 && text[eol-1]=='\n')) {
815 //glRecti(0, curr_line*LINE_HEIGHT, 3, (curr_line+1)*LINE_HEIGHT);
820 scrollbar->set_int_val(start_line);
821 if (curr_line < start_line || curr_line > (start_line + visible_lines)) /* Insertion pt out of draw area */
825 + 2 /* The edittext box has a 2-pixel margin */
826 + GLUI_TEXTBOX_BOXINNERMARGINX; /** plus this many pixels blank space
827 between the text and the box **/
829 curr_x += substring_width(sol,insertion_pt-1);
830 if (insertion_pt == text.length() && text[text.length()-1] == '\n'
831 || curr_x-this->x_abs > (w - 2 - GLUI_TEXTBOX_BOXINNERMARGINX)) { // Insert on the next line
832 curr_x = this->x_abs + GLUI_TEXTBOX_BOXINNERMARGINX;
835 /* update insertion coordinates */
836 insert_x = curr_x+5; /* I hate magic numbers too, these offset the imagined insertion point */
837 insert_y = (curr_line-start_line+2)*LINE_HEIGHT;
840 glColor3f( 0.0, 0.0, 0.0 );
841 glBegin( GL_LINE_LOOP );
844 glVertex2i( curr_x+1, (curr_line-start_line)*LINE_HEIGHT + 4 );
845 glVertex2i( curr_x, (curr_line-start_line)*LINE_HEIGHT + 4 );
846 glVertex2i( curr_x+1, (curr_line-start_line)*LINE_HEIGHT + 16 );
847 glVertex2i( curr_x, (curr_line-start_line)*LINE_HEIGHT + 16 );
851 if ( debug ) dump( stdout, "-> DRAW_INS_PT" );
857 /******************************** GLUI_TextBox::substring_width() *********/
858 int GLUI_TextBox::substring_width( int start, int end, int initial_width )
860 // This function only works properly if start is really the start of a line.
861 // Otherwise tabs will be messed up.
862 int i, width = initial_width;
864 for( i=start; i<=end; i++ )
865 if (text[i] == '\t') { // Character is a tab, jump to next tab stop
866 width += tab_width-(width%tab_width);
867 //while (width == 0 || width % tab_width)
871 width += char_width( text[i] );
877 /***************************** GLUI_TextBox::update_and_draw_text() ********/
879 void GLUI_TextBox::update_and_draw_text( void )
881 //update_substring_bounds();
882 /* printf( "ss: %d/%d\n", substring_start, substring_end ); */
888 /********************************* GLUI_TextBox::special_handler() **********/
890 int GLUI_TextBox::special_handler( int key,int modifiers )
892 int tmp_insertion_pt;
897 printf( "SPECIAL:%d - mod:%d subs:%d/%d ins:%d sel:%d/%d\n",
898 key, modifiers, substring_start, substring_end,insertion_pt,
899 sel_start, sel_end );
901 if ( key == GLUT_KEY_DOWN ) {
902 if (insert_x == -1 || insert_y == -1)
904 tmp_insertion_pt = find_insertion_pt( keygoal_x, insert_y+LINE_HEIGHT);
905 if (tmp_insertion_pt < 0)
907 insertion_pt = tmp_insertion_pt;
908 sel_end = insertion_pt;
909 if (!(modifiers & GLUT_ACTIVE_SHIFT)) {
913 update_and_draw_text();
914 } else if ( key == GLUT_KEY_UP ) {
915 if (insert_x == -1 || insert_y == -1)
917 tmp_insertion_pt = find_insertion_pt( keygoal_x, insert_y-LINE_HEIGHT);
918 if (tmp_insertion_pt < 0)
920 insertion_pt = tmp_insertion_pt;
921 sel_end = insertion_pt;
922 if (!(modifiers & GLUT_ACTIVE_SHIFT)) {
926 update_and_draw_text();
927 } else if ( key == GLUT_KEY_LEFT ) {
928 if ( (modifiers & GLUT_ACTIVE_CTRL) != 0 ) {
929 insertion_pt = find_word_break( insertion_pt, -1 );
936 else if ( key == GLUT_KEY_RIGHT ) {
937 if ( (modifiers & GLUT_ACTIVE_CTRL) != 0 ) {
938 insertion_pt = find_word_break( insertion_pt, +1 );
945 else if ( key == GLUT_KEY_HOME ) {
949 else if ( key == GLUT_KEY_END ) {
950 insertion_pt = int(text.length());
954 /*** Update selection if shift key is down ***/
955 if ( (modifiers & GLUT_ACTIVE_SHIFT ) != 0 )
956 sel_end = insertion_pt;
958 sel_start = sel_end = insertion_pt;
961 CLAMP( insertion_pt, 0, (int)text.length()); /* Make sure insertion_pt
963 CLAMP( sel_start, 0, (int)text.length()); /* Make sure insertion_pt
965 CLAMP( sel_end, 0, (int)text.length()); /* Make sure insertion_pt
968 /******** Now redraw text ***********/
970 update_and_draw_text();
976 /****************************** GLUI_TextBox::find_word_break() **********/
977 /* It looks either left or right (depending on value of 'direction' */
978 /* for the beginning of the next 'word', where word are characters */
979 /* separated by one of the following tokens: " :-.," */
980 /* If there is no next word in the specified direction, this returns */
981 /* the beginning of 'text', or the very end. */
983 int GLUI_TextBox::find_word_break( int start, int direction )
986 char breaks[] = " \n\t:-.,";
987 int num_break_chars = (int)strlen(breaks), text_len = int(text.length());
990 /** If we're moving left, we have to start two back, in case we're either
991 already at the beginning of a word, or on a separating token.
992 Otherwise, this function would just return the word we're already at **/
993 if ( direction == -1 ) {
997 /***** Iterate over text in the specified direction *****/
998 for ( i=start; i >= 0 AND i < text_len; i += direction ) {
1000 /** For each character in text, iterate over list of separating tokens **/
1001 for( j=0; j<num_break_chars; j++ ) {
1002 if ( text[i] == breaks[j] ) {
1004 /** character 'i' is a separating token, so we return i+1 **/
1007 CLAMP( new_pt, 0, text_len );
1014 if ( direction > 0 ) /* Return the end of string */
1016 else /* Return the beginning of the text */
1021 /********************************** GLUI_TextBox::clear_substring() ********/
1023 void GLUI_TextBox::clear_substring( int start, int end )
1025 text.erase(start,end-start);
1030 /************************************ GLUI_TextBox::update_size() **********/
1032 void GLUI_TextBox::update_size( void )
1037 if ( w < GLUI_TEXTBOX_MIN_TEXT_WIDTH )
1038 w = GLUI_TEXTBOX_MIN_TEXT_WIDTH;
1042 /****************************** GLUI_TextBox::set_text() **********/
1044 void GLUI_TextBox::set_text( const char *new_text )
1048 substring_start = 0;
1049 substring_end = int(text.length()) - 1;
1059 update_and_draw_text();
1061 /*** Now update the live variable ***/
1066 /*************************************** GLUI_TextBox::dump() **************/
1068 void GLUI_TextBox::dump( FILE *out, const char *name )
1071 "%s (edittext@%p): line:%d ins_pt:%d subs:%d/%d sel:%d/%d len:%zu\n",
1072 name, this, curr_line,
1073 insertion_pt, substring_start, substring_end, sel_start, sel_end,
1078 /**************************************** GLUI_TextBox::mouse_over() ********/
1080 int GLUI_TextBox::mouse_over( int state, int x, int y )
1082 if ( state && enabled) {
1083 /* curr_cursor = GLUT_CURSOR_TEXT; */
1084 glutSetCursor( GLUT_CURSOR_TEXT );
1087 /* printf( "OUT\n" ); */
1088 glutSetCursor( GLUT_CURSOR_LEFT_ARROW );
1094 void GLUI_TextBox::scrollbar_callback(GLUI_Control *my_scrollbar) {
1095 GLUI_Scrollbar *sb = my_scrollbar->dynamicCastGLUI_Scrollbar();
1097 GLUI_TextBox* me = (GLUI_TextBox*) sb->associated_object;
1098 if (me->scrollbar == NULL)
1100 int new_start_line = sb->get_int_val(); // ??
1101 me->start_line = new_start_line;
1102 if (new_start_line < (me->curr_line - me->visible_lines))
1103 me->curr_line = new_start_line + me->visible_lines;
1104 if (new_start_line > me->curr_line)
1105 me->curr_line = new_start_line;
1106 if ( me->can_draw() )
1107 me->update_and_draw_text();