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