~ubuntu-branches/ubuntu/vivid/emscripten/vivid

« back to all changes in this revision

Viewing changes to tests/bullet/Extras/glui/glui_textbox.cpp

  • Committer: Package Import Robot
  • Author(s): Sylvestre Ledru
  • Date: 2013-05-02 13:11:51 UTC
  • Revision ID: package-import@ubuntu.com-20130502131151-q8dvteqr1ef2x7xz
Tags: upstream-1.4.1~20130504~adb56cb
ImportĀ upstreamĀ versionĀ 1.4.1~20130504~adb56cb

Show diffs side-by-side

added added

removed removed

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