1 // Crimson Fields -- a game of tactical warfare
2 // Copyright (C) 2000-2007 Jens Granseuer
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 //
18 
19 ////////////////////////////////////////////////////////////////////////
20 // textbox.cpp
21 ////////////////////////////////////////////////////////////////////////
22 
23 #include <cctype>
24 #include <string.h>
25 #include <stdlib.h>
26 
27 #include "textbox.h"
28 #include "misc.h"
29 
30 ////////////////////////////////////////////////////////////////////////
31 // NAME       : TextWidget::TextWidget
32 // DESCRIPTION: Create a new TextWidget
33 // PARAMETERS : id     - widget identifier
34 //              x      - left edge of widget
35 //              y      - top edge of widget
36 //              w      - widget width
37 //              h      - widget height
38 //              str    - text to display (may be NULL)
39 //              flags  - widget flags (see widget.h for details)
40 //              title  - widget title (currently unused)
41 //              window - widget parent window
42 // RETURNS    : -
43 ////////////////////////////////////////////////////////////////////////
44 
TextWidget(short id,short x,short y,unsigned short w,unsigned short h,const char * str,unsigned short flags,const char * title,Window * window)45 TextWidget::TextWidget( short id, short x, short y,
46            unsigned short w, unsigned short h, const char *str,
47            unsigned short flags, const char *title, Window *window ) :
48     Widget( id, x, y, w, h, flags, title, window ) {
49   textsurface = NULL;
50   spacing = 2;
51   SetText( str );
52 }
53 
54 ////////////////////////////////////////////////////////////////////////
55 // NAME       : TextWidget::~TextWidget
56 // DESCRIPTION: Destroy a TextWidget
57 // PARAMETERS : -
58 // RETURNS    : -
59 ////////////////////////////////////////////////////////////////////////
60 
~TextWidget(void)61 TextWidget::~TextWidget( void ) {
62   delete textsurface;
63 }
64 
65 ////////////////////////////////////////////////////////////////////////
66 // NAME       : TextWidget::SetText
67 // DESCRIPTION: Prepare the text for use and format it appropriately.
68 // PARAMETERS : str - string to display (may be NULL)
69 // RETURNS    : -
70 ////////////////////////////////////////////////////////////////////////
71 
SetText(const char * str)72 void TextWidget::SetText( const char *str ) {
73   delete textsurface;
74   textsurface = NULL;
75   rows = visrows = toprow = 0;
76 
77   if ( !str ) return;
78 
79   rows = font->TextHeight( str, w - 10, spacing );
80   if ( rows > 0 ) {
81     textsurface = new Surface();
82     if ( textsurface->Create( w - 10, rows, surface->GetView()->ScreenBPP(), 0 ) ) {
83       delete textsurface;
84       textsurface = NULL;
85       return;
86     }
87 
88     textsurface->SetColorKey( Color(CF_COLOR_BLACK) );
89     textsurface->Flood( Color(CF_COLOR_BLACK) );
90 
91     string full( str );
92     int pos = 0, endpos = full.size(), rowcnt = 0;
93     unsigned short xoff = 0;
94 
95     while ( pos < endpos ) {
96       size_t linelen = font->FitText( &str[pos], w - 10, true );
97       string sub( full.substr( pos, linelen ) );
98 
99       // remove newlines
100       size_t rep = 0;
101       while ( (rep = sub.find( '\n', rep )) != string::npos )
102         sub.erase( rep, 1 );
103 
104       if ( flags & WIDGET_ALIGN_CENTER )
105         xoff = (w - 10 - font->TextWidth( sub.c_str() )) / 2;
106       font->Write( sub.c_str(), textsurface, xoff, rowcnt );
107 
108       rowcnt += font->Height() + spacing;
109       pos += linelen;
110     }
111 
112     visrows = h - 6;
113     if ( visrows > rows ) visrows = rows;
114   }
115 }
116 
117 ////////////////////////////////////////////////////////////////////////
118 // NAME       : TextWidget::Draw
119 // DESCRIPTION: Draw the widget and render the text.
120 // PARAMETERS : -
121 // RETURNS    : -
122 ////////////////////////////////////////////////////////////////////////
123 
Draw(void)124 void TextWidget::Draw( void ) {
125   surface->DrawBack( *this );
126   surface->DrawBox( *this, BOX_RECESSED );
127 
128   if ( textsurface )
129     textsurface->Blit( surface, Rect(0,toprow,textsurface->Width(),visrows),
130                        x + 5, y + 3 );
131 }
132 
133 ////////////////////////////////////////////////////////////////////////
134 // NAME       : TextWidget::SetRow
135 // DESCRIPTION: Set the first row visible in the widget textbox.
136 // PARAMETERS : top - first row to be visible; the widget automatically
137 //                    adjusts this value to fit as much text as possible
138 //                    into the box
139 // RETURNS    : new top row set (after optimization)
140 ////////////////////////////////////////////////////////////////////////
141 
SetRow(unsigned short top)142 unsigned short TextWidget::SetRow( unsigned short top ) {
143   unsigned short maxrow = rows - visrows;
144   if ( top <= maxrow ) toprow = top;
145   else toprow = maxrow;
146   Draw();
147   Show();
148   return toprow;
149 }
150 
151 
152 ////////////////////////////////////////////////////////////////////////
153 // NAME       : TextScrollWidget::TextScrollWidget
154 // DESCRIPTION: Create a new TextScrollWidget.
155 // PARAMETERS : id     - widget identifier
156 //              x      - left edge of widget
157 //              y      - top edge of widget
158 //              w      - widget width
159 //              h      - widget height
160 //              str    - text to display (may be NULL)
161 //              flags  - widget flags (see widget.h for details)
162 //              title  - widget title (currently unused)
163 //              window - widget parent window
164 // RETURNS    : -
165 ////////////////////////////////////////////////////////////////////////
166 
TextScrollWidget(short id,short x,short y,unsigned short w,unsigned short h,const char * str,unsigned short flags,const char * title,Window * window)167 TextScrollWidget::TextScrollWidget( short id, short x, short y,
168            unsigned short w, unsigned short h, const char *str,
169            unsigned short flags, const char *title, Window *window ) :
170     CompositeWidget( id, x, y, w, h, 0, 0, window ) {
171 
172   TextWidget *text = new TextWidget( 0, x, y, w, h, 0,
173               flags|WIDGET_COMPOSITE, title, window );
174   AddWidget( text );
175 
176   SetText( str );
177 }
178 
179 ////////////////////////////////////////////////////////////////////////
180 // NAME       : TextWidget::WidgetActivated
181 // DESCRIPTION: Scroller has been used. Scroll the widget to the current
182 //              row.
183 // PARAMETERS : widget - calling widget (slider)
184 //              win    - window the widget belongs to
185 // RETURNS    : GUI_OK
186 ////////////////////////////////////////////////////////////////////////
187 
WidgetActivated(Widget * widget,Window * win)188 GUI_Status TextScrollWidget::WidgetActivated( Widget *widget, Window *win ) {
189   TextWidget *t = static_cast<TextWidget *>( GetWidget( 0 ) );
190   t->SetRow( static_cast<SliderWidget *>(widget)->Level() );
191   return GUI_OK;
192 }
193 
194 ////////////////////////////////////////////////////////////////////////
195 // NAME       : TextScrollWidget::MouseDown
196 // DESCRIPTION: Distribute mouse down events to all components. Redirect
197 //              all scrollwheel events to the slider so that scrolling
198 //              works even if the pointer doesn't hover over the slider.
199 // PARAMETERS : button - button event
200 // RETURNS    : GUI status
201 ////////////////////////////////////////////////////////////////////////
202 
MouseDown(const SDL_MouseButtonEvent & button)203 GUI_Status TextScrollWidget::MouseDown( const SDL_MouseButtonEvent &button ) {
204   SDL_MouseButtonEvent mybutton = button;
205   if ( ((button.button == SDL_BUTTON_WHEELUP) ||
206        (button.button == SDL_BUTTON_WHEELDOWN)) &&
207        Contains( button.x - surface->LeftEdge(), button.y - surface->TopEdge() ) ) {
208     SliderWidget *s = static_cast<SliderWidget *>( GetWidget(1) );
209     if ( s ) {
210       // to address the slider widget we must adjust the click
211       // coordinates to be inside the slider box
212       mybutton.x = s->LeftEdge() + surface->LeftEdge();
213       mybutton.y = s->TopEdge() + surface->TopEdge();
214     }
215   }
216 
217   return CompositeWidget::MouseDown( mybutton );
218 }
219 
220 ////////////////////////////////////////////////////////////////////////
221 // NAME       : TextScrollWidget::SetText
222 // DESCRIPTION: Set a new text to be displayed in the widget.
223 // PARAMETERS : str - string to display (may be NULL)
224 // RETURNS    : -
225 ////////////////////////////////////////////////////////////////////////
226 
SetText(const char * str)227 void TextScrollWidget::SetText( const char *str ) {
228   TextWidget *t = static_cast<TextWidget *>( GetWidget(0) );
229   SliderWidget *s = static_cast<SliderWidget *>( GetWidget(1) );
230 
231   bool needslider = str && (font->TextHeight( str, w - 10, 2 ) > h - 6);
232 
233   if ( needslider ) {
234     if ( !s ) {
235       s = new SliderWidget( 1, x + w - DEFAULT_SLIDER_SIZE, y,
236           DEFAULT_SLIDER_SIZE, h, 0, 0, 0, 0,
237           WIDGET_VSCROLL|WIDGET_HSCROLLKEY|WIDGET_VSCROLLKEY|WIDGET_COMPOSITE,
238           NULL, surface );
239       s->SetKeyStep( font->Height() + 2 );
240       s->SetHook( this );
241       AddWidget( s );
242 
243       t->SetSize( x, y, w - s->Width(), h );
244     }
245   } else if ( s ) {
246     RemoveWidget( s );
247     t->SetSize( x, y, w + s->Width(), h );
248     delete s;
249   }
250 
251   t->SetText( str );
252 
253   if ( needslider ) {
254     s->Adjust( 0, t->Rows() - t->RowsVisible(), MAX(t->RowsVisible(),1) );
255     s->ScrollTo( 0 );
256   }
257 }
258 
259 
260 ////////////////////////////////////////////////////////////////////////
261 // NAME       : TextListWidget::DrawNodes
262 // DESCRIPTION: Draw the list nodes.
263 // PARAMETERS : -
264 // RETURNS    : -
265 ////////////////////////////////////////////////////////////////////////
266 
DrawNodes(void)267 void TextListWidget::DrawNodes( void ) {
268   Rect box( x + 4, y + 1 + spacing, listboxw - 8, h - 2 - 2 * spacing );
269   Rect area( x + 1, y + 1, listboxw - 2, h - 2 );
270   short num = toprow / ItemHeight();                        // number of top node
271   TLWNode *n = static_cast<TLWNode *>(list->GetNode( num ));  // top node
272   short xoff = box.x, yoff = box.y + (num * ItemHeight()) - toprow;
273   Color fcol = font->GetColor();
274 
275   if ( Disabled() )
276     font->SetColor( Color(CF_COLOR_GHOSTED) );
277 
278   surface->DrawBack( area );
279 
280   while ( n ) {
281     if ( flags & WIDGET_ALIGN_CENTER )
282       xoff = box.x + (box.w - font->TextWidth(n->Name())) / 2;
283 
284     if ( num == current ) {
285       Rect hilite( x + 2, yoff, listboxw - 4, ItemHeight() );
286       hilite.Clip( area );
287       surface->FillRectAlpha( hilite, surface->GetFGPen() );
288     }
289 
290     // print node name and clip to box
291     PrintItem( n, surface, xoff, yoff + 1, box );
292 
293     yoff += ItemHeight();
294     if ( yoff >= box.y + box.h ) break;
295 
296     ++num;
297     n = static_cast<TLWNode *>( n->Next() );
298   }
299 
300   font->SetColor( fcol );
301 }
302 
303 ////////////////////////////////////////////////////////////////////////
304 // NAME       : TextListWidget::PrintItem
305 // DESCRIPTION: Print one item of the list widget. This can be used to
306 //              customize the look of the list, e.g. colour and font.
307 //              The item height must keep the same, though.
308 // PARAMETERS : item - node to print
309 //              dest - destination surface
310 //              x    - left edge of printing area
311 //              y    - top edge of printing area
312 //              clip - clipping rectangle
313 // RETURNS    : -
314 ////////////////////////////////////////////////////////////////////////
315 
PrintItem(const TLWNode * item,Surface * dest,short x,short y,const Rect & clip) const316 void TextListWidget::PrintItem( const TLWNode *item, Surface *dest,
317                                 short x, short y, const Rect &clip ) const {
318     font->Write( item->Name(), dest, x, y, clip );
319 }
320 
321 
322 ////////////////////////////////////////////////////////////////////////
323 // NAME       : StringWidget::StringWidget
324 // DESCRIPTION: Create a new StringWidget.
325 // PARAMETERS : id     - widget identifier
326 //              x      - left edge of widget
327 //              y      - top edge of widget
328 //              w      - widget width
329 //              h      - widget height
330 //              str    - string to display
331 //              maxlen - maximum length of string in characters (not
332 //                       including the trailing NUL-byte)
333 //              flags  - widget flags (see widget.h for details)
334 //              title  - widget title
335 //              window - widget parent window
336 // RETURNS    : -
337 ////////////////////////////////////////////////////////////////////////
338 
StringWidget(short id,short x,short y,unsigned short w,unsigned short h,const char * str,unsigned short maxlen,unsigned short flags,const char * title,Window * window)339 StringWidget::StringWidget( short id, short x, short y, unsigned short w,
340               unsigned short h, const char *str,
341               unsigned short maxlen, unsigned short flags,
342               const char *title, Window *window ) :
343       Widget( id, x, y, w, h, flags, title, window ),
344       cursor(0), maxlen(maxlen), offset(0),
345       strbox(x+4,y+(h-font->Height())/2,w-8,font->Height()),
346       validator(0) {
347   SetString( str, false );
348 }
349 
350 ////////////////////////////////////////////////////////////////////////
351 // NAME       : StringWidget::SetString
352 // DESCRIPTION: Set a new string to display in the widget.
353 // PARAMETERS : newstr - string to display (may be NULL)
354 //              upd    - whether to update the display (default value
355 //                       is "true")
356 // RETURNS    : -
357 ////////////////////////////////////////////////////////////////////////
358 
SetString(const char * newstr,bool upd)359 void StringWidget::SetString( const char *newstr, bool upd /* = true */ ) {
360   if ( newstr ) buffer.assign( newstr );
361   else buffer.erase();
362 
363   if ( upd ) {
364     Draw();
365     Show();
366   }
367 }
368 
369 ////////////////////////////////////////////////////////////////////////
370 // NAME       : StringWidget::String
371 // DESCRIPTION: Get the current string.
372 // PARAMETERS : -
373 // RETURNS    : pointer to the string, or NULL if string is empty
374 ////////////////////////////////////////////////////////////////////////
375 
String(void) const376 const char *StringWidget::String( void ) const {
377   if ( !buffer.empty() ) return buffer.c_str();
378   return NULL;
379 }
380 
381 ////////////////////////////////////////////////////////////////////////
382 // NAME       : StringWidget::Draw
383 // DESCRIPTION: Draw the widget.
384 // PARAMETERS : -
385 // RETURNS    : -
386 ////////////////////////////////////////////////////////////////////////
387 
Draw(void)388 void StringWidget::Draw( void ) {
389   Rect clip;
390   surface->DrawBack( *this );
391   if ( !(flags & WIDGET_STYLE_NOBORDER ) )
392     surface->DrawBox( *this, BOX_RECESSED );
393   surface->GetClipRect( clip );
394   surface->SetClipRect( strbox );
395 
396   if ( Clicked() )
397     surface->FillRect( strbox.x + CursorPos(), strbox.y,
398                        CursorWidth(), font->Height(), surface->GetFGPen() );
399 
400   short xoff = strbox.x - offset;
401   if ( flags & WIDGET_STR_PASSWORD ) {
402     unsigned short starw = font->CharWidth('*');
403     unsigned short len = buffer.size();
404 
405     for ( int i = 0; i < len; ++i ) {
406       if ( Clicked() && (cursor == i) )
407         font->Write( '*', surface, xoff, strbox.y, surface->GetBGPen() );
408       else font->Write( '*', surface, xoff, strbox.y );
409       xoff += starw;
410     }
411 
412   } else {
413     font->Write( buffer.c_str(), surface, strbox.x - offset, strbox.y, strbox );
414     if ( Clicked() && (cursor < buffer.size()) ) {
415       font->Write( buffer[cursor], surface, strbox.x + CursorPos(), strbox.y, surface->GetBGPen() );
416     }
417   }
418 
419   surface->SetClipRect( clip );
420   PrintTitle( surface->GetFGPen() );
421 }
422 
423 ////////////////////////////////////////////////////////////////////////
424 // NAME       : StringWidget::MouseDown
425 // DESCRIPTION: React to mouse button presses.
426 // PARAMETERS : button - SDL_MouseButtonEvent received from the event
427 //                       handler
428 // RETURNS    : GUI status
429 ////////////////////////////////////////////////////////////////////////
430 
MouseDown(const SDL_MouseButtonEvent & button)431 GUI_Status StringWidget::MouseDown( const SDL_MouseButtonEvent &button ) {
432   if ( (button.button == SDL_BUTTON_LEFT) && !(flags & WIDGET_STR_CONST) &&
433         Contains( button.x - surface->LeftEdge(),
434                   button.y - surface->TopEdge() ) ) {
435     short xoff = button.x - surface->LeftEdge() - x;
436     unsigned short len = buffer.size();
437 
438     cursor = 0;
439     while ( (cursor < len) && (CursorPos() + CursorWidth() < xoff) )
440       ++cursor;
441 
442     short curpix = CursorPos();
443     if ( curpix < 0 ) offset -= curpix;
444     else if ( curpix + CursorWidth() >= strbox.w ) {
445       offset += curpix + CursorWidth() - strbox.w;
446     }
447 
448     return InputLoop();
449   }
450   return GUI_OK;
451 }
452 
453 ////////////////////////////////////////////////////////////////////////
454 // NAME       : StringWidget::KeyDown
455 // DESCRIPTION: React to key presses.
456 // PARAMETERS : key - SDL_keysym received from the event handler
457 // RETURNS    : GUI status
458 ////////////////////////////////////////////////////////////////////////
459 
KeyDown(const SDL_keysym & key)460 GUI_Status StringWidget::KeyDown( const SDL_keysym &key ) {
461   if ( (key.sym == this->key) && !(flags & WIDGET_STR_CONST) ) {
462     unsigned short buflen = font->TextWidth( buffer.c_str() );
463 
464     cursor = buffer.size();
465     offset = MAX( 0, buflen + CursorWidth() - strbox.w );
466     return InputLoop();
467   }
468   return GUI_OK;
469 }
470 
471 ////////////////////////////////////////////////////////////////////////
472 // NAME       : StringWidget::InputLoop
473 // DESCRIPTION: After activation of the StringWidget, all events are
474 //              exclusively handled by this function, until the
475 //              widget is deselected at which point the control is given
476 //              back to the main event handler.
477 // PARAMETERS : -
478 // RETURNS    : GUI status
479 ////////////////////////////////////////////////////////////////////////
480 
InputLoop(void)481 GUI_Status StringWidget::InputLoop( void ) {
482   SDL_Event event;
483   GUI_Status rc = GUI_OK;
484   int unicode;
485   bool quit = false;
486 
487   Push();
488   unicode = SDL_EnableUNICODE( 1 );
489 
490   do {
491     rc = surface->GetView()->FetchEvent( event );
492     if ( (rc == GUI_QUIT) || (rc == GUI_ERROR) ) quit = true;
493     else {
494       if ( ((event.type == SDL_MOUSEBUTTONDOWN) &&
495              !Contains( event.button.x - x, event.button.y - y )) ||
496            ((event.type == SDL_KEYUP) && ((event.key.keysym.sym == SDLK_RETURN) ||
497                                           (event.key.keysym.sym == SDLK_TAB))) ) {
498         quit = true;
499       } else if ( event.type == SDL_KEYDOWN ) {
500         CharInput( event.key.keysym.sym, event.key.keysym.unicode );
501       }
502     }
503   } while ( !quit );
504 
505   cursor = offset = 0;
506 
507   SDL_EnableUNICODE( unicode );
508   Release();
509   if ( hook ) hook->WidgetActivated( this, surface );
510   return rc;
511 }
512 
513 ////////////////////////////////////////////////////////////////////////
514 // NAME       : StringWidget::CharInput
515 // DESCRIPTION: Insert a new character in the input buffer.
516 // PARAMETERS : sym     - ASCII symbol for printable characters
517 //              unicode - UNICODE representation for non-printable chars
518 // RETURNS    : -
519 ////////////////////////////////////////////////////////////////////////
520 
CharInput(short sym,unsigned short unicode)521 void StringWidget::CharInput( short sym, unsigned short unicode ) {
522   bool changed = false;
523 
524   if ( unicode ) {
525     switch ( unicode ) {
526     case '\b':                // BACKSPACE - delete char at cursor-1
527       if ( cursor > 0 ) {
528         buffer.erase( --cursor, 1 );
529         changed = true;
530       }
531       break;
532     case 127:                 // DELETE - delete char at cursor
533       if ( cursor < buffer.size() ) {
534         buffer.erase( cursor, 1 );
535         changed = true;
536       }
537       break;
538     default:
539       if ( buffer.size() < maxlen ) {   // insert char at cursor pos
540         bool accept = (validator ?
541                        validator->ValidateKey( buffer.c_str(), unicode, cursor ) :
542                        isprint(unicode) != 0 );
543         if ( accept ) {
544           buffer.insert( cursor++, 1, unicode );
545           changed = true;
546         }
547       }
548     }
549   } else switch ( sym ) {
550     case SDLK_LEFT:                     // move cursor left
551       if ( cursor > 0 ) {
552         --cursor;
553         changed = true;
554       }
555       break;
556     case SDLK_RIGHT:
557       if ( cursor < buffer.size() ) {
558         ++cursor;
559         changed = true;
560       }
561       break;
562     default:
563       break;
564   }
565 
566   if ( changed ) {
567     short curpix = CursorPos();
568     if ( curpix < 0 ) offset += curpix;
569     else if ( curpix + CursorWidth() >= strbox.w )
570       offset += curpix + CursorWidth() - strbox.w;
571 
572     Draw();
573     Show();
574   }
575 }
576 
577 ////////////////////////////////////////////////////////////////////////
578 // NAME       : StringWidget::CursorWidth
579 // DESCRIPTION: Get width of cursor in pixels.
580 // PARAMETERS : -
581 // RETURNS    : cursor width
582 ////////////////////////////////////////////////////////////////////////
583 
CursorWidth(void) const584 unsigned short StringWidget::CursorWidth( void ) const {
585   unsigned short curw;
586 
587   if ( flags & WIDGET_STR_PASSWORD ) curw = font->CharWidth('*');
588   else if ( cursor < buffer.size() ) curw = font->CharWidth( buffer[cursor] );
589   else curw = font->Width();
590 
591   return curw;
592 }
593 
594 ////////////////////////////////////////////////////////////////////////
595 // NAME       : StringWidget::CursorPos
596 // DESCRIPTION: Get position of cursor on display in pixels.
597 // PARAMETERS : -
598 // RETURNS    : cursor position (- current offset)
599 ////////////////////////////////////////////////////////////////////////
600 
CursorPos(void) const601 short StringWidget::CursorPos( void ) const {
602   unsigned short cp;
603   if ( flags & WIDGET_STR_PASSWORD )
604     cp = cursor * font->CharWidth('*');
605   else {
606     cp = font->TextWidth(buffer.substr(0, cursor + 1).c_str());
607     if ( buffer.size() > cursor ) cp -= CursorWidth();
608   }
609   return cp - offset;
610 }
611 
612 ////////////////////////////////////////////////////////////////////////
613 // NAME       : StringWidget::DispWidth
614 // DESCRIPTION: Get width of a character in pixels.
615 // PARAMETERS : ch - glyph to measure
616 // RETURNS    : character width
617 ////////////////////////////////////////////////////////////////////////
618 
DispWidth(short ch) const619 unsigned short StringWidget::DispWidth( short ch ) const {
620   unsigned short cw;
621 
622   if ( flags & WIDGET_STR_PASSWORD ) cw = font->CharWidth('*');
623   else cw = font->CharWidth( ch );
624 
625   return cw;
626 }
627 
628 ////////////////////////////////////////////////////////////////////////
629 // NAME       : StringWidget::SetFocus
630 // DESCRIPTION: Activate the widget, i.e. show the cursor and wait for
631 //              user input.
632 // PARAMETERS : -
633 // RETURNS    : GUI status
634 ////////////////////////////////////////////////////////////////////////
635 
SetFocus(void)636 GUI_Status StringWidget::SetFocus( void ) {
637   cursor = 0;
638   return InputLoop();
639 }
640 
641 
642 ////////////////////////////////////////////////////////////////////////
643 // NAME       : NumberWidget::NumberWidget
644 // DESCRIPTION: Create a new NumberWidget.
645 // PARAMETERS : id     - widget identifier
646 //              x      - left edge of widget
647 //              y      - top edge of widget
648 //              w      - widget width
649 //              h      - widget height
650 //              number - initial value to display
651 //              min    - minimum allowed value
652 //              max    - maximum allowed value
653 //              flags  - widget flags (see widget.h for details)
654 //              title  - widget title
655 //              window - widget parent window
656 // RETURNS    : -
657 ////////////////////////////////////////////////////////////////////////
658 
NumberWidget(short id,short x,short y,unsigned short w,unsigned short h,long number,long min,long max,unsigned short flags,const char * title,Window * window)659 NumberWidget::NumberWidget( short id, short x, short y, unsigned short w, unsigned short h,
660             long number, long min, long max, unsigned short flags,
661             const char *title, Window *window ) :
662     StringWidget( id, x, y, w, h, itoa(number, numbuf), strlen(itoa(max,numbuf2)),
663                   flags, title, window ) {
664   num = number;
665   minval = min;
666   maxval = max;
667   SetValidator( this );
668 }
669 
670 ////////////////////////////////////////////////////////////////////////
671 // NAME       : NumberWidget::SetNumber
672 // DESCRIPTION: Fill the widget with a value.
673 // PARAMETERS : number - new widget value
674 //              upd    - whether to update the display (default is true)
675 // RETURNS    : -
676 ////////////////////////////////////////////////////////////////////////
677 
SetNumber(long number,bool upd)678 void NumberWidget::SetNumber( long number, bool upd /* = true */ ) {
679   num = MIN( MAX( minval, number ), maxval );
680 
681   SetString( itoa( num, numbuf ), upd );
682 }
683 
684 ////////////////////////////////////////////////////////////////////////
685 // NAME       : NumberWidget::ValidateKey
686 // DESCRIPTION: Only accept numbers for input.
687 // PARAMETERS : str - string currently entered in widget (not used)
688 //              key - char to be entered
689 //              pos - position at which to enter the char
690 // RETURNS    : TRUE if key is accepted, FALSE if refused
691 ////////////////////////////////////////////////////////////////////////
692 
ValidateKey(const char * str,unsigned short key,unsigned short pos) const693 bool NumberWidget::ValidateKey( const char *str, unsigned short key,
694                                 unsigned short pos ) const {
695   return ((key >= '0') && (key <= '9')) || ((key == '-') && (pos == 0));
696 }
697 
698 ////////////////////////////////////////////////////////////////////////
699 // NAME       : NumberWidget::SetMin
700 // DESCRIPTION: Set a new minimum value.
701 // PARAMETERS : min - new minimum
702 // RETURNS    : -
703 ////////////////////////////////////////////////////////////////////////
704 
SetMin(long min)705 void NumberWidget::SetMin( long min ) {
706   minval = min;
707 
708   if ( Number() < min ) SetNumber( min );
709 }
710 
711 ////////////////////////////////////////////////////////////////////////
712 // NAME       : NumberWidget::SetMax
713 // DESCRIPTION: Set a new maximum value.
714 // PARAMETERS : max - new maximum
715 // RETURNS    : -
716 ////////////////////////////////////////////////////////////////////////
717 
SetMax(long max)718 void NumberWidget::SetMax( long max ) {
719   maxval = max;
720 
721   if ( Number() > max ) SetNumber( max );
722 }
723 
724 ////////////////////////////////////////////////////////////////////////
725 // NAME       : NumberWidget::Release
726 // DESCRIPTION: When the widget is released make sure the number which
727 //              was entered is inside the requested boundaries and
728 //              adjust it if necessary.
729 // PARAMETERS : -
730 // RETURNS    : -
731 ////////////////////////////////////////////////////////////////////////
732 
Release(void)733 void NumberWidget::Release( void ) {
734   const char *str = String();
735   short n = (str ? atoi(str) : 0);
736   SetNumber( n );
737   StringWidget::Release();
738 }
739 
740 
741 ////////////////////////////////////////////////////////////////////////
742 // NAME       : TLWNode::TLWNode
743 // DESCRIPTION: Create a new TextListWidget node.
744 // PARAMETERS : name - name to be displayed in TextListWidget
745 //              data - arbitrary data for private use by caller
746 //              id   - node identifier; this is not used internally
747 //                     either and may be used by the caller
748 // RETURNS    : -
749 ////////////////////////////////////////////////////////////////////////
750 
TLWNode(const char * name,void * data,unsigned short id)751 TLWNode::TLWNode( const char *name, void *data, unsigned short id ) :
752          name(name), id(id), user_data(data) {
753 }
754 
TLWNode(const char * name,void * data)755 TLWNode::TLWNode( const char *name, void *data ) :
756          name(name), id(0), user_data(data) {
757 }
758 
TLWNode(const char * name)759 TLWNode::TLWNode( const char *name ) :
760          name(name), id(0), user_data(0) {
761 }
762 
763 ////////////////////////////////////////////////////////////////////////
764 // NAME       : TLWList::Sort
765 // DESCRIPTION: Sort the list in ascending alphabetical order.
766 // PARAMETERS : -
767 // RETURNS    : -
768 ////////////////////////////////////////////////////////////////////////
769 
Sort(void)770 void TLWList::Sort( void ) {
771   TLWList s;
772 
773   while ( !IsEmpty() )
774     s.AddHead( RemHead() );
775 
776   while ( !s.IsEmpty() )
777     InsertNodeSorted( static_cast<TLWNode *>(s.RemTail()) );
778 }
779 
780 ////////////////////////////////////////////////////////////////////////
781 // NAME       : TLWList::InsertNodeSorted
782 // DESCRIPTION: Insert a node into the list according to its name.
783 //              Nodes will be sorted in ascending alphabetical order.
784 // PARAMETERS : n - TextListWidget node to be inserted
785 // RETURNS    : -
786 ////////////////////////////////////////////////////////////////////////
787 
InsertNodeSorted(TLWNode * n)788 void TLWList::InsertNodeSorted( TLWNode *n ) {
789   TLWNode *walk = static_cast<TLWNode *>( Tail() );
790 
791   while ( walk && (strcmp( n->Name(), walk->Name()) < 0) )
792     walk = static_cast<TLWNode *>( walk->Prev() );
793 
794   InsertNode( n, walk );
795 }
796 
797 ////////////////////////////////////////////////////////////////////////
798 // NAME       : TLWList::GetNodeByID
799 // DESCRIPTION: Retrieve the node with the given ID from the list.
800 // PARAMETERS : id - requested node identifier
801 // RETURNS    : pointer to the node, or NULL if not found
802 ////////////////////////////////////////////////////////////////////////
803 
GetNodeByID(unsigned short id) const804 TLWNode *TLWList::GetNodeByID( unsigned short id ) const {
805   TLWNode *walk = static_cast<TLWNode *>( Head() );
806 
807   while ( walk && (walk->ID() != id) )
808     walk = static_cast<TLWNode *>( walk->Next() );
809 
810   return walk;
811 }
812 
813