1 /*
2 * sdlgui.c - A tiny graphical user interface for the SDL library.
3 *
4 * This file 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 file is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * (http://www.gnu.org/licenses/) for more details.
13 */
14 const char SDLGui_fileid[] = "sdlgui.c : " __DATE__ " " __TIME__;
15
16 #include <SDL.h>
17 #include <ctype.h>
18 #include <string.h>
19 #include <stdbool.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22
23 #include "i18n.h"
24 #include "sdlgui.h"
25 #include "baller1.h"
26 #include "screen.h"
27
28 #include "font8x16.h"
29
30 #if WITH_SDL2
31 #define SDL_SRCCOLORKEY SDL_TRUE
32 #endif
33
34 int sdlgui_fontwidth; /* Width of the actual font */
35 int sdlgui_fontheight; /* Height of the actual font */
36
37 static SDL_Surface *pSdlGuiScrn; /* Pointer to the actual main SDL screen surface */
38 //static SDL_Surface *pSmallFontGfx = NULL; /* The small font graphics */
39 static SDL_Surface *pBigFontGfx = NULL; /* The big font graphics */
40 static SDL_Surface *pFontGfx = NULL; /* The actual font graphics */
41 static int current_object = 0; /* Current selected object */
42 static bool has_utf8;
43
44 #if WITH_SDL2
SDL_SetColors(SDL_Surface * surface,SDL_Color * colors,int firstcolor,int ncolors)45 static inline int SDL_SetColors(SDL_Surface *surface, SDL_Color *colors,
46 int firstcolor, int ncolors)
47 {
48 return SDL_SetPaletteColors(surface->format->palette, colors,
49 firstcolor, ncolors);
50 }
51 #endif
52
53 /**
54 * Load an 1 plane XBM into a 8 planes SDL_Surface.
55 */
SDLGui_LoadXBM(int w,int h,const void * pXbmBits)56 static SDL_Surface *SDLGui_LoadXBM(int w, int h, const void *pXbmBits)
57 {
58 SDL_Surface *bitmap;
59 Uint8 *dstbits;
60 const Uint8 *srcbits;
61 int x, y, srcpitch;
62 int mask;
63
64 srcbits = pXbmBits;
65
66 /* Allocate the bitmap */
67 bitmap = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 8, 0, 0, 0, 0);
68 if (bitmap == NULL)
69 {
70 fprintf(stderr, "Failed to allocate bitmap: %s", SDL_GetError());
71 return NULL;
72 }
73
74 srcpitch = ((w + 7) / 8);
75 dstbits = (Uint8 *)bitmap->pixels;
76 mask = 1;
77
78 /* Copy the pixels */
79 for (y = 0 ; y < h ; y++)
80 {
81 for (x = 0 ; x < w ; x++)
82 {
83 dstbits[x] = (srcbits[x / 8] & mask) ? 1 : 0;
84 mask <<= 1;
85 mask |= (mask >> 8);
86 mask &= 0xFF;
87 }
88 dstbits += bitmap->pitch;
89 srcbits += srcpitch;
90 }
91
92 return bitmap;
93 }
94
95
96 /*-----------------------------------------------------------------------*/
97 /**
98 * Initialize the GUI.
99 */
SDLGui_Init(void)100 int SDLGui_Init(void)
101 {
102 char *lang;
103
104 SDL_Color blackWhiteColors[2] = {{255, 255, 255, 0}, {0, 0, 0, 0}};
105
106 if (/*pSmallFontGfx &&*/ pBigFontGfx)
107 {
108 /* already initialized */
109 return 0;
110 }
111
112 /* Initialize the font graphics: */
113 // pSmallFontGfx = SDLGui_LoadXBM(font5x8_width, font5x8_height, font5x8_bits);
114 pBigFontGfx = SDLGui_LoadXBM(font8x16_width, font8x16_height, font8x16_bits);
115 if (/*pSmallFontGfx == NULL ||*/ pBigFontGfx == NULL)
116 {
117 fprintf(stderr, "Error: Can not init font graphics!\n");
118 return -1;
119 }
120
121 /* Set color palette of the font graphics: */
122 //SDL_SetColors(pSmallFontGfx, blackWhiteColors, 0, 2);
123 SDL_SetColors(pBigFontGfx, blackWhiteColors, 0, 2);
124
125 /* Set font color 0 as transparent: */
126 //SDL_SetColorKey(pSmallFontGfx, SDL_SRCCOLORKEY, 0);
127 SDL_SetColorKey(pBigFontGfx, SDL_SRCCOLORKEY, 0);
128
129 /* Check for UTF-8 locale */
130 lang = getenv("LANG");
131 if (lang != NULL)
132 {
133 has_utf8 = (strstr(lang, "utf-8") != NULL)
134 || (strstr(lang, "UTF-8") != NULL)
135 || (strstr(lang, "utf8") != NULL)
136 || (strstr(lang, "UTF8") != NULL);
137 }
138 else
139 {
140 has_utf8 = false;
141 }
142
143 return 0;
144 }
145
146
147 /*-----------------------------------------------------------------------*/
148 /**
149 * Uninitialize the GUI.
150 */
SDLGui_UnInit(void)151 int SDLGui_UnInit(void)
152 {
153 /*
154 if (pSmallFontGfx)
155 {
156 SDL_FreeSurface(pSmallFontGfx);
157 pSmallFontGfx = NULL;
158 }
159 */
160
161 if (pBigFontGfx)
162 {
163 SDL_FreeSurface(pBigFontGfx);
164 pBigFontGfx = NULL;
165 }
166
167 return 0;
168 }
169
170
171 /*-----------------------------------------------------------------------*/
172 /**
173 * Inform the SDL-GUI about the actual SDL_Surface screen pointer and
174 * prepare the font to suit the actual resolution.
175 */
SDLGui_SetScreen(SDL_Surface * pScrn)176 int SDLGui_SetScreen(SDL_Surface *pScrn)
177 {
178 pSdlGuiScrn = pScrn;
179
180 /* Decide which font to use - small or big one: */
181 // if (pSdlGuiScrn->w >= 640 && pSdlGuiScrn->h >= 400 && pBigFontGfx != NULL)
182 {
183 pFontGfx = pBigFontGfx;
184 }
185 /*
186 else
187 {
188 pFontGfx = pSmallFontGfx;
189 }
190 */
191
192 if (pFontGfx == NULL)
193 {
194 fprintf(stderr, "Error: A problem with the font occured!\n");
195 return -1;
196 }
197
198 /* Get the font width and height: */
199 sdlgui_fontwidth = pFontGfx->w/16;
200 sdlgui_fontheight = pFontGfx->h/16;
201
202 return 0;
203 }
204
205 /*-----------------------------------------------------------------------*/
206 /**
207 * Return character size for current font in given arguments.
208 */
SDLGui_GetFontSize(int * width,int * height)209 void SDLGui_GetFontSize(int *width, int *height)
210 {
211 *width = sdlgui_fontwidth;
212 *height = sdlgui_fontheight;
213 }
214
215 /*-----------------------------------------------------------------------*/
216 /**
217 * Center a dialog so that it appears in the middle of the screen.
218 * Note: We only store the coordinates in the root box of the dialog,
219 * all other objects in the dialog are positioned relatively to this one.
220 */
SDLGui_CenterDlg(SGOBJ * dlg)221 void SDLGui_CenterDlg(SGOBJ *dlg)
222 {
223 dlg[0].x = (pSdlGuiScrn->w/sdlgui_fontwidth-dlg[0].w)/2;
224 dlg[0].y = (pSdlGuiScrn->h/sdlgui_fontheight-dlg[0].h)/2;
225 }
226
227
228 /**
229 * Convert UTF-8 character to our internal Latin1 representation and
230 * increase the string index.
231 */
SDLGui_Utf8ToLatin1(const char * txt,int * i)232 static char SDLGui_Utf8ToLatin1(const char *txt, int *i)
233 {
234 unsigned char c;
235
236 c = txt[*i];
237 *i += 1;
238 if (c < 0x80 || !has_utf8)
239 {
240 return c;
241 }
242
243 /* Quick and dirty convertion for latin1 characters only... */
244 if ((c & 0xc0) == 0xc0)
245 {
246 c = c << 6;
247 c |= (txt[*i] & 0x7f);
248 *i += 1;
249 }
250 else
251 {
252 printf("Unsupported character '%c' (0x%x)\n", c, c);
253 }
254
255 return c;
256 }
257
258 /**
259 * Draw a text string.
260 */
SDLGui_Text(int x,int y,const char * txt)261 void SDLGui_Text(int x, int y, const char *txt)
262 {
263 int i, p;
264 unsigned char c;
265 SDL_Rect sr, dr;
266
267 sr.w = dr.w = sdlgui_fontwidth;
268 sr.h = dr.h = sdlgui_fontheight;
269
270 i = p = 0;
271 while (txt[i] != 0)
272 {
273 c = SDLGui_Utf8ToLatin1(txt, &i);
274 sr.x = sdlgui_fontwidth * (c % 16);
275 sr.y = sdlgui_fontheight * (c / 16);
276 dr.x = x + p * sdlgui_fontwidth;
277 dr.y = y;
278 SDL_BlitSurface(pFontGfx, &sr, pSdlGuiScrn, &dr);
279 p += 1;
280 }
281 }
282
283
284 /*-----------------------------------------------------------------------*/
285 /**
286 * Draw a dialog text object.
287 */
SDLGui_DrawText(const SGOBJ * tdlg,int objnum)288 static void SDLGui_DrawText(const SGOBJ *tdlg, int objnum)
289 {
290 int x, y;
291 x = (tdlg[0].x+tdlg[objnum].x)*sdlgui_fontwidth;
292 y = (tdlg[0].y+tdlg[objnum].y)*sdlgui_fontheight;
293 SDLGui_Text(x, y, _(tdlg[objnum].txt));
294 }
295
296
297 /*-----------------------------------------------------------------------*/
298 /**
299 * Draw a edit field object.
300 */
SDLGui_DrawEditField(const SGOBJ * edlg,int objnum)301 static void SDLGui_DrawEditField(const SGOBJ *edlg, int objnum)
302 {
303 int x, y;
304 SDL_Rect rect;
305
306 x = (edlg[0].x+edlg[objnum].x)*sdlgui_fontwidth;
307 y = (edlg[0].y+edlg[objnum].y)*sdlgui_fontheight;
308 SDLGui_Text(x, y, edlg[objnum].txt);
309
310 rect.x = x;
311 rect.y = y + edlg[objnum].h * sdlgui_fontheight;
312 rect.w = edlg[objnum].w * sdlgui_fontwidth;
313 rect.h = 1;
314 SDL_FillRect(pSdlGuiScrn, &rect, SDL_MapRGB(pSdlGuiScrn->format,160,160,160));
315 }
316
317
318 /*-----------------------------------------------------------------------*/
319 /**
320 * Draw a dialog box object.
321 */
SDLGui_DrawBox(const SGOBJ * bdlg,int objnum)322 static void SDLGui_DrawBox(const SGOBJ *bdlg, int objnum)
323 {
324 SDL_Rect rect;
325 int x, y, w, h, offset;
326 Uint32 grey = SDL_MapRGB(pSdlGuiScrn->format,192,192,192);
327 Uint32 upleftc, downrightc;
328
329 x = bdlg[objnum].x*sdlgui_fontwidth;
330 y = bdlg[objnum].y*sdlgui_fontheight;
331 if (objnum > 0) /* Since the root object is a box, too, */
332 {
333 /* we have to look for it now here and only */
334 x += bdlg[0].x*sdlgui_fontwidth; /* add its absolute coordinates if we need to */
335 y += bdlg[0].y*sdlgui_fontheight;
336 }
337 w = bdlg[objnum].w*sdlgui_fontwidth;
338 h = bdlg[objnum].h*sdlgui_fontheight;
339
340 if (bdlg[objnum].state & SG_SELECTED)
341 {
342 upleftc = SDL_MapRGB(pSdlGuiScrn->format,128,128,128);
343 downrightc = SDL_MapRGB(pSdlGuiScrn->format,255,255,255);
344 }
345 else
346 {
347 upleftc = SDL_MapRGB(pSdlGuiScrn->format,255,255,255);
348 downrightc = SDL_MapRGB(pSdlGuiScrn->format,128,128,128);
349 }
350
351 /* The root box should be bigger than the screen, so we disable the offset there: */
352 if (objnum != 0)
353 offset = 1;
354 else
355 offset = 0;
356
357 /* Draw background: */
358 rect.x = x;
359 rect.y = y;
360 rect.w = w;
361 rect.h = h;
362 SDL_FillRect(pSdlGuiScrn, &rect, grey);
363
364 /* Draw upper border: */
365 rect.x = x;
366 rect.y = y - offset;
367 rect.w = w;
368 rect.h = 1;
369 SDL_FillRect(pSdlGuiScrn, &rect, upleftc);
370
371 /* Draw left border: */
372 rect.x = x - offset;
373 rect.y = y;
374 rect.w = 1;
375 rect.h = h;
376 SDL_FillRect(pSdlGuiScrn, &rect, upleftc);
377
378 /* Draw bottom border: */
379 rect.x = x;
380 rect.y = y + h - 1 + offset;
381 rect.w = w;
382 rect.h = 1;
383 SDL_FillRect(pSdlGuiScrn, &rect, downrightc);
384
385 /* Draw right border: */
386 rect.x = x + w - 1 + offset;
387 rect.y = y;
388 rect.w = 1;
389 rect.h = h;
390 SDL_FillRect(pSdlGuiScrn, &rect, downrightc);
391 }
392
393
394 /*-----------------------------------------------------------------------*/
395 /**
396 * Draw a normal button.
397 */
SDLGui_DrawButton(const SGOBJ * bdlg,int objnum)398 void SDLGui_DrawButton(const SGOBJ *bdlg, int objnum)
399 {
400 int x,y;
401 char *txt = _(bdlg[objnum].txt);
402
403 SDLGui_DrawBox(bdlg, objnum);
404
405 x = (bdlg[0].x + bdlg[objnum].x) * sdlgui_fontwidth
406 + (bdlg[objnum].w - strlen(txt)) * sdlgui_fontwidth / 2;
407 y = (bdlg[0].y + bdlg[objnum].y + (bdlg[objnum].h-1)/2)
408 * sdlgui_fontheight;
409
410 if (bdlg[objnum].state & SG_SELECTED)
411 {
412 x+=1;
413 y+=1;
414 }
415 SDLGui_Text(x, y, txt);
416 }
417
418
419 /*-----------------------------------------------------------------------*/
420 /**
421 * Draw a dialog radio button object.
422 */
SDLGui_DrawRadioButton(const SGOBJ * rdlg,int objnum)423 static void SDLGui_DrawRadioButton(const SGOBJ *rdlg, int objnum)
424 {
425 char str[80];
426 int x, y;
427
428 x = (rdlg[0].x + rdlg[objnum].x) * sdlgui_fontwidth;
429 y = (rdlg[0].y + rdlg[objnum].y) * sdlgui_fontheight;
430
431 if (rdlg[objnum].state & SG_SELECTED)
432 str[0]=SGRADIOBUTTON_SELECTED;
433 else
434 str[0]=SGRADIOBUTTON_NORMAL;
435 str[1]=' ';
436 strcpy(&str[2], _(rdlg[objnum].txt));
437
438 SDLGui_Text(x, y, str);
439 }
440
441
442 /*-----------------------------------------------------------------------*/
443 /**
444 * Draw a dialog check box object.
445 */
SDLGui_DrawCheckBox(const SGOBJ * cdlg,int objnum)446 static void SDLGui_DrawCheckBox(const SGOBJ *cdlg, int objnum)
447 {
448 char str[80];
449 int x, y;
450
451 x = (cdlg[0].x + cdlg[objnum].x) * sdlgui_fontwidth;
452 y = (cdlg[0].y + cdlg[objnum].y) * sdlgui_fontheight;
453
454 if ( cdlg[objnum].state&SG_SELECTED )
455 str[0]=SGCHECKBOX_SELECTED;
456 else
457 str[0]=SGCHECKBOX_NORMAL;
458 str[1]=' ';
459 strcpy(&str[2], _(cdlg[objnum].txt));
460
461 SDLGui_Text(x, y, str);
462 }
463
464
465 /*-----------------------------------------------------------------------*/
466 /**
467 * Draw a scrollbar button.
468 */
SDLGui_DrawScrollbar(const SGOBJ * bdlg,int objnum)469 static void SDLGui_DrawScrollbar(const SGOBJ *bdlg, int objnum)
470 {
471 SDL_Rect rect;
472 int x, y, w, h;
473 Uint32 grey0 = SDL_MapRGB(pSdlGuiScrn->format,128,128,128);
474 Uint32 grey1 = SDL_MapRGB(pSdlGuiScrn->format,196,196,196);
475 Uint32 grey2 = SDL_MapRGB(pSdlGuiScrn->format, 64, 64, 64);
476
477 x = bdlg[objnum].x * sdlgui_fontwidth;
478 y = bdlg[objnum].y * sdlgui_fontheight + bdlg[objnum].h;
479
480 x += bdlg[0].x*sdlgui_fontwidth; /* add mainbox absolute coordinates */
481 y += bdlg[0].y*sdlgui_fontheight; /* add mainbox absolute coordinates */
482
483 w = 1 * sdlgui_fontwidth;
484 h = bdlg[objnum].w;
485
486 /* Draw background: */
487 rect.x = x;
488 rect.y = y;
489 rect.w = w;
490 rect.h = h;
491 SDL_FillRect(pSdlGuiScrn, &rect, grey0);
492
493 /* Draw upper border: */
494 rect.x = x;
495 rect.y = y;
496 rect.w = w;
497 rect.h = 1;
498 SDL_FillRect(pSdlGuiScrn, &rect, grey1);
499
500 /* Draw bottom border: */
501 rect.x = x;
502 rect.y = y + h - 1;
503 rect.w = w;
504 rect.h = 1;
505 SDL_FillRect(pSdlGuiScrn, &rect, grey2);
506
507 }
508
509 /*-----------------------------------------------------------------------*/
510 /**
511 * Draw a dialog popup button object.
512 */
SDLGui_DrawPopupButton(const SGOBJ * pdlg,int objnum)513 static void SDLGui_DrawPopupButton(const SGOBJ *pdlg, int objnum)
514 {
515 int x, y, w;
516 const char *downstr = "\x02";
517
518 SDLGui_DrawBox(pdlg, objnum);
519
520 x = (pdlg[0].x + pdlg[objnum].x) * sdlgui_fontwidth;
521 y = (pdlg[0].y + pdlg[objnum].y) * sdlgui_fontheight;
522 w = pdlg[objnum].w*sdlgui_fontwidth;
523 //h = pdlg[objnum].h*sdlgui_fontheight;
524
525 SDLGui_Text(x, y, pdlg[objnum].txt);
526 SDLGui_Text(x+w-sdlgui_fontwidth, y, downstr);
527 }
528
529
530 /*-----------------------------------------------------------------------*/
531 /**
532 * Let the user insert text into an edit field object.
533 * NOTE: The dlg[objnum].txt must point to an an array that is big enough
534 * for dlg[objnum].w characters!
535 */
SDLGui_EditField(SGOBJ * dlg,int objnum)536 static void SDLGui_EditField(SGOBJ *dlg, int objnum)
537 {
538 size_t cursorPos; /* Position of the cursor in the edit field */
539 int blinkState = 0; /* Used for cursor blinking */
540 int bStopEditing = false; /* true if user wants to exit the edit field */
541 char *txt; /* Shortcut for dlg[objnum].txt */
542 SDL_Rect rect;
543 Uint32 grey, cursorCol;
544 SDL_Event event;
545 #if !WITH_SDL2
546 int nOldUnicodeMode;
547 #endif
548
549 grey = SDL_MapRGB(pSdlGuiScrn->format, 192, 192, 192);
550 cursorCol = SDL_MapRGB(pSdlGuiScrn->format, 128, 128, 128);
551
552 rect.x = (dlg[0].x + dlg[objnum].x) * sdlgui_fontwidth;
553 rect.y = (dlg[0].y + dlg[objnum].y) * sdlgui_fontheight;
554 rect.w = (dlg[objnum].w + 1) * sdlgui_fontwidth - 1;
555 rect.h = dlg[objnum].h * sdlgui_fontheight;
556
557 #if WITH_SDL2
558 SDL_SetTextInputRect(&rect);
559 SDL_StartTextInput();
560 #else
561 /* Enable unicode translation to get proper characters with SDL_PollEvent */
562 nOldUnicodeMode = SDL_EnableUNICODE(true);
563 #endif
564
565 txt = dlg[objnum].txt;
566 cursorPos = strlen(txt);
567
568 do
569 {
570 /* Look for events */
571 if (SDL_PollEvent(&event) == 0)
572 {
573 /* No event: Wait some time for cursor blinking */
574 SDL_Delay(250);
575 blinkState ^= 1;
576 }
577 else
578 {
579 /* Handle events */
580 do
581 {
582 switch (event.type)
583 {
584 case SDL_QUIT: /* User wants to quit */
585 // bQuitProgram = true;
586 bStopEditing = true;
587 break;
588 case SDL_MOUSEBUTTONDOWN: /* Mouse pressed -> stop editing */
589 bStopEditing = true;
590 break;
591 #if WITH_SDL2
592 case SDL_TEXTINPUT:
593 if (strlen(txt) < (size_t)dlg[objnum].w)
594 {
595 memmove(&txt[cursorPos+1], &txt[cursorPos], strlen(&txt[cursorPos])+1);
596 txt[cursorPos] = event.text.text[0];
597 cursorPos += 1;
598 }
599 break;
600 #endif
601 case SDL_KEYDOWN: /* Key pressed */
602 switch (event.key.keysym.sym)
603 {
604 case SDLK_RETURN:
605 case SDLK_KP_ENTER:
606 bStopEditing = true;
607 break;
608 case SDLK_LEFT:
609 if (cursorPos > 0)
610 cursorPos -= 1;
611 break;
612 case SDLK_RIGHT:
613 if (cursorPos < strlen(txt))
614 cursorPos += 1;
615 break;
616 case SDLK_BACKSPACE:
617 if (cursorPos > 0)
618 {
619 memmove(&txt[cursorPos-1], &txt[cursorPos], strlen(&txt[cursorPos])+1);
620 cursorPos -= 1;
621 }
622 break;
623 case SDLK_DELETE:
624 if (cursorPos < strlen(txt))
625 memmove(&txt[cursorPos], &txt[cursorPos+1], strlen(&txt[cursorPos+1])+1);
626 break;
627 default:
628 #if !WITH_SDL2
629 /* If it is a "good" key then insert it into the text field */
630 if (event.key.keysym.unicode >= 32 && event.key.keysym.unicode < 128
631 /* && event.key.keysym.unicode != PATHSEP*/)
632 {
633 if (strlen(txt) < (size_t)dlg[objnum].w)
634 {
635 memmove(&txt[cursorPos+1], &txt[cursorPos], strlen(&txt[cursorPos])+1);
636 txt[cursorPos] = event.key.keysym.unicode;
637 cursorPos += 1;
638 }
639 }
640 #endif
641 break;
642 }
643 break;
644 }
645 }
646 while (SDL_PollEvent(&event));
647
648 blinkState = 1;
649 }
650
651 /* Redraw the text field: */
652 SDL_FillRect(pSdlGuiScrn, &rect, grey); /* Draw background */
653 /* Draw the cursor: */
654 if (blinkState && !bStopEditing)
655 {
656 SDL_Rect cursorrect;
657 cursorrect.x = rect.x + cursorPos * sdlgui_fontwidth;
658 cursorrect.y = rect.y;
659 cursorrect.w = sdlgui_fontwidth;
660 cursorrect.h = rect.h;
661 SDL_FillRect(pSdlGuiScrn, &cursorrect, cursorCol);
662 }
663 SDLGui_Text(rect.x, rect.y, dlg[objnum].txt); /* Draw text */
664 SDL_UpdateRects(pSdlGuiScrn, 1, &rect);
665 }
666 while (!bStopEditing);
667
668 #if WITH_SDL2
669 SDL_StopTextInput();
670 #else
671 SDL_EnableUNICODE(nOldUnicodeMode);
672 #endif
673 }
674
675
676 /**
677 * Draw a user object.
678 */
SDLGui_DrawUserObj(const SGOBJ * bdlg,int objnum)679 static void SDLGui_DrawUserObj(const SGOBJ *bdlg, int objnum)
680 {
681 int x, y, w, h;
682 char (*userfun)(int x, int y, int w, int h);
683
684 x = bdlg[objnum].x*sdlgui_fontwidth;
685 y = bdlg[objnum].y*sdlgui_fontheight;
686
687 /* add absolute coordinates */
688 x += bdlg[0].x*sdlgui_fontwidth;
689 y += bdlg[0].y*sdlgui_fontheight;
690
691 w = bdlg[objnum].w*sdlgui_fontwidth;
692 h = bdlg[objnum].h*sdlgui_fontheight;
693
694 userfun = (void*)bdlg[objnum].txt;
695
696 userfun(x, y, w, h);
697 }
698
699
700 /*-----------------------------------------------------------------------*/
701 /**
702 * Draw a whole dialog.
703 */
SDLGui_DrawDialog(const SGOBJ * dlg)704 void SDLGui_DrawDialog(const SGOBJ *dlg)
705 {
706 int i;
707
708 for (i = 0; dlg[i].type != -1; i++)
709 {
710 switch (dlg[i].type)
711 {
712 case SGBOX:
713 SDLGui_DrawBox(dlg, i);
714 break;
715 case SGTEXT:
716 SDLGui_DrawText(dlg, i);
717 break;
718 case SGEDITFIELD:
719 SDLGui_DrawEditField(dlg, i);
720 break;
721 case SGBUTTON:
722 SDLGui_DrawButton(dlg, i);
723 break;
724 case SGRADIOBUT:
725 SDLGui_DrawRadioButton(dlg, i);
726 break;
727 case SGCHECKBOX:
728 SDLGui_DrawCheckBox(dlg, i);
729 break;
730 case SGPOPUP:
731 SDLGui_DrawPopupButton(dlg, i);
732 break;
733 case SGSCROLLBAR:
734 SDLGui_DrawScrollbar(dlg, i);
735 break;
736 case SGUSER:
737 SDLGui_DrawUserObj(dlg, i);
738 break;
739 }
740 }
741 SDL_UpdateRect(pSdlGuiScrn, 0,0,0,0);
742 }
743
744
745 /*-----------------------------------------------------------------------*/
746 /**
747 * Search an object at a certain position.
748 */
SDLGui_FindObj(const SGOBJ * dlg,int fx,int fy)749 static int SDLGui_FindObj(const SGOBJ *dlg, int fx, int fy)
750 {
751 int len, i;
752 int ob = -1;
753 int xpos, ypos;
754
755 len = 0;
756 while (dlg[len].type != -1) len++;
757
758 xpos = fx / sdlgui_fontwidth;
759 ypos = fy / sdlgui_fontheight;
760 /* Now search for the object: */
761 for (i = len; i >= 0; i--)
762 {
763 /* clicked on a scrollbar ? */
764 if (dlg[i].type == SGSCROLLBAR) {
765 if (xpos >= dlg[0].x+dlg[i].x && xpos < dlg[0].x+dlg[i].x+1) {
766 ypos = dlg[i].y * sdlgui_fontheight + dlg[i].h + dlg[0].y * sdlgui_fontheight;
767 if (fy >= ypos && fy < ypos + dlg[i].w) {
768 ob = i;
769 break;
770 }
771 }
772 }
773 /* clicked on another object ? */
774 else if (xpos >= dlg[0].x+dlg[i].x && ypos >= dlg[0].y+dlg[i].y
775 && xpos < dlg[0].x+dlg[i].x+dlg[i].w && ypos < dlg[0].y+dlg[i].y+dlg[i].h)
776 {
777 ob = i;
778 break;
779 }
780 }
781
782 return ob;
783 }
784
785
786 /*-----------------------------------------------------------------------*/
787 /**
788 * Search a button with a special flag (e.g. SG_DEFAULT or SG_CANCEL).
789 */
SDLGui_SearchFlaggedButton(const SGOBJ * dlg,int flag)790 static int SDLGui_SearchFlaggedButton(const SGOBJ *dlg, int flag)
791 {
792 int i = 0;
793
794 while (dlg[i].type != -1)
795 {
796 if (dlg[i].flags & flag)
797 return i;
798 i++;
799 }
800
801 return 0;
802 }
803
804
805 /*-----------------------------------------------------------------------*/
806 /**
807 * Show and process a dialog. Returns the button number that has been
808 * pressed or SDLGUI_UNKNOWNEVENT if an unsupported event occured (will be
809 * stored in parameter pEventOut).
810 */
SDLGui_DoDialog(SGOBJ * dlg,SDL_Event * pEventOut)811 int SDLGui_DoDialog(SGOBJ *dlg, SDL_Event *pEventOut)
812 {
813 int obj=0;
814 int oldbutton=0;
815 int retbutton=0;
816 int i, j, b;
817 SDL_Event sdlEvent;
818 SDL_Rect rct;
819 Uint32 grey;
820 SDL_Surface *pBgSurface;
821 SDL_Rect dlgrect, bgrect;
822
823 if (pSdlGuiScrn->h / sdlgui_fontheight < dlg[0].h)
824 {
825 fprintf(stderr, "Screen size too small for dialog!\n");
826 return SDLGUI_ERROR;
827 }
828
829 grey = SDL_MapRGB(pSdlGuiScrn->format,192,192,192);
830
831 dlgrect.x = dlg[0].x * sdlgui_fontwidth;
832 dlgrect.y = dlg[0].y * sdlgui_fontheight;
833 dlgrect.w = dlg[0].w * sdlgui_fontwidth;
834 dlgrect.h = dlg[0].h * sdlgui_fontheight;
835
836 bgrect.x = bgrect.y = 0;
837 bgrect.w = dlgrect.w;
838 bgrect.h = dlgrect.h;
839
840 /* Save background */
841 pBgSurface = SDL_CreateRGBSurface(SDL_SWSURFACE, dlgrect.w, dlgrect.h, pSdlGuiScrn->format->BitsPerPixel,
842 pSdlGuiScrn->format->Rmask, pSdlGuiScrn->format->Gmask, pSdlGuiScrn->format->Bmask, pSdlGuiScrn->format->Amask);
843 if (pSdlGuiScrn->format->palette != NULL)
844 {
845 SDL_SetColors(pBgSurface, pSdlGuiScrn->format->palette->colors, 0, pSdlGuiScrn->format->palette->ncolors-1);
846 }
847
848 if (pBgSurface != NULL)
849 {
850 SDL_BlitSurface(pSdlGuiScrn, &dlgrect, pBgSurface, &bgrect);
851 }
852 else
853 {
854 fprintf(stderr, "SDLGUI_DoDialog: CreateRGBSurface failed: %s\n", SDL_GetError());
855 }
856
857 /* (Re-)draw the dialog */
858 SDLGui_DrawDialog(dlg);
859
860 /* Is the left mouse button still pressed? Yes -> Handle TOUCHEXIT objects here */
861 SDL_PumpEvents();
862 b = SDL_GetMouseState(&i, &j);
863
864 /* If current object is the scrollbar, and mouse is still down, we can scroll it */
865 /* also if the mouse pointer has left the scrollbar */
866 if (dlg[current_object].type == SGSCROLLBAR) {
867 if (b & SDL_BUTTON(1)) {
868 obj = current_object;
869 dlg[obj].state |= SG_MOUSEDOWN;
870 oldbutton = obj;
871 retbutton = obj;
872 }
873 else {
874 obj = current_object;
875 current_object = 0;
876 dlg[obj].state &= SG_MOUSEUP;
877 retbutton = obj;
878 oldbutton = obj;
879 }
880 }
881 else {
882 obj = SDLGui_FindObj(dlg, i, j);
883 current_object = obj;
884 if (obj > 0 && (dlg[obj].flags&SG_TOUCHEXIT) )
885 {
886 oldbutton = obj;
887 if (b & SDL_BUTTON(1))
888 {
889 dlg[obj].state |= SG_SELECTED;
890 retbutton = obj;
891 }
892 }
893 }
894
895
896 /* The main loop */
897 while (retbutton == 0 /*&& !bQuitProgram*/)
898 {
899 if (SDL_WaitEvent(&sdlEvent) == 1) /* Wait for events */
900
901 switch (sdlEvent.type)
902 {
903 case SDL_QUIT:
904 retbutton = SDLGUI_QUIT;
905 break;
906
907 case SDL_MOUSEBUTTONDOWN:
908 if (sdlEvent.button.button != SDL_BUTTON_LEFT)
909 {
910 /* Not left mouse button -> unsupported event */
911 if (pEventOut)
912 retbutton = SDLGUI_UNKNOWNEVENT;
913 break;
914 }
915 /* It was the left button: Find the object under the mouse cursor */
916 obj = SDLGui_FindObj(dlg, sdlEvent.button.x, sdlEvent.button.y);
917 if (obj>0)
918 {
919 if (dlg[obj].type==SGBUTTON)
920 {
921 dlg[obj].state |= SG_SELECTED;
922 SDLGui_DrawButton(dlg, obj);
923 SDL_UpdateRect(pSdlGuiScrn, (dlg[0].x+dlg[obj].x)*sdlgui_fontwidth-2, (dlg[0].y+dlg[obj].y)*sdlgui_fontheight-2,
924 dlg[obj].w*sdlgui_fontwidth+4, dlg[obj].h*sdlgui_fontheight+4);
925 oldbutton=obj;
926 }
927 if (dlg[obj].type==SGSCROLLBAR)
928 {
929 dlg[obj].state |= SG_MOUSEDOWN;
930 oldbutton=obj;
931 }
932 if ( dlg[obj].flags&SG_TOUCHEXIT )
933 {
934 dlg[obj].state |= SG_SELECTED;
935 retbutton = obj;
936 }
937 }
938 break;
939
940 case SDL_MOUSEBUTTONUP:
941 if (sdlEvent.button.button != SDL_BUTTON_LEFT)
942 {
943 /* Not left mouse button -> unsupported event */
944 if (pEventOut)
945 retbutton = SDLGUI_UNKNOWNEVENT;
946 break;
947 }
948 /* It was the left button: Find the object under the mouse cursor */
949 obj = SDLGui_FindObj(dlg, sdlEvent.button.x, sdlEvent.button.y);
950 if (obj>0)
951 {
952 switch (dlg[obj].type)
953 {
954 case SGBUTTON:
955 if (oldbutton==obj)
956 retbutton=obj;
957 break;
958 case SGSCROLLBAR:
959 dlg[obj].state &= SG_MOUSEUP;
960
961 if (oldbutton==obj)
962 retbutton=obj;
963 break;
964 case SGEDITFIELD:
965 SDLGui_EditField(dlg, obj);
966 break;
967 case SGRADIOBUT:
968 for (i = obj-1; i > 0 && dlg[i].type == SGRADIOBUT; i--)
969 {
970 dlg[i].state &= ~SG_SELECTED; /* Deselect all radio buttons in this group */
971 rct.x = (dlg[0].x+dlg[i].x)*sdlgui_fontwidth;
972 rct.y = (dlg[0].y+dlg[i].y)*sdlgui_fontheight;
973 rct.w = sdlgui_fontwidth;
974 rct.h = sdlgui_fontheight;
975 SDL_FillRect(pSdlGuiScrn, &rct, grey); /* Clear old */
976 SDLGui_DrawRadioButton(dlg, i);
977 SDL_UpdateRects(pSdlGuiScrn, 1, &rct);
978 }
979 for (i = obj+1; dlg[i].type == SGRADIOBUT; i++)
980 {
981 dlg[i].state &= ~SG_SELECTED; /* Deselect all radio buttons in this group */
982 rct.x = (dlg[0].x+dlg[i].x)*sdlgui_fontwidth;
983 rct.y = (dlg[0].y+dlg[i].y)*sdlgui_fontheight;
984 rct.w = sdlgui_fontwidth;
985 rct.h = sdlgui_fontheight;
986 SDL_FillRect(pSdlGuiScrn, &rct, grey); /* Clear old */
987 SDLGui_DrawRadioButton(dlg, i);
988 SDL_UpdateRects(pSdlGuiScrn, 1, &rct);
989 }
990 dlg[obj].state |= SG_SELECTED; /* Select this radio button */
991 rct.x = (dlg[0].x+dlg[obj].x)*sdlgui_fontwidth;
992 rct.y = (dlg[0].y+dlg[obj].y)*sdlgui_fontheight;
993 rct.w = sdlgui_fontwidth;
994 rct.h = sdlgui_fontheight;
995 SDL_FillRect(pSdlGuiScrn, &rct, grey); /* Clear old */
996 SDLGui_DrawRadioButton(dlg, obj);
997 SDL_UpdateRects(pSdlGuiScrn, 1, &rct);
998 break;
999 case SGCHECKBOX:
1000 dlg[obj].state ^= SG_SELECTED;
1001 rct.x = (dlg[0].x+dlg[obj].x)*sdlgui_fontwidth;
1002 rct.y = (dlg[0].y+dlg[obj].y)*sdlgui_fontheight;
1003 rct.w = sdlgui_fontwidth;
1004 rct.h = sdlgui_fontheight;
1005 SDL_FillRect(pSdlGuiScrn, &rct, grey); /* Clear old */
1006 SDLGui_DrawCheckBox(dlg, obj);
1007 SDL_UpdateRects(pSdlGuiScrn, 1, &rct);
1008 break;
1009 case SGPOPUP:
1010 dlg[obj].state |= SG_SELECTED;
1011 SDLGui_DrawPopupButton(dlg, obj);
1012 SDL_UpdateRect(pSdlGuiScrn, (dlg[0].x+dlg[obj].x)*sdlgui_fontwidth-2, (dlg[0].y+dlg[obj].y)*sdlgui_fontheight-2,
1013 dlg[obj].w*sdlgui_fontwidth+4, dlg[obj].h*sdlgui_fontheight+4);
1014 retbutton=obj;
1015 break;
1016 }
1017 }
1018 if (oldbutton > 0 && dlg[oldbutton].type == SGBUTTON)
1019 {
1020 dlg[oldbutton].state &= ~SG_SELECTED;
1021 SDLGui_DrawButton(dlg, oldbutton);
1022 SDL_UpdateRect(pSdlGuiScrn, (dlg[0].x+dlg[oldbutton].x)*sdlgui_fontwidth-2, (dlg[0].y+dlg[oldbutton].y)*sdlgui_fontheight-2,
1023 dlg[oldbutton].w*sdlgui_fontwidth+4, dlg[oldbutton].h*sdlgui_fontheight+4);
1024 oldbutton = 0;
1025 }
1026 if (obj >= 0 && (dlg[obj].flags&SG_EXIT))
1027 {
1028 retbutton = obj;
1029 }
1030 break;
1031
1032 case SDL_JOYAXISMOTION:
1033 case SDL_JOYBALLMOTION:
1034 case SDL_JOYHATMOTION:
1035 case SDL_MOUSEMOTION:
1036 break;
1037
1038 case SDL_KEYDOWN: /* Key pressed */
1039 if (sdlEvent.key.keysym.sym == SDLK_RETURN
1040 || sdlEvent.key.keysym.sym == SDLK_KP_ENTER)
1041 {
1042 retbutton = SDLGui_SearchFlaggedButton(dlg, SG_DEFAULT);
1043 }
1044 else if (sdlEvent.key.keysym.sym == SDLK_ESCAPE)
1045 {
1046 retbutton = SDLGui_SearchFlaggedButton(dlg, SG_CANCEL);
1047 }
1048 else if (pEventOut)
1049 {
1050 retbutton = SDLGUI_UNKNOWNEVENT;
1051 }
1052 break;
1053
1054 default:
1055 if (pEventOut)
1056 retbutton = SDLGUI_UNKNOWNEVENT;
1057 break;
1058 }
1059 }
1060
1061 /* Restore background */
1062 if (pBgSurface)
1063 {
1064 SDL_BlitSurface(pBgSurface, &bgrect, pSdlGuiScrn, &dlgrect);
1065 SDL_FreeSurface(pBgSurface);
1066 }
1067
1068 /* Copy event data of unsupported events if caller wants to have it */
1069 if (retbutton == SDLGUI_UNKNOWNEVENT && pEventOut)
1070 memcpy(pEventOut, &sdlEvent, sizeof(SDL_Event));
1071
1072 //if (retbutton == SDLGUI_QUIT)
1073 // bQuitProgram = true;
1074
1075 bt = 0; // FIXME: Hack
1076
1077 return retbutton;
1078 }
1079