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.
7
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 ***********************************************************************/
13
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 ***************************************************************************/
21
22 #ifdef HAVE_CONFIG_H
23 #include <fc_config.h>
24 #endif
25
26 /* SDL */
27 #include <SDL/SDL.h>
28
29 /* utility */
30 #include "fcintl.h"
31 #include "log.h"
32 #include "mem.h"
33
34 /* client/gui-sdl */
35 #include "colors.h"
36 #include "graphics.h"
37 #include "gui_iconv.h"
38 #include "gui_main.h"
39 #include "themespec.h"
40 #include "unistring.h"
41
42 #include "gui_string.h"
43
44 /* =================================================== */
45
46 static struct TTF_Font_Chain {
47 struct TTF_Font_Chain *next;
48 TTF_Font *font;
49 Uint16 ptsize; /* size of font */
50 Uint16 count; /* number of strings alliased with this font */
51 } *Font_TAB = NULL;
52
53 static unsigned int Sizeof_Font_TAB;
54 static char *pFont_with_FullPath = NULL;
55
56 static TTF_Font *load_font(Uint16 ptsize);
57
58 static SDL_Surface *create_str16_surf(SDL_String16 * pString);
59 static SDL_Surface *create_str16_multi_surf(SDL_String16 * pString);
60
61 /**************************************************************************
62 Adjust font sizes for small screen.
63 **************************************************************************/
64 #ifdef SMALL_SCREEN
adj_font(int size)65 int adj_font(int size) {
66 switch(size) {
67 case 24:
68 return 12;
69 case 20:
70 return 12;
71 case 16:
72 return 10;
73 case 14:
74 return 8;
75 case 13:
76 return 8;
77 case 12:
78 return 8;
79 case 11:
80 return 7;
81 case 10:
82 return 7;
83 case 8:
84 return 6;
85 default:
86 return size;
87 }
88 }
89 #endif /* SMALL_SCREEN */
90
91 /**************************************************************************
92 Calculate display size of string.
93 **************************************************************************/
str16size(SDL_String16 * pString16)94 SDL_Rect str16size(SDL_String16 *pString16)
95 {
96 SDL_Rect Ret = {0, 0, 0, 0};
97
98 if (pString16 && pString16->text && pString16->text[0] != '\0') {
99 Uint16 *pStr16 = pString16->text;
100 Uint16 c = *pStr16;
101 bool new_line = FALSE;
102 int w, h;
103
104 /* find '\n' */
105 while (c != '\0') {
106 if (c == 10) {
107 new_line = TRUE;
108 break;
109 }
110 pStr16++;
111 c = *pStr16;
112 }
113
114 if (!((pString16->style & 0x0F) & TTF_STYLE_NORMAL)) {
115 TTF_SetFontStyle(pString16->font, (pString16->style & 0x0F));
116 }
117
118 if (new_line) {
119 int ww, hh, count = 0;
120 Uint16 **UniTexts = create_new_line_unistrings(pString16->text);
121
122 w = 0;
123 h = 0;
124 while (UniTexts[count]) {
125 if (TTF_SizeUNICODE(pString16->font, UniTexts[count], &ww, &hh) < 0) {
126 do {
127 FC_FREE(UniTexts[count]);
128 count++;
129 } while(UniTexts[count]);
130 log_error("TTF_SizeUNICODE return ERROR !");
131 }
132 w = MAX(w, ww);
133 h += hh;
134 FC_FREE(UniTexts[count]);
135 count++;
136 }
137 } else {
138 if (TTF_SizeUNICODE(pString16->font, pString16->text, &w, &h) < 0) {
139 log_error("TTF_SizeUNICODE return ERROR !");
140 }
141 }
142
143 if (!((pString16->style & 0x0F) & TTF_STYLE_NORMAL)) {
144 TTF_SetFontStyle(pString16->font, TTF_STYLE_NORMAL);
145 }
146
147 Ret.w = w;
148 Ret.h = h;
149 } else {
150 Ret.h = (pString16 ? TTF_FontHeight(pString16->font) : 0);
151 }
152
153 return Ret;
154 }
155
156 /**************************************************************************
157 Create string16 struct with ptsize font.
158 Font will be loaded or aliased with existing font of that size.
159 pInTextString must be allocated in memory (MALLOC/fc_calloc)
160 **************************************************************************/
create_string16(Uint16 * pInTextString,size_t n_alloc,Uint16 ptsize)161 SDL_String16 *create_string16(Uint16 *pInTextString,
162 size_t n_alloc, Uint16 ptsize)
163 {
164 SDL_String16 *str = fc_calloc(1, sizeof(SDL_String16));
165
166 if (!ptsize) {
167 str->ptsize = theme_default_font_size(theme);
168 } else {
169 str->ptsize = ptsize;
170 }
171
172 if ((str->font = load_font(str->ptsize)) == NULL) {
173 log_error("create_string16: load_font failed");
174 FC_FREE(str);
175
176 return NULL;
177 }
178
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;
183
184 /* pInTextString must be allocated in memory (MALLOC/fc_calloc) */
185 str->text = pInTextString;
186 str->n_alloc = n_alloc;
187
188 return str;
189 }
190
191 /**************************************************************************
192 Convert char array to SDL_String16. Pointer to target string is needed
193 as parameter, but also returned for convenience.
194 **************************************************************************/
copy_chars_to_string16(SDL_String16 * pString,const char * pCharString)195 SDL_String16 * copy_chars_to_string16(SDL_String16 *pString,
196 const char *pCharString)
197 {
198 size_t n;
199
200 fc_assert_ret_val(pString != NULL, NULL);
201 fc_assert_ret_val(pCharString != NULL, NULL);
202
203 n = (strlen(pCharString) + 1) * 2;
204
205 if (n > pString->n_alloc) {
206 /* allocated more if this is only a small increase on before: */
207 size_t n1 = (3 * pString->n_alloc) / 2;
208 pString->n_alloc = (n > n1) ? n : n1;
209 pString->text = fc_realloc(pString->text, pString->n_alloc);
210 }
211
212 convertcopy_to_utf16(pString->text, pString->n_alloc, pCharString);
213
214 return pString;
215 }
216
217 /**************************************************************************
218 Blit text to surface.
219 **************************************************************************/
write_text16(SDL_Surface * pDest,Sint16 x,Sint16 y,SDL_String16 * pString)220 int write_text16(SDL_Surface * pDest, Sint16 x, Sint16 y,
221 SDL_String16 * pString)
222 {
223 SDL_Rect dst_rect = { x, y, 0, 0 };
224 SDL_Surface *pText = create_text_surf_from_str16(pString);
225
226 if (alphablit(pText, NULL, pDest, &dst_rect) < 0) {
227 log_error("write_text16: couldn't blit text to display: %s",
228 SDL_GetError());
229 FREESURFACE(pText);
230 return -1;
231 }
232
233 FREESURFACE(pText);
234 return 0;
235 }
236
237 /**************************************************************************
238 Create Text Surface from SDL_String16
239 **************************************************************************/
create_str16_surf(SDL_String16 * pString)240 static SDL_Surface *create_str16_surf(SDL_String16 * pString)
241 {
242 SDL_Surface *pText = NULL; /* FIXME: possibly uninitialized */
243
244 if (!pString) {
245 return NULL;
246 }
247
248 if (!((pString->style & 0x0F) & TTF_STYLE_NORMAL)) {
249 TTF_SetFontStyle(pString->font, (pString->style & 0x0F));
250 }
251
252 switch (pString->render) {
253 case 0:
254 pText = TTF_RenderUNICODE_Shaded(pString->font,
255 pString->text, pString->fgcol,
256 pString->bgcol);
257 break;
258 case 1:
259 {
260 SDL_Surface *pTmp = TTF_RenderUNICODE_Solid(pString->font,
261 pString->text, pString->fgcol);
262
263 if ((pText = SDL_DisplayFormat(pTmp)) == NULL) {
264 log_error("SDL_create_str16_surf: couldn't convert text "
265 "to display format: %s", SDL_GetError());
266 pText = pTmp;
267 } else {
268 FREESURFACE( pTmp );
269 }
270
271 }
272 break;
273 case 2:
274 pText = TTF_RenderUNICODE_Blended(pString->font,
275 pString->text, pString->fgcol);
276 break;
277 }
278
279 if (pText != NULL) {
280 log_debug("SDL_create_str16_surf: Font is generally %d big, and "
281 "string is %d big", TTF_FontHeight(pString->font), pText->h);
282 log_debug("SDL_create_str16_surf: String is %d length", pText->w);
283 } else {
284 log_debug("SDL_create_str16_surf: pText NULL");
285 pText = create_surf_alpha(0, 0, SDL_SWSURFACE);
286 }
287
288 if (!((pString->style & 0x0F) & TTF_STYLE_NORMAL)) {
289 TTF_SetFontStyle(pString->font, TTF_STYLE_NORMAL);
290 }
291
292 return pText;
293 }
294
295 /**************************************************************************
296 Create surface with multiline text drawn.
297 **************************************************************************/
create_str16_multi_surf(SDL_String16 * pString)298 static SDL_Surface *create_str16_multi_surf(SDL_String16 * pString)
299 {
300 SDL_Rect des = {0, 0, 0, 0};
301 SDL_Surface *pText = NULL, **pTmp = NULL;
302 Uint16 i, w = 0, count = 0;
303 Uint32 color;
304 Uint16 *pBuf = pString->text;
305 Uint16 **UniTexts = create_new_line_unistrings(pString->text);
306
307 while (UniTexts[count]) {
308 count++;
309 }
310
311 pTmp = fc_calloc(count, sizeof(SDL_Surface *));
312
313 for (i = 0; i < count; i++) {
314 pString->text = UniTexts[i];
315 pTmp[i] = create_str16_surf(pString);
316 }
317
318 pString->text = pBuf;
319
320 /* find max len */
321 for (i = 0; i < count; i++) {
322 if (pTmp[i]->w > w) {
323 w = pTmp[i]->w;
324 }
325 }
326
327 /* create and fill surface */
328
329 color = pTmp[0]->format->colorkey;
330
331 switch (pString->render) {
332 case 1:
333 pText = create_surf(w, count * pTmp[0]->h, SDL_SWSURFACE);
334 SDL_FillRect(pText, NULL, color);
335 SDL_SetColorKey(pText, SDL_SRCCOLORKEY, color);
336 break;
337 case 2:
338 pText = create_surf_with_format(pTmp[0]->format,
339 w, count * pTmp[0]->h, pTmp[0]->flags);
340 SDL_FillRect(pText, NULL, color);
341 break;
342 default:
343 pText = create_surf(w, count * pTmp[0]->h, SDL_SWSURFACE);
344 SDL_FillRect(pText, NULL, color);
345 break;
346 }
347
348 /* blit (default: center left) */
349 for (i = 0; i < count; i++) {
350 if (pString->style & SF_CENTER) {
351 des.x = (w - pTmp[i]->w) / 2;
352 } else {
353 if (pString->style & SF_CENTER_RIGHT) {
354 des.x = w - pTmp[i]->w;
355 } else {
356 des.x = 0;
357 }
358 }
359
360 alphablit(pTmp[i], NULL, pText, &des);
361 des.y += pTmp[i]->h;
362 }
363
364
365 /* Free Memmory */
366 for (i = 0; i < count; i++) {
367 FC_FREE(UniTexts[i]);
368 FREESURFACE(pTmp[i]);
369 }
370
371 FC_FREE(pTmp);
372
373 return pText;
374 }
375
376 /**************************************************************************
377 Generic function to create surface with any kind of text, single line or
378 multiline, drawn.
379 **************************************************************************/
create_text_surf_from_str16(SDL_String16 * pString)380 SDL_Surface *create_text_surf_from_str16(SDL_String16 *pString)
381 {
382 if (pString && pString->text) {
383 Uint16 *pStr16 = pString->text;
384 Uint16 c = *pStr16;
385
386 /* find '\n' */
387 while (c != '\0') {
388 if (c == 10) {
389 return create_str16_multi_surf(pString);
390 }
391 pStr16++;
392 c = *pStr16;
393 }
394
395 return create_str16_surf(pString);
396 }
397
398 return NULL;
399 }
400
401 #if 0
402 /**************************************************************************
403 ...
404 **************************************************************************/
405 SDL_Surface * create_text_surf_smaller_that_w(SDL_String16 *pString, int w)
406 {
407 fc_assert_ret_val(pString != NULL, NULL);
408
409 SDL_Surface *pText = create_text_surf_from_str16(pString);
410
411 if(pText && pText->w > w - 4) {
412 /* cut string length to w length by replacing space " " with new line "\n" */
413 int ptsize = pString->ptsize;
414 Uint16 pNew_Line[2], pSpace[2];
415 Uint16 *ptr = pString->text;
416
417 convertcopy_to_utf16(pNew_Line, sizeof(pNew_Line), "\n");
418 convertcopy_to_utf16(pSpace, sizeof(pSpace), " ");
419
420 do {
421 if (*ptr) {
422 if(*ptr == *pSpace) {
423 *ptr = *pNew_Line;/* "\n" */
424 FREESURFACE(pText);
425 pText = create_text_surf_from_str16(pString);
426 }
427 ptr++;
428 } else {
429 FREESURFACE(pText);
430 if (pString->ptsize > 8) {
431 change_ptsize16(pString, pString->ptsize - 1);
432 pText = create_text_surf_from_str16(pString);
433 } else {
434 fc_assert_ret_val(pText != NULL, NULL);
435 }
436 }
437 } while (pText->w > w - 4);
438
439 if(pString->ptsize != ptsize) {
440 change_ptsize16(pString, ptsize);
441 }
442 }
443
444 return pText;
445 }
446 #endif /* 0 */
447
448 /**************************************************************************
449 Wrap text to make it fit to given screen width.
450 **************************************************************************/
convert_string_to_const_surface_width(SDL_String16 * pString,int width)451 bool convert_string_to_const_surface_width(SDL_String16 *pString,
452 int width)
453 {
454 int w;
455 bool converted = FALSE;
456
457 fc_assert_ret_val(pString != NULL, FALSE);
458 fc_assert_ret_val(pString->text != NULL, FALSE);
459
460 w = str16size(pString).w;
461 if(w > width) {
462 /* cut string length to w length by replacing space " " with new line "\n" */
463 bool resize = FALSE;
464 int len = 0, adv;
465 Uint16 New_Line, Space;
466 Uint16 *ptr_rev, *ptr = pString->text;
467
468 {
469 Uint16 pBuf[2];
470 convertcopy_to_utf16(pBuf, sizeof(pBuf), "\n");
471 New_Line = pBuf[0];
472 convertcopy_to_utf16(pBuf, sizeof(pBuf), " ");
473 Space = pBuf[0];
474 }
475
476 converted = TRUE;
477
478 do {
479 if (!resize) {
480
481 if (*ptr == '\0') {
482 resize = TRUE;
483 continue;
484 }
485
486 if (*ptr == New_Line) {
487 len = 0;
488 ptr++;
489 continue;
490 }
491
492 if (!((pString->style & 0x0F) & TTF_STYLE_NORMAL)) {
493 TTF_SetFontStyle(pString->font, (pString->style & 0x0F));
494 }
495 TTF_GlyphMetrics(pString->font, *ptr, NULL, NULL, NULL, NULL, &adv);
496 if (!((pString->style & 0x0F) & TTF_STYLE_NORMAL)) {
497 TTF_SetFontStyle(pString->font, TTF_STYLE_NORMAL);
498 }
499
500 len += adv;
501
502 if (len > width) {
503 ptr_rev = ptr;
504 while(ptr_rev != pString->text) {
505 if(*ptr_rev == Space) {
506 *ptr_rev = New_Line;/* "\n" */
507 w = str16size(pString).w;
508 len = 0;
509 break;
510 }
511 if(*ptr_rev == New_Line) {
512 resize = TRUE;
513 break;
514 }
515 ptr_rev--;
516 }
517 if (ptr_rev == pString->text) {
518 resize = TRUE;
519 }
520 }
521
522 ptr++;
523 } else {
524 if (pString->ptsize > 8) {
525 change_ptsize16(pString, pString->ptsize - 1);
526 w = str16size(pString).w;
527 } else {
528 log_error("Can't convert string to const width");
529 break;
530 }
531 }
532
533 } while (w > width);
534 }
535
536 return converted;
537 }
538
539 /**************************************************************************
540 Create surface with text drawn to it. Wrap text as needed to make it
541 fit in given width.
542 **************************************************************************/
create_text_surf_smaller_that_w(SDL_String16 * pString,int w)543 SDL_Surface * create_text_surf_smaller_that_w(SDL_String16 *pString, int w)
544 {
545 int ptsize;
546 SDL_Surface *pText;
547
548 fc_assert_ret_val(pString != NULL, NULL);
549
550 ptsize = pString->ptsize;
551 convert_string_to_const_surface_width(pString, w);
552 pText = create_text_surf_from_str16(pString);
553 if(pString->ptsize != ptsize) {
554 change_ptsize16(pString, ptsize);
555 }
556
557 return pText;
558 }
559
560 /**************************************************************************
561 Change font size of text.
562 **************************************************************************/
change_ptsize16(SDL_String16 * pString,Uint16 new_ptsize)563 void change_ptsize16(SDL_String16 *pString, Uint16 new_ptsize)
564 {
565 TTF_Font *pBuf;
566
567 if (pString->ptsize == new_ptsize) {
568 return;
569 }
570
571 if ((pBuf = load_font(new_ptsize)) == NULL) {
572 log_error("change_ptsize: load_font failed");
573 return;
574 }
575
576 unload_font(pString->ptsize);
577 pString->ptsize = new_ptsize;
578 pString->font = pBuf;
579 }
580
581 /* =================================================== */
582
583 /**************************************************************************
584 Load font of given pointsize.
585 **************************************************************************/
load_font(Uint16 ptsize)586 static TTF_Font *load_font(Uint16 ptsize)
587 {
588 struct TTF_Font_Chain *Font_TAB_TMP = Font_TAB;
589 TTF_Font *font_tmp = NULL;
590
591 /* find existing font and return pointer to it */
592 if (Sizeof_Font_TAB) {
593 while (Font_TAB_TMP) {
594 if (Font_TAB_TMP->ptsize == ptsize) {
595 Font_TAB_TMP->count++;
596 return Font_TAB_TMP->font;
597 }
598 Font_TAB_TMP = Font_TAB_TMP->next;
599 }
600 }
601
602 if(!pFont_with_FullPath) {
603 const char *path = theme_font_filename(theme);
604 pFont_with_FullPath = fc_strdup(path);
605 fc_assert_ret_val(pFont_with_FullPath != NULL, NULL);
606 }
607
608 /* Load Font */
609 if ((font_tmp = TTF_OpenFont(pFont_with_FullPath, ptsize)) == NULL) {
610 log_error("load_font: Couldn't load %d pt font from %s: %s",
611 ptsize, pFont_with_FullPath, SDL_GetError());
612 return font_tmp;
613 }
614
615 /* add new font to list */
616 if (Sizeof_Font_TAB == 0) {
617 Sizeof_Font_TAB++;
618 Font_TAB_TMP = fc_calloc(1, sizeof(struct TTF_Font_Chain));
619 Font_TAB = Font_TAB_TMP;
620 } else {
621 /* Go to end of chain */
622 Font_TAB_TMP = Font_TAB;
623 while (Font_TAB_TMP->next)
624 Font_TAB_TMP = Font_TAB_TMP->next;
625
626 Sizeof_Font_TAB++;
627 Font_TAB_TMP->next = fc_calloc(1, sizeof(struct TTF_Font_Chain));
628 Font_TAB_TMP = Font_TAB_TMP->next;
629 }
630
631 Font_TAB_TMP->ptsize = ptsize;
632 Font_TAB_TMP->count = 1;
633 Font_TAB_TMP->font = font_tmp;
634 Font_TAB_TMP->next = NULL;
635
636 return font_tmp;
637 }
638
639 /**************************************************************************
640 Free font of given pointsize.
641 **************************************************************************/
unload_font(Uint16 ptsize)642 void unload_font(Uint16 ptsize)
643 {
644 int index;
645
646 struct TTF_Font_Chain *Font_TAB_PREV = NULL;
647 struct TTF_Font_Chain *Font_TAB_TMP = Font_TAB;
648
649 if (Sizeof_Font_TAB == 0) {
650 log_error("unload_font: Trying unload from empty Font ARRAY");
651 return;
652 }
653
654 for (index = 0; index < Sizeof_Font_TAB; index++) {
655 if (Font_TAB_TMP->ptsize == ptsize) {
656 break;
657 }
658 Font_TAB_PREV = Font_TAB_TMP;
659 Font_TAB_TMP = Font_TAB_TMP->next;
660 }
661
662 if (index == Sizeof_Font_TAB) {
663 log_error("unload_font: Trying unload Font which is "
664 "not included in Font ARRAY");
665 return;
666 }
667
668 Font_TAB_TMP->count--;
669
670 if (Font_TAB_TMP->count) {
671 return;
672 }
673
674 TTF_CloseFont(Font_TAB_TMP->font);
675 Font_TAB_TMP->font = NULL;
676 if (Font_TAB_TMP == Font_TAB) {
677 Font_TAB = Font_TAB_TMP->next;
678 } else {
679 Font_TAB_PREV->next = Font_TAB_TMP->next;
680 }
681 Font_TAB_TMP->next = NULL;
682 Sizeof_Font_TAB--;
683 FC_FREE(Font_TAB_TMP);
684 }
685
686 /**************************************************************************
687 Free all fonts.
688 **************************************************************************/
free_font_system(void)689 void free_font_system(void)
690 {
691 struct TTF_Font_Chain *Font_TAB_TMP;
692
693 FC_FREE(pFont_with_FullPath);
694 while(Font_TAB) {
695 if (Font_TAB->next) {
696 Font_TAB_TMP = Font_TAB;
697 Font_TAB = Font_TAB->next;
698 if(Font_TAB_TMP->font) {
699 TTF_CloseFont(Font_TAB_TMP->font);
700 }
701 FC_FREE(Font_TAB_TMP);
702 } else {
703 if(Font_TAB->font) {
704 TTF_CloseFont(Font_TAB->font);
705 }
706 FC_FREE(Font_TAB);
707 }
708 }
709 }
710