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