glui_textbox.cpp
上传用户:gb3593
上传日期:2022-01-07
资源大小:3028k
文件大小:32k
源码类别:

游戏引擎

开发平台:

Visual C++

  1. /****************************************************************************
  2.   
  3.   GLUI User Interface Toolkit
  4.   ---------------------------
  5.      glui_textbox.cpp - GLUI_TextBox control class
  6.           --------------------------------------------------
  7.   Copyright (c) 1998 Paul Rademacher, 2004 John Kew
  8.   WWW:    http://sourceforge.net/projects/glui/
  9.   Forums: http://sourceforge.net/forum/?group_id=92496
  10.   This library is free software; you can redistribute it and/or
  11.   modify it under the terms of the GNU Lesser General Public
  12.   License as published by the Free Software Foundation; either
  13.   version 2.1 of the License, or (at your option) any later version.
  14.   This library is distributed in the hope that it will be useful,
  15.   but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17.   Lesser General Public License for more details.
  18.   You should have received a copy of the GNU Lesser General Public
  19.   License along with this library; if not, write to the Free Software
  20.   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  21. *****************************************************************************/
  22. #include "glui_internal_control.h"
  23. #include <cmath>
  24. static const int LINE_HEIGHT = 15;
  25. /****************************** GLUI_TextBox::GLUI_TextBox() **********/
  26. GLUI_TextBox::GLUI_TextBox(GLUI_Node *parent, GLUI_String &live_var,
  27.                            bool scroll, int id, GLUI_CB callback )
  28. {
  29.   common_construct(parent, &live_var, scroll, id, callback);
  30. }
  31. /****************************** GLUI_TextBox::GLUI_TextBox() **********/
  32. GLUI_TextBox::GLUI_TextBox( GLUI_Node *parent, bool scroll, int id,
  33.                             GLUI_CB callback )
  34. {
  35.   common_construct(parent, NULL, scroll, id, callback);
  36. }
  37. /****************************** GLUI_TextBox::common_construct() **********/
  38. void GLUI_TextBox::common_construct(
  39.   GLUI_Node *parent, GLUI_String *data, 
  40.   bool scroll, int id, GLUI_CB callback)
  41. {
  42.   common_init();
  43.   GLUI_Node *tb_panel = parent;
  44.   if (scroll) {
  45.     GLUI_Panel *p = new GLUI_Panel(parent,"",GLUI_PANEL_NONE);
  46.     p->x_off = 1;
  47.     tb_panel = p;
  48.   }
  49.   this->ptr_val     = data;
  50.   if (data) {
  51.     this->live_type = GLUI_LIVE_STRING;
  52.   } else {
  53.     this->live_type = GLUI_LIVE_NONE;
  54.   }
  55.   this->user_id     = id;
  56.   this->callback    = callback;
  57.   this->name        = "textbox";
  58.   tb_panel->add_control( this );
  59.   if (scroll) {
  60.     new GLUI_Column(tb_panel, false);
  61.     scrollbar = 
  62.       new GLUI_Scrollbar(tb_panel,
  63.                          "scrollbar",
  64.                          GLUI_SCROLL_VERTICAL,
  65.                          GLUI_SCROLL_INT);
  66.     scrollbar->set_object_callback(GLUI_TextBox::scrollbar_callback, this);
  67.     scrollbar->set_alignment(GLUI_ALIGN_LEFT);
  68.     // scrollbar->can_activate = false; //kills ability to mouse drag too
  69.   }
  70.   init_live();
  71. }
  72. /****************************** GLUI_TextBox::mouse_down_handler() **********/
  73. int    GLUI_TextBox::mouse_down_handler( int local_x, int local_y )
  74. {
  75.   int tmp_insertion_pt;
  76.   if ( debug )    dump( stdout, "-> MOUSE DOWN" );
  77.   tmp_insertion_pt = find_insertion_pt( local_x, local_y );  
  78.   if ( tmp_insertion_pt == -1 ) {
  79.     if ( glui )
  80.       glui->deactivate_current_control(  );
  81.     return false;
  82.   }
  83.   insertion_pt = tmp_insertion_pt;
  84.   sel_start = sel_end = insertion_pt;
  85.  
  86.   keygoal_x = insert_x;
  87.   if ( can_draw())
  88.     update_and_draw_text();
  89.   if ( debug )    dump( stdout, "<- MOUSE UP" );
  90.   return true;
  91. }
  92. /******************************** GLUI_TextBox::mouse_up_handler() **********/
  93. int    GLUI_TextBox::mouse_up_handler( int local_x, int local_y, bool inside )
  94. {
  95.   return false;
  96. }
  97. /***************************** GLUI_TextBox::mouse_held_down_handler() ******/
  98. int    GLUI_TextBox::mouse_held_down_handler( int local_x, int local_y,
  99.                            bool new_inside)
  100. {
  101.   int tmp_pt;
  102.   if ( NOT new_inside )     return false;
  103.   if ( debug )    dump( stdout, "-> HELD DOWN" );
  104.   
  105.   tmp_pt = find_insertion_pt( local_x, local_y );
  106.   keygoal_x = insert_x;
  107.   
  108.   if ( tmp_pt == -1 AND sel_end != 0 ) {    /* moved mouse past left edge */
  109.     special_handler( GLUT_KEY_LEFT, GLUT_ACTIVE_SHIFT );
  110.   }
  111.   else if ( tmp_pt == substring_end+1 AND sel_end != (int) text.length()) {    
  112.     /* moved mouse past right edge */
  113.     special_handler( GLUT_KEY_RIGHT, GLUT_ACTIVE_SHIFT );    
  114.   }
  115.   else if ( tmp_pt != -1 AND tmp_pt != sel_end ) {
  116.     sel_end = insertion_pt = tmp_pt;
  117.     
  118.     update_and_draw_text();
  119.   }
  120.   if ( debug )
  121.     dump( stdout, "<- HELD DOWN" );
  122.   return false;
  123. }
  124. /****************************** GLUI_TextBox::key_handler() **********/
  125. int    GLUI_TextBox::key_handler( unsigned char key,int modifiers )
  126. {
  127.   int regular_key;
  128.   /* int has_selection;              */
  129.   if ( NOT glui )
  130.     return false;
  131.   if ( debug )
  132.     dump( stdout, "-> KEY HANDLER" );
  133.   regular_key = false;
  134.   bool ctrl_down = (modifiers & GLUT_ACTIVE_CTRL)!=0;
  135.   /*  has_selection = (sel_start != sel_end);              */
  136.   if ( key  == CTRL('[')) {         /* ESCAPE */
  137.     glui->deactivate_current_control();
  138.     return true;
  139.   }
  140.   else if ( (key == 127 AND !ctrl_down) OR  /* FORWARD DELETE */
  141.             ( key == CTRL('d') AND modifiers == GLUT_ACTIVE_CTRL) ) 
  142.   {
  143.     if ( sel_start == sel_end ) {   /* no selection */
  144.       if ( insertion_pt < (int)text.length() ) {
  145.         text.erase(insertion_pt,1);
  146.       }
  147.     }
  148.     else {                         /* There is a selection */
  149.       clear_substring( MIN(sel_start,sel_end), MAX(sel_start,sel_end ));
  150.       insertion_pt = MIN(sel_start,sel_end);
  151.       sel_start = sel_end = insertion_pt;
  152.     }
  153.   }
  154.   else if ( ((key == 127) AND ctrl_down) OR   // Delete word forward
  155.             ((key == 'd') AND (modifiers == GLUT_ACTIVE_ALT)) )
  156.   {
  157.     if ( sel_start == sel_end ) {   /* no selection */
  158.       sel_start = insertion_pt;
  159.       sel_end = find_word_break( insertion_pt, +1 );
  160.     }
  161.     clear_substring( MIN(sel_start,sel_end), MAX(sel_start,sel_end ));
  162.     insertion_pt = MIN(sel_start,sel_end);
  163.     sel_start = sel_end = insertion_pt;
  164.   }
  165.   else if ( key == CTRL('h') ) {       /* BACKSPACE */
  166.     if ( sel_start == sel_end ) {   /* no selection */
  167.       if ( insertion_pt > 0 ) {
  168.         insertion_pt--;
  169.         text.erase(insertion_pt,1);
  170.       }
  171.     }
  172.     else {                         /* There is a selection */
  173.       clear_substring( MIN(sel_start,sel_end), MAX(sel_start,sel_end ));
  174.       insertion_pt = MIN(sel_start,sel_end);
  175.       sel_start = sel_end = insertion_pt;
  176.     }
  177.   }
  178.   else if ( modifiers == GLUT_ACTIVE_CTRL )  /* CTRL ONLY */ 
  179.   {
  180.     /* Ctrl-key bindings */
  181.     if ( key == CTRL('a') ) {
  182.       return special_handler( GLUT_KEY_HOME, 0 );
  183.     }
  184.     else if ( key == CTRL('e') ) {
  185.       return special_handler( GLUT_KEY_END, 0 );
  186.     }
  187.     else if ( key == CTRL('b') ) {
  188.       return special_handler( GLUT_KEY_LEFT, 0 );
  189.     }
  190.     else if ( key == CTRL('f') ) {
  191.       return special_handler( GLUT_KEY_RIGHT, 0 );
  192.     }
  193.     else if ( key == CTRL('p') ) {
  194.       return special_handler( GLUT_KEY_UP, 0 );
  195.     }
  196.     else if ( key == CTRL('n') ) {
  197.       return special_handler( GLUT_KEY_DOWN, 0 );
  198.     }
  199.     else if ( key == CTRL('u') ) { /* ERASE LINE */
  200.       insertion_pt = 0;  
  201.       text.erase(0,text.length());
  202.       sel_start = sel_end = 0;
  203.     }
  204.     else if ( key == CTRL('k') ) { /* KILL TO END OF LINE */
  205.       sel_start = sel_end = insertion_pt;
  206.       text.erase(insertion_pt,GLUI_String::npos);
  207.     }
  208.   }
  209.   else if ( modifiers == GLUT_ACTIVE_ALT ) /* ALT ONLY */
  210.   {
  211.     if ( key == 'b' ) { // Backward word
  212.       return special_handler ( GLUT_KEY_LEFT, GLUT_ACTIVE_CTRL );
  213.     }
  214.     if ( key == 'f' ) { // Forward word
  215.       return special_handler ( GLUT_KEY_RIGHT, GLUT_ACTIVE_CTRL );
  216.     }
  217.   }
  218.   else if ( (modifiers & GLUT_ACTIVE_CTRL) OR
  219.             (modifiers & GLUT_ACTIVE_ALT) ) 
  220.   {
  221.     /** ignore other keys with modifiers */
  222.     return true;
  223.   }
  224.   else { /* Regular key */    
  225.     if ( key == 13 )           /* RETURNS are written as newlines*/
  226.       key = 'n';
  227.     regular_key = true;
  228.     /** This is just to get rid of warnings - the flag regular_key is 
  229.         set if the key was not a backspace, return, whatever.  But I
  230.         believe if we're here, we know it was a regular key anyway */
  231.     if ( regular_key ) {
  232.     }
  233.     /**** If there's a current selection, erase it ******/
  234.     if ( sel_start != sel_end ) {
  235.       clear_substring( MIN(sel_start,sel_end), MAX(sel_start,sel_end ));
  236.       insertion_pt = MIN(sel_start,sel_end);
  237.       sel_start = sel_end = insertion_pt;
  238.     }
  239.     /******** We insert the character into the string ***/
  240.     text.insert(insertion_pt,1,key);
  241.     /******** Move the insertion point and substring_end one over ******/
  242.     insertion_pt++;
  243.     substring_end++;
  244.     sel_start = sel_end = insertion_pt;
  245.   }
  246.   /******** Now redraw text ***********/
  247.   /* Hack to prevent text box from being cleared first **/  
  248.   /**  int substring_change =  update_substring_bounds();
  249.        draw_text_only = 
  250.        (NOT substring_change AND NOT has_selection AND regular_key ); 
  251.   */
  252.   draw_text_only = false;  /** Well, hack is not yet working **/
  253.   update_and_draw_text();
  254.   draw_text_only = false;
  255.   if ( debug )
  256.     dump( stdout, "<- KEY HANDLER" );
  257.   return true;
  258. }
  259. /****************************** GLUI_TextBox::enable() **********/
  260. void GLUI_TextBox::enable( void )
  261. {
  262.   GLUI_Control::enable();
  263.   scrollbar->enable();
  264. }
  265. /****************************** GLUI_TextBox::disable() **********/
  266. void GLUI_TextBox::disable( void )
  267. {
  268.   GLUI_Control::disable();
  269.   scrollbar->disable();
  270. }
  271. /****************************** GLUI_TextBox::activate() **********/
  272. void    GLUI_TextBox::activate( int how )
  273. {
  274.   if ( debug )
  275.     dump( stdout, "-> ACTIVATE" );
  276.   active = true;
  277.   if ( how == GLUI_ACTIVATE_MOUSE )
  278.     return;  /* Don't select everything if activated with mouse */
  279.   orig_text = text;
  280.   sel_start    = 0;
  281.   sel_end      = text.length();
  282.   insertion_pt = 0;
  283.   if ( debug )
  284.     dump( stdout, "<- ACTIVATE" );
  285. }
  286. /****************************** GLUI_TextBox::deactivate() **********/
  287. void    GLUI_TextBox::deactivate( void )
  288. {
  289.   active = false;
  290.   if ( NOT glui )
  291.     return;
  292.   if ( debug )
  293.     dump( stdout, "-> DISACTIVATE" );
  294.   sel_start = sel_end = insertion_pt = -1; 
  295.   /***** Retrieve the current value from the text *****/
  296.   /***** The live variable will be updated by set_text() ****/
  297.   set_text(text.c_str()); /* This will force callbacks and gfx refresh */
  298.   update_substring_bounds();
  299.   /******** redraw text without insertion point ***********/
  300.   redraw();
  301.   /***** Now do callbacks if value changed ******/
  302.   if ( orig_text != text ) {
  303.     this->execute_callback();
  304.     
  305.   }
  306.   if ( debug )
  307.     dump( stdout, "<- DISACTIVATE" );
  308. }
  309. /****************************** GLUI_TextBox::draw() **********/
  310. void    GLUI_TextBox::draw( int x, int y )
  311. {
  312.   GLUI_DRAWINGSENTINAL_IDIOM
  313.   int line = 0;
  314.   int text_length;
  315.   int box_width;
  316.   int i;
  317.   /* Bevelled Border */
  318.   glBegin( GL_LINES );
  319.   glColor3f( .5, .5, .5 );
  320.   glVertex2i( 0, 0 );     glVertex2i( w, 0 );
  321.   glVertex2i( 0, 0 );     glVertex2i( 0, h );     
  322.   glColor3f( 1., 1., 1. );
  323.   glVertex2i( 0, h );     glVertex2i( w, h );
  324.   glVertex2i( w, h );     glVertex2i( w, 0 );
  325.   if ( enabled )
  326.     glColor3f( 0., 0., 0. );
  327.   else
  328.     glColor3f( .25, .25, .25 );
  329.   glVertex2i( 1, 1 );     glVertex2i( w-1, 1 );
  330.   glVertex2i( 1, 1 );     glVertex2i( 1, h-1 );
  331.   glColor3f( .75, .75, .75 );
  332.   glVertex2i( 1, h-1 );     glVertex2i( w-1, h-1 );
  333.   glVertex2i( w-1, h-1 );   glVertex2i( w-1, 1 );
  334.   glEnd();
  335.   /* Draw Background if enabled*/
  336.   if (enabled) {
  337.     glColor3f( 1., 1., 1. );
  338.     glDisable( GL_CULL_FACE );
  339.     glBegin( GL_QUADS );
  340.     glVertex2i( 2, 2 );     glVertex2i( w-2, 2 );
  341.     glVertex2i( w-2, h-2 );               glVertex2i(2, h-2 );
  342.     glEnd();
  343.   } else {
  344.     glColor3f( .8, .8, .8 );
  345.     glDisable( GL_CULL_FACE );
  346.     glBegin( GL_QUADS );
  347.     glVertex2i( 2, 2 );     glVertex2i( w-2, 2 );
  348.     glVertex2i( w-2, h-2 );               glVertex2i(2, h-2 );
  349.     glEnd();
  350.   }
  351.   /* Begin Drawing Lines of Text */
  352.   substring_start = 0;
  353.   substring_end = 0;
  354.   text_length = text.length()-1;
  355.   /* Figure out how wide the box is */
  356.   box_width = get_box_width();
  357.   /* Get the first line substring */
  358.   while (substring_width(substring_start, substring_end+1 ) < box_width && 
  359.  substring_end < text_length && text[substring_end+1] != 'n')
  360.     substring_end++;
  361.   /* Figure out which lines are visible*/
  362.   visible_lines = (int)(h-20)/LINE_HEIGHT;
  363.   if (start_line < (curr_line-visible_lines)) {
  364.     for (i = 0; ((curr_line-i)*LINE_HEIGHT+20) > h; i++);
  365.     start_line = i;
  366.   } else if ( start_line > curr_line) {
  367.     start_line = curr_line;
  368.   }
  369.   line = 0;
  370.   do {
  371.     if (line && substring_end < text_length) {
  372.       substring_start = substring_end+1;
  373.       while (substring_width(substring_start, substring_end+1 ) < box_width && 
  374.      substring_end < text_length && text[substring_end+1] != 'n')
  375. substring_end++; 
  376.     }
  377.     if (text[substring_end+1] == 'n') { /* Skip newline */
  378.       substring_end++;
  379.     }
  380.     if (line < start_line || (line > curr_line && curr_line > (start_line + visible_lines))) {
  381.       line++;
  382.       continue;
  383.     }
  384.     if ((line - start_line) <= visible_lines)
  385.       draw_text(0,(line - start_line)*LINE_HEIGHT); /* tabs and other nasties are handled by substring_width */
  386.     line++;
  387.   } while (substring_end < text_length);
  388.   num_lines = line;
  389.   draw_insertion_pt();
  390.   if (scrollbar) {
  391.     scrollbar->set_int_limits(MAX(0,num_lines/*-1*/-visible_lines),0);
  392.     glPushMatrix();
  393.     glTranslatef(scrollbar->x_abs-x_abs, scrollbar->y_abs-y_abs,0.0);
  394.     scrollbar->draw_scroll();
  395.     glPopMatrix();
  396.   }
  397. }
  398. /************************** GLUI_TextBox::update_substring_bounds() *********/
  399. int    GLUI_TextBox::update_substring_bounds( void )
  400. {
  401.   int box_width;
  402.   int text_len = text.length();
  403.   int old_start, old_end;
  404.   old_start = substring_start;
  405.   old_end = substring_end;
  406.   /*** Calculate the width of the usable area of the edit box ***/
  407.   box_width = get_box_width();
  408.   CLAMP( substring_end, 0, MAX(text_len-1,0) );
  409.   CLAMP( substring_start, 0, MAX(text_len-1,0) );
  410.   if ( debug )    dump( stdout, "-> UPDATE SS" );
  411.   if ( insertion_pt >= 0 AND 
  412.        insertion_pt < substring_start ) {   /* cursor moved left */
  413.     substring_start = insertion_pt;
  414.     while ( substring_width( substring_start, substring_end ) > box_width )
  415.       substring_end--;
  416.   }
  417.   else if ( insertion_pt > substring_end ) {  /* cursor moved right */
  418.     substring_end = insertion_pt-1;
  419.     while ( substring_width( substring_start, substring_end ) > box_width )
  420.       substring_start++;
  421.   }
  422.   else {   /* cursor is within old substring bounds */
  423.     if ( last_insertion_pt > insertion_pt ) {  /* cursor moved left */
  424.     }
  425.     else {
  426.       while ( substring_width( substring_start, substring_end ) > box_width )
  427.     substring_end--;
  428.       while(substring_width( substring_start, substring_end+1 ) <= box_width
  429.         AND substring_end < text_len-1 )
  430.         substring_end++;
  431.     }
  432.   }
  433.   while ( substring_width( substring_start, substring_end ) > box_width )
  434.     substring_end--;
  435.   last_insertion_pt = insertion_pt;
  436.   /*** No selection if not enabled ***/
  437.   if ( NOT enabled ) {
  438.     sel_start = sel_end = 0;
  439.   }
  440.   if ( debug )    dump( stdout, "<- UPDATE SS" );
  441.   if ( substring_start == old_start AND substring_end == old_end )
  442.     return false;  /*** bounds did not change ***/
  443.   else 
  444.     return true;   /*** bounds did change ***/
  445.   
  446. }
  447. /********************************* GLUI_TextBox::update_x_offsets() *********/
  448. void    GLUI_TextBox::update_x_offsets( void )
  449. {
  450. }
  451.  
  452. /********************************* GLUI_TextBox::draw_text() ****************/
  453. void    GLUI_TextBox::draw_text( int x, int y )
  454. {
  455.   GLUI_DRAWINGSENTINAL_IDIOM
  456.   int text_x, i, sel_lo, sel_hi, x_pos;
  457.   if ( debug )    dump( stdout, "-> DRAW_TEXT" );
  458.   /** Find where to draw the text **/
  459.   text_x = 2 + GLUI_TEXTBOX_BOXINNERMARGINX;
  460.   /** Find lower and upper selection bounds **/
  461.   sel_lo = MIN(sel_start, sel_end );
  462.   sel_hi = MAX(sel_start, sel_end );
  463.   int sel_x_start, sel_x_end, delta;
  464.   /** Draw selection area dark **/
  465.   if ( sel_start != sel_end ) {
  466.     sel_x_start = text_x;
  467.     sel_x_end   = text_x;
  468.     delta = 0;
  469.     for( i=substring_start; sel_x_end < (w - text_x) && i<=substring_end; i++ ) {
  470.       delta = 0;
  471.       if (text[i] == 't') // Character is a tab, go to next tab stop
  472.         while (((delta + sel_x_end) < (w - text_x)) && 
  473.           (delta == 0 || delta % tab_width))
  474.           delta++;
  475.         else
  476.           delta = char_width( text[i] );
  477.         
  478.         if ( i < sel_lo ) {
  479.           sel_x_start += delta;
  480.           sel_x_end   += delta;
  481.         }
  482.         else if ( i < sel_hi ) {
  483.           sel_x_end   += delta;
  484.         }
  485.     }
  486.     
  487.     glColor3f( 0.0f, 0.0f, .6f );
  488.     glRecti(sel_x_start, y+5, sel_x_end, y+20);
  489.   }
  490.   
  491.   if ( sel_start == sel_end ) {   // No current selection 
  492.     x_pos = text_x;
  493.     if ( enabled )
  494.       glColor3b( 0, 0, 0 );
  495.     else
  496.       glColor3b( 32, 32, 32 );
  497.       
  498.     glRasterPos2i( text_x, y+LINE_HEIGHT);
  499.     for( i=substring_start; i<=substring_end; i++ ) {
  500.       if (this->text[i] == 't') { // Character is a tab, go to next tab stop
  501.         x_pos = ((x_pos-text_x)/tab_width)*tab_width+tab_width+text_x;
  502.         glRasterPos2i( x_pos, y+LINE_HEIGHT); // Reposition pen after tab
  503.       } else {
  504.         glutBitmapCharacter( get_font(), this->text[i] );
  505.         x_pos += char_width( this->text[i] );
  506.       }
  507.     }
  508.   }
  509.   else {                        // There is a selection
  510.     x_pos = text_x;
  511.     for( i=substring_start; i<=substring_end; i++ ) {
  512.       if ( IN_BOUNDS( i, sel_lo, sel_hi-1)) { // This character is selected
  513.         glColor3f( 1., 1., 1. );
  514.         glRasterPos2i( x_pos, y+LINE_HEIGHT);
  515.         if (this->text[i] == 't') { // Character is a tab, go to next tab stop
  516.          x_pos = ((x_pos-text_x)/tab_width)*tab_width+tab_width+text_x;
  517.         } 
  518.         else
  519.           glutBitmapCharacter( get_font(), this->text[i] );
  520.       }
  521.       else {
  522.         glColor3f( 0., 0., 0. );
  523.         glRasterPos2i( x_pos, y+LINE_HEIGHT);
  524.         if (this->text[i] == 't') { // Character is a tab, go to next tab stop
  525.           x_pos = ((x_pos-text_x)/tab_width)*tab_width+tab_width+text_x;
  526.           glRasterPos2i( x_pos, y+LINE_HEIGHT); // Reposition pen after tab 
  527.         } else
  528.           glutBitmapCharacter( get_font(), this->text[i] );
  529.       }
  530.       
  531.       x_pos += char_width( text[i] );
  532.     }
  533.   }
  534.   if ( debug )    dump( stdout, "<- DRAW_TEXT" );  
  535. }
  536. /******************************** GLUI_TextBox::find_insertion_pt() *********/
  537. /* This function returns the character number *before which* the insertion  */
  538. /* point goes                                                               */
  539. int  GLUI_TextBox::find_insertion_pt( int x, int y )
  540. {
  541.   /*** See if we clicked outside box ***/
  542.   if ( x < this->x_abs || y < this->y_abs)
  543.     return -1;
  544.   
  545.   /*** See if we clicked in an empty box ***/
  546.   if ( text.empty() ) 
  547.     return 0;
  548.   
  549.   /* update insert variables */
  550.   insert_x = x;
  551.   insert_y = y;
  552.   int text_length = text.length()-1;
  553.   int box_width = get_box_width();
  554.   int sol = 0;
  555.   int eol = 0;
  556.   int line = 0;
  557.   int y_off = y - (y_abs + 2 + GLUI_TEXTBOX_BOXINNERMARGINX);
  558.   int x_off = x - (x_abs + 2 + GLUI_TEXTBOX_BOXINNERMARGINX);
  559.   /* Find the line clicked, 
  560.      The possibility of long lines getting wrapped complicates this. */
  561.   while ((line-start_line+1)*LINE_HEIGHT < y_off && eol < text_length) 
  562.   {
  563.     while (eol < text_length && text[eol] != 'n' && 
  564.            substring_width(sol, eol+1) <= box_width)
  565.     {
  566.       eol++;
  567.     }
  568.     if (text[eol]=='n' && eol<text_length) { eol++; }
  569.     line++;
  570.     sol = eol;
  571.   }
  572.   curr_line = line;
  573.   // Now search to the end of this line for the closest insertion point
  574.   int prev_w=0,total_w=0,prev_eol=eol;
  575.   while (eol <= text_length 
  576.          && (total_w=substring_width(prev_eol,eol,prev_w))< x_off 
  577.          && (eol==text_length||text[eol]!='n')) 
  578.   {
  579.     prev_w=total_w;
  580.     eol++;
  581.     prev_eol=eol;
  582.   }
  583.   if (total_w>=x_off) {  
  584.     // did we go far enough? (see if click was >1/2 width of last char)
  585.     int decision_pt = prev_w+(total_w-prev_w)/2;
  586.     if (x_off>decision_pt) eol++;
  587.   }
  588.   return eol;
  589. #if 0
  590.   while (eol < text_length && text[eol] != 'n' && 
  591.          substring_width(sol, eol+1) < box_width )
  592.   {
  593.     eol++;
  594.   }
  595.   /* We move from right to left, looking to see if the mouse was clicked
  596.      to the right of the ith character */
  597. #if 0
  598.   int curr_x = this->x_abs 
  599.     + substring_width( sol, eol )
  600.     + 2                             /* The edittext box has a 2-pixel margin */
  601.     + GLUI_TEXTBOX_BOXINNERMARGINX;   /** plus this many pixels blank space
  602.                      between the text and the box       **/
  603. #endif
  604.   
  605.   /** find mouse click in text **/
  606.   if (x_off > substring_width(sol, eol))
  607.     return eol;
  608.   for(i = sol; i <= eol+1; i++) {
  609.     if (x_off <= substring_width(sol, i))
  610.     return i+1;
  611.   }
  612.   return 0;
  613. #endif
  614. }
  615. int GLUI_TextBox::get_box_width() 
  616. {
  617.   return MAX( this->w 
  618.               - 4     /*  2 * the two-line box border */ 
  619.               - 2 * GLUI_TEXTBOX_BOXINNERMARGINX, 0 );
  620. }
  621. /******************************** GLUI_TextBox::draw_insertion_pt() *********/
  622. void     GLUI_TextBox::draw_insertion_pt( void )
  623. {
  624.   int curr_x, box_width, text_length, eol, sol, line;
  625.   if ( NOT can_draw() )
  626.     return;
  627.   /*** Don't draw insertion pt if control is disabled ***/
  628.   if ( NOT enabled )
  629.     return;
  630.   if ( sel_start != sel_end OR insertion_pt < 0 ) {
  631.     return;  /* Don't draw insertion point if there is a current selection */
  632.   }
  633.   if ( debug )    dump( stdout, "-> DRAW_INS_PT" );
  634.   /*    printf( "insertion pt: %dn", insertion_pt );              */
  635.   box_width = get_box_width();
  636.   // This function is unable to distinguish whether an insertion
  637.   // point on a line break should be drawn on the line before or the line after.
  638.   // This depends on the sequence of operations used to get there, and this
  639.   // function just doesn't have that information.  If curr_line were kept up
  640.   // to date elsewhere that could be used here to disambiguate, but arrow keys
  641.   // and such do not update it.
  642.   sol = 0;
  643.   eol = 0;
  644.   text_length = text.length()-1;
  645.   //while (eol < text_length && text[eol] != 'n' 
  646.   //       && substring_width(sol, eol + 1) < box_width )
  647.   //  eol++;
  648.   line = 0;
  649.   while (eol < insertion_pt && eol <= text_length) 
  650.   {
  651.     if (text[eol] == 'n' || substring_width(sol, eol + 1) >= box_width) 
  652.     {
  653.       eol++;
  654.       if (text[eol]=='n'||eol!=insertion_pt
  655.           ||(eol==insertion_pt && eol>0 && text[eol-1]=='n')) {
  656.         sol = eol;
  657.         line++;
  658.       }
  659.     } 
  660.     else {
  661.       eol++;
  662.     }
  663.   }
  664.   //glColor3f(1,0,0);
  665.   //glRecti(0, curr_line*LINE_HEIGHT, 3, (curr_line+1)*LINE_HEIGHT);
  666.   curr_line = line;
  667.   if (scrollbar)
  668.     scrollbar->set_int_val(start_line);
  669.   if (curr_line < start_line || curr_line > (start_line + visible_lines)) /* Insertion pt out of draw area */
  670.     return;
  671.   curr_x = this->x_abs 
  672.     + 2                               /* The edittext box has a 2-pixel margin */
  673.     + GLUI_TEXTBOX_BOXINNERMARGINX;   /** plus this many pixels blank space
  674.                                           between the text and the box       **/
  675.   
  676.   curr_x += substring_width(sol,insertion_pt-1);
  677.   if (insertion_pt == text.length() && text[text.length()-1] == 'n'
  678.       || curr_x-this->x_abs > (w - 2 - GLUI_TEXTBOX_BOXINNERMARGINX)) { // Insert on the next line 
  679.     curr_x = this->x_abs + GLUI_TEXTBOX_BOXINNERMARGINX;
  680.     line++;
  681.   } 
  682.   /* update insertion coordinates */
  683.   insert_x = curr_x+5; /* I hate magic numbers too, these offset the imagined insertion point */
  684.   insert_y = (curr_line-start_line+2)*LINE_HEIGHT;
  685.   glColor3f( 0.0, 0.0, 0.0 );
  686.   glBegin( GL_LINE_LOOP );
  687.   curr_x -= x_abs;
  688.   glVertex2i( curr_x+1, (curr_line-start_line)*LINE_HEIGHT + 4 );
  689.   glVertex2i( curr_x,   (curr_line-start_line)*LINE_HEIGHT + 4 );
  690.   glVertex2i( curr_x+1, (curr_line-start_line)*LINE_HEIGHT + 16 );
  691.   glVertex2i( curr_x,   (curr_line-start_line)*LINE_HEIGHT + 16 );
  692.   glEnd();
  693.   if ( debug )    dump( stdout, "-> DRAW_INS_PT" );
  694. }
  695. /******************************** GLUI_TextBox::substring_width() *********/
  696. int  GLUI_TextBox::substring_width( int start, int end, int initial_width )
  697. {
  698.   // This function only works properly if start is really the start of a line.
  699.   // Otherwise tabs will be messed up.
  700.   int i, width = initial_width;
  701.   for( i=start; i<=end; i++ )
  702.     if (text[i] == 't') { // Character is a tab, jump to next tab stop
  703.       width += tab_width-(width%tab_width);
  704.       //while (width == 0 || width % tab_width) 
  705.     //  width++;
  706.     }
  707.     else
  708.       width += char_width( text[i] ); 
  709.   return width;
  710. }
  711.  
  712. /***************************** GLUI_TextBox::update_and_draw_text() ********/
  713. void   GLUI_TextBox::update_and_draw_text( void )
  714. {
  715.   //update_substring_bounds();
  716.   /*  printf( "ss: %d/%dn", substring_start, substring_end );                  */
  717.   redraw();
  718. }
  719. /********************************* GLUI_TextBox::special_handler() **********/
  720. int    GLUI_TextBox::special_handler( int key,int modifiers )
  721. {
  722.   int tmp_insertion_pt;
  723.   if ( NOT glui )
  724.     return false;
  725.   if ( debug )
  726.     printf( "SPECIAL:%d - mod:%d   subs:%d/%d  ins:%d  sel:%d/%dn", 
  727.         key, modifiers, substring_start, substring_end,insertion_pt,
  728.         sel_start, sel_end );    
  729.   if ( key == GLUT_KEY_DOWN ) {
  730.     if (insert_x == -1 || insert_y == -1)
  731.       return false;
  732.     tmp_insertion_pt = find_insertion_pt( keygoal_x, insert_y+LINE_HEIGHT);
  733.     if (tmp_insertion_pt < 0)
  734.       return false;
  735.     insertion_pt = tmp_insertion_pt;
  736.     sel_end = insertion_pt;
  737.     if (!(modifiers & GLUT_ACTIVE_SHIFT)) {
  738.       sel_start = sel_end;
  739.     }
  740.     if ( can_draw())
  741.       update_and_draw_text();    
  742.   } else if ( key == GLUT_KEY_UP ) {
  743.     if (insert_x == -1 || insert_y == -1)
  744.       return false;
  745.     tmp_insertion_pt = find_insertion_pt( keygoal_x, insert_y-LINE_HEIGHT);  
  746.     if (tmp_insertion_pt < 0)
  747.       return false;
  748.     insertion_pt = tmp_insertion_pt;
  749.     sel_end = insertion_pt;
  750.     if (!(modifiers & GLUT_ACTIVE_SHIFT)) {
  751.       sel_start = sel_end;
  752.     }
  753.     if ( can_draw())
  754.       update_and_draw_text();    
  755.   } else if ( key == GLUT_KEY_LEFT ) {
  756.     if ( (modifiers & GLUT_ACTIVE_CTRL) != 0 ) {
  757.       insertion_pt = find_word_break( insertion_pt, -1 );
  758.     }
  759.     else {
  760.       insertion_pt--;
  761.     }
  762.     // update keygoal_x!
  763.   }
  764.   else if ( key == GLUT_KEY_RIGHT ) {
  765.     if ( (modifiers & GLUT_ACTIVE_CTRL) != 0 ) {
  766.       insertion_pt = find_word_break( insertion_pt, +1 );
  767.     }
  768.     else {
  769.       insertion_pt++;
  770.     }
  771.     // update keygoal_x!
  772.   }
  773.   else if ( key == GLUT_KEY_HOME ) {
  774.     insertion_pt = 0;
  775.     // update keygoal_x!
  776.   }
  777.   else if ( key == GLUT_KEY_END ) {
  778.     insertion_pt = text.length();
  779.     // update keygoal_x!
  780.   }
  781.   /*** Update selection if shift key is down ***/
  782.   if ( (modifiers & GLUT_ACTIVE_SHIFT ) != 0 )
  783.     sel_end = insertion_pt;
  784.   else 
  785.     sel_start = sel_end = insertion_pt;
  786.   
  787.   CLAMP( insertion_pt, 0, (int)text.length()); /* Make sure insertion_pt 
  788.                            is in bounds */
  789.   CLAMP( sel_start, 0, (int)text.length()); /* Make sure insertion_pt 
  790.                         is in bounds */
  791.   CLAMP( sel_end, 0, (int)text.length()); /* Make sure insertion_pt 
  792.                           is in bounds */
  793.   /******** Now redraw text ***********/
  794.   if ( can_draw())
  795.     update_and_draw_text();
  796.   return true;
  797. }
  798. /****************************** GLUI_TextBox::find_word_break() **********/
  799. /* It looks either left or right (depending on value of 'direction'       */
  800. /* for the beginning of the next 'word', where word are characters        */
  801. /* separated by one of the following tokens:  " :-.,"                     */
  802. /* If there is no next word in the specified direction, this returns      */
  803. /* the beginning of 'text', or the very end.                              */
  804. int    GLUI_TextBox::find_word_break( int start, int direction )
  805. {
  806.   int    i, j;
  807.   char    breaks[] = " nt:-.,";
  808.   int     num_break_chars = (int)strlen(breaks), text_len = text.length();
  809.   int     new_pt;
  810.   /** If we're moving left, we have to start two back, in case we're either
  811.     already at the beginning of a word, or on a separating token.  
  812.     Otherwise, this function would just return the word we're already at **/
  813.   if ( direction == -1 ) {
  814.     start -= 2;
  815.   }
  816.   /***** Iterate over text in the specified direction *****/
  817.   for ( i=start; i >= 0 AND i < text_len; i += direction ) {
  818.     /** For each character in text, iterate over list of separating tokens **/
  819.     for( j=0; j<num_break_chars; j++ ) {
  820.       if ( text[i] == breaks[j] ) {
  821.     /** character 'i' is a separating token, so we return i+1 **/
  822.     new_pt = i + 1;
  823.     CLAMP( new_pt, 0, text_len );
  824.     return new_pt;
  825.       }
  826.     }
  827.   }
  828.   if ( direction > 0 )  /* Return the end of string */
  829.     return text_len;
  830.   else                  /* Return the beginning of the text */
  831.     return 0;
  832. }
  833. /********************************** GLUI_TextBox::clear_substring() ********/
  834. void   GLUI_TextBox::clear_substring( int start, int end )
  835. {
  836.   text.erase(start,end-start);
  837. }
  838. /************************************ GLUI_TextBox::update_size() **********/
  839. void   GLUI_TextBox::update_size( void )
  840. {
  841.   if ( NOT glui )
  842.     return;
  843.   if ( w < GLUI_TEXTBOX_MIN_TEXT_WIDTH )
  844.       w = GLUI_TEXTBOX_MIN_TEXT_WIDTH;
  845. }
  846. /****************************** GLUI_TextBox::set_text() **********/
  847. void    GLUI_TextBox::set_text( const char *new_text )
  848. {
  849.   text = new_text;
  850.   substring_start = 0;
  851.   substring_end   = text.length() - 1;
  852.   insertion_pt    = -1;
  853.   sel_start       = 0;
  854.   sel_end         = 0;
  855.   visible_lines   = 0;
  856.   start_line      = 0;
  857.   curr_line       = 0;
  858.   num_lines       = 0;
  859.   if ( can_draw() )
  860.     update_and_draw_text();
  861.   /*** Now update the live variable ***/
  862.   output_live(true);
  863. }
  864. /*************************************** GLUI_TextBox::dump() **************/
  865. void   GLUI_TextBox::dump( FILE *out, char *name )
  866. {
  867.   fprintf( out, 
  868.        "%s (edittext@%p):   line:%d ins_pt:%d  subs:%d/%d  sel:%d/%d   len:%dn",
  869.        name, this, curr_line,
  870.        insertion_pt, substring_start, substring_end, sel_start, sel_end,
  871.        text.length());
  872. }
  873. /**************************************** GLUI_TextBox::mouse_over() ********/
  874. int    GLUI_TextBox::mouse_over( int state, int x, int y )
  875. {
  876.   if ( state && enabled) {
  877.     /*  curr_cursor = GLUT_CURSOR_TEXT;              */
  878.     glutSetCursor( GLUT_CURSOR_TEXT );
  879.   }
  880.   else {
  881.     /*    printf( "OUTn" );              */
  882.     glutSetCursor( GLUT_CURSOR_LEFT_ARROW );
  883.   }
  884.   return true;
  885. }
  886. void GLUI_TextBox::scrollbar_callback(GLUI_Control *my_scrollbar) {
  887.   GLUI_Scrollbar *sb = dynamic_cast<GLUI_Scrollbar*>(my_scrollbar);
  888.   if (!sb) return;
  889.   GLUI_TextBox* me = (GLUI_TextBox*) sb->associated_object;
  890.   if (me->scrollbar == NULL)
  891.     return;
  892.   int new_start_line = sb->get_int_val(); // ??
  893.   me->start_line = new_start_line;
  894.   if (new_start_line < (me->curr_line - me->visible_lines))
  895.     me->curr_line = new_start_line + me->visible_lines;
  896.   if (new_start_line > me->curr_line)
  897.     me->curr_line = new_start_line;
  898.   if ( me->can_draw() )
  899.     me->update_and_draw_text();
  900. }