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, char *name )
1071
"%s (edittext@%p): line:%d ins_pt:%d subs:%d/%d sel:%d/%d len:%d\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();