webperimental: Mountain vision bonus.
[freeciv.git] / client / gui-sdl2 / gui_string.c
blob9511b32a6cd72ed94d765bd93125a304bf6e14d9
1 /***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
14 /***********************************************************************
15 gui_string.c - description
16 -------------------
17 begin : June 30 2002
18 copyright : (C) 2002 by Rafał Bursig
19 email : Rafał Bursig <bursig@poczta.fm>
20 ***********************************************************************/
22 #ifdef HAVE_CONFIG_H
23 #include <fc_config.h>
24 #endif
26 /* SDL2 */
27 #ifdef SDL2_PLAIN_INCLUDE
28 #include <SDL.h>
29 #else /* SDL2_PLAIN_INCLUDE */
30 #include <SDL2/SDL.h>
31 #endif /* SDL2_PLAIN_INCLUDE */
33 /* utility */
34 #include "fcintl.h"
35 #include "log.h"
36 #include "mem.h"
38 /* client/gui-sdl2 */
39 #include "colors.h"
40 #include "graphics.h"
41 #include "gui_iconv.h"
42 #include "gui_main.h"
43 #include "themespec.h"
44 #include "unistring.h"
45 #include "utf8string.h"
47 #include "gui_string.h"
49 /* =================================================== */
51 static struct TTF_Font_Chain {
52 struct TTF_Font_Chain *next;
53 TTF_Font *font;
54 Uint16 ptsize; /* size of font */
55 Uint16 count; /* number of strings alliased with this font */
56 } *Font_TAB = NULL;
58 static unsigned int Sizeof_Font_TAB;
59 static char *pFont_with_FullPath = NULL;
61 static TTF_Font *load_font(Uint16 ptsize);
63 static SDL_Surface *create_utf8_surf(utf8_str *pstr);
64 static SDL_Surface *create_utf8_multi_surf(utf8_str *pstr);
66 /**************************************************************************
67 Adjust font sizes for small screen.
68 **************************************************************************/
69 #ifdef SMALL_SCREEN
70 int adj_font(int size) {
71 switch(size) {
72 case 24:
73 return 12;
74 case 20:
75 return 12;
76 case 16:
77 return 10;
78 case 14:
79 return 8;
80 case 13:
81 return 8;
82 case 12:
83 return 8;
84 case 11:
85 return 7;
86 case 10:
87 return 7;
88 case 8:
89 return 6;
90 default:
91 return size;
94 #endif /* SMALL_SCREEN */
96 /**************************************************************************
97 Calculate display size of string.
98 **************************************************************************/
99 void utf8_str_size(utf8_str *pstr, SDL_Rect *fill)
101 if (pstr != NULL && pstr->text != NULL && pstr->text != '\0') {
102 char *current = pstr->text;
103 char c = *current;
104 bool new_line = FALSE;
105 int w, h;
107 /* find '\n' */
108 while (c != '\0') {
109 if (c == '\n') {
110 new_line = TRUE;
111 break;
113 current++;
114 c = *current;
117 if (!((pstr->style & 0x0F) & TTF_STYLE_NORMAL)) {
118 TTF_SetFontStyle(pstr->font, (pstr->style & 0x0F));
121 if (new_line) {
122 int ww, hh, count = 0;
123 char **utf8_texts = create_new_line_utf8strs(pstr->text);
125 w = 0;
126 h = 0;
127 while (utf8_texts[count]) {
128 if (TTF_SizeUTF8(pstr->font, utf8_texts[count], &ww, &hh) < 0) {
129 do {
130 FC_FREE(utf8_texts[count]);
131 count++;
132 } while (utf8_texts[count]);
133 log_error("TTF_SizeUTF8() return ERROR !");
135 w = MAX(w, ww);
136 h += hh;
137 FC_FREE(utf8_texts[count]);
138 count++;
140 } else {
141 if (TTF_SizeUTF8(pstr->font, pstr->text, &w, &h) < 0) {
142 log_error("TTF_SizeUTF8() return ERROR !");
146 if (!((pstr->style & 0x0F) & TTF_STYLE_NORMAL)) {
147 TTF_SetFontStyle(pstr->font, TTF_STYLE_NORMAL);
150 fill->w = w;
151 fill->h = h;
152 } else {
153 fill->h = (pstr ? TTF_FontHeight(pstr->font) : 0);
157 /**************************************************************************
158 Create utf8_str struct with ptsize font.
159 Font will be loaded or aliased with existing font of that size.
160 pInTextString must be allocated in memory (MALLOC/fc_calloc)
161 **************************************************************************/
162 utf8_str *create_utf8_str(char *in_text, size_t n_alloc, Uint16 ptsize)
164 utf8_str *str = fc_calloc(1, sizeof(utf8_str));
166 if (!ptsize) {
167 str->ptsize = theme_default_font_size(theme);
168 } else {
169 str->ptsize = ptsize;
172 if ((str->font = load_font(str->ptsize)) == NULL) {
173 log_error("create_utf8_str(): load_font failed");
174 FC_FREE(str);
176 return NULL;
179 str->style = TTF_STYLE_NORMAL;
180 str->bgcol = (SDL_Color) {0, 0, 0, 0};
181 str->fgcol = *get_theme_color(COLOR_THEME_TEXT);
182 str->render = 2;
184 /* pInTextString must be allocated in memory (MALLOC/fc_calloc) */
185 str->text = in_text;
186 str->n_alloc = n_alloc;
188 return str;
191 /**************************************************************************
192 Convert char array to utf8_str. Pointer to target string is needed
193 as parameter, but also returned for convenience.
194 **************************************************************************/
195 utf8_str *copy_chars_to_utf8_str(utf8_str *pstr, const char *pchars)
197 size_t n;
199 fc_assert_ret_val(pstr != NULL, NULL);
200 fc_assert_ret_val(pchars != NULL, NULL);
202 n = (strlen(pchars) + 1);
204 if (n > pstr->n_alloc) {
205 /* allocated more if this is only a small increase on before: */
206 size_t n1 = (3 * pstr->n_alloc) / 2;
208 pstr->n_alloc = (n > n1) ? n : n1;
209 pstr->text = fc_realloc(pstr->text, pstr->n_alloc);
212 fc_snprintf(pstr->text, pstr->n_alloc, "%s", pchars);
214 return pstr;
217 /**************************************************************************
218 Blit text to surface.
219 **************************************************************************/
220 int write_utf8(SDL_Surface *dest, Sint16 x, Sint16 y,
221 utf8_str *pstr)
223 SDL_Rect dst_rect = { x, y, 0, 0 };
224 SDL_Surface *text = create_text_surf_from_utf8(pstr);
226 if (alphablit(text, NULL, dest, &dst_rect, 255) < 0) {
227 log_error("write_utf8(): couldn't blit text to display: %s",
228 SDL_GetError());
229 FREESURFACE(text);
230 return -1;
233 FREESURFACE(text);
235 return 0;
238 /**************************************************************************
239 Create Text Surface from utf8_str
240 **************************************************************************/
241 static SDL_Surface *create_utf8_surf(utf8_str *pstr)
243 SDL_Surface *text = NULL;
245 if (pstr == NULL) {
246 return NULL;
249 if (!((pstr->style & 0x0F) & TTF_STYLE_NORMAL)) {
250 TTF_SetFontStyle(pstr->font, (pstr->style & 0x0F));
253 switch (pstr->render) {
254 case 0:
255 text = TTF_RenderUTF8_Shaded(pstr->font,
256 pstr->text, pstr->fgcol,
257 pstr->bgcol);
258 break;
259 case 1:
260 text = TTF_RenderUTF8_Solid(pstr->font, pstr->text, pstr->fgcol);
261 break;
262 case 2:
263 text = TTF_RenderUTF8_Blended(pstr->font, pstr->text, pstr->fgcol);
264 break;
267 if (text != NULL) {
268 log_debug("create_utf8_surf: Font is generally %d big, and "
269 "string is %d big", TTF_FontHeight(pstr->font), text->h);
270 log_debug("create_utf8_surf: String is %d length", text->w);
271 } else {
272 log_debug("create_utf8_surf: text NULL");
273 text = create_surf(0, 0, SDL_SWSURFACE);
276 if (!((pstr->style & 0x0F) & TTF_STYLE_NORMAL)) {
277 TTF_SetFontStyle(pstr->font, TTF_STYLE_NORMAL);
280 return text;
283 /**************************************************************************
284 Create surface with multiline text drawn.
285 **************************************************************************/
286 static SDL_Surface *create_utf8_multi_surf(utf8_str *pstr)
288 SDL_Rect des = {0, 0, 0, 0};
289 SDL_Surface *text = NULL, **tmp = NULL;
290 Uint16 i, w = 0, count = 0;
291 Uint32 color;
292 char *buf = pstr->text;
293 char **utf8_texts = create_new_line_utf8strs(pstr->text);
295 while (utf8_texts[count]) {
296 count++;
299 tmp = fc_calloc(count, sizeof(SDL_Surface *));
301 for (i = 0; i < count; i++) {
302 pstr->text = utf8_texts[i];
303 tmp[i] = create_utf8_surf(pstr);
305 /* find max len */
306 if (tmp[i]->w > w) {
307 w = tmp[i]->w;
311 pstr->text = buf;
313 /* create and fill surface */
315 SDL_GetColorKey(tmp[0], &color);
317 switch (pstr->render) {
318 case 1:
319 text = create_surf(w, count * tmp[0]->h, SDL_SWSURFACE);
320 SDL_FillRect(text, NULL, color);
321 SDL_SetColorKey(text, SDL_TRUE, color);
322 break;
323 case 2:
324 text = create_surf_with_format(tmp[0]->format,
325 w, count * tmp[0]->h, tmp[0]->flags);
326 SDL_FillRect(text, NULL, color);
327 break;
328 default:
329 text = create_surf(w, count * tmp[0]->h, SDL_SWSURFACE);
330 SDL_FillRect(text, NULL, color);
331 break;
334 /* blit (default: center left) */
335 for (i = 0; i < count; i++) {
336 if (pstr->style & SF_CENTER) {
337 des.x = (w - tmp[i]->w) / 2;
338 } else {
339 if (pstr->style & SF_CENTER_RIGHT) {
340 des.x = w - tmp[i]->w;
341 } else {
342 des.x = 0;
346 alphablit(tmp[i], NULL, text, &des, 255);
347 des.y += tmp[i]->h;
351 /* Free Memmory */
352 for (i = 0; i < count; i++) {
353 FC_FREE(utf8_texts[i]);
354 FREESURFACE(tmp[i]);
357 FC_FREE(tmp);
359 return text;
362 /**************************************************************************
363 Generic function to create surface with any kind of text, single line or
364 multiline, drawn.
365 **************************************************************************/
366 SDL_Surface *create_text_surf_from_utf8(utf8_str *pstr)
368 if (pstr != NULL && pstr->text != NULL) {
369 char *current = pstr->text;
370 char c = *(pstr->text);
372 /* find '\n' */
373 while (c != '\0') {
374 if (c == '\n') {
375 return create_utf8_multi_surf(pstr);
377 current++;
378 c = *current;
381 return create_utf8_surf(pstr);
384 return NULL;
387 /**************************************************************************
388 Wrap text to make it fit to given screen width.
389 **************************************************************************/
390 bool convert_utf8_str_to_const_surface_width(utf8_str *pstr, int width)
392 SDL_Rect size;
393 bool converted = FALSE;
395 fc_assert_ret_val(pstr != NULL, FALSE);
396 fc_assert_ret_val(pstr->text != NULL, FALSE);
398 utf8_str_size(pstr, &size);
399 if (size.w > width) {
400 /* cut string length to w length by replacing space " " with new line "\n" */
401 bool resize = FALSE;
402 int len = 0;
403 char *ptr_rev, *ptr = pstr->text;
405 converted = TRUE;
407 do {
408 if (!resize) {
409 char utf8char[9];
410 int i;
411 int fw, fh;
413 if (*ptr == '\0') {
414 resize = TRUE;
415 continue;
418 if (*ptr == '\n') {
419 len = 0;
420 ptr++;
421 continue;
424 utf8char[0] = ptr[0];
425 for (i = 1; i < 8 && (ptr[i] & (128 + 64)) == 128; i++) {
426 utf8char[i] = ptr[i];
428 utf8char[i] = '\0';
429 if (!((pstr->style & 0x0F) & TTF_STYLE_NORMAL)) {
430 TTF_SetFontStyle(pstr->font, (pstr->style & 0x0F));
432 TTF_SizeUTF8(pstr->font, utf8char, &fw, &fh);
433 if (!((pstr->style & 0x0F) & TTF_STYLE_NORMAL)) {
434 TTF_SetFontStyle(pstr->font, TTF_STYLE_NORMAL);
437 len += fw;
439 if (len > width) {
440 ptr_rev = ptr;
441 while (ptr_rev != pstr->text) {
442 if (*ptr_rev == ' ') {
443 *ptr_rev = '\n';
444 utf8_str_size(pstr, &size);
445 len = 0;
446 break;
448 if (*ptr_rev == '\n') {
449 resize = TRUE;
450 break;
452 ptr_rev--;
454 if (ptr_rev == pstr->text) {
455 resize = TRUE;
459 ptr += i;
460 } else {
461 if (pstr->ptsize > 8) {
462 change_ptsize_utf8(pstr, pstr->ptsize - 1);
463 utf8_str_size(pstr, &size);
464 } else {
465 log_error("Can't convert string to const width");
466 break;
470 } while (size.w > width);
473 return converted;
476 /**************************************************************************
477 Create surface with text drawn to it. Wrap text as needed to make it
478 fit in given width.
479 **************************************************************************/
480 SDL_Surface *create_text_surf_smaller_than_w(utf8_str *pstr, int w)
482 int ptsize;
483 SDL_Surface *text;
485 fc_assert_ret_val(pstr != NULL, NULL);
487 ptsize = pstr->ptsize;
488 convert_utf8_str_to_const_surface_width(pstr, w);
489 text = create_text_surf_from_utf8(pstr);
490 if (pstr->ptsize != ptsize) {
491 change_ptsize_utf8(pstr, ptsize);
494 return text;
497 /**************************************************************************
498 Change font size of text.
499 **************************************************************************/
500 void change_ptsize_utf8(utf8_str *pstr, Uint16 new_ptsize)
502 TTF_Font *buf;
504 if (pstr->ptsize == new_ptsize) {
505 return;
508 if ((buf = load_font(new_ptsize)) == NULL) {
509 log_error("change_ptsize: load_font() failed");
510 return;
513 unload_font(pstr->ptsize);
514 pstr->ptsize = new_ptsize;
515 pstr->font = buf;
518 /* =================================================== */
520 /**************************************************************************
521 Load font of given pointsize.
522 **************************************************************************/
523 static TTF_Font *load_font(Uint16 ptsize)
525 struct TTF_Font_Chain *Font_TAB_TMP = Font_TAB;
526 TTF_Font *font_tmp = NULL;
528 /* find existing font and return pointer to it */
529 if (Sizeof_Font_TAB) {
530 while (Font_TAB_TMP) {
531 if (Font_TAB_TMP->ptsize == ptsize) {
532 Font_TAB_TMP->count++;
533 return Font_TAB_TMP->font;
535 Font_TAB_TMP = Font_TAB_TMP->next;
539 if (!pFont_with_FullPath) {
540 const char *path = theme_font_filename(theme);
542 pFont_with_FullPath = fc_strdup(path);
543 fc_assert_ret_val(pFont_with_FullPath != NULL, NULL);
546 /* Load Font */
547 if ((font_tmp = TTF_OpenFont(pFont_with_FullPath, ptsize)) == NULL) {
548 log_error("load_font: Couldn't load %d pt font from %s: %s",
549 ptsize, pFont_with_FullPath, SDL_GetError());
550 return font_tmp;
553 /* add new font to list */
554 if (Sizeof_Font_TAB == 0) {
555 Sizeof_Font_TAB++;
556 Font_TAB_TMP = fc_calloc(1, sizeof(struct TTF_Font_Chain));
557 Font_TAB = Font_TAB_TMP;
558 } else {
559 /* Go to end of chain */
560 Font_TAB_TMP = Font_TAB;
561 while (Font_TAB_TMP->next) {
562 Font_TAB_TMP = Font_TAB_TMP->next;
565 Sizeof_Font_TAB++;
566 Font_TAB_TMP->next = fc_calloc(1, sizeof(struct TTF_Font_Chain));
567 Font_TAB_TMP = Font_TAB_TMP->next;
570 Font_TAB_TMP->ptsize = ptsize;
571 Font_TAB_TMP->count = 1;
572 Font_TAB_TMP->font = font_tmp;
573 Font_TAB_TMP->next = NULL;
575 return font_tmp;
578 /**************************************************************************
579 Free font of given pointsize.
580 **************************************************************************/
581 void unload_font(Uint16 ptsize)
583 int index;
584 struct TTF_Font_Chain *Font_TAB_PREV = NULL;
585 struct TTF_Font_Chain *Font_TAB_TMP = Font_TAB;
587 if (Sizeof_Font_TAB == 0) {
588 log_error("unload_font: Trying unload from empty Font ARRAY");
589 return;
592 for (index = 0; index < Sizeof_Font_TAB; index++) {
593 if (Font_TAB_TMP->ptsize == ptsize) {
594 break;
596 Font_TAB_PREV = Font_TAB_TMP;
597 Font_TAB_TMP = Font_TAB_TMP->next;
600 if (index == Sizeof_Font_TAB) {
601 log_error("unload_font: Trying unload Font which is "
602 "not included in Font ARRAY");
603 return;
606 Font_TAB_TMP->count--;
608 if (Font_TAB_TMP->count) {
609 return;
612 TTF_CloseFont(Font_TAB_TMP->font);
613 Font_TAB_TMP->font = NULL;
614 if (Font_TAB_TMP == Font_TAB) {
615 Font_TAB = Font_TAB_TMP->next;
616 } else {
617 Font_TAB_PREV->next = Font_TAB_TMP->next;
619 Font_TAB_TMP->next = NULL;
620 Sizeof_Font_TAB--;
621 FC_FREE(Font_TAB_TMP);
624 /**************************************************************************
625 Free all fonts.
626 **************************************************************************/
627 void free_font_system(void)
629 struct TTF_Font_Chain *Font_TAB_TMP;
631 FC_FREE(pFont_with_FullPath);
632 while (Font_TAB) {
633 if (Font_TAB->next) {
634 Font_TAB_TMP = Font_TAB;
635 Font_TAB = Font_TAB->next;
636 if (Font_TAB_TMP->font) {
637 TTF_CloseFont(Font_TAB_TMP->font);
639 FC_FREE(Font_TAB_TMP);
640 } else {
641 if (Font_TAB->font) {
642 TTF_CloseFont(Font_TAB->font);
644 FC_FREE(Font_TAB);