1 /*
2  * See Licensing and Copyright notice in naev.h
3  */
4 
5 /**
6  * @file input.c
7  *
8  * @brief Input widget.
9  */
10 
11 
12 #include "tk/toolkit_priv.h"
13 
14 #include <stdlib.h>
15 #include <assert.h>
16 
17 #include "nstd.h"
18 #include "nstring.h"
19 
20 
21 static void inp_render( Widget* inp, double bx, double by );
22 static int inp_isBreaker(char c);
23 static int inp_key( Widget* inp, SDLKey key, SDLMod mod );
24 static int inp_text( Widget* inp, const char *buf );
25 static int inp_addKey( Widget* inp, SDLKey key );
26 static void inp_clampView( Widget *inp );
27 static void inp_cleanup( Widget* inp );
28 static void inp_focusGain( Widget* inp );
29 static void inp_focusLose( Widget* inp );
30 
31 
32 /**
33  * @brief Adds an input widget to a window.
34  *
35  * Position origin is 0,0 at bottom left.  If you use negative X or Y
36  *  positions.  They actually count from the opposite side in.
37  *
38  *    @param wid ID of the window to add the widget to.
39  *    @param x X position within the window to use.
40  *    @param y Y position within the window to use.
41  *    @param w Width of the widget.
42  *    @param h Height of the widget.
43  *    @param name Name of the widget to use internally.
44  *    @param max Max amount of characters that can be written.
45  *    @param oneline Whether widget should only be one line long.
46  *    @param font Font to use.
47  */
window_addInput(const unsigned int wid,const int x,const int y,const int w,const int h,char * name,const int max,const int oneline,glFont * font)48 void window_addInput( const unsigned int wid,
49                       const int x, const int y, /* position */
50                       const int w, const int h, /* size */
51                       char* name, const int max, const int oneline,
52                       glFont *font )
53 {
54    Window *wdw = window_wget(wid);
55    Widget *wgt = window_newWidget(wdw, name);
56    if (wgt == NULL)
57       return;
58 
59    /* generic */
60    wgt->type   = WIDGET_INPUT;
61 
62    /* specific */
63    wgt->render          = inp_render;
64    wgt->cleanup         = inp_cleanup;
65    wgt_setFlag(wgt, WGT_FLAG_CANFOCUS);
66    wgt->keyevent        = inp_key;
67    wgt->textevent       = inp_text;
68    wgt->focusGain       = inp_focusGain;
69    wgt->focusLose       = inp_focusLose;
70    wgt->dat.inp.font    = (font != NULL) ? font : &gl_smallFont;
71    wgt->dat.inp.max     = max+1;
72    wgt->dat.inp.oneline = oneline;
73    wgt->dat.inp.pos     = 0;
74    wgt->dat.inp.view    = 0;
75    wgt->dat.inp.input   = calloc( wgt->dat.inp.max, 1 );
76    wgt->dat.inp.fptr    = NULL;
77 
78    /* position/size */
79    wgt->w = (double) w;
80    wgt->h = (double) h;
81    toolkit_setPos( wdw, wgt, x, y );
82 }
83 
84 
85 /**
86  * @brief Renders a input widget.
87  *
88  *    @param inp Input widget to render.
89  *    @param bx Base X position.
90  *    @param by Base Y position.
91  */
inp_render(Widget * inp,double bx,double by)92 static void inp_render( Widget* inp, double bx, double by )
93 {
94    double x, y, ty;
95    char buf[ 512 ], *str;
96    int w, m, p, s;
97    int lines;
98    char c;
99 
100    x = bx + inp->x;
101    y = by + inp->y;
102 
103    /* main background */
104    toolkit_drawRect( x, y, inp->w, inp->h, &cWhite, NULL );
105 
106    if (inp->dat.inp.oneline)
107       /* center vertically */
108       ty = y - (inp->h - gl_smallFont.h)/2.;
109    else {
110       /* Align top-left. */
111       ty = y - gl_smallFont.h / 2.;
112    }
113 
114    /* Draw text. */
115    gl_printTextRaw( inp->dat.inp.font, inp->w-10., inp->h,
116          x+5., ty, &cBlack, &inp->dat.inp.input[ inp->dat.inp.view ] );
117 
118    /* Draw cursor. */
119    if (wgt_isFlag( inp, WGT_FLAG_FOCUSED )) {
120       if (inp->dat.inp.oneline) {
121          m = MIN( inp->dat.inp.pos - inp->dat.inp.view, (int)sizeof(buf)-1 );
122          strncpy( buf, &inp->dat.inp.input[ inp->dat.inp.view ], m );
123          buf[ m ] = '\0';
124          w = gl_printWidthRaw( inp->dat.inp.font, buf );
125          toolkit_drawRect( x + 5. + w, y + (inp->h - inp->dat.inp.font->h - 4.)/2.,
126                1., inp->dat.inp.font->h + 4., &cBlack, &cBlack );
127       }
128       else {
129          /* Wrap the cursor around if the text is longer than the width of the widget. */
130          str   = inp->dat.inp.input;
131          w     = 0;
132          p     = 0;
133          lines = 0;
134          s     = 0;
135          do {
136             p     += w;
137             if ((s != 0) && ((str[p] == '\n') || (str[p] == ' ')))
138                p++;
139             s      = 1;
140             w      = gl_printWidthForText( inp->dat.inp.font, &str[p], inp->w-10 );
141             lines += 1;
142             if (str[p+w] == '\0')
143                break;
144          } while (p+w < inp->dat.inp.pos);
145 
146          /* Hack because we have to avoid wraps when counting lines, so here we
147           * handle the last line partially. */
148          c = str[ inp->dat.inp.pos ];
149          str[ inp->dat.inp.pos ] = '\0';
150          w = gl_printWidthRaw( inp->dat.inp.font, &str[p] );
151          str[ inp->dat.inp.pos ] = c;
152 
153          /* Get the actual width now. */
154          toolkit_drawRect( x + 5. + w, y + inp->h - lines * (inp->dat.inp.font->h + 5) - 3.,
155                1., inp->dat.inp.font->h + 4., &cBlack, &cBlack );
156       }
157    }
158 
159    /* inner outline */
160    toolkit_drawOutline( x, y, inp->w, inp->h, 0.,
161          toolkit_colLight, toolkit_col );
162    /* outer outline */
163    toolkit_drawOutline( x, y, inp->w, inp->h, 1.,
164          toolkit_colDark, NULL );
165 }
166 
167 
168 /**
169  * @brief Handles input text.
170  *
171  *    @param inp Input widget to handle event.
172  *    @param buf Text to handle.
173  *    @return 1 if text was used;
174  */
inp_text(Widget * inp,const char * buf)175 static int inp_text( Widget* inp, const char *buf )
176 {
177    int i;
178    int ret;
179 
180    i = 0;
181    ret = 0;
182    while (buf[i] != '\0') {
183       ret |= inp_addKey( inp, buf[i] );
184       i++;
185    }
186 
187    if (ret && inp->dat.inp.fptr != NULL)
188       inp->dat.inp.fptr( inp->wdw, inp->name );
189 
190    return ret;
191 }
192 
193 
194 /**
195  * @brief Adds a single key to the input.
196  *
197  *    @param inp Input widget receiving the key.
198  *    @param key Key to receive.
199  *    @return 1 if key was used.
200  */
inp_addKey(Widget * inp,SDLKey key)201 static int inp_addKey( Widget* inp, SDLKey key )
202 {
203    int i;
204    int n;
205    char c;
206 
207    /*
208     * Handle arrow keys.
209     * @todo finish implementing, no cursor makes it complicated to see where you are.
210     */
211 
212    /* Only catch some keys. */
213    if (!nstd_isgraph(key) && (key != ' '))
214       return 0;
215 
216    /* No sense to use SDLKey below this. */
217    c = key;
218 
219    /* Check to see if is in filter to ignore. */
220    if (inp->dat.inp.filter != NULL)
221       for (i=0; inp->dat.inp.filter[i] != '\0'; i++)
222          if (inp->dat.inp.filter[i] == c)
223             return 1; /* Ignored. */
224 
225    /* Make sure it's not full. */
226    if (strlen(inp->dat.inp.input) >= (size_t)inp->dat.inp.max-1)
227       return 1;
228 
229    /* Add key. */
230    memmove( &inp->dat.inp.input[ inp->dat.inp.pos+1 ],
231          &inp->dat.inp.input[ inp->dat.inp.pos ],
232          inp->dat.inp.max - inp->dat.inp.pos - 2 );
233    inp->dat.inp.input[ inp->dat.inp.pos++ ] = c;
234    inp->dat.inp.input[ inp->dat.inp.max-1 ] = '\0';
235 
236    if (inp->dat.inp.oneline) {
237       /* We can't wrap the text, so we need to scroll it out. */
238       n = gl_printWidthRaw( inp->dat.inp.font, inp->dat.inp.input+inp->dat.inp.view );
239       if (n+10 > inp->w)
240          inp->dat.inp.view++;
241    }
242 
243    return 1;
244 }
245 
246 /**
247  * @brief Checks if a character is a breaker character (for editing purposes)
248  *
249  *    @param c character to check.
250  *    @return 1 if the char is a breaker, 0 if it isn't.
251  */
inp_isBreaker(char c)252 static int inp_isBreaker(char c)
253 {
254    char* breakers = ";:.-_ \n";
255    int i;
256 
257    for (i = 0; i < (int)strlen(breakers); i++) {
258       if (breakers[i] == c)
259          return 1;
260    }
261 
262    return 0;
263 }
264 
265 
266 /**
267  * @brief Handles input for an input widget.
268  *
269  *    @param inp Input widget to handle event.
270  *    @param key Key being handled.
271  *    @param mod Mods when key is being pressed.
272  *    @return 1 if the event was used, 0 if it wasn't.
273  */
inp_key(Widget * inp,SDLKey key,SDLMod mod)274 static int inp_key( Widget* inp, SDLKey key, SDLMod mod )
275 {
276    (void) mod;
277    int n, curpos, prevpos, curchars, prevchars, charsfromleft, lines;
278    int len;
279    char* str;
280 
281    /*
282     * Handle arrow keys.
283     */
284     if ((key == SDLK_LEFT) ||
285          (key == SDLK_RIGHT) ||
286          (key == SDLK_UP) ||
287          (key == SDLK_DOWN)) {
288       /* Move pointer. */
289       if (key == SDLK_LEFT) {
290          if (inp->dat.inp.pos > 0) {
291             if (mod & KMOD_CTRL) {
292                /* We want to position the cursor at the start of the previous or current word. */
293                /* Begin by skipping all breakers. */
294                while (inp_isBreaker(inp->dat.inp.input[inp->dat.inp.pos-1]) && inp->dat.inp.pos > 0) {
295                   inp->dat.inp.pos--;
296                }
297                /* Now skip until we encounter a breaker (or SOL). */
298                while (!inp_isBreaker(inp->dat.inp.input[inp->dat.inp.pos-1]) && inp->dat.inp.pos > 0) {
299                   inp->dat.inp.pos--;
300                }
301             }
302             else
303                inp->dat.inp.pos -= 1;
304 
305             inp_clampView( inp );
306          }
307       }
308       else if (key == SDLK_RIGHT) {
309          len = (int)strlen(inp->dat.inp.input);
310          if (inp->dat.inp.pos < len) {
311             if (mod & KMOD_CTRL) {
312                /* We want to position the cursor at the start of the next word. */
313                /* Begin by skipping all non-breakers. */
314                while (!inp_isBreaker(inp->dat.inp.input[inp->dat.inp.pos])
315                      && (inp->dat.inp.pos < len)) {
316                   inp->dat.inp.pos++;
317                }
318                /* Now skip until we encounter a non-breaker (or EOL). */
319                while (inp_isBreaker(inp->dat.inp.input[inp->dat.inp.pos])
320                      && (inp->dat.inp.pos < len)) {
321                   inp->dat.inp.pos++;
322                }
323             }
324             else
325                inp->dat.inp.pos += 1;
326 
327             inp_clampView( inp );
328          }
329       }
330       else if (key == SDLK_UP) {
331          if (inp->dat.inp.oneline)
332             return 0;
333 
334          str      = inp->dat.inp.input;
335          curpos   = 0;
336          prevpos  = 0;
337          curchars = 0;
338          lines    = 0;
339 
340          if (inp->dat.inp.pos == 0) /* We can't move beyond the current line, as it is the first one. */
341             return 1;
342 
343          /* Keep not-printing the lines until the current pos is smaller than the virtual pos.
344           * At this point, we've arrived at the line the cursor is on. */
345          while (inp->dat.inp.pos > curpos) {
346             prevpos   = curpos;
347             prevchars = curchars;
348 
349             curchars  = gl_printWidthForText( inp->dat.inp.font, &str[curpos], inp->w-10 );
350             curpos   += curchars;
351             /* Handle newlines. */
352             if (str[curpos] == '\n') {
353                curchars++;
354                curpos++;
355             }
356 
357             lines++;
358          }
359 
360          /* Set the pos to the same number of characters from the left hand
361           * edge, on the previous line (unless there aren't that many chars).
362           * This is more or less equal to going up a line. */
363          charsfromleft     = inp->dat.inp.pos - prevpos;
364          /* Hack for moving up to the first line. */
365          if (lines == 2)
366             charsfromleft--;
367 
368          inp->dat.inp.pos  = prevpos - prevchars;
369          inp->dat.inp.pos += MIN(charsfromleft, prevchars);
370       }
371       else if (key == SDLK_DOWN) {
372          if (inp->dat.inp.oneline)
373             return 0;
374 
375          str      = inp->dat.inp.input;
376          curpos   = 0;
377          prevpos  = 0;
378          curchars = 0;
379          lines    = 0;
380 
381          /* We can't move beyond the current line, as it is the last one. */
382          if (inp->dat.inp.pos == (int)strlen(inp->dat.inp.input))
383             return 1;
384 
385          /* Keep not-printing the lines until the current pos is smaller than the virtual pos.
386           * At this point, we've arrived at the line the cursor is on. */
387          while (inp->dat.inp.pos >= curpos) {
388             prevpos   = curpos;
389             prevchars = curchars;
390 
391             curchars  = gl_printWidthForText( inp->dat.inp.font, &str[curpos], inp->w-10 );
392             curpos   += curchars;
393             /* Handle newlines. */
394             if (str[curpos] == '\n') {
395                curchars++;
396                curpos++;
397             }
398             lines++;
399          }
400 
401          /* Take note how many chars from the left we have. */
402          charsfromleft = inp->dat.inp.pos - prevpos;
403          /* Hack for moving down from the first line. */
404          if (lines == 1)
405             charsfromleft++;
406 
407          /* Now not-print one more line. This is the line we want to move the cursor to. */
408          prevpos   = curpos;
409          prevchars = curchars;
410          curchars  = gl_printWidthForText( inp->dat.inp.font, &str[curpos], inp->w-10 );
411          curpos   += curchars;
412 
413          /* Set the pos to the same number of characters from the left hand
414           * edge, on this line (unless there aren't that many chars).
415           * This is more or less equal to going down a line.
416           * But make sure never to go past the end of the string. */
417          inp->dat.inp.pos  = prevpos;
418          inp->dat.inp.pos += MIN(charsfromleft, curchars);
419          inp->dat.inp.pos  = MIN(inp->dat.inp.pos, (int)strlen(inp->dat.inp.input));
420       }
421 
422       return 1;
423    }
424 
425 
426 #if SDL_VERSION_ATLEAST(2,0,0)
427    /* Don't use, but don't eat, either. */
428    if ((key == SDLK_TAB) || (key == SDLK_ESCAPE))
429       return 0;
430 #endif /* SDL_VERSION_ATLEAST(2,0,0) */
431 
432    /* Eat everything else that isn't usable. Om nom. */
433    /* Only catch some keys. */
434    if ((key != SDLK_BACKSPACE) &&
435          (key != SDLK_DELETE) &&
436          (key != SDLK_RETURN) &&
437          (key != SDLK_KP_ENTER) &&
438          (key != SDLK_HOME) &&
439          (key != SDLK_END) &&
440          (key != SDLK_PAGEUP) &&
441          (key != SDLK_PAGEDOWN))
442 #if SDL_VERSION_ATLEAST(2,0,0)
443       return 1; /* SDL2 uses TextInput and should eat most keys. Om nom. */
444 #else /* SDL_VERSION_ATLEAST(2,0,0) */
445       return 0;
446 #endif /* SDL_VERSION_ATLEAST(2,0,0) */
447 
448    /* backspace -> delete text */
449    if (key == SDLK_BACKSPACE) {
450       if (inp->dat.inp.pos <= 0)
451          return 1; /* We still catch the event. */
452 
453       /* We want to move inp->dat.inp.pos backward and delete all characters caught between it and curpos at the end. */
454       curpos = inp->dat.inp.pos;
455       if (inp->dat.inp.pos > 0) {
456          if (mod & KMOD_CTRL) {
457             /* We want to delete up to the start of the previous or current word. */
458             /* Begin by skipping all breakers. */
459             while (inp_isBreaker(inp->dat.inp.input[inp->dat.inp.pos-1]) && inp->dat.inp.pos > 0) {
460                inp->dat.inp.pos--;
461             }
462             /* Now skip until we encounter a breaker (or SOL). */
463             while (!inp_isBreaker(inp->dat.inp.input[inp->dat.inp.pos-1]) && inp->dat.inp.pos > 0) {
464                inp->dat.inp.pos--;
465             }
466          }
467          else {
468             inp->dat.inp.pos--;
469          }
470       }
471       /* Actually delete the chars. */
472       memmove( &inp->dat.inp.input[ inp->dat.inp.pos ],
473             &inp->dat.inp.input[ curpos ],
474             (inp->dat.inp.max - curpos) );
475       inp->dat.inp.input[ inp->dat.inp.max - curpos + inp->dat.inp.pos ] = '\0';
476 
477       if (inp->dat.inp.oneline && inp->dat.inp.view > 0) {
478          n = gl_printWidthRaw( &gl_smallFont,
479                inp->dat.inp.input + inp->dat.inp.view - 1 );
480          if (n+10 < inp->w)
481             inp->dat.inp.view--;
482       }
483 
484       if (inp->dat.inp.fptr != NULL)
485          inp->dat.inp.fptr( inp->wdw, inp->name );
486 
487       inp_clampView( inp );
488       return 1;
489    }
490    /* delete -> delete text */
491    else if (key == SDLK_DELETE) {
492       len = (int)strlen(inp->dat.inp.input);
493       /* We want to move curpos forward and delete all characters caught between it and inp->dat.inp.pos at the end. */
494       curpos = inp->dat.inp.pos;
495       if (inp->dat.inp.pos < len) {
496          if (mod & KMOD_CTRL) {
497             /* We want to delete up until the start of the next word. */
498             /* Begin by skipping all non-breakers. */
499             while (!inp_isBreaker(inp->dat.inp.input[curpos])
500                   && (curpos < len)) {
501                curpos++;
502             }
503             /* Now skip until we encounter a non-breaker (or EOL). */
504             while (inp_isBreaker(inp->dat.inp.input[curpos])
505                   && (curpos < len)) {
506                curpos++;
507             }
508          }
509          else {
510             curpos++;
511          }
512       }
513       /* Actually delete the chars. */
514       memmove( &inp->dat.inp.input[ inp->dat.inp.pos ],
515             &inp->dat.inp.input[ curpos ],
516             (inp->dat.inp.max - curpos) );
517       inp->dat.inp.input[ inp->dat.inp.max - curpos + inp->dat.inp.pos ] = '\0';
518 
519       if (inp->dat.inp.fptr != NULL)
520          inp->dat.inp.fptr( inp->wdw, inp->name );
521 
522       return 1;
523    }
524    /* home -> move to start */
525    else if (key == SDLK_HOME) {
526       inp->dat.inp.pos = 0;
527       inp_clampView( inp );
528 
529       return 1;
530    }
531    /* end -> move to end */
532    else if (key == SDLK_END) {
533       inp->dat.inp.pos = strlen(inp->dat.inp.input);
534       inp_clampView( inp );
535 
536       return 1;
537    }
538 
539    /* in limits. */
540    else if (key==SDLK_RETURN || key==SDLK_KP_ENTER) {
541       if (inp->dat.inp.oneline)
542          return 0; /* Enter does not work in one-liners. */
543       /* Empty. */
544       if ((inp->dat.inp.pos >= inp->dat.inp.max-1))
545          return 1;
546 
547       memmove( &inp->dat.inp.input[ inp->dat.inp.pos+1 ],
548             &inp->dat.inp.input[ inp->dat.inp.pos ],
549             inp->dat.inp.max - inp->dat.inp.pos - 2 );
550       inp->dat.inp.input[ inp->dat.inp.pos++ ] = '\n';
551       inp->dat.inp.input[ inp->dat.inp.max-1 ] = '\0'; /* Make sure it's NUL terminated. */
552 
553       if (inp->dat.inp.fptr != NULL)
554          inp->dat.inp.fptr( inp->wdw, inp->name );
555 
556       return 1;
557    }
558 
559    /* Nothing caught anything so just return. */
560    return 0;
561 }
562 
563 
564 /*
565  * @brief Keeps the input widget's view in sync with its cursor
566  *
567  *    @param inp Input widget to operate on.
568  */
inp_clampView(Widget * inp)569 static void inp_clampView( Widget *inp )
570 {
571    int visible;
572 
573    /* @todo Handle multiline input widgets. */
574    if (!inp->dat.inp.oneline)
575       return;
576 
577    /* If the cursor is behind the view, shift the view backwards. */
578    if (inp->dat.inp.view > inp->dat.inp.pos) {
579       inp->dat.inp.view = inp->dat.inp.pos;
580       return;
581    }
582 
583    visible = gl_printWidthForText( inp->dat.inp.font,
584          &inp->dat.inp.input[ inp->dat.inp.view ], inp->w - 10 );
585 
586    /* Shift the view right until the cursor is visible. */
587    while (inp->dat.inp.view + visible < inp->dat.inp.pos)
588       visible = gl_printWidthForText( inp->dat.inp.font,
589             &inp->dat.inp.input[ inp->dat.inp.view++ ], inp->w - 10 );
590 }
591 
592 
593 /**
594  * @brief Clean up function for the input widget.
595  *
596  *    @param inp Input widget to clean up.
597  */
inp_cleanup(Widget * inp)598 static void inp_cleanup( Widget* inp )
599 {
600    /* Free filter if needed. */
601    if (inp->dat.inp.filter != NULL)
602       free(inp->dat.inp.filter);
603 
604    free(inp->dat.inp.input); /* frees the input buffer */
605 }
606 
607 
608 /**
609  * @brief Gets the input from an input widget.
610  *
611  *    @param wid ID of the window to get widget from.
612  *    @param name Name of the widget.
613  */
window_getInput(const unsigned int wid,char * name)614 char* window_getInput( const unsigned int wid, char* name )
615 {
616    Widget *wgt;
617 
618    /* Get the widget. */
619    wgt = window_getwgt(wid,name);
620    if (wgt == NULL)
621       return NULL;
622 
623    /* Check the type. */
624    if (wgt->type != WIDGET_INPUT) {
625       WARN("Trying to get input from non-input widget '%s'.", name);
626       return NULL;
627    }
628 
629    /* Get the value. */
630    return wgt->dat.inp.input;
631 }
632 
633 /**
634  * @brief Sets the input for an input widget.
635  *
636  *    @param wid Window to which the widget belongs.
637  *    @param name Name of the widget to modify.
638  *    @param msg Message to set for the input box or NULL to clear.
639  * @return The message actually set (can be truncated).
640  */
window_setInput(const unsigned int wid,char * name,const char * msg)641 char* window_setInput( const unsigned int wid, char* name, const char *msg )
642 {
643    Widget *wgt;
644 
645    /* Get the widget. */
646    wgt = window_getwgt(wid,name);
647    if (wgt == NULL)
648       return NULL;
649 
650    /* Check the type. */
651    if (wgt->type != WIDGET_INPUT) {
652       WARN("Trying to set input on non-input widget '%s'.", name);
653       return NULL;
654    }
655 
656    /* Set the message. */
657    if (msg == NULL) {
658       memset( wgt->dat.inp.input, 0, wgt->dat.inp.max );
659       wgt->dat.inp.pos     = 0;
660       wgt->dat.inp.view    = 0;
661    }
662    else {
663       strncpy( wgt->dat.inp.input, msg, wgt->dat.inp.max );
664       wgt->dat.inp.input[ wgt->dat.inp.max-1 ] = '\0';
665       wgt->dat.inp.pos = strlen( wgt->dat.inp.input );
666    }
667 
668    /* Get the value. */
669    if (wgt->dat.inp.fptr != NULL)
670       wgt->dat.inp.fptr( wid, name );
671 
672    return wgt->dat.inp.input;
673 }
674 
675 
676 /**
677  * @brief Sets the input filter.
678  *
679  * This is a list of characters which won't be accepted as input.
680  *
681  *    @param wid Window to which input widget belongs.
682  *    @param name Input widget to set filter on.
683  *    @param filter '\0' terminated list of characters to filter.
684  */
window_setInputFilter(const unsigned int wid,char * name,const char * filter)685 void window_setInputFilter( const unsigned int wid, char* name, const char *filter )
686 {
687    Widget *wgt;
688 
689    /* Get the widget. */
690    wgt = window_getwgt(wid,name);
691    if (wgt == NULL)
692       return;
693 
694    /* Check the type. */
695    if (wgt->type != WIDGET_INPUT) {
696       WARN("Trying to set input filter on non-input widget '%s'.", name);
697       return;
698    }
699 
700    /* Free if already exists. */
701    if (wgt->dat.inp.filter != NULL)
702       free(wgt->dat.inp.filter);
703 
704    /* Copy filter over. */
705    wgt->dat.inp.filter = strdup( filter );
706 }
707 
708 /**
709  * @brief Sets the callback used when the input's text is modified.
710  *
711  *    @param wid Window to which input widget belongs.
712  *    @param name Input widget to set callback for.
713  *    @param fptr Function to trigger when the input's text is modified.
714  */
window_setInputCallback(const unsigned int wid,char * name,void (* fptr)(unsigned int,char *))715 void window_setInputCallback( const unsigned int wid, char* name, void (*fptr)(unsigned int, char*) )
716 {
717    Widget *wgt;
718 
719    /* Get the widget. */
720    wgt = window_getwgt(wid, name);
721    if (wgt == NULL)
722       return;
723 
724    /* Check the type. */
725    if (wgt->type != WIDGET_INPUT) {
726       WARN("Trying to set callback on non-input widget '%s'.", name);
727       return;
728    }
729 
730    wgt->dat.inp.fptr = fptr;
731 }
732 
733 /**
734  * @brief Input widget gains focus.
735  *
736  *    @param inp Widget gaining the focus.
737  */
inp_focusGain(Widget * inp)738 static void inp_focusGain( Widget* inp )
739 {
740 #if SDL_VERSION_ATLEAST(2,0,0)
741    SDL_Rect input_pos;
742 
743    input_pos.x = (int)inp->x;
744    input_pos.y = (int)inp->y;
745    input_pos.w = (int)inp->w;
746    input_pos.h = (int)inp->h;
747 
748    SDL_StartTextInput();
749    SDL_SetTextInputRect( &input_pos );
750 #else /* SDL_VERSION_ATLEAST(2,0,0) */
751    (void) inp;
752 #endif /* SDL_VERSION_ATLEAST(2,0,0) */
753 }
754 
755 /**
756  * @brief Input widget loses focus.
757  *
758  *    @param inp Widget losing the focus.
759  */
inp_focusLose(Widget * inp)760 static void inp_focusLose( Widget* inp )
761 {
762    (void) inp;
763 #if SDL_VERSION_ATLEAST(2,0,0)
764    SDL_StopTextInput();
765 #endif /* SDL_VERSION_ATLEAST(2,0,0) */
766 }
767 
768 
769