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)
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
18 copyright : (C) 2002 by Rafał Bursig
19 email : Rafał Bursig <bursig@poczta.fm>
20 ***********************************************************************/
23 #include <fc_config.h>
27 #ifdef SDL2_PLAIN_INCLUDE
29 #else /* SDL2_PLAIN_INCLUDE */
31 #endif /* SDL2_PLAIN_INCLUDE */
41 #include "gui_iconv.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
;
54 Uint16 ptsize
; /* size of font */
55 Uint16 count
; /* number of strings alliased with this font */
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 **************************************************************************/
70 int adj_font(int 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
;
104 bool new_line
= FALSE
;
117 if (!((pstr
->style
& 0x0F) & TTF_STYLE_NORMAL
)) {
118 TTF_SetFontStyle(pstr
->font
, (pstr
->style
& 0x0F));
122 int ww
, hh
, count
= 0;
123 char **utf8_texts
= create_new_line_utf8strs(pstr
->text
);
127 while (utf8_texts
[count
]) {
128 if (TTF_SizeUTF8(pstr
->font
, utf8_texts
[count
], &ww
, &hh
) < 0) {
130 FC_FREE(utf8_texts
[count
]);
132 } while (utf8_texts
[count
]);
133 log_error("TTF_SizeUTF8() return ERROR !");
137 FC_FREE(utf8_texts
[count
]);
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
);
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
));
167 str
->ptsize
= theme_default_font_size(theme
);
169 str
->ptsize
= ptsize
;
172 if ((str
->font
= load_font(str
->ptsize
)) == NULL
) {
173 log_error("create_utf8_str(): load_font failed");
179 str
->style
= TTF_STYLE_NORMAL
;
180 str
->bgcol
= (SDL_Color
) {0, 0, 0, 0};
181 str
->fgcol
= *get_theme_color(COLOR_THEME_TEXT
);
184 /* pInTextString must be allocated in memory (MALLOC/fc_calloc) */
186 str
->n_alloc
= n_alloc
;
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
)
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
);
217 /**************************************************************************
218 Blit text to surface.
219 **************************************************************************/
220 int write_utf8(SDL_Surface
*dest
, Sint16 x
, Sint16 y
,
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",
238 /**************************************************************************
239 Create Text Surface from utf8_str
240 **************************************************************************/
241 static SDL_Surface
*create_utf8_surf(utf8_str
*pstr
)
243 SDL_Surface
*text
= NULL
;
249 if (!((pstr
->style
& 0x0F) & TTF_STYLE_NORMAL
)) {
250 TTF_SetFontStyle(pstr
->font
, (pstr
->style
& 0x0F));
253 switch (pstr
->render
) {
255 text
= TTF_RenderUTF8_Shaded(pstr
->font
,
256 pstr
->text
, pstr
->fgcol
,
260 text
= TTF_RenderUTF8_Solid(pstr
->font
, pstr
->text
, pstr
->fgcol
);
263 text
= TTF_RenderUTF8_Blended(pstr
->font
, pstr
->text
, pstr
->fgcol
);
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
);
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
);
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;
292 char *buf
= pstr
->text
;
293 char **utf8_texts
= create_new_line_utf8strs(pstr
->text
);
295 while (utf8_texts
[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
);
313 /* create and fill surface */
315 SDL_GetColorKey(tmp
[0], &color
);
317 switch (pstr
->render
) {
319 text
= create_surf(w
, count
* tmp
[0]->h
, SDL_SWSURFACE
);
320 SDL_FillRect(text
, NULL
, color
);
321 SDL_SetColorKey(text
, SDL_TRUE
, color
);
324 text
= create_surf_with_format(tmp
[0]->format
,
325 w
, count
* tmp
[0]->h
, tmp
[0]->flags
);
326 SDL_FillRect(text
, NULL
, color
);
329 text
= create_surf(w
, count
* tmp
[0]->h
, SDL_SWSURFACE
);
330 SDL_FillRect(text
, NULL
, color
);
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;
339 if (pstr
->style
& SF_CENTER_RIGHT
) {
340 des
.x
= w
- tmp
[i
]->w
;
346 alphablit(tmp
[i
], NULL
, text
, &des
, 255);
352 for (i
= 0; i
< count
; i
++) {
353 FC_FREE(utf8_texts
[i
]);
362 /**************************************************************************
363 Generic function to create surface with any kind of text, single line or
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
);
375 return create_utf8_multi_surf(pstr
);
381 return create_utf8_surf(pstr
);
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
)
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" */
403 char *ptr_rev
, *ptr
= pstr
->text
;
424 utf8char
[0] = ptr
[0];
425 for (i
= 1; i
< 8 && (ptr
[i
] & (128 + 64)) == 128; i
++) {
426 utf8char
[i
] = ptr
[i
];
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
);
441 while (ptr_rev
!= pstr
->text
) {
442 if (*ptr_rev
== ' ') {
444 utf8_str_size(pstr
, &size
);
448 if (*ptr_rev
== '\n') {
454 if (ptr_rev
== pstr
->text
) {
461 if (pstr
->ptsize
> 8) {
462 change_ptsize_utf8(pstr
, pstr
->ptsize
- 1);
463 utf8_str_size(pstr
, &size
);
465 log_error("Can't convert string to const width");
470 } while (size
.w
> width
);
476 /**************************************************************************
477 Create surface with text drawn to it. Wrap text as needed to make it
479 **************************************************************************/
480 SDL_Surface
*create_text_surf_smaller_than_w(utf8_str
*pstr
, int w
)
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
);
497 /**************************************************************************
498 Change font size of text.
499 **************************************************************************/
500 void change_ptsize_utf8(utf8_str
*pstr
, Uint16 new_ptsize
)
504 if (pstr
->ptsize
== new_ptsize
) {
508 if ((buf
= load_font(new_ptsize
)) == NULL
) {
509 log_error("change_ptsize: load_font() failed");
513 unload_font(pstr
->ptsize
);
514 pstr
->ptsize
= new_ptsize
;
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
);
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());
553 /* add new font to list */
554 if (Sizeof_Font_TAB
== 0) {
556 Font_TAB_TMP
= fc_calloc(1, sizeof(struct TTF_Font_Chain
));
557 Font_TAB
= Font_TAB_TMP
;
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
;
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
;
578 /**************************************************************************
579 Free font of given pointsize.
580 **************************************************************************/
581 void unload_font(Uint16 ptsize
)
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");
592 for (index
= 0; index
< Sizeof_Font_TAB
; index
++) {
593 if (Font_TAB_TMP
->ptsize
== ptsize
) {
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");
606 Font_TAB_TMP
->count
--;
608 if (Font_TAB_TMP
->count
) {
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
;
617 Font_TAB_PREV
->next
= Font_TAB_TMP
->next
;
619 Font_TAB_TMP
->next
= NULL
;
621 FC_FREE(Font_TAB_TMP
);
624 /**************************************************************************
626 **************************************************************************/
627 void free_font_system(void)
629 struct TTF_Font_Chain
*Font_TAB_TMP
;
631 FC_FREE(pFont_with_FullPath
);
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
);
641 if (Font_TAB
->font
) {
642 TTF_CloseFont(Font_TAB
->font
);