Atrinik Client  4.0
text.c
Go to the documentation of this file.
1 /*************************************************************************
2  * Atrinik, a Multiplayer Online Role Playing Game *
3  * *
4  * Copyright (C) 2009-2014 Alex Tokar and Atrinik Development Team *
5  * *
6  * Fork from Crossfire (Multiplayer game for X-windows). *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
21  * *
22  * The author can be reached at admin@atrinik.org *
23  ************************************************************************/
24 
32 #include <global.h>
33 #include <toolkit/string.h>
34 
39 static uint8_t text_debug = 0;
40 
48 static uint8_t text_anchor_help_clicked = 0;
49 
51 static char text_anchor_help[HUGE_BUF];
52 
54 static int text_offset_mx = -1;
56 static int text_offset_my = -1;
57 
59 static int64_t *selection_start = NULL;
61 static int64_t *selection_end = NULL;
63 static uint8_t *selection_started = NULL;
64 
66 static SDL_Color text_link_color_default = {96, 160, 255, 0};
68 static SDL_Color text_link_color = {0, 0, 0, 0};
69 
72 
78 static void *text_anchor_info_ptr = NULL;
79 
84 
98 static char *font_get_hash_key(const char *name, uint8_t size, char *buf,
99  size_t buf_size)
100 {
101  snprintf(buf, buf_size, "%s@%d", name, size);
102  return buf;
103 }
104 
115 static TTF_Font *font_open(const char *name, uint8_t size)
116 {
117  char path[MAX_BUF];
118  TTF_Font *ttf_font;
119 
120  snprintf(path, sizeof(path), "fonts/%s.ttf", name);
121  ttf_font = TTF_OpenFont_wrapper(path, size);
122 
123  if (ttf_font == NULL) {
124  snprintf(path, sizeof(path), "fonts/%s.otf", name);
125  ttf_font = TTF_OpenFont_wrapper(path, size);
126  }
127 
128  if (ttf_font == NULL) {
129  LOG(ERROR, "Unable to load font (%s, %d): %s", name, size,
130  TTF_GetError());
131  }
132 
133  return ttf_font;
134 }
135 
145 static font_struct *font_new(const char *name, uint8_t size)
146 {
147  TTF_Font *ttf_font;
148  font_struct *font;
149  char key[MAX_BUF];
150 
151  ttf_font = font_open(name, size);
152 
153  if (ttf_font == NULL) {
154  return NULL;
155  }
156 
157  font = ecalloc(1, sizeof(*font));
158  font->key = estrdup(font_get_hash_key(name, size, key, sizeof(key)));
159  font->name = estrdup(name);
160  font->size = size;
161  font->last_used = time(NULL);
162  font->font = ttf_font;
163  font->height = TTF_FontLineSkip(ttf_font);
164  font->ref = 1; /* One because we're inserting it into a hash table. */
165 
166  HASH_ADD_KEYPTR(hh, fonts, font->key, strlen(font->key), font);
167 
168  return font;
169 }
170 
186 font_struct *font_get_weak(const char *name, uint8_t size)
187 {
188  char key[MAX_BUF];
189  font_struct *font;
190 
191  HARD_ASSERT(name != NULL);
192  SOFT_ASSERT_RC(size != 0, NULL, "Size is 0.");
193 
194  font_get_hash_key(name, size, key, sizeof(key));
195  HASH_FIND_STR(fonts, key, font);
196 
197  if (font == NULL) {
198  font = font_new(name, size);
199  } else {
200  font->last_used = time(NULL);
201  }
202 
203  return font;
204 }
205 
216 font_struct *font_get(const char *name, uint8_t size)
217 {
218  font_struct *font;
219 
220  HARD_ASSERT(name != NULL);
221  SOFT_ASSERT_RC(size != 0, NULL, "Size is 0.");
222 
223  font = font_get_weak(name, size);
224 
225  if (font) {
226  font->ref++;
227  }
228 
229  return font;
230 }
231 
243 {
244  int size_desired;
245 
246  HARD_ASSERT(font != NULL);
247 
248  size_desired = font->size + size;
249 
250  if (size_desired < 1 || size_desired > UINT8_MAX) {
251  return NULL;
252  }
253 
254  return font_get_weak(font->name, size_desired);
255 }
256 
263 {
264  HARD_ASSERT(font != NULL);
265 
266  if (font->ref > 1) {
267  font->ref--;
268  return;
269  }
270 
271 #ifndef NDEBUG
272  {
273  char key[MAX_BUF];
274  font_struct *tmp;
275 
276  font_get_hash_key(font->name, font->size, key, sizeof(key));
277  HASH_FIND_STR(fonts, key, tmp);
278 
279  if (tmp != NULL) {
280  LOG(ERROR, "Attempted to free a font that was still "
281  "in the hash table! Font name: %s, "
282  "size: %d", font->name, font->size);
283  abort();
284  }
285  }
286 #endif
287 
288  efree(font->name);
289  efree(font->key);
290  TTF_CloseFont(font->font);
291  efree(font);
292 }
293 
297 void font_gc(void)
298 {
299  time_t now;
300  struct timeval tv1, tv2;
301  font_struct *font, *next;
302 
303  if (!rndm_chance(FONT_GC_CHANCE)) {
304  return;
305  }
306 
307  now = time(NULL);
308  gettimeofday(&tv1, NULL);
309 
310  HASH_ITER(hh, fonts, font, next)
311  {
312  if (gettimeofday(&tv2, NULL) == 0 &&
313  tv2.tv_usec - tv1.tv_usec >= FONT_GC_MAX_TIME) {
314  break;
315  }
316 
317  /* If this is not the only reference, skip it. */
318  if (font->ref > 1) {
319  continue;
320  }
321 
322  /* If not enough time has passed, skip it. */
323  if (now - font->last_used < FONT_GC_FREE_TIME) {
324  continue;
325  }
326 
327  HASH_DEL(fonts, font);
328  FONT_DECREF(font);
329  font_free(font);
330  }
331 }
332 
336 void text_init(void)
337 {
338  TTF_Init();
339  fonts = NULL;
340 
342 }
343 
347 void text_deinit(void)
348 {
349  font_struct *font, *next;
350 
351  HASH_ITER(hh, fonts, font, next)
352  {
353  HASH_DEL(fonts, font);
354  FONT_DECREF(font);
355 
356  if (font->ref > 0) {
357  LOG(DEVEL, "Font %s (size: %u) still has %u references",
358  font->name, font->size, font->ref);
359  font->ref = 0;
360  }
361 
362  font_free(font);
363  }
364 
365  TTF_Quit();
366 }
367 
381 void text_offset_set(int x, int y)
382 {
383  text_offset_mx = x;
384  text_offset_my = y;
385 }
386 
392 {
394 }
395 
406 void text_color_set(int r, int g, int b)
407 {
408  text_link_color.r = r;
409  text_link_color.g = g;
410  text_link_color.b = b;
411 }
412 
425 void text_set_selection(int64_t *start, int64_t *end, uint8_t *started)
426 {
427  selection_start = start;
428  selection_end = end;
429  selection_started = started;
430 }
431 
438 {
439  text_anchor_handle = func;
440 }
441 
447 void text_set_anchor_info(void *ptr)
448 {
449  text_anchor_info_ptr = ptr;
450 }
451 
468 char *text_strip_markup(char *buf, size_t *buf_len, uint8_t do_free)
469 {
470  char *cp;
471  size_t pos = 0, cp_pos = 0, len;
472  uint8_t in_tag = 0;
473 
474  if (buf_len) {
475  len = *buf_len;
476  } else {
477  len = strlen(buf);
478  }
479 
480  cp = emalloc(sizeof(char) * (len + 1));
481 
482  while (pos < len) {
483  if (buf[pos] == '[') {
484  in_tag = 1;
485  } else if (buf[pos] == ']') {
486  in_tag = 0;
487  } else if (buf[pos] == '<') {
488  in_tag = 1;
489  } else if (buf[pos] == '>') {
490  in_tag = 0;
491  } else if (!in_tag) {
492  if (!strncmp(buf + pos, "&lsqb;", 6)) {
493  cp[cp_pos++] = '[';
494  pos += 5;
495  } else if (!strncmp(buf + pos, "&rsqb;", 6)) {
496  cp[cp_pos++] = ']';
497  pos += 5;
498  } else if (!strncmp(buf + pos, "&lbrack;", 7)) {
499  cp[cp_pos++] = '[';
500  pos += 6;
501  } else if (!strncmp(buf + pos, "&rbrack;", 7)) {
502  cp[cp_pos++] = ']';
503  pos += 6;
504  } else {
505  cp[cp_pos++] = buf[pos];
506  }
507  }
508 
509  pos++;
510  }
511 
512  cp[cp_pos] = '\0';
513 
514  if (do_free) {
515  efree(buf);
516  }
517 
518  if (buf_len) {
519  *buf_len = strlen(cp);
520  }
521 
522  return cp;
523 }
524 
532 char *text_escape_markup(const char *buf)
533 {
534  StringBuffer *sb;
535 
536  sb = stringbuffer_new();
537 
538  while (*buf != '\0') {
539  if (*buf == '[') {
540  stringbuffer_append_string(sb, "&lsqb;");
541  } else if (*buf == ']') {
542  stringbuffer_append_string(sb, "&rsqb;");
543  } else {
544  stringbuffer_append_char(sb, *buf);
545  }
546 
547  buf++;
548  }
549 
550  return stringbuffer_finish(sb);
551 }
552 
563 static int text_adjust_coords(SDL_Surface *surface, int *mx, int *my)
564 {
565  if (popup_get_head() && surface != popup_get_head()->surface) {
566  return 0;
567  }
568 
569  if (surface == ScreenSurface) {
570  return 1;
571  }
572 
573  if (text_offset_mx != -1 || text_offset_my != -1) {
574  if (text_offset_mx != -1) {
575  *mx -= text_offset_mx;
576  }
577 
578  if (text_offset_my != -1) {
579  *my -= text_offset_my;
580  }
581  } else {
582  widgetdata *widget = widget_find(NULL, -1, NULL, surface);
583 
584  if (widget) {
585  *mx -= widget->x;
586  *my -= widget->y;
587  }
588  }
589 
590  return 1;
591 }
592 
603 int text_color_parse(const char *color_notation, SDL_Color *color)
604 {
605  uint32_t r, g, b;
606 
607  if (*color_notation == '#') {
608  color_notation++;
609  }
610 
611  if (sscanf(color_notation, "%2X%2X%2X", &r, &g, &b) == 3) {
612  if (color) {
613  color->r = r;
614  color->g = g;
615  color->b = b;
616  }
617 
618  return 1;
619  }
620 
621  return 0;
622 }
623 
632 void text_anchor_execute(text_info_struct *info, void *custom_data)
633 {
634  size_t len;
635  char *buf, *pos;
636 
637  /* Sanity check. */
638  if (info->anchor_action[0] == '\0' || !info->anchor_tag) {
639  return;
640  }
641 
642  pos = strchr(info->anchor_action, ':');
643 
644  if (pos && pos + 1) {
645  buf = estrdup(pos + 1);
646  len = strlen(buf);
647  info->anchor_action[pos - info->anchor_action] = '\0';
648  } else {
649  char *tag;
650 
651  tag = strstr(info->anchor_tag, "[/a]");
652 
653  if (tag == NULL) {
654  return;
655  }
656 
657  /* Get the length of the text until the ending [/a]. */
658  len = tag - info->anchor_tag;
659  /* Allocate a temporary buffer and copy the text until the
660  * ending [/a], so we have the text between the anchor tags. */
661  buf = emalloc(len + 1);
662  memcpy(buf, info->anchor_tag, len);
663  buf[len] = '\0';
664  }
665 
666  buf = text_strip_markup(buf, &len, 1);
667 
668  if (text_anchor_handle && text_anchor_handle(info->anchor_action, buf, len, custom_data)) {
669  } else if (info->anchor_action[0] == '\0') {
670  /* No action specified. */
671 
672  if (cpl.state == ST_PLAY) {
673  /* It's not a command, so prepend "/say " to it. */
674  if (buf[0] != '/') {
675  /* Resize the buffer so it can hold 5 more bytes. */
676  buf = erealloc(buf, len + 5 + 1);
677  /* Copy the existing bytes to the end, so we have 5
678  * we can use in the front. */
679  memmove(buf + 5, buf, len + 1);
680  /* Prepend "/say ". */
681  memcpy(buf, "/say ", 5);
682  }
683 
684  send_command_check(buf);
685  }
686  } else if (!strcmp(info->anchor_action, "help")) {
687  /* Help GUI. */
688  strncpy(text_anchor_help, buf, sizeof(text_anchor_help) - 1);
689  text_anchor_help[sizeof(text_anchor_help) - 1] = '\0';
691  } else if (!strcmp(info->anchor_action, "url")) {
692  browser_open(buf);
693  }
694 
695  efree(buf);
696 }
697 
705 {
706  info->anchor_tag = NULL;
707  info->anchor_action[0] = '\0';
708  info->outline_color.r = info->outline_color.g = info->outline_color.b = 0;
709  info->outline_show = 0;
710  info->in_book_title = 0;
711  info->used_alpha = 255;
712  info->in_bold = info->in_italic = info->in_underline = info->in_strikethrough = 0;
713  info->obscured = 0;
714  info->calc_bold = 0;
715  info->calc_font = NULL;
716  info->hcenter_y = 0;
717  info->height = 0;
718  info->start_x = 0;
719  info->start_y = 0;
720  info->highlight = 0;
721  info->highlight_color.r = info->highlight_color.g = info->highlight_color.b = 0;
722  info->tooltip_text[0] = '\0';
723  info->tooltip_font = NULL;
724  info->tooltip_delay = 0;
725  info->tooltip_width = 0;
726  info->flip = 0;
727 }
728 
753 int text_show_character(font_struct **font, font_struct *orig_font, SDL_Surface *surface, SDL_Rect *dest, const char *cp, SDL_Color *color, SDL_Color *orig_color, uint64_t flags, SDL_Rect *box, int *x_adjust, text_info_struct *info)
754 {
755  int width, minx, ret = 1, new_style;
756  font_struct *restore_font = NULL;
757  char c = *cp;
758  uint8_t remove_bold = 0;
759 
760  /* Doing markup? */
761  if (flags & TEXT_MARKUP && c == '[') {
762  const char *pos, *tag, *tag2;
763  size_t tag_len;
764 
765  /* Get the position of the ending ']'. */
766  pos = strchr(cp, ']');
767 
768  if (pos == NULL) {
769  return ret;
770  }
771 
772  ret = 1 + (pos - cp);
773  tag = cp + 1;
774  tag_len = MAX(0, ret - 2);
775 
776  if (tag_len == 0) {
777  } else if (tag_len >= 3 && strncmp(tag, "c=#", 3) == 0) {
778  /* Color tag: [c=#RRGGBB] */
779 
780  if (color && (surface || info->obscured) && !(flags & TEXT_NO_COLOR_CHANGE)) {
781  uint32_t r, g, b;
782  int change_orig = 0;
783 
784  /* Parse the r,g,b colors. */
785  if (sscanf(tag + 3, "%2X%2X%2X %d", &r, &g, &b, &change_orig) >= 3) {
786  color->r = r;
787  color->g = g;
788  color->b = b;
789 
790  if (change_orig) {
791  SDL_color_copy(orig_color, color);
792  }
793  }
794  }
795  } else if (tag_len == 2 && strncmp(tag, "/c", tag_len) == 0) {
796  /* End of color tag. */
797 
798  if (color && (surface || info->obscured)) {
799  SDL_color_copy(color, orig_color);
800  }
801  } else if (tag_len == 5 && strncmp(tag, "green", tag_len) == 0) {
802  /* Convenience tag to make string green. */
803 
804  if (color && (surface || info->obscured) && !(flags & TEXT_NO_COLOR_CHANGE)) {
805  color->r = 0;
806  color->g = 255;
807  color->b = 0;
808  }
809  } else if (tag_len == 6 && strncmp(tag, "/green", tag_len) == 0) {
810  if (color && (surface || info->obscured)) {
811  SDL_color_copy(color, orig_color);
812  }
813  } else if (tag_len == 6 && strncmp(tag, "yellow", tag_len) == 0) {
814  /* Convenience tag to make string yellow. */
815 
816  if (color && (surface || info->obscured) && !(flags & TEXT_NO_COLOR_CHANGE)) {
817  color->r = 255;
818  color->g = 255;
819  color->b = 0;
820  }
821  } else if (tag_len == 7 && strncmp(tag, "/yellow", tag_len) == 0) {
822  if (color && (surface || info->obscured)) {
823  SDL_color_copy(color, orig_color);
824  }
825  } else if (tag_len == 3 && strncmp(tag, "red", tag_len) == 0) {
826  /* Convenience tag to make string red. */
827 
828  if (color && (surface || info->obscured) && !(flags & TEXT_NO_COLOR_CHANGE)) {
829  color->r = 255;
830  color->g = 0;
831  color->b = 0;
832  }
833  } else if (tag_len == 4 && strncmp(tag, "/red", tag_len) == 0) {
834  if (color && (surface || info->obscured)) {
835  SDL_color_copy(color, orig_color);
836  }
837  } else if (tag_len == 4 && strncmp(tag, "blue", tag_len) == 0) {
838  /* Convenience tag to make string blue. */
839 
840  if (color && (surface || info->obscured) && !(flags & TEXT_NO_COLOR_CHANGE)) {
841  color->r = 0;
842  color->g = 0;
843  color->b = 255;
844  }
845  } else if (tag_len == 5 && strncmp(tag, "/blue", tag_len) == 0) {
846  if (color && (surface || info->obscured)) {
847  SDL_color_copy(color, orig_color);
848  }
849  } else if (tag_len == 1 && strncmp(tag, "b", tag_len) == 0) {
850  /* Bold. */
851 
852  if (surface || info->obscured) {
853  info->in_bold = 1;
854  } else {
855  info->calc_bold = 1;
856  }
857  } else if (tag_len == 2 && strncmp(tag, "/b", tag_len) == 0) {
858  if (surface || info->obscured) {
859  info->in_bold = 0;
860  } else {
861  info->calc_bold = 0;
862  }
863  } else if (tag_len == 1 && strncmp(tag, "i", tag_len) == 0) {
864  /* Italic. */
865 
866  if (surface || info->obscured) {
867  info->in_italic = 1;
868  }
869  } else if (tag_len == 2 && strncmp(tag, "/i", tag_len) == 0) {
870  if (surface || info->obscured) {
871  info->in_italic = 0;
872  }
873  } else if (tag_len == 1 && strncmp(tag, "u", tag_len) == 0) {
874  /* Underscore. */
875 
876  if (surface || info->obscured) {
877  info->in_underline = 1;
878  }
879  } else if (tag_len == 2 && strncmp(tag, "/u", tag_len) == 0) {
880  if (surface || info->obscured) {
881  info->in_underline = 0;
882  }
883  } else if (tag_len == 1 && strncmp(tag, "s", tag_len) == 0) {
884  /* Strikethrough. */
885 
886  if (surface || info->obscured) {
887  info->in_strikethrough = 1;
888  }
889  } else if (tag_len == 2 && strncmp(tag, "/s", tag_len) == 0) {
890  if (surface || info->obscured) {
891  info->in_strikethrough = 0;
892  }
893  } else if (tag_len >= 5 && strncmp(tag, "font=", 5) == 0) {
894  int font_size = 10;
895  char font_name[MAX_BUF];
896 
897  /* Font change. */
898 
899  if (!(flags & TEXT_NO_FONT_CHANGE) && sscanf(tag + 5, "%64[^] >] %d", font_name, &font_size) >= 1) {
900  font_struct *font_new = font_get_weak(font_name, font_size);
901 
902  if (font_new != NULL) {
903  if (surface || info->obscured) {
904  *font = font_new;
905  } else {
906  info->calc_font = font_new;
907  }
908  }
909  }
910  } else if (tag_len >= 5 && strncmp(tag, "size=", 5) == 0) {
911  if (!(flags & TEXT_NO_FONT_CHANGE)) {
912  int font_size;
913  font_struct *font_new = NULL;
914  if (strncmp(tag + 5, "+", 1) == 0 ||
915  strncmp(tag + 5, "-", 1) == 0) {
916  font_new = font_get_size(*font, atoi(tag + 5));
917  } else if (sscanf(tag + 5, "%d", &font_size) == 1) {
918  font_new = font_get_weak((*font)->name, font_size);
919  }
920 
921  if (font_new != NULL) {
922  if (surface || info->obscured) {
923  *font = font_new;
924  } else {
925  info->calc_font = font_new;
926  }
927  }
928  }
929  } else if (tag_len == 5 && (strncmp(tag, "/font", tag_len) == 0 || strncmp(tag, "/size", tag_len) == 0)) {
930  if (surface || info->obscured) {
931  *font = orig_font;
932  } else {
933  info->calc_font = NULL;
934  }
935  } else if (tag_len == 6 && strncmp(tag, "center", tag_len) == 0) {
936  /* Find the ending tag. */
937  tag2 = strstr(tag + tag_len, "[/center]");
938 
939  if (tag2 != NULL && box != NULL && box->w != 0) {
940  /* Copy the string between [center] and [/center] to a
941  * temporary buffer so we can calculate its width. */
942  char *buf = emalloc(tag2 - cp - 8 + 1);
943  memcpy(buf, cp + 8, tag2 - cp - 8);
944  buf[tag2 - cp - 8] = '\0';
945  int text_width = text_get_width(*font, buf, flags);
946  efree(buf);
947 
948  if (surface != NULL && text_width < box->w - dest->x) {
949  dest->x += box->w / 2 - text_width / 2;
950  }
951  }
952  } else if (tag_len == 7 && strncmp(tag, "/center", tag_len) == 0) {
953  } else if (tag_len == 5 && strncmp(tag, "right", tag_len) == 0) {
954  /* Find the ending tag. */
955  tag2 = strstr(tag + tag_len, "[/right]");
956 
957  if (tag2 && box && box->w) {
958  char *buf = emalloc(tag2 - cp - 7 + 1);
959  int w;
960 
961  /* Copy the string between [right] and [/right] to a
962  * temporary buffer so we can calculate its width. */
963  memcpy(buf, cp + 7, tag2 - cp - 7);
964  buf[tag2 - cp - 7] = '\0';
965  w = info->start_x + box->w - text_get_width(*font, buf, flags);
966  efree(buf);
967 
968  if (surface) {
969  if (w > dest->x) {
970  dest->x = w;
971  }
972  }
973  }
974  } else if (tag_len == 6 && strncmp(tag, "/right", tag_len) == 0) {
975  } else if ((tag_len == 1 && strncmp(tag, "a", tag_len) == 0) || (tag_len >= 2 && strncmp(tag, "a=", 2) == 0)) {
976  /* Scan for action other than the default. */
977  if (sscanf(tag, "a=%1024[^]>]", info->anchor_action) != 1) {
978  info->anchor_action[0] = '\0';
979  }
980 
981  if (surface || info->obscured) {
982  /* Change to light blue only if no custom color was
983  * specified. */
984  if (color && color->r == orig_color->r && color->g == orig_color->g && color->b == orig_color->b && !(flags & TEXT_NO_COLOR_CHANGE) && !string_startswith(info->anchor_action, "#")) {
985  color->r = text_link_color.r;
986  color->g = text_link_color.g;
987  color->b = text_link_color.b;
988  }
989 
990  info->anchor_tag = pos + 1;
991  }
992  } else if (tag_len == 2 && strncmp(tag, "/a", tag_len) == 0) {
993  if (surface || info->obscured) {
994  if (color) {
995  SDL_color_copy(color, orig_color);
996  }
997 
998  info->anchor_tag = NULL;
999  }
1000  } else if (tag_len >= 2 && strncmp(tag, "y=", 2) == 0) {
1001  if (surface) {
1002  int height;
1003 
1004  if (sscanf(tag, "y=%d", &height) == 1) {
1005  dest->y += height;
1006  }
1007  }
1008  } else if (tag_len >= 2 && !strncmp(tag, "x=", 2)) {
1009  if (surface) {
1010  int w;
1011 
1012  if (sscanf(tag, "x=%d", &w) == 1) {
1013  dest->x += w;
1014  }
1015  }
1016  } else if (tag_len >= 4 && strncmp(tag, "img=", 4) == 0) {
1017  if (surface) {
1018  char face[MAX_BUF];
1019  int x = 0, y = 0, align = 0, wd = 0, ht = 0, quick_pos = 0;
1020  uint16_t dark_level = 0, alpha = 0;
1021 
1023  memset(&sprite_effects, 0, sizeof(sprite_effects));
1024 
1025  if (sscanf(tag + 4, "%128[^] >] %d %d %d %u %" SCNu16 " %d "
1026  "%" SCNu16 " %" SCNd16 " %" SCNd16 " %" SCNd16 " "
1027  "%u %d %d", face, &x, &y, &align,
1028  &sprite_effects.flags, &dark_level, &quick_pos, &alpha,
1029  &sprite_effects.zoom_x, &sprite_effects.zoom_y,
1030  &sprite_effects.rotate, &sprite_effects.stretch,
1031  &wd, &ht) >= 1) {
1032  int id;
1033 
1034  id = image_get_id(face);
1035  sprite_effects.dark_level = dark_level;
1036  sprite_effects.alpha = alpha;
1037 
1038  if (id != -1 && FaceList[id].sprite) {
1039  int w, h;
1040  SDL_Rect srcrect;
1041 
1042  w = FaceList[id].sprite->bitmap->w;
1043  h = FaceList[id].sprite->bitmap->h;
1044 
1045  if (sprite_effects.rotate) {
1046  rotozoomSurfaceSizeXY(w, h, sprite_effects.rotate,
1047  sprite_effects.zoom_x ?
1048  sprite_effects.zoom_x / 100.0 : 1.0,
1049  sprite_effects.zoom_y ?
1050  sprite_effects.zoom_y / 100.0 : 1.0,
1051  &w, &h);
1052  } else if ((sprite_effects.zoom_x &&
1053  sprite_effects.zoom_x != 100) ||
1054  (sprite_effects.zoom_y &&
1055  sprite_effects.zoom_y != 100)) {
1056  zoomSurfaceSize(w, h,
1057  sprite_effects.zoom_x ?
1058  sprite_effects.zoom_x / 100.0 : 1.0,
1059  sprite_effects.zoom_y ?
1060  sprite_effects.zoom_y / 100.0 : 1.0,
1061  &w, &h);
1062  }
1063 
1064  if (align & 1) {
1065  x += box->w - w - dest->x;
1066  }
1067 
1068  if (align & 2) {
1069  y += -dest->y;
1070  }
1071 
1072  if (align & 4) {
1073  if (quick_pos) {
1074  int mnr, mid;
1075 
1076  mnr = quick_pos;
1077  mid = mnr >> 4;
1078  mnr &= 0x0f;
1079  y = y - MultiArchs[mid].part[mnr].yoff +
1080  MultiArchs[mid].ylen - h;
1081  x -= MultiArchs[mid].part[mnr].xoff;
1082 
1083  if (w > MultiArchs[mid].xlen) {
1084  x += (MultiArchs[mid].xlen - w) >> 1;
1085  }
1086  } else {
1087  y = (y + MAP_TILE_POS_YOFF) - h;
1088 
1089  if (w > MAP_TILE_POS_XOFF) {
1090  x -= (w - MAP_TILE_POS_XOFF) / 2;
1091  }
1092  }
1093  }
1094 
1095  if (wd || ht) {
1096  srcrect.x = 0;
1097  srcrect.y = 0;
1098  srcrect.w = wd;
1099  srcrect.h = ht;
1100  }
1101 
1102  surface_show_effects(surface, dest->x + x, dest->y + y,
1103  wd || ht ? &srcrect : NULL,
1104  FaceList[id].sprite->bitmap, &sprite_effects);
1105  }
1106  }
1107  }
1108  } else if (tag_len >= 3 && strncmp(tag, "o=#", 3) == 0) {
1109  uint32_t r, g, b;
1110 
1111  /* Parse the r,g,b colors. */
1112  if (sscanf(tag + 3, "%2X%2X%2X", &r, &g, &b) == 3) {
1113  info->outline_color.r = r;
1114  info->outline_color.g = g;
1115  info->outline_color.b = b;
1116  info->outline_show = 1;
1117  }
1118  } else if (tag_len == 2 && strncmp(tag, "/o", tag_len) == 0) {
1119  if (surface || info->obscured) {
1120  info->outline_color.r = info->outline_color.g = info->outline_color.b = 0;
1121  info->outline_show = 0;
1122  }
1123  } else if (tag_len >= 6 && strncmp(tag, "alpha=", 6) == 0) {
1124  if (surface || info->obscured) {
1125  int alpha;
1126 
1127  if (sscanf(tag, "alpha=%d", &alpha) == 1) {
1128  info->used_alpha = alpha;
1129  }
1130  }
1131  } else if (tag_len == 6 && strncmp(tag, "/alpha", tag_len) == 0) {
1132  info->used_alpha = 255;
1133  } else if (tag_len == 4 && strncmp(tag, "book", tag_len) == 0) {
1134  tag2 = strstr(tag + tag_len, "[/book]");
1135 
1136  if (tag2) {
1137  if (flags & TEXT_LINES_CALC) {
1138  book_name_change(tag + tag_len + 1, tag2 - tag - 5);
1139  }
1140 
1141  ret += tag2 - tag - 5;
1142  }
1143  } else if (tag_len == 5 && strncmp(tag, "/book", tag_len) == 0) {
1144  } else if (tag_len == 1 && strncmp(tag, "p", tag_len) == 0) {
1145  if (surface && box && box->w) {
1146  SDL_Rect rect;
1147 
1148  rect.y = dest->y + FONT_HEIGHT(*font) / 2;
1149  rect.w = 1;
1150  rect.h = 3;
1151  rect.x = dest->x;
1152  SDL_FillRect(surface, &rect, SDL_MapRGB(surface->format, 96, 96, 96));
1153  rect.x += box->w;
1154  SDL_FillRect(surface, &rect, SDL_MapRGB(surface->format, 96, 96, 96));
1155 
1156  rect.x = dest->x + 1;
1157  rect.w = box->w - 1;
1158  rect.h = 1;
1159  SDL_FillRect(surface, &rect, SDL_MapRGB(surface->format, 96, 96, 96));
1160  rect.y++;
1161  SDL_FillRect(surface, &rect, SDL_MapRGB(surface->format, 110, 110, 110));
1162  rect.y++;
1163  SDL_FillRect(surface, &rect, SDL_MapRGB(surface->format, 96, 96, 96));
1164  }
1165  } else if (tag_len == 5 && strncmp(tag, "title", tag_len) == 0) {
1166  if (!(flags & TEXT_NO_FONT_CHANGE)) {
1167  if (surface || info->obscured) {
1168  *font = FONT_SERIF14;
1169  } else {
1170  info->calc_font = FONT_SERIF14;
1171  }
1172  }
1173 
1174  if (surface || info->obscured) {
1175  info->in_underline = 1;
1176  }
1177  } else if (tag_len == 6 && strncmp(tag, "/title", tag_len) == 0) {
1178  if (surface || info->obscured) {
1179  info->in_underline = 0;
1180  *font = orig_font;
1181  } else {
1182  info->calc_font = NULL;
1183  }
1184  } else if (tag_len >= 8 && strncmp(tag, "padding=", 8) == 0) {
1185  int val;
1186 
1187  if (x_adjust && sscanf(tag + 8, "%d", &val) == 1) {
1188  if (surface) {
1189  dest->x += val;
1190  }
1191 
1192  dest->w += val;
1193  *x_adjust = val;
1194  }
1195  } else if (tag_len == 8 && strncmp(tag, "/padding", tag_len) == 0) {
1196  if (x_adjust) {
1197  *x_adjust = 0;
1198  }
1199  } else if (tag_len >= 4 && strncmp(tag, "bar=", 4) == 0) {
1200  if (surface && !(flags & TEXT_NO_COLOR_CHANGE)) {
1201  char texture[MAX_BUF];
1202  int bar_w, bar_h;
1203 
1204  bar_w = -1;
1205  bar_h = -1;
1206 
1207  if (sscanf(tag + 4, "%128[^] >] %d %d", texture, &bar_w, &bar_h) >= 1) {
1208  SDL_Rect bar_dst;
1209  SDL_Color bar_color;
1210 
1211  bar_dst.x = dest->x;
1212  bar_dst.y = dest->y;
1213  bar_dst.w = box && bar_w == -1 ? box->w : bar_w;
1214  bar_dst.h = box && bar_h == -1 ? box->h : bar_h;
1215 
1216  if (*texture == '#' && text_color_parse(texture, &bar_color)) {
1217  SDL_FillRect(surface, &bar_dst, SDL_MapRGB(surface->format, bar_color.r, bar_color.g, bar_color.b));
1218  } else {
1219  surface_show_fill(surface, bar_dst.x, bar_dst.y, NULL, TEXTURE_CLIENT(texture), &bar_dst);
1220  }
1221  }
1222  }
1223  } else if (tag_len >= 7 && strncmp(tag, "border=", 7) == 0) {
1224  if (surface && !(flags & TEXT_NO_COLOR_CHANGE)) {
1225  char texture[MAX_BUF];
1226  int wd, ht, thickness = 1;
1227 
1228  wd = -1;
1229  ht = -1;
1230 
1231  if (sscanf(tag + 7, "%128[^] >] %d %d %d", texture, &wd, &ht, &thickness) >= 1) {
1232  SDL_Rect border_dst;
1233  SDL_Color border_color;
1234 
1235  border_dst.x = dest->x;
1236  border_dst.y = dest->y;
1237  border_dst.w = box && wd == -1 ? box->w : wd;
1238  border_dst.h = box && ht == -1 ? box->h : ht;
1239 
1240  if (*texture == '#' && text_color_parse(texture, &border_color)) {
1241  border_create(surface, border_dst.x, border_dst.y, border_dst.w, border_dst.h, SDL_MapRGB(surface->format, border_color.r, border_color.g, border_color.b), thickness);
1242  } else {
1243  border_create_texture(surface, &border_dst, thickness, TEXTURE_CLIENT(texture));
1244  }
1245  }
1246  }
1247  } else if (tag_len >= 8 && strncmp(tag, "hcenter=", 8) == 0) {
1248  if (surface) {
1249  int ht;
1250 
1251  tag2 = strstr(tag + tag_len, "[/hcenter]");
1252 
1253  if (tag2 && sscanf(tag + 8, "%d", &ht) == 1) {
1254  size_t len;
1255  char *tmpbuf;
1256  SDL_Rect hcenter_box;
1257 
1258  len = tag2 - (tag + tag_len + 1);
1259  tmpbuf = emalloc(len + 1);
1260  memcpy(tmpbuf, tag + tag_len + 1, len);
1261  tmpbuf[len] = '\0';
1262 
1263  hcenter_box.w = box ? (box->w - (dest->w - box->w)) : 0;
1264  hcenter_box.h = 0;
1265  hcenter_box.y = 0;
1266  text_show(NULL, *font, tmpbuf, 0, 0, "000000", flags | TEXT_HEIGHT, &hcenter_box);
1267  dest->y += ht / 2 - hcenter_box.h / 2;
1268  info->hcenter_y = MAX(0, ht / 2 - hcenter_box.h / 2);
1269  efree(tmpbuf);
1270  }
1271  }
1272  } else if (tag_len == 8 && strncmp(tag, "/hcenter", tag_len) == 0) {
1273  if (surface) {
1274  dest->y += info->hcenter_y;
1275  }
1276  } else if (tag_len >= 5 && strncmp(tag, "icon=", 5) == 0) {
1277  if (surface) {
1278  char face[MAX_BUF];
1279  int wd, ht, fit_to_size, flip;
1280  float show_percentage;
1281 
1282  wd = ht = -1;
1283  fit_to_size = flip = 0;
1284  show_percentage = 1.0;
1285  int rotate = 0;
1286 
1287  if (sscanf(tag + 5, "%255[^] >] %d %d %d %d %f %d", face, &wd, &ht, &fit_to_size, &flip, &show_percentage, &rotate) >= 1) {
1288  int id;
1289  sprite_struct *icon_sprite;
1290  SDL_Surface *icon_surface;
1291 
1292  id = image_get_id(face);
1293  icon_sprite = NULL;
1294  icon_surface = NULL;
1295 
1296  if (id != -1 && FaceList[id].sprite) {
1297  icon_sprite = FaceList[id].sprite;
1298  icon_surface = icon_sprite->bitmap;
1299  }
1300 
1301  if (!icon_sprite) {
1302  uint8_t is_software;
1303 
1304  is_software = strncmp(face, "soft:", 5) == 0;
1305  icon_surface = texture_surface(texture_get(is_software ? TEXTURE_TYPE_SOFTWARE : TEXTURE_TYPE_CLIENT, is_software ? face + 5 : face));
1306  }
1307 
1308  if (icon_surface) {
1309  int icon_w, icon_h, icon_orig_w, icon_orig_h;
1310  int border_up, border_down, border_left, border_right;
1311  SDL_Rect icon_box, icon_dst;
1312  double zoom_factor;
1313  double zoom_x, zoom_y;
1314 
1315  if (icon_sprite) {
1316  border_up = icon_sprite->border_up;
1317  border_down = icon_sprite->border_down;
1318  border_left = icon_sprite->border_left;
1319  border_right = icon_sprite->border_right;
1320  } else {
1321  surface_borders_get(icon_surface, &border_up, &border_down, &border_left, &border_right, icon_surface->format->colorkey);
1322  }
1323 
1324  icon_w = icon_orig_w = icon_surface->w - border_left - border_right;
1325  icon_h = icon_orig_h = icon_surface->h - border_up - border_down;
1326 
1327  if (wd == -1) {
1328  wd = icon_w;
1329  }
1330 
1331  if (ht == -1) {
1332  ht = icon_h;
1333  }
1334 
1335  if (fit_to_size) {
1336  if (icon_w < wd) {
1337  zoom_factor = (double) wd / icon_w;
1338  icon_w *= zoom_factor;
1339  icon_h *= zoom_factor;
1340  }
1341 
1342  if (icon_h < ht) {
1343  zoom_factor = (double) ht / icon_h;
1344  icon_w *= zoom_factor;
1345  icon_h *= zoom_factor;
1346  }
1347  }
1348 
1349  if (icon_w > wd) {
1350  zoom_factor = (double) wd / icon_w;
1351  icon_w *= zoom_factor;
1352  icon_h *= zoom_factor;
1353  }
1354 
1355  if (icon_h > ht) {
1356  zoom_factor = (double) ht / icon_h;
1357  icon_w *= zoom_factor;
1358  icon_h *= zoom_factor;
1359  }
1360 
1361  icon_box.x = border_left;
1362  icon_box.y = border_up;
1363  icon_box.w = icon_w;
1364  icon_box.h = icon_h;
1365 
1366  icon_dst.x = dest->x + wd / 2 - icon_w / 2;
1367  icon_dst.y = dest->y + ht / 2 - icon_h / 2;
1368 
1369  zoom_x = (double) icon_w / icon_orig_w;
1370  zoom_y = (double) icon_h / icon_orig_h;
1371 
1372  icon_box.x *= zoom_x;
1373  icon_box.y *= zoom_y;
1374 
1375  if (flip & TEXT_FLIP_HORIZONTAL) {
1376  zoom_x *= -1;
1377  }
1378 
1379  if (flip & TEXT_FLIP_VERTICAL) {
1380  zoom_y *= -1;
1381  }
1382 
1383  if (!FLT_EQUAL(show_percentage, 1.0)) {
1384  // Left to right
1385  if (show_percentage >= 6.0) {
1386  icon_box.w *= show_percentage - 6.0;
1387  }// Bottom to top
1388  else if (show_percentage >= 4.0) {
1389  int offset;
1390 
1391  offset = icon_box.h - ((float) icon_box.h * (show_percentage - 4.0));
1392  icon_box.y += offset;
1393  icon_dst.y += offset;
1394  }// Right to left
1395  else if (show_percentage >= 2.0) {
1396  int offset;
1397 
1398  offset = icon_box.w - ((float) icon_box.w * (show_percentage - 2.0));
1399  icon_box.x += offset;
1400  icon_dst.x += offset;
1401  }// Top to bottom
1402  else {
1403  icon_box.h *= show_percentage;
1404  }
1405  }
1406 
1407  if (rotate != 0) {
1408  int dstwd, dstht;
1409  rotozoomSurfaceSizeXY(icon_w,
1410  icon_h,
1411  rotate, 1.0, 1.0,
1412  &dstwd, &dstht);
1413  int xoff = (dstwd - icon_w) / 2;
1414  icon_dst.x -= xoff;
1415  icon_box.w += xoff;
1416  int yoff = (dstht - icon_h) / 2;
1417  icon_dst.y -= yoff;
1418  icon_box.h += yoff;
1419  }
1420 
1421  if (icon_w != icon_orig_w ||
1422  icon_h != icon_orig_h ||
1423  flip != 0 ||
1424  rotate != 0) {
1425  SDL_Surface *tmp_icon;
1426 
1427  bool smooth = false;
1428  if (icon_w != icon_orig_w ||
1429  icon_h != icon_orig_h ||
1430  rotate != 0) {
1432  OPT_ZOOM_SMOOTH);
1433  }
1434 
1435  tmp_icon = rotozoomSurfaceXY(icon_surface,
1436  rotate,
1437  zoom_x,
1438  zoom_y,
1439  smooth);
1440 
1441  SDL_BlitSurface(tmp_icon, &icon_box, surface, &icon_dst);
1442  SDL_FreeSurface(tmp_icon);
1443  } else {
1444  SDL_BlitSurface(icon_surface, &icon_box, surface, &icon_dst);
1445  }
1446  }
1447  }
1448  }
1449  } else if (tag_len >= 4 && strncmp(tag, "obj=", 4) == 0) {
1450  if (surface) {
1451  tag_t id;
1452  int wd, ht;
1453  int fit = 0;
1454 
1455  wd = ht = INVENTORY_ICON_SIZE;
1456 
1457  if (sscanf(tag + 4, "%" SCNu32 " %d %d %d", &id, &wd, &ht,
1458  &fit) >= 1) {
1459  object *tmp = object_find(id);
1460 
1461  if (tmp != NULL) {
1462  object_show_centered(surface, tmp, dest->x, dest->y,
1463  wd, ht, !!fit);
1464  }
1465  }
1466  }
1467  } else if (tag_len >= 9 && strncmp(tag, "ctooltip=", 9) == 0) {
1468  if (surface || info->obscured) {
1469  int mx, my;
1470  char tooltip_width[MAX_BUF], tooltip_height[MAX_BUF], tooltip_text[HUGE_BUF];
1471  int tooltip_max_width;
1472 
1473  SDL_GetMouseState(&mx, &my);
1474 
1475  if (text_adjust_coords(surface, &mx, &my) && mx >= dest->x && my >= dest->y && sscanf(tag + 9, "%64s %64s %d %512[^]>]", tooltip_width, tooltip_height, &tooltip_max_width, tooltip_text) == 4) {
1476  int wd, ht;
1477 
1478  if (string_startswith(tooltip_width, "w:")) {
1479  wd = text_get_width(*font, tooltip_width + 2, 0);
1480  } else {
1481  wd = atoi(tooltip_width);
1482  }
1483 
1484  if (string_startswith(tooltip_height, "h:")) {
1485  ht = text_get_height(*font, tooltip_height + 2, 0);
1486  } else {
1487  ht = atoi(tooltip_height);
1488  }
1489 
1490  if (box) {
1491  if (box->w) {
1492  wd = MIN(box->w - (dest->x - info->start_x), wd);
1493  }
1494 
1495  if (box->h) {
1496  ht = MIN(box->h - (dest->y - info->start_y), ht);
1497  }
1498  }
1499 
1500  if (info->obscured) {
1501  ht -= info->height;
1502  }
1503 
1504  if (mx < dest->x + wd && my < dest->y + ht) {
1505  SDL_GetMouseState(&mx, &my);
1506  tooltip_create(mx, my, *font, tooltip_text);
1507 
1508  if (tooltip_max_width >= 0) {
1509  tooltip_multiline(tooltip_max_width);
1510  }
1511  }
1512  }
1513  }
1514  } else if (tag_len >= 8 && strncmp(tag, "tooltip=", 8) == 0) {
1515  if (surface || info->obscured) {
1516  if (sscanf(tag + 8, "%512[^]>]", info->tooltip_text) == 1) {
1517  info->tooltip_font = *font;
1518  }
1519  }
1520  } else if (tag_len == 8 && strncmp(tag, "/tooltip", tag_len) == 0) {
1521  if (surface || info->obscured) {
1522  info->tooltip_text[0] = '\0';
1523  }
1524  } else if (tag_len >= 3 && strncmp(tag, "h=#", 3) == 0) {
1525  int r, g, b;
1526 
1527  if ((surface || info->obscured) && sscanf(tag + 3, "%2X%2X%2X", &r, &g, &b) == 3) {
1528  /* Find the ending tag. */
1529  tag2 = strstr(tag + tag_len, "[/h]");
1530 
1531  if (tag2) {
1532  char *buf;
1533 
1534  buf = emalloc(tag2 - cp - 11 + 1);
1535  memcpy(buf, cp + 11, tag2 - cp - 11);
1536  buf[tag2 - cp - 11] = '\0';
1537  info->highlight_rect.x = dest->x;
1538  info->highlight_rect.y = dest->y;
1539  info->highlight_rect.w = text_get_width(*font, buf, flags);
1540  info->highlight_rect.h = text_get_height(*font, buf, flags);
1541  efree(buf);
1542 
1543  info->highlight = 1;
1544  info->highlight_color.r = r;
1545  info->highlight_color.g = g;
1546  info->highlight_color.b = b;
1547  }
1548  }
1549  } else if (tag_len == 2 && strncmp(tag, "/h", tag_len) == 0) {
1550  if (surface || info->obscured) {
1551  info->highlight = 0;
1552  }
1553  } else if (tag_len >= 5 && strncmp(tag, "line=", 5) == 0) {
1554  int x, y, x2, y2;
1555 
1556  if ((surface || info->obscured) && sscanf(tag + 5, "%d,%d,%d,%d", &x, &y, &x2, &y2) == 4) {
1557  if (info->obscured) {
1558  y = MAX(0, y - info->height);
1559  y2 = MAX(1, y2 - info->height);
1560  }
1561 
1562  if (surface) {
1563  lineRGBA(surface, dest->x + x, dest->y + y, dest->x + x2, dest->y + y2, color->r, color->g, color->b, info->used_alpha);
1564  }
1565  }
1566  } else if (tag_len >= 5 && strncmp(tag, "flip=", 5) == 0) {
1567  if (surface || info->obscured) {
1568  char flip_type[MAX_BUF];
1569 
1570  if (sscanf(tag + 5, "%64[^]>]", flip_type) == 1) {
1571  if (strcmp(flip_type, "horizontal") == 0) {
1572  info->flip = TEXT_FLIP_HORIZONTAL;
1573  } else if (strcmp(flip_type, "vertical") == 0) {
1574  info->flip = TEXT_FLIP_VERTICAL;
1575  } else if (strcmp(flip_type, "both") == 0) {
1576  info->flip = TEXT_FLIP_BOTH;
1577  }
1578  }
1579  }
1580  } else if (tag_len == 5 && strncmp(tag, "/flip", tag_len) == 0) {
1581  if (surface || info->obscured) {
1582  info->flip = 0;
1583  }
1584  } else if (tag_len >= 13 && strncmp(tag, "tooltip_conf=", 13) == 0) {
1585  if (surface || info->obscured) {
1586  if (sscanf(tag + 13,
1587  "%" SCNu32 " %d",
1588  &info->tooltip_delay,
1589  &info->tooltip_width) >= 1) {
1590  }
1591  }
1592  } else {
1593  char *cp2;
1594 
1595  cp2 = text_escape_markup(cp);
1596  log_error("Unknown tag found in message: %s", cp2);
1597  efree(cp2);
1598  }
1599 
1600  if (tag_len != 0) {
1601  return ret;
1602  }
1603  }
1604 
1605  /* Parse entities. */
1606  if (flags & TEXT_MARKUP && c == '&') {
1607  if (!strncmp(cp, "&lsqb;", 6)) {
1608  c = '[';
1609  ret = 6;
1610  } else if (!strncmp(cp, "&rsqb;", 6)) {
1611  c = ']';
1612  ret = 6;
1613  } else if (!strncmp(cp, "&lbrack;", 7)) {
1614  c = '[';
1615  ret = 7;
1616  } else if (!strncmp(cp, "&rbrack;", 7)) {
1617  c = ']';
1618  ret = 7;
1619  }
1620  }
1621 
1622  new_style = 0;
1623 
1624  /* Try to set applicable font style. */
1625  if (surface || info->obscured) {
1626  if (info->in_bold) {
1627  new_style |= TTF_STYLE_BOLD;
1628  }
1629 
1630  if (info->in_italic) {
1631  new_style |= TTF_STYLE_ITALIC;
1632  }
1633 
1634  if (info->in_underline) {
1635  new_style |= TTF_STYLE_UNDERLINE;
1636  }
1637 
1638  if (TTF_GetFontStyle((*font)->font) != new_style) {
1639  TTF_SetFontStyle((*font)->font, new_style);
1640  }
1641  } else {
1642  uint8_t is_bold;
1643 
1644  /* Deals with the case when calculating width. */
1645 
1646  /* Different font sizes affect the width, so we need to
1647  * temporarily change the current font. */
1648  if (info->calc_font != NULL) {
1649  restore_font = (*font);
1650  (*font) = info->calc_font;
1651  }
1652 
1653  is_bold = TTF_GetFontStyle((*font)->font) & TTF_STYLE_BOLD;
1654 
1655  /* Bold style also slightly affects the width. */
1656  if (info->calc_bold && !is_bold) {
1657  TTF_SetFontStyle((*font)->font, TTF_GetFontStyle((*font)->font) | TTF_STYLE_BOLD);
1658  remove_bold = 1;
1659  } else if (!info->calc_bold && is_bold) {
1660  TTF_SetFontStyle((*font)->font, TTF_GetFontStyle((*font)->font) & ~TTF_STYLE_BOLD);
1661  }
1662  }
1663 
1664  /* Get the glyph's metrics. */
1665  if (TTF_GlyphMetrics((*font)->font, c == '\t' ? ' ' : c, &minx, NULL, NULL, NULL, &width) == -1) {
1666  return ret;
1667  }
1668 
1669  /* Remove temporary bold style. */
1670  if (remove_bold) {
1671  TTF_SetFontStyle((*font)->font, TTF_GetFontStyle((*font)->font) & ~TTF_STYLE_BOLD);
1672  }
1673 
1674  /* Restore font. */
1675  if (restore_font != NULL) {
1676  *font = restore_font;
1677  restore_font = NULL;
1678  }
1679 
1680  if (minx < 0) {
1681  width -= minx;
1682  }
1683 
1684  /* Outline, add a little bit to the width. */
1685  if (info->outline_show) {
1686  width += 2;
1687  }
1688 
1689  if (c == '\t') {
1690  width *= 4;
1691  }
1692 
1693  /* Draw the character (unless it's a space, since there's no point in
1694  * drawing whitespace [but only if underline style is not active,
1695  * since we do want the underline below the space]). */
1696  if (surface && ((c != ' ' && c != '\t') || info->in_underline || info->anchor_tag || info->highlight || *info->tooltip_text != '\0')) {
1697  SDL_Surface *ttf_surface;
1698  char buf[2];
1699  SDL_Color *use_color;
1700  SDL_Rect dstrect, srcrect;
1701 
1702  buf[0] = c;
1703  buf[1] = '\0';
1704  use_color = color;
1705 
1706  if (info->anchor_tag || info->highlight || *info->tooltip_text != '\0') {
1707  int state, mx, my, orig_mx, orig_my;
1708 
1709  if (info->anchor_tag && cp == info->anchor_tag && text_anchor_info_ptr) {
1711  }
1712 
1713  state = SDL_GetMouseState(&mx, &my);
1714  orig_mx = mx;
1715  orig_my = my;
1716 
1717  if (text_adjust_coords(surface, &mx, &my)) {
1718  if (mx >= dest->x && mx < dest->x + width && my >= dest->y && my < dest->y + FONT_HEIGHT(*font)) {
1719  static uint32_t ticks = 0;
1720 
1721  if (info->anchor_tag) {
1722  if (state == SDL_BUTTON_LEFT && (!selection_start || !selection_end || *selection_start == -1 || *selection_end == -1) && (!ticks || SDL_GetTicks() - ticks > 125)) {
1723  ticks = SDL_GetTicks();
1724  text_anchor_execute(info, NULL);
1725  } else {
1726  cursor_texture = texture_get(TEXTURE_TYPE_CLIENT, "cursor_pointer");
1727  }
1728  }
1729 
1730  if (*info->tooltip_text != '\0') {
1731  tooltip_create(orig_mx,
1732  orig_my,
1733  info->tooltip_font,
1734  info->tooltip_text);
1735  tooltip_multiline(info->tooltip_width);
1736  if (info->tooltip_delay != 0) {
1737  tooltip_enable_delay(info->tooltip_delay);
1738  }
1739  }
1740  }
1741 
1742  if (info->highlight && mx >= info->highlight_rect.x && mx < info->highlight_rect.x + info->highlight_rect.w && my >= info->highlight_rect.y && my < info->highlight_rect.y + info->highlight_rect.h) {
1743  use_color = &info->highlight_color;
1744  }
1745  }
1746  }
1747 
1748  if (info->outline_show || flags & TEXT_OUTLINE) {
1749  int outline_x, outline_y;
1750  SDL_Rect outline_box;
1751 
1752  for (outline_x = -1; outline_x < 2; outline_x++) {
1753  for (outline_y = -1; outline_y < 2; outline_y++) {
1754  if (outline_x == 0 && outline_y == 0) {
1755  continue;
1756  }
1757 
1758  outline_box.x = dest->x + outline_x;
1759  outline_box.y = dest->y + outline_y + MAX(info->start_y - dest->y + outline_y, 0);
1760 
1761  if (flags & TEXT_SOLID) {
1762  ttf_surface = TTF_RenderText_Solid((*font)->font, buf, info->outline_color);
1763  } else {
1764  ttf_surface = TTF_RenderText_Blended((*font)->font, buf, info->outline_color);
1765  }
1766 
1767  if (info->used_alpha != 255) {
1768  surface_set_alpha(ttf_surface, info->used_alpha);
1769  }
1770 
1771  srcrect.x = 0;
1772  srcrect.y = MAX(info->start_y - dest->y + outline_y, 0);
1773  srcrect.w = ttf_surface->w;
1774  srcrect.h = box && box->h ? MAX(MIN(box->h - (outline_box.y - info->start_y), ttf_surface->h), 0) : ttf_surface->h;
1775 
1776  SDL_BlitSurface(ttf_surface, &srcrect, surface, &outline_box);
1777  SDL_FreeSurface(ttf_surface);
1778  }
1779  }
1780  }
1781 
1782  /* Render the character. */
1783  if (flags & TEXT_SOLID) {
1784  ttf_surface = TTF_RenderText_Solid((*font)->font, buf, *use_color);
1785 
1786  /* Opacity. */
1787  if (info->used_alpha != 255) {
1788  SDL_Surface *new_ttf_surface;
1789 
1790  /* Remove black border. */
1791  SDL_SetColorKey(ttf_surface, SDL_SRCCOLORKEY | SDL_ANYFORMAT, 0);
1792  /* Set the opacity. */
1793  SDL_SetAlpha(ttf_surface, SDL_SRCALPHA | SDL_RLEACCEL, info->used_alpha);
1794  /* Create new surface to blit. */
1795  new_ttf_surface = SDL_DisplayFormatAlpha(ttf_surface);
1796  /* Free the old one. */
1797  SDL_FreeSurface(ttf_surface);
1798  ttf_surface = new_ttf_surface;
1799  }
1800  } else {
1801  ttf_surface = TTF_RenderText_Blended((*font)->font, buf, *use_color);
1802 
1803  if (info->used_alpha != 255) {
1804  surface_set_alpha(ttf_surface, info->used_alpha);
1805  }
1806  }
1807 
1808  if (info->in_strikethrough) {
1809  int font_height;
1810 
1811  font_height = TTF_FontHeight((*font)->font);
1812  lineRGBA(ttf_surface, 0, font_height / 2, ttf_surface->w - 1, font_height / 2, use_color->r, use_color->g, use_color->b, 255);
1813  }
1814 
1815  if (info->flip) {
1816  SDL_Surface *ttf_surface_orig;
1817 
1818  ttf_surface_orig = ttf_surface;
1819  ttf_surface = zoomSurface(ttf_surface_orig, info->flip & TEXT_FLIP_HORIZONTAL ? -1.0 : 1.0, info->flip & TEXT_FLIP_VERTICAL ? -1.0 : 1.0, 0);
1820  SDL_FreeSurface(ttf_surface_orig);
1821  }
1822 
1823  /* Output the rendered character to the screen and free the
1824  * used surface. */
1825  dstrect.x = dest->x;
1826  dstrect.y = dest->y + MAX(info->start_y - dest->y, 0);
1827  srcrect.x = 0;
1828  srcrect.y = MAX(info->start_y - dest->y, 0);
1829  srcrect.w = ttf_surface->w;
1830  srcrect.h = box && box->h ? MAX(MIN(box->h - (dstrect.y - info->start_y), ttf_surface->h), 0) : ttf_surface->h;
1831 
1832  SDL_BlitSurface(ttf_surface, &srcrect, surface, &dstrect);
1833  SDL_FreeSurface(ttf_surface);
1834  }
1835 
1836  /* Update the x/w of the destination with the character's width. */
1837  if (surface) {
1838  dest->x += width;
1839  }
1840 
1841  dest->w += width;
1842 
1843  return ret;
1844 }
1845 
1855 int glyph_get_width(font_struct *font, char c)
1856 {
1857  int minx, width;
1858 
1859  if (TTF_GlyphMetrics(font->font, c == '\t' ? ' ' : c, &minx, NULL, NULL, NULL, &width) != -1) {
1860  if (minx < 0) {
1861  width -= minx;
1862  }
1863 
1864  if (c == '\t') {
1865  return width * 4;
1866  }
1867 
1868  return width;
1869  }
1870 
1871  return 0;
1872 }
1873 
1883 int glyph_get_height(font_struct *font, char c)
1884 {
1885  int miny, maxy;
1886 
1887  if (TTF_GlyphMetrics(font->font, c, NULL, NULL, &miny, &maxy, NULL) != -1) {
1888  if (miny) {
1889  maxy -= miny;
1890  }
1891 
1892  return maxy;
1893  }
1894 
1895  return 0;
1896 }
1897 
1901 #define TEXT_SHOW_SELECT_BEGIN() \
1902  { \
1903  if (!skip && selection_start && selection_end && surface != NULL) \
1904  { \
1905  select_start = *selection_start; \
1906  select_end = *selection_end; \
1907 \
1908  if (select_end < select_start) \
1909  { \
1910  select_start = *selection_end; \
1911  select_end = *selection_start; \
1912  } \
1913 \
1914  if (select_start >= 0 && select_end >= 0 && cp - text >= select_start && cp - text <= select_end) \
1915  { \
1916  SDL_Rect selection_box; \
1917 \
1918  selection_box.x = dest.x; \
1919  selection_box.y = dest.y; \
1920  selection_box.w = 0; \
1921  selection_box.h = FONT_HEIGHT(FONT_TRY_INFO(font, info, surface)); \
1922 \
1923  if (text_show_character(&font, orig_font, NULL, &selection_box, cp, &color, &orig_color, flags, box, &x_adjust, &info) == 1) \
1924  { \
1925  SDL_FillRect(surface, &selection_box, -1); \
1926 \
1927  select_color_orig = color; \
1928  color.r = color.g = color.b = 0; \
1929  select_color_changed = 1; \
1930  } \
1931  } \
1932  } \
1933  }
1934 
1938 #define TEXT_SHOW_SELECT_END() \
1939  { \
1940  if (select_color_changed) \
1941  { \
1942  color = select_color_orig; \
1943  select_color_changed = 0; \
1944  } \
1945  if (selection_start && selection_end && mstate == SDL_BUTTON_LEFT) \
1946  { \
1947  if (my >= dest.y && my <= dest.y + FONT_HEIGHT(FONT_TRY_INFO(font, info, surface)) && mx >= old_x && mx <= old_x + glyph_get_width(FONT_TRY_INFO(font, info, surface), *cp)) \
1948  { \
1949  if (*selection_started) \
1950  { \
1951  *selection_end = cp - text; \
1952  } \
1953  else \
1954  { \
1955  *selection_start = cp - text; \
1956  } \
1957  } \
1958  } \
1959  }
1960 
1983 void text_show(SDL_Surface *surface, font_struct *font, const char *text, int x, int y, const char *color_notation, uint64_t flags, SDL_Rect *box)
1984 {
1985  const char *cp = text;
1986  SDL_Rect dest;
1987  int pos = 0, last_space = 0, is_lf, ret, skip, max_height, max_width, height = 0;
1988  SDL_Color color, orig_color, select_color_orig;
1989  int lines = 1, width = 0;
1990  uint16_t *heights = NULL;
1991  size_t num_heights = 0;
1992  int x_adjust = 0;
1993  int mx, my, mstate = 0, old_x;
1994  int64_t select_start = 0, select_end = 0;
1995  uint8_t select_color_changed = 0;
1996  text_info_struct info;
1997  font_struct *orig_font = font;
1998 
1999  if (text_color_parse(color_notation, &color)) {
2000  orig_color = color;
2001  } else {
2002  LOG(BUG, "Invalid color: %s, text: %s", color_notation, text);
2003  return;
2004  }
2005 
2006  text_show_character_init(&info);
2007  info.start_x = x;
2008  info.start_y = y;
2009 
2010  if (text_debug && box && surface) {
2011  draw_frame(surface, x, y, box->w, box->h);
2012  }
2013 
2014  /* Align to the center. */
2015  if (box && flags & (TEXT_ALIGN_CENTER | TEXT_VALIGN_CENTER)) {
2016  uint16_t w, h;
2017 
2018  text_get_width_height(font, text, flags & ~(TEXT_ALIGN_CENTER | TEXT_VALIGN_CENTER), box, flags & TEXT_ALIGN_CENTER ? &w : NULL, flags & TEXT_VALIGN_CENTER ? &h : NULL);
2019 
2020  if (flags & TEXT_ALIGN_CENTER) {
2021  x += box->w / 2 - w / 2;
2022  }
2023 
2024  if (flags & TEXT_VALIGN_CENTER) {
2025  y += box->h / 2 - h / 2;
2026  }
2027  }
2028 
2029  if (selection_start && selection_end) {
2030  mstate = SDL_GetMouseState(&mx, &my);
2031 
2032  if (!text_adjust_coords(surface, &mx, &my)) {
2033  mstate = 0;
2034  }
2035 
2036  if (box && mx >= x && mx <= x + box->w && my >= y && my <= y + box->h) {
2037  cursor_texture = texture_get(TEXTURE_TYPE_CLIENT, "cursor_text");
2038  }
2039  }
2040 
2041  /* Store the x/y. */
2042  dest.x = x;
2043  dest.y = y;
2044  dest.w = 0;
2045  height = 0;
2046  max_height = 0;
2047  max_width = 0;
2048 
2049  if (flags & TEXT_HEIGHT) {
2050  dest.y -= box->y;
2051  }
2052 
2053  while (cp[pos] != '\0') {
2054  /* Have we gone over the height limit yet? */
2055  if (box && box->h && dest.y + (flags & (TEXT_LINES_CALC | TEXT_LINES_SKIP) ? FONT_HEIGHT(FONT_TRY_INFO(font, info, surface)) : 0) - y > box->h) {
2056  /* We are calculating height/lines, keep going on but without
2057  * any more drawing. */
2058  if ((flags & TEXT_LINES_CALC) || (flags & TEXT_HEIGHT && box->y == 0)) {
2059  surface = NULL;
2060  } else {
2061  return;
2062  }
2063  }
2064 
2065  is_lf = cp[pos] == '\n';
2066 
2067  /* Is this a newline, or word wrap was set and we are over
2068  * maximum width? */
2069  if (is_lf || (flags & TEXT_WORD_WRAP && box && box->w && dest.w + (flags & TEXT_MARKUP && cp[pos] == '[' ? 0 : glyph_get_width(FONT_TRY_INFO(font, info, surface), cp[pos])) > box->w)) {
2070  /* Store the last space. */
2071  if (is_lf || last_space == 0) {
2072  last_space = pos;
2073 
2074  if (last_space == 0 && !is_lf) {
2075  last_space = 1;
2076  }
2077  }
2078 
2079  skip = 0;
2080  max_height = 0;
2081 
2082  /* See if we should skip drawing. */
2083  if (flags & TEXT_MAX_WIDTH) {
2084  skip = 1;
2085  } else if (flags & TEXT_LINES_SKIP) {
2086  skip = box->y && lines - 1 < box->y;
2087  }
2088 
2089  info.height = height;
2090 
2091  /* Draw characters until we have reached the cut point (last_space).
2092  * */
2093  while (*cp != '\0' && last_space > 0) {
2094  old_x = dest.x;
2095 
2097 
2098  info.obscured = skip;
2099  ret = text_show_character(&font, orig_font, skip ? NULL : surface, &dest, cp, &color, &orig_color, flags, box, &x_adjust, &info);
2100  info.obscured = 0;
2101 
2103 
2104  cp += ret;
2105  last_space -= ret;
2106 
2107  if (FONT_HEIGHT(FONT_TRY_INFO(font, info, surface)) > max_height) {
2108  max_height = FONT_HEIGHT(FONT_TRY_INFO(font, info, surface));
2109  }
2110  }
2111 
2112  if (selection_start && selection_end && mstate == SDL_BUTTON_LEFT && box && my >= dest.y && my <= dest.y + FONT_HEIGHT(FONT_TRY_INFO(font, info, surface)) && mx >= dest.x && mx <= dest.x + (box->w - (dest.x - info.start_x))) {
2113  if (*selection_started) {
2114  *selection_end = cp - text;
2115  } else {
2116  *selection_start = cp - text;
2117  }
2118  }
2119 
2120  if (!max_height) {
2121  max_height = FONT_HEIGHT(FONT_TRY_INFO(font, info, surface));
2122  }
2123 
2124  /* Update the Y position. */
2125  if (!skip) {
2126  dest.y += max_height;
2127  }
2128 
2129  height += max_height;
2130  lines++;
2131 
2132  /* Calculating lines, store the height of this line. */
2133  if (flags & TEXT_LINES_CALC) {
2134  heights = erealloc(heights, sizeof(*heights) * (num_heights + 1));
2135  heights[num_heights] = max_height;
2136  num_heights++;
2137  }
2138 
2139  if (flags & TEXT_MAX_WIDTH && dest.w / 2 > max_width) {
2140  max_width = dest.w / 2;
2141  }
2142 
2143  /* Jump over the newline, if any. */
2144  if (is_lf) {
2145  cp++;
2146  } else {
2147  /* Strip leading spaces. */
2148  while (*cp != '\0' && *cp == ' ') {
2149  cp++;
2150  }
2151  }
2152 
2153  /* Update the coordinates. */
2154  last_space = pos = 0;
2155  dest.w = x_adjust;
2156  dest.x = x + x_adjust;
2157  max_height = 0;
2158  } else {
2159  /* Store last space position. */
2160  if (cp[pos] == ' ') {
2161  last_space = pos;
2162  }
2163 
2164  /* Do not do any drawing, just calculate how many characters
2165  * to jump and the width. */
2166  pos += text_show_character(&font, orig_font, NULL, &dest, cp + pos, &color, &orig_color, flags, box, &x_adjust, &info);
2167  }
2168  }
2169 
2170  max_height = 0;
2171 
2172  /* Draw leftover characters. */
2173  while (*cp != '\0') {
2174  if (flags & TEXT_WIDTH && box) {
2175  int w = glyph_get_width(font, *cp);
2176 
2177  if (box->w && width + w > box->w) {
2178  break;
2179  }
2180 
2181  width += w;
2182  }
2183 
2184  old_x = dest.x;
2185  skip = 0;
2187 
2188  cp += text_show_character(&font, orig_font, surface, &dest, cp, &color, &orig_color, flags, box, &x_adjust, &info);
2189 
2191 
2192  if (FONT_HEIGHT(FONT_TRY_INFO(font, info, surface)) > max_height) {
2193  max_height = FONT_HEIGHT(FONT_TRY_INFO(font, info, surface));
2194  }
2195  }
2196 
2197  if (selection_start && selection_end && mstate == SDL_BUTTON_LEFT && box && my >= dest.y + max_height && my <= dest.y + max_height + (box->h - ((dest.y + max_height) - info.start_y))) {
2198  if (*selection_started) {
2199  *selection_end = cp - text;
2200  } else {
2201  *selection_start = cp - text;
2202  }
2203  }
2204 
2205  if (!max_height) {
2206  max_height = FONT_HEIGHT(FONT_TRY_INFO(font, info, surface));
2207  }
2208 
2209  if (flags & TEXT_MAX_WIDTH && dest.w / 2 > max_width) {
2210  max_width = dest.w / 2;
2211  }
2212 
2213  if (box && flags & TEXT_MAX_WIDTH) {
2214  box->w = max_width;
2215  }
2216 
2217  /* Give caller access to the calculated height. */
2218  if (box && flags & TEXT_HEIGHT) {
2219  box->h = height + max_height;
2220  }
2221 
2222  /* Calculating lines? */
2223  if (box && flags & TEXT_LINES_CALC) {
2224  int total_height = 0, i, last_lines = 0;
2225 
2226  heights = erealloc(heights, sizeof(*heights) * (num_heights + 1));
2227  heights[num_heights] = max_height;
2228  num_heights++;
2229 
2230  /* Go backwards to figure out the maximum number of lines shown
2231  * at the end of the string. */
2232  for (i = num_heights - 1; i >= 0; i--) {
2233  if (total_height + heights[i] > box->h) {
2234  break;
2235  }
2236 
2237  total_height += heights[i];
2238  last_lines++;
2239  }
2240 
2241  efree(heights);
2242  box->y = last_lines;
2243  box->h = lines;
2244  }
2245 
2249  }
2250 
2252 }
2253 
2278 void text_show_shadow(SDL_Surface *surface, font_struct *font, const char *text, int x, int y, const char *color_notation, const char *color_shadow_notation, uint64_t flags, SDL_Rect *box)
2279 {
2280  text_show(surface, font, text, x + 1, y + 1, color_shadow_notation, flags | TEXT_NO_COLOR_CHANGE, box);
2281  text_show(surface, font, text, x, y, color_notation, flags, box);
2282 }
2283 
2289 void text_show_format(SDL_Surface *surface, font_struct *font, int x, int y, const char *color_notation, uint64_t flags, SDL_Rect *box, const char *format, ...)
2290 {
2291  char buf[HUGE_BUF * 4];
2292  va_list ap;
2293 
2294  va_start(ap, format);
2295  vsnprintf(buf, sizeof(buf), format, ap);
2296  text_show(surface, font, buf, x, y, color_notation, flags, box);
2297  va_end(ap);
2298 }
2299 
2305 void text_show_shadow_format(SDL_Surface *surface, font_struct *font, int x, int y, const char *color_notation, const char *color_shadow_notation, uint64_t flags, SDL_Rect *box, const char *format, ...)
2306 {
2307  char buf[HUGE_BUF * 4];
2308  va_list ap;
2309 
2310  va_start(ap, format);
2311  vsnprintf(buf, sizeof(buf), format, ap);
2312  text_show_shadow(surface, font, buf, x, y, color_notation, color_shadow_notation, flags, box);
2313  va_end(ap);
2314 }
2315 
2328 int text_get_width(font_struct *font, const char *text, uint64_t flags)
2329 {
2330  SDL_Rect dest;
2331  const char *cp = text;
2332  text_info_struct info;
2333 
2334  text_show_character_init(&info);
2335  TTF_SetFontStyle(font->font, TTF_STYLE_NORMAL);
2336 
2337  dest.w = 0;
2338  dest.x = 0;
2339  dest.y = 0;
2340 
2341  while (*cp != '\0') {
2342  cp += text_show_character(&font, font, NULL, &dest, cp, NULL, NULL, flags, NULL, NULL, &info);
2343  }
2344 
2345  return dest.w;
2346 }
2347 
2364 int text_get_height(font_struct *font, const char *text, uint64_t flags)
2365 {
2366  SDL_Rect dest;
2367  const char *cp;
2368  int max_height;
2369  text_info_struct info;
2370 
2371  max_height = FONT_HEIGHT(font);
2372 
2373  /* No markup, the text cannot become different size. */
2374  if (!(flags & TEXT_MARKUP)) {
2375  return max_height;
2376  }
2377 
2378  text_show_character_init(&info);
2379 
2380  cp = text;
2381  dest.w = 0;
2382  dest.x = 0;
2383  dest.y = 0;
2384 
2385  while (*cp != '\0') {
2386  cp += text_show_character(&font, font, NULL, &dest, cp, NULL, NULL, flags, NULL, NULL, &info);
2387 
2388  if (FONT_HEIGHT(font) > max_height) {
2389  max_height = FONT_HEIGHT(font);
2390  }
2391  }
2392 
2393  return max_height;
2394 }
2395 
2410 void text_get_width_height(font_struct *font, const char *text, uint64_t flags, SDL_Rect *box, uint16_t *w, uint16_t *h)
2411 {
2412  SDL_Rect box2;
2413 
2414  box2.w = box ? box->w : 0;
2415  box2.h = box ? box->h : 0;
2416  box2.y = 0;
2417 
2418  if (w) {
2419  flags |= TEXT_MAX_WIDTH;
2420  }
2421 
2422  if (h) {
2423  flags |= TEXT_HEIGHT;
2424  }
2425 
2426  text_show(NULL, font, text, 0, 0, COLOR_WHITE, flags, &box2);
2427 
2428  if (w) {
2429  *w = box2.w;
2430  }
2431 
2432  if (h) {
2433  *h = box2.h;
2434  }
2435 }
2436 
2446 void text_truncate_overflow(font_struct *font, char *text, int max_width)
2447 {
2448  size_t pos = 0;
2449  int width = 0;
2450 
2451  while (text[pos] != '\0') {
2452  width += glyph_get_width(font, text[pos]);
2453 
2454  if (width > max_width) {
2455  text[pos] = '\0';
2456  break;
2457  }
2458 
2459  pos++;
2460  }
2461 }
2462 
2471 void text_anchor_parse(text_info_struct *info, const char *text)
2472 {
2473  const char *cp = text;
2474  SDL_Rect dest;
2475  font_struct *font = FONT_ARIAL10;
2476 
2478  info->obscured = 1;
2479 
2480  dest.w = 0;
2481 
2482  while (*cp != '\0') {
2483  cp += text_show_character(&font, font, NULL, &dest, cp, NULL, NULL, TEXT_MARKUP, NULL, NULL, info);
2484 
2485  if (info->anchor_tag) {
2486  return;
2487  }
2488  }
2489 }
2490 
2495 {
2496  text_debug = 1;
2497 }
int(* text_anchor_handle_func)(const char *anchor_action, const char *buf, size_t len, void *custom_data)
Definition: text.h:348
void surface_show_effects(SDL_Surface *surface, int x, int y, SDL_Rect *srcrect, SDL_Surface *src, const sprite_effects_t *effects)
Definition: sprite.c:830
#define MAP_TILE_POS_YOFF
Definition: map.h:34
#define TEXT_WORD_WRAP
Definition: text.h:226
Definition: main.h:332
int text_color_parse(const char *color_notation, SDL_Color *color)
Definition: text.c:603
static uint32_t ticks
Definition: range_buttons.c:38
void text_set_selection(int64_t *start, int64_t *end, uint8_t *started)
Definition: text.c:425
char anchor_action[HUGE_BUF]
Definition: text.h:150
#define TEXT_LINES_SKIP
Definition: text.h:263
void text_deinit(void)
Definition: text.c:347
uint8_t obscured
Definition: text.h:180
uint8_t alpha
Alpha value.
Definition: sprite.h:48
SDL_Surface * bitmap
Definition: sprite.h:96
#define TEXT_SHOW_SELECT_BEGIN()
Definition: text.c:1901
uint8_t in_book_title
Definition: text.h:159
SDL_Surface * texture_surface(texture_struct *texture)
Definition: texture.c:303
int16_t rotate
Rotate value.
Definition: sprite.h:52
int16_t zoom_x
Horizontal zoom.
Definition: sprite.h:50
SDL_Color outline_color
Definition: text.h:153
#define SDL_color_copy(_color, _color2)
Definition: client.h:94
static font_struct * fonts
Definition: text.c:83
_multi_part_tile part[16]
Definition: map.h:113
font_struct * font_get_size(font_struct *font, int8_t size)
Definition: text.c:242
uint32_t flags
Bit combination of Sprite drawing flags.
Definition: sprite.h:46
char * name
Definition: text.h:39
void text_show(SDL_Surface *surface, font_struct *font, const char *text, int x, int y, const char *color_notation, uint64_t flags, SDL_Rect *box)
Definition: text.c:1983
void text_show_character_init(text_info_struct *info)
Definition: text.c:704
void draw_frame(SDL_Surface *surface, int x, int y, int w, int h)
Definition: sprite.c:1185
int border_left
Definition: sprite.h:90
static TTF_Font * font_open(const char *name, uint8_t size)
Definition: text.c:115
int border_down
Definition: sprite.h:87
void text_init(void)
Definition: text.c:336
void tooltip_create(int mx, int my, font_struct *font, const char *text)
Definition: tooltip.c:60
static font_struct * font_new(const char *name, uint8_t size)
Definition: text.c:145
static char tooltip_text[HUGE_BUF *4]
Definition: tooltip.c:35
void text_offset_reset(void)
Definition: text.c:391
texture_struct * texture_get(texture_type_t type, const char *name)
Definition: texture.c:279
void font_free(font_struct *font)
Definition: text.c:262
font_struct * font_get(const char *name, uint8_t size)
Definition: text.c:216
TTF_Font * TTF_OpenFont_wrapper(const char *file, int ptsize)
Definition: wrapper.c:545
SDL_Surface * ScreenSurface
Definition: main.c:47
int height
Definition: text.h:48
#define TEXT_MARKUP
Definition: text.h:224
uint32_t stretch
Tile stretching value.
Definition: sprite.h:49
int border_right
Definition: sprite.h:93
void browser_open(const char *url)
Definition: misc.c:37
#define TEXT_ALIGN_CENTER
Definition: text.h:230
static char * font_get_hash_key(const char *name, uint8_t size, char *buf, size_t buf_size)
Definition: text.c:98
const char * anchor_tag
Definition: text.h:147
uint8_t in_strikethrough
Definition: text.h:174
void text_offset_set(int x, int y)
Definition: text.c:381
widgetdata * widget_find(widgetdata *where, int type, const char *id, SDL_Surface *surface)
Definition: widget.c:2091
void help_show(const char *name)
Definition: help.c:219
_multi_part_obj MultiArchs[16]
Definition: map.c:69
int xlen
Definition: map.h:107
#define FONT_DECREF(font)
Definition: text.h:73
static uint8_t text_debug
Definition: text.c:39
#define FONT_GC_CHANCE
Definition: text.h:83
#define TEXT_NO_FONT_CHANGE
Definition: text.h:267
void text_get_width_height(font_struct *font, const char *text, uint64_t flags, SDL_Rect *box, uint16_t *w, uint16_t *h)
Definition: text.c:2410
font_struct * font_get_weak(const char *name, uint8_t size)
Definition: text.c:186
int yoff
Definition: map.h:95
uint8_t outline_show
Definition: text.h:156
int text_get_height(font_struct *font, const char *text, uint64_t flags)
Definition: text.c:2364
void text_show_shadow_format(SDL_Surface *surface, font_struct *font, int x, int y, const char *color_notation, const char *color_shadow_notation, uint64_t flags, SDL_Rect *box, const char *format,...)
Definition: text.c:2305
static int64_t * selection_start
Definition: text.c:59
Client_Player cpl
Definition: client.c:50
_face_struct FaceList[MAX_FACE_TILES]
Definition: main.c:77
font_struct * calc_font
Definition: text.h:189
static text_anchor_handle_func text_anchor_handle
Definition: text.c:71
void text_set_anchor_handle(text_anchor_handle_func func)
Definition: text.c:437
int text_show_character(font_struct **font, font_struct *orig_font, SDL_Surface *surface, SDL_Rect *dest, const char *cp, SDL_Color *color, SDL_Color *orig_color, uint64_t flags, SDL_Rect *box, int *x_adjust, text_info_struct *info)
Definition: text.c:753
void text_truncate_overflow(font_struct *font, char *text, int max_width)
Definition: text.c:2446
static int text_offset_mx
Definition: text.c:54
int xoff
Definition: map.h:92
int image_get_id(const char *name)
Definition: image.c:448
int64_t setting_get_int(int cat, int setting)
Definition: settings.c:414
static SDL_Color text_link_color_default
Definition: text.c:66
uint8_t calc_bold
Definition: text.h:183
int glyph_get_height(font_struct *font, char c)
Definition: text.c:1883
static int text_offset_my
Definition: text.c:56
#define FONT_HEIGHT(font)
Definition: text.h:327
void text_set_anchor_info(void *ptr)
Definition: text.c:447
int surface_borders_get(SDL_Surface *surface, int *top, int *bottom, int *left, int *right, uint32_t color)
Definition: sprite.c:1117
void text_show_shadow(SDL_Surface *surface, font_struct *font, const char *text, int x, int y, const char *color_notation, const char *color_shadow_notation, uint64_t flags, SDL_Rect *box)
Definition: text.c:2278
int border_up
Definition: sprite.h:84
void font_gc(void)
Definition: text.c:297
int x
Definition: widget.h:48
#define TEXT_HEIGHT
Definition: text.h:241
#define FONT_GC_FREE_TIME
Definition: text.h:88
uint8_t dark_level
Dark level.
Definition: sprite.h:47
void text_anchor_execute(text_info_struct *info, void *custom_data)
Definition: text.c:632
void tooltip_enable_delay(uint32_t delay)
Definition: tooltip.c:82
uint8_t size
Definition: text.h:42
static char text_anchor_help[HUGE_BUF]
Definition: text.c:51
#define MAP_TILE_POS_XOFF
Definition: map.h:40
#define TEXT_OUTLINE
Definition: text.h:255
uint8_t in_bold
Definition: text.h:165
#define INVENTORY_ICON_SIZE
Definition: inventory.h:60
static uint8_t text_anchor_help_clicked
Definition: text.c:48
void text_anchor_parse(text_info_struct *info, const char *text)
Definition: text.c:2471
static void * text_anchor_info_ptr
Definition: text.c:78
#define TEXT_NO_COLOR_CHANGE
Definition: text.h:250
static int text_adjust_coords(SDL_Surface *surface, int *mx, int *my)
Definition: text.c:563
void text_color_set(int r, int g, int b)
Definition: text.c:406
int ylen
Definition: map.h:110
#define TEXT_WIDTH
Definition: text.h:272
uint8_t used_alpha
Definition: text.h:162
void tooltip_multiline(int max_width)
Definition: tooltip.c:96
void text_enable_debug(void)
Definition: text.c:2494
void border_create(SDL_Surface *surface, int x, int y, int w, int h, int color, int size)
Definition: sprite.c:1226
object * object_find(tag_t tag)
Definition: item.c:139
static SDL_Color text_link_color
Definition: text.c:68
void border_create_texture(SDL_Surface *surface, SDL_Rect *coords, int thickness, SDL_Surface *texture)
Definition: sprite.c:1388
void surface_show_fill(SDL_Surface *surface, int x, int y, SDL_Rect *srcsize, SDL_Surface *src, SDL_Rect *box)
Definition: sprite.c:792
static uint8_t * selection_started
Definition: text.c:63
uint8_t in_italic
Definition: text.h:168
int text_get_width(font_struct *font, const char *text, uint64_t flags)
Definition: text.c:2328
int send_command_check(const char *cmd)
Definition: menu.c:376
int16_t zoom_y
Vertical zoom.
Definition: sprite.h:51
uint8_t in_underline
Definition: text.h:171
TTF_Font * font
Definition: text.h:45
int glyph_get_width(font_struct *font, char c)
Definition: text.c:1855
char * key
Definition: text.h:36
#define TEXT_SOLID
Definition: text.h:228
#define TEXT_LINES_CALC
Definition: text.h:259
void book_name_change(const char *name, size_t len)
Definition: book.c:59
char * text_strip_markup(char *buf, size_t *buf_len, uint8_t do_free)
Definition: text.c:468
static int64_t * selection_end
Definition: text.c:61
void text_show_format(SDL_Surface *surface, font_struct *font, int x, int y, const char *color_notation, uint64_t flags, SDL_Rect *box, const char *format,...)
Definition: text.c:2289
#define TEXT_SHOW_SELECT_END()
Definition: text.c:1938
void object_show_centered(SDL_Surface *surface, object *tmp, int x, int y, int w, int h, bool fit)
Definition: item.c:545
int hcenter_y
Definition: text.h:194
#define TEXT_MAX_WIDTH
Definition: text.h:277
#define TEXT_VALIGN_CENTER
Definition: text.h:246
unsigned int ref
Definition: text.h:51
time_t last_used
Definition: text.h:54
#define FONT_GC_MAX_TIME
Definition: text.h:79
#define COLOR_WHITE
Definition: text.h:289
void surface_set_alpha(SDL_Surface *surface, uint8_t alpha)
Definition: sprite.c:1476
char * text_escape_markup(const char *buf)
Definition: text.c:532
int y
Definition: widget.h:51