aboutsummaryrefslogtreecommitdiff
path: root/tests/bullet/Extras/glui/glui_textbox.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/bullet/Extras/glui/glui_textbox.cpp')
-rw-r--r--tests/bullet/Extras/glui/glui_textbox.cpp1108
1 files changed, 1108 insertions, 0 deletions
diff --git a/tests/bullet/Extras/glui/glui_textbox.cpp b/tests/bullet/Extras/glui/glui_textbox.cpp
new file mode 100644
index 00000000..9bf04265
--- /dev/null
+++ b/tests/bullet/Extras/glui/glui_textbox.cpp
@@ -0,0 +1,1108 @@
+/****************************************************************************
+
+ GLUI User Interface Toolkit
+ ---------------------------
+
+ glui_textbox.cpp - GLUI_TextBox control class
+
+
+ --------------------------------------------------
+
+ Copyright (c) 1998 Paul Rademacher, 2004 John Kew
+
+ 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 <cmath>
+
+
+static const int LINE_HEIGHT = 15;
+
+/****************************** GLUI_TextBox::GLUI_TextBox() **********/
+
+GLUI_TextBox::GLUI_TextBox(GLUI_Node *parent, GLUI_String &live_var,
+ bool scroll, int id, GLUI_CB callback )
+{
+ common_construct(parent, &live_var, scroll, id, callback);
+}
+
+/****************************** GLUI_TextBox::GLUI_TextBox() **********/
+
+GLUI_TextBox::GLUI_TextBox( GLUI_Node *parent, bool scroll, int id,
+ GLUI_CB callback )
+{
+ common_construct(parent, NULL, scroll, id, callback);
+}
+
+/****************************** GLUI_TextBox::common_construct() **********/
+void GLUI_TextBox::common_construct(
+ GLUI_Node *parent, GLUI_String *data,
+ bool scroll, int id, GLUI_CB callback)
+{
+ common_init();
+
+ GLUI_Node *tb_panel = parent;
+
+ if (scroll) {
+ GLUI_Panel *p = new GLUI_Panel(parent,"",GLUI_PANEL_NONE);
+ p->x_off = 1;
+ tb_panel = p;
+ }
+ this->ptr_val = data;
+ if (data) {
+ this->live_type = GLUI_LIVE_STRING;
+ } else {
+ this->live_type = GLUI_LIVE_NONE;
+ }
+ this->user_id = id;
+ this->callback = callback;
+ this->name = "textbox";
+ tb_panel->add_control( this );
+ if (scroll) {
+ new GLUI_Column(tb_panel, false);
+ scrollbar =
+ new GLUI_Scrollbar(tb_panel,
+ "scrollbar",
+ GLUI_SCROLL_VERTICAL,
+ GLUI_SCROLL_INT);
+ scrollbar->set_object_callback(GLUI_TextBox::scrollbar_callback, this);
+ scrollbar->set_alignment(GLUI_ALIGN_LEFT);
+ // scrollbar->can_activate = false; //kills ability to mouse drag too
+ }
+ init_live();
+}
+
+/****************************** GLUI_TextBox::mouse_down_handler() **********/
+
+int GLUI_TextBox::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;
+
+ keygoal_x = insert_x;
+
+ if ( can_draw())
+ update_and_draw_text();
+
+ if ( debug ) dump( stdout, "<- MOUSE UP" );
+
+ return true;
+}
+
+
+/******************************** GLUI_TextBox::mouse_up_handler() **********/
+
+int GLUI_TextBox::mouse_up_handler( int local_x, int local_y, bool inside )
+{
+ return false;
+}
+
+
+/***************************** GLUI_TextBox::mouse_held_down_handler() ******/
+
+int GLUI_TextBox::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 );
+ keygoal_x = insert_x;
+
+ 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_TextBox::key_handler() **********/
+int GLUI_TextBox::key_handler( unsigned char key,int modifiers )
+{
+ int 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('[')) { /* 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() ) {
+ 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 ) {
+ 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 */
+ if ( key == 13 ) /* RETURNS are written as newlines*/
+ key = '\n';
+
+ regular_key = true;
+
+ /** 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" );
+
+ return true;
+}
+
+/****************************** GLUI_TextBox::enable() **********/
+
+void GLUI_TextBox::enable( void )
+{
+ GLUI_Control::enable();
+ scrollbar->enable();
+}
+
+/****************************** GLUI_TextBox::disable() **********/
+
+void GLUI_TextBox::disable( void )
+{
+ GLUI_Control::disable();
+ scrollbar->disable();
+}
+
+/****************************** GLUI_TextBox::activate() **********/
+
+void GLUI_TextBox::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_TextBox::deactivate() **********/
+
+void GLUI_TextBox::deactivate( void )
+{
+ 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() ****/
+ set_text(text.c_str()); /* 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 ( debug )
+ dump( stdout, "<- DISACTIVATE" );
+}
+
+/****************************** GLUI_TextBox::draw() **********/
+
+void GLUI_TextBox::draw( int x, int y )
+{
+ GLUI_DRAWINGSENTINAL_IDIOM
+ int line = 0;
+ int text_length;
+ int box_width;
+ int i;
+
+ /* Bevelled Border */
+ glBegin( GL_LINES );
+ glColor3f( .5, .5, .5 );
+ glVertex2i( 0, 0 ); glVertex2i( w, 0 );
+ glVertex2i( 0, 0 ); glVertex2i( 0, h );
+
+ glColor3f( 1., 1., 1. );
+ glVertex2i( 0, h ); glVertex2i( w, h );
+ glVertex2i( w, h ); glVertex2i( w, 0 );
+
+ if ( enabled )
+ glColor3f( 0., 0., 0. );
+ else
+ glColor3f( .25, .25, .25 );
+ glVertex2i( 1, 1 ); glVertex2i( w-1, 1 );
+ glVertex2i( 1, 1 ); glVertex2i( 1, h-1 );
+
+ glColor3f( .75, .75, .75 );
+ glVertex2i( 1, h-1 ); glVertex2i( w-1, h-1 );
+ glVertex2i( w-1, h-1 ); glVertex2i( w-1, 1 );
+ glEnd();
+
+ /* Draw Background if enabled*/
+ if (enabled) {
+ glColor3f( 1., 1., 1. );
+ glDisable( GL_CULL_FACE );
+ glBegin( GL_QUADS );
+ glVertex2i( 2, 2 ); glVertex2i( w-2, 2 );
+ glVertex2i( w-2, h-2 ); glVertex2i(2, h-2 );
+ glEnd();
+ } else {
+ glColor3f( .8, .8, .8 );
+ glDisable( GL_CULL_FACE );
+ glBegin( GL_QUADS );
+ glVertex2i( 2, 2 ); glVertex2i( w-2, 2 );
+ glVertex2i( w-2, h-2 ); glVertex2i(2, h-2 );
+ glEnd();
+ }
+
+ /* Begin Drawing Lines of Text */
+ substring_start = 0;
+ substring_end = 0;
+ text_length = int(text.length())-1;
+
+ /* Figure out how wide the box is */
+ box_width = get_box_width();
+
+ /* Get the first line substring */
+ while (substring_width(substring_start, substring_end+1 ) < box_width &&
+ substring_end < text_length && text[substring_end+1] != '\n')
+ substring_end++;
+
+ /* Figure out which lines are visible*/
+
+ visible_lines = (int)(h-20)/LINE_HEIGHT;
+ if (start_line < (curr_line-visible_lines)) {
+ for (i = 0; ((curr_line-i)*LINE_HEIGHT+20) > h; i++);
+ start_line = i;
+ } else if ( start_line > curr_line) {
+ start_line = curr_line;
+ }
+ line = 0;
+ do {
+ if (line && substring_end < text_length) {
+ substring_start = substring_end+1;
+ while (substring_width(substring_start, substring_end+1 ) < box_width &&
+ substring_end < text_length && text[substring_end+1] != '\n')
+ substring_end++;
+ }
+ if (text[substring_end+1] == '\n') { /* Skip newline */
+ substring_end++;
+ }
+ if (line < start_line || (line > curr_line && curr_line > (start_line + visible_lines))) {
+ line++;
+ continue;
+ }
+ if ((line - start_line) <= visible_lines)
+ draw_text(0,(line - start_line)*LINE_HEIGHT); /* tabs and other nasties are handled by substring_width */
+ line++;
+ } while (substring_end < text_length);
+
+ num_lines = line;
+
+ draw_insertion_pt();
+ if (scrollbar) {
+ scrollbar->set_int_limits(MAX(0,num_lines/*-1*/-visible_lines),0);
+ glPushMatrix();
+ glTranslatef(scrollbar->x_abs-x_abs, scrollbar->y_abs-y_abs,0.0);
+ scrollbar->draw_scroll();
+ glPopMatrix();
+ }
+}
+
+
+
+/************************** GLUI_TextBox::update_substring_bounds() *********/
+
+int GLUI_TextBox::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 = get_box_width();
+
+ 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_width( substring_start, substring_end+1 ) <= box_width
+ AND substring_end < text_len-1 )
+ 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_TextBox::update_x_offsets() *********/
+
+void GLUI_TextBox::update_x_offsets( void )
+{
+}
+
+
+/********************************* GLUI_TextBox::draw_text() ****************/
+
+void GLUI_TextBox::draw_text( int x, int y )
+{
+ GLUI_DRAWINGSENTINAL_IDIOM
+ int text_x, i, sel_lo, sel_hi, x_pos;
+
+ if ( debug ) dump( stdout, "-> DRAW_TEXT" );
+
+ /** Find where to draw the text **/
+
+ text_x = 2 + GLUI_TEXTBOX_BOXINNERMARGINX;
+
+ /** 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;
+ delta = 0;
+ for( i=substring_start; sel_x_end < (w - text_x) && i<=substring_end; i++ ) {
+ delta = 0;
+ if (text[i] == '\t') // Character is a tab, go to next tab stop
+ while (((delta + sel_x_end) < (w - text_x)) &&
+ (delta == 0 || delta % tab_width))
+ delta++;
+ else
+ 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 );
+ glRecti(sel_x_start, y+5, sel_x_end, y+20);
+ }
+
+
+ if ( sel_start == sel_end ) { // No current selection
+ x_pos = text_x;
+ if ( enabled )
+ glColor3b( 0, 0, 0 );
+ else
+ glColor3b( 32, 32, 32 );
+
+ glRasterPos2i( text_x, y+LINE_HEIGHT);
+ for( i=substring_start; i<=substring_end; i++ ) {
+ if (this->text[i] == '\t') { // Character is a tab, go to next tab stop
+ x_pos = ((x_pos-text_x)/tab_width)*tab_width+tab_width+text_x;
+ glRasterPos2i( x_pos, y+LINE_HEIGHT); // Reposition pen after tab
+ } else {
+ glutBitmapCharacter( get_font(), this->text[i] );
+ x_pos += char_width( this->text[i] );
+ }
+ }
+ }
+ else { // There is a selection
+ x_pos = 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_pos, y+LINE_HEIGHT);
+ if (this->text[i] == '\t') { // Character is a tab, go to next tab stop
+ x_pos = ((x_pos-text_x)/tab_width)*tab_width+tab_width+text_x;
+ }
+ else
+ glutBitmapCharacter( get_font(), this->text[i] );
+ }
+ else {
+ glColor3f( 0., 0., 0. );
+ glRasterPos2i( x_pos, y+LINE_HEIGHT);
+ if (this->text[i] == '\t') { // Character is a tab, go to next tab stop
+ x_pos = ((x_pos-text_x)/tab_width)*tab_width+tab_width+text_x;
+ glRasterPos2i( x_pos, y+LINE_HEIGHT); // Reposition pen after tab
+ } else
+ glutBitmapCharacter( get_font(), this->text[i] );
+ }
+
+ x_pos += char_width( text[i] );
+ }
+ }
+
+ if ( debug ) dump( stdout, "<- DRAW_TEXT" );
+}
+
+
+/******************************** GLUI_TextBox::find_insertion_pt() *********/
+/* This function returns the character number *before which* the insertion */
+/* point goes */
+
+int GLUI_TextBox::find_insertion_pt( int x, int y )
+{
+ /*** See if we clicked outside box ***/
+ if ( x < this->x_abs || y < this->y_abs)
+ return -1;
+
+ /*** See if we clicked in an empty box ***/
+ if ( text.empty() )
+ return 0;
+
+ /* update insert variables */
+ insert_x = x;
+ insert_y = y;
+
+ int text_length = int(text.length())-1;
+ int box_width = get_box_width();
+
+ int sol = 0;
+ int eol = 0;
+ int line = 0;
+
+ int y_off = y - (y_abs + 2 + GLUI_TEXTBOX_BOXINNERMARGINX);
+ int x_off = x - (x_abs + 2 + GLUI_TEXTBOX_BOXINNERMARGINX);
+
+ /* Find the line clicked,
+ The possibility of long lines getting wrapped complicates this. */
+ while ((line-start_line+1)*LINE_HEIGHT < y_off && eol < text_length)
+ {
+ while (eol < text_length && text[eol] != '\n' &&
+ substring_width(sol, eol+1) <= box_width)
+ {
+ eol++;
+ }
+ if (text[eol]=='\n' && eol<text_length) { eol++; }
+ line++;
+ sol = eol;
+ }
+ curr_line = line;
+ // Now search to the end of this line for the closest insertion point
+ int prev_w=0,total_w=0,prev_eol=eol;
+ while (eol <= text_length
+ && (total_w=substring_width(prev_eol,eol,prev_w))< x_off
+ && (eol==text_length||text[eol]!='\n'))
+ {
+ prev_w=total_w;
+ eol++;
+ prev_eol=eol;
+ }
+ if (total_w>=x_off) {
+ // did we go far enough? (see if click was >1/2 width of last char)
+ int decision_pt = prev_w+(total_w-prev_w)/2;
+ if (x_off>decision_pt) eol++;
+ }
+ return eol;
+
+#if 0
+ while (eol < text_length && text[eol] != '\n' &&
+ substring_width(sol, eol+1) < box_width )
+ {
+ eol++;
+ }
+
+
+ /* We move from right to left, looking to see if the mouse was clicked
+ to the right of the ith character */
+#if 0
+ int curr_x = this->x_abs
+ + substring_width( sol, eol )
+ + 2 /* The edittext box has a 2-pixel margin */
+ + GLUI_TEXTBOX_BOXINNERMARGINX; /** plus this many pixels blank space
+ between the text and the box **/
+#endif
+
+ /** find mouse click in text **/
+
+ if (x_off > substring_width(sol, eol))
+ return eol;
+
+ for(i = sol; i <= eol+1; i++) {
+ if (x_off <= substring_width(sol, i))
+ return i+1;
+ }
+ return 0;
+#endif
+}
+
+
+int GLUI_TextBox::get_box_width()
+{
+ return MAX( this->w
+ - 4 /* 2 * the two-line box border */
+ - 2 * GLUI_TEXTBOX_BOXINNERMARGINX, 0 );
+
+}
+
+/******************************** GLUI_TextBox::draw_insertion_pt() *********/
+
+void GLUI_TextBox::draw_insertion_pt( void )
+{
+ int curr_x, box_width, text_length, eol, sol, line;
+
+ if ( NOT can_draw() )
+ return;
+
+ /*** Don't draw insertion pt if control is disabled ***/
+ if ( NOT enabled )
+ return;
+
+ if ( sel_start != sel_end OR insertion_pt < 0 ) {
+ return; /* Don't draw insertion point if there is a current selection */
+ }
+
+ if ( debug ) dump( stdout, "-> DRAW_INS_PT" );
+
+ /* printf( "insertion pt: %d\n", insertion_pt ); */
+
+ box_width = get_box_width();
+
+ // This function is unable to distinguish whether an insertion
+ // point on a line break should be drawn on the line before or the line after.
+ // This depends on the sequence of operations used to get there, and this
+ // function just doesn't have that information. If curr_line were kept up
+ // to date elsewhere that could be used here to disambiguate, but arrow keys
+ // and such do not update it.
+
+ sol = 0;
+ eol = 0;
+ text_length = int(text.length())-1;
+
+ //while (eol < text_length && text[eol] != '\n'
+ // && substring_width(sol, eol + 1) < box_width )
+ // eol++;
+ line = 0;
+ while (eol < insertion_pt && eol <= text_length)
+ {
+ if (text[eol] == '\n' || substring_width(sol, eol + 1) >= box_width)
+ {
+ eol++;
+ if (text[eol]=='\n'||eol!=insertion_pt
+ ||(eol==insertion_pt && eol>0 && text[eol-1]=='\n')) {
+ sol = eol;
+ line++;
+ }
+ }
+ else {
+ eol++;
+ }
+ }
+
+ //glColor3f(1,0,0);
+ //glRecti(0, curr_line*LINE_HEIGHT, 3, (curr_line+1)*LINE_HEIGHT);
+
+ curr_line = line;
+
+ if (scrollbar)
+ scrollbar->set_int_val(start_line);
+ if (curr_line < start_line || curr_line > (start_line + visible_lines)) /* Insertion pt out of draw area */
+ return;
+
+ curr_x = this->x_abs
+ + 2 /* The edittext box has a 2-pixel margin */
+ + GLUI_TEXTBOX_BOXINNERMARGINX; /** plus this many pixels blank space
+ between the text and the box **/
+
+ curr_x += substring_width(sol,insertion_pt-1);
+ if (insertion_pt == text.length() && text[text.length()-1] == '\n'
+ || curr_x-this->x_abs > (w - 2 - GLUI_TEXTBOX_BOXINNERMARGINX)) { // Insert on the next line
+ curr_x = this->x_abs + GLUI_TEXTBOX_BOXINNERMARGINX;
+ line++;
+ }
+ /* update insertion coordinates */
+ insert_x = curr_x+5; /* I hate magic numbers too, these offset the imagined insertion point */
+ insert_y = (curr_line-start_line+2)*LINE_HEIGHT;
+
+
+ glColor3f( 0.0, 0.0, 0.0 );
+ glBegin( GL_LINE_LOOP );
+
+ curr_x -= x_abs;
+ glVertex2i( curr_x+1, (curr_line-start_line)*LINE_HEIGHT + 4 );
+ glVertex2i( curr_x, (curr_line-start_line)*LINE_HEIGHT + 4 );
+ glVertex2i( curr_x+1, (curr_line-start_line)*LINE_HEIGHT + 16 );
+ glVertex2i( curr_x, (curr_line-start_line)*LINE_HEIGHT + 16 );
+ glEnd();
+
+
+ if ( debug ) dump( stdout, "-> DRAW_INS_PT" );
+}
+
+
+
+
+/******************************** GLUI_TextBox::substring_width() *********/
+int GLUI_TextBox::substring_width( int start, int end, int initial_width )
+{
+ // This function only works properly if start is really the start of a line.
+ // Otherwise tabs will be messed up.
+ int i, width = initial_width;
+
+ for( i=start; i<=end; i++ )
+ if (text[i] == '\t') { // Character is a tab, jump to next tab stop
+ width += tab_width-(width%tab_width);
+ //while (width == 0 || width % tab_width)
+ // width++;
+ }
+ else
+ width += char_width( text[i] );
+
+ return width;
+}
+
+
+/***************************** GLUI_TextBox::update_and_draw_text() ********/
+
+void GLUI_TextBox::update_and_draw_text( void )
+{
+ //update_substring_bounds();
+ /* printf( "ss: %d/%d\n", substring_start, substring_end ); */
+
+ redraw();
+}
+
+
+/********************************* GLUI_TextBox::special_handler() **********/
+
+int GLUI_TextBox::special_handler( int key,int modifiers )
+{
+ int tmp_insertion_pt;
+ 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_DOWN ) {
+ if (insert_x == -1 || insert_y == -1)
+ return false;
+ tmp_insertion_pt = find_insertion_pt( keygoal_x, insert_y+LINE_HEIGHT);
+ if (tmp_insertion_pt < 0)
+ return false;
+ insertion_pt = tmp_insertion_pt;
+ sel_end = insertion_pt;
+ if (!(modifiers & GLUT_ACTIVE_SHIFT)) {
+ sel_start = sel_end;
+ }
+ if ( can_draw())
+ update_and_draw_text();
+ } else if ( key == GLUT_KEY_UP ) {
+ if (insert_x == -1 || insert_y == -1)
+ return false;
+ tmp_insertion_pt = find_insertion_pt( keygoal_x, insert_y-LINE_HEIGHT);
+ if (tmp_insertion_pt < 0)
+ return false;
+ insertion_pt = tmp_insertion_pt;
+ sel_end = insertion_pt;
+ if (!(modifiers & GLUT_ACTIVE_SHIFT)) {
+ sel_start = sel_end;
+ }
+ if ( can_draw())
+ update_and_draw_text();
+ } else if ( key == GLUT_KEY_LEFT ) {
+ if ( (modifiers & GLUT_ACTIVE_CTRL) != 0 ) {
+ insertion_pt = find_word_break( insertion_pt, -1 );
+ }
+ else {
+ insertion_pt--;
+ }
+ // update keygoal_x!
+ }
+ else if ( key == GLUT_KEY_RIGHT ) {
+ if ( (modifiers & GLUT_ACTIVE_CTRL) != 0 ) {
+ insertion_pt = find_word_break( insertion_pt, +1 );
+ }
+ else {
+ insertion_pt++;
+ }
+ // update keygoal_x!
+ }
+ else if ( key == GLUT_KEY_HOME ) {
+ insertion_pt = 0;
+ // update keygoal_x!
+ }
+ else if ( key == GLUT_KEY_END ) {
+ insertion_pt = int(text.length());
+ // update keygoal_x!
+ }
+
+ /*** 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_TextBox::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_TextBox::find_word_break( int start, int direction )
+{
+ int i, j;
+ char breaks[] = " \n\t:-.,";
+ 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_TextBox::clear_substring() ********/
+
+void GLUI_TextBox::clear_substring( int start, int end )
+{
+ text.erase(start,end-start);
+}
+
+
+
+/************************************ GLUI_TextBox::update_size() **********/
+
+void GLUI_TextBox::update_size( void )
+{
+ if ( NOT glui )
+ return;
+
+ if ( w < GLUI_TEXTBOX_MIN_TEXT_WIDTH )
+ w = GLUI_TEXTBOX_MIN_TEXT_WIDTH;
+}
+
+
+/****************************** GLUI_TextBox::set_text() **********/
+
+void GLUI_TextBox::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;
+ visible_lines = 0;
+ start_line = 0;
+ curr_line = 0;
+ num_lines = 0;
+
+ if ( can_draw() )
+ update_and_draw_text();
+
+ /*** Now update the live variable ***/
+ output_live(true);
+}
+
+
+/*************************************** GLUI_TextBox::dump() **************/
+
+void GLUI_TextBox::dump( FILE *out, char *name )
+{
+ fprintf( out,
+ "%s (edittext@%p): line:%d ins_pt:%d subs:%d/%d sel:%d/%d len:%d\n",
+ name, this, curr_line,
+ insertion_pt, substring_start, substring_end, sel_start, sel_end,
+ text.length());
+}
+
+
+/**************************************** GLUI_TextBox::mouse_over() ********/
+
+int GLUI_TextBox::mouse_over( int state, int x, int y )
+{
+ if ( state && enabled) {
+ /* curr_cursor = GLUT_CURSOR_TEXT; */
+ glutSetCursor( GLUT_CURSOR_TEXT );
+ }
+ else {
+ /* printf( "OUT\n" ); */
+ glutSetCursor( GLUT_CURSOR_LEFT_ARROW );
+ }
+
+ return true;
+}
+
+void GLUI_TextBox::scrollbar_callback(GLUI_Control *my_scrollbar) {
+ GLUI_Scrollbar *sb = my_scrollbar->dynamicCastGLUI_Scrollbar();
+ if (!sb) return;
+ GLUI_TextBox* me = (GLUI_TextBox*) sb->associated_object;
+ if (me->scrollbar == NULL)
+ return;
+ int new_start_line = sb->get_int_val(); // ??
+ me->start_line = new_start_line;
+ if (new_start_line < (me->curr_line - me->visible_lines))
+ me->curr_line = new_start_line + me->visible_lines;
+ if (new_start_line > me->curr_line)
+ me->curr_line = new_start_line;
+ if ( me->can_draw() )
+ me->update_and_draw_text();
+}