Permalink
Switch branches/tags
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1203 lines (930 sloc) 33.2 KB
/****************************************************************************
GLUI User Interface Toolkit
---------------------------
glui_edittext.cpp - GLUI_EditText control class
--------------------------------------------------
Copyright (c) 1998 Paul Rademacher
WWW: https://github.com/libglui/glui
Issues: https://github.com/libglui/glui/issues
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*****************************************************************************/
#include "glui_internal_control.h"
#include "tinyformat.h"
#include <algorithm>
#include <cassert>
/****************************** GLUI_EditText::GLUI_EditText() **********/
GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const GLUI_String &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 GLUI_String &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 GLUI_String &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 GLUI_String &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 GLUI_String &name,
GLUI_String *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 GLUI_String &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 GLUI_String &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( std::min(sel_start,sel_end), std::max(sel_start,sel_end ));
insertion_pt = std::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( std::min(sel_start,sel_end), std::max(sel_start,sel_end ));
insertion_pt = std::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( std::min(sel_start,sel_end), std::max(sel_start,sel_end ));
insertion_pt = std::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 ( std::min(sel_start,sel_end) == 0 AND
std::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=std::min(sel_end,sel_start); i<std::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 ( std::min(sel_start,sel_end) == 0 AND
std::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( std::min(sel_start,sel_end), std::max(sel_start,sel_end ));
insertion_pt = std::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()
{
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 = std::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()
{
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 = std::max( this->w - this->text_x_offset
- 4 /* 2 * the two-line box border */
- 2 * GLUI_EDITTEXT_BOXINNERMARGINX, 0 );
CLAMP( substring_end, 0, std::max(text_len-1,0) );
CLAMP( substring_start, 0, std::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()
{
}
/********************************* 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 = std::min(sel_start, sel_end );
sel_hi = std::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()
{
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()
{
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;
const 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()
{
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 GLUI_String &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()
{
GLUI_String buf_num;
if ( data_type == GLUI_EDITTEXT_FLOAT ) {
buf_num = tfm::format("%#g", float_val);
num_periods = 0;
for ( size_t i=0; i<buf_num.size(); ++i ) {
if ( buf_num[i] == '.' ) num_periods++;
}
/* Now remove trailing zeros */
if ( num_periods > 0 ) {
size_t i = 0;
for ( size_t i=buf_num.size()-1; i>0 && i<buf_num.size(); i-- )
{
if ( buf_num[i]!='0' || buf_num[i-1]=='.' )
{
buf_num = buf_num.substr(0,i+1);
break;
}
}
}
}
else {
buf_num = tfm::format("%d", int_val);
}
set_text( buf_num );
}
/*************************************** GLUI_EditText::dump() **************/
void GLUI_EditText::dump( FILE *out, const GLUI_String &name )
{
const GLUI_String buffer = tfm::format(
"%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,
text.length());
fprintf(out, "%s", buffer.c_str());
}
/**************************************** 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;
}