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 #include <fc_config.h>
34 #include "featured_text.h"
40 #define MAX_LEN_STR 32
41 #define log_featured_text log_verbose
43 #define text_tag_list_rev_iterate(tags, ptag) \
44 TYPED_LIST_ITERATE_REV(struct text_tag, tags, ptag)
45 #define text_tag_list_rev_iterate_end LIST_ITERATE_REV_END
47 /* The text_tag structure. See documentation in featured_text.h. */
49 enum text_tag_type type
; /* The type of the tag. */
50 ft_offset_t start_offset
; /* The start offset (in bytes). */
51 ft_offset_t stop_offset
; /* The stop offset (in bytes). */
53 struct { /* TTT_COLOR only. */
54 char foreground
[MAX_LEN_STR
]; /* foreground color name. */
55 char background
[MAX_LEN_STR
]; /* background color name. */
57 struct { /* TTT_LINK only. */
58 enum text_link_type type
; /* The target type of the link. */
59 int id
; /* The id of linked object. */
60 char name
[MAX_LEN_STR
]; /* A string to indentify the link. */
66 ST_START
, /* e.g. [sequence]. */
67 ST_STOP
, /* e.g. [/sequence]. */
68 ST_SINGLE
/* e.g. [sequence/]. */
71 /* Predefined colors. */
72 const struct ft_color ftc_any
= FT_COLOR(NULL
, NULL
);
74 const struct ft_color ftc_warning
= FT_COLOR("#FF0000", NULL
);
75 const struct ft_color ftc_log
= FT_COLOR("#7F7F7F", NULL
);
76 const struct ft_color ftc_server
= FT_COLOR("#8B0000", NULL
);
77 const struct ft_color ftc_client
= FT_COLOR("#EF7F00", NULL
);
78 const struct ft_color ftc_editor
= FT_COLOR("#0000FF", NULL
);
79 const struct ft_color ftc_command
= FT_COLOR("#006400", NULL
);
80 struct ft_color ftc_changed
= FT_COLOR("#FF0000", NULL
);
81 const struct ft_color ftc_server_prompt
= FT_COLOR("#FF0000", "#BEBEBE");
82 const struct ft_color ftc_player_lost
= FT_COLOR("#FFFFFF", "#000000");
83 const struct ft_color ftc_game_start
= FT_COLOR("#00FF00", "#115511");
85 const struct ft_color ftc_chat_public
= FT_COLOR("#00008B", NULL
);
86 const struct ft_color ftc_chat_ally
= FT_COLOR("#551166", NULL
);
87 const struct ft_color ftc_chat_private
= FT_COLOR("#A020F0", NULL
);
88 const struct ft_color ftc_chat_luaconsole
= FT_COLOR("#006400", NULL
);
90 const struct ft_color ftc_vote_public
= FT_COLOR("#FFFFFF", "#AA0000");
91 const struct ft_color ftc_vote_team
= FT_COLOR("#FFFFFF", "#5555CC");
92 const struct ft_color ftc_vote_passed
= FT_COLOR("#006400", "#AAFFAA");
93 const struct ft_color ftc_vote_failed
= FT_COLOR("#8B0000", "#FFAAAA");
94 const struct ft_color ftc_vote_yes
= FT_COLOR("#000000", "#C8FFD5");
95 const struct ft_color ftc_vote_no
= FT_COLOR("#000000", "#FFD2D2");
96 const struct ft_color ftc_vote_abstain
= FT_COLOR("#000000", "#E8E8E8");
98 const struct ft_color ftc_luaconsole_input
= FT_COLOR("#2B008B", NULL
);
99 const struct ft_color ftc_luaconsole_error
= FT_COLOR("#FF0000", NULL
);
100 const struct ft_color ftc_luaconsole_normal
= FT_COLOR("#006400", NULL
);
101 const struct ft_color ftc_luaconsole_verbose
= FT_COLOR("#B8B8B8", NULL
);
102 const struct ft_color ftc_luaconsole_debug
= FT_COLOR("#B87676", NULL
);
104 /**************************************************************************
105 Return the long name of the text tag type.
106 See also text_tag_type_short_name().
107 **************************************************************************/
108 static const char *text_tag_type_name(enum text_tag_type type
)
124 /* Don't handle the default case to be warned if a new value was added. */
128 /**************************************************************************
129 Return the name abbreviation of the text tag type.
130 See also text_tag_type_name().
131 **************************************************************************/
132 static const char *text_tag_type_short_name(enum text_tag_type type
)
148 /* Don't handle the default case to be warned if a new value was added. */
152 /**************************************************************************
153 Return the name of the text tag link target type.
154 **************************************************************************/
155 static const char *text_link_type_name(enum text_link_type type
)
165 /* Don't handle the default case to be warned if a new value was added. */
169 /**************************************************************************
170 Find inside a sequence the string associated to a particular option name.
171 Returns TRUE on success.
172 **************************************************************************/
173 static bool find_option(const char *buf_in
, const char *option
,
174 char *buf_out
, size_t write_len
)
176 size_t option_len
= strlen(option
);
178 while (*buf_in
!= '\0') {
179 while (fc_isspace(*buf_in
) && *buf_in
!= '\0') {
183 if (0 == strncasecmp(buf_in
, option
, option_len
)) {
184 /* This is this one. */
185 buf_in
+= option_len
;
187 while ((fc_isspace(*buf_in
) || *buf_in
== '=') && *buf_in
!= '\0') {
190 if (*buf_in
== '"') {
192 const char *end
= strchr(++buf_in
, '"');
197 if (end
- buf_in
+ 1 > 0) {
198 fc_strlcpy(buf_out
, buf_in
, MIN(end
- buf_in
+ 1, write_len
));
204 while (fc_isalnum(*buf_in
) && write_len
> 1) {
205 *buf_out
++ = *buf_in
++;
218 /**************************************************************************
219 Initialize a text_tag structure from a string sequence.
220 Returns TRUE on success.
221 **************************************************************************/
222 static bool text_tag_init_from_sequence(struct text_tag
*ptag
,
223 enum text_tag_type type
,
224 ft_offset_t start_offset
,
225 const char *sequence
)
228 ptag
->start_offset
= start_offset
;
229 ptag
->stop_offset
= FT_OFFSET_UNSET
;
239 if (!find_option(sequence
, "foreground", ptag
->color
.foreground
,
240 sizeof(ptag
->color
.foreground
))
241 && !find_option(sequence
, "fg", ptag
->color
.foreground
,
242 sizeof(ptag
->color
.foreground
))) {
243 ptag
->color
.foreground
[0] = '\0';
245 if (!find_option(sequence
, "background", ptag
->color
.background
,
246 sizeof(ptag
->color
.background
))
247 && !find_option(sequence
, "bg", ptag
->color
.background
,
248 sizeof(ptag
->color
.background
))) {
249 ptag
->color
.background
[0] = '\0';
259 if (!find_option(sequence
, "target", buf
, sizeof(buf
))
260 && !find_option(sequence
, "tgt", buf
, sizeof(buf
))) {
261 log_featured_text("text_tag_init_from_sequence(): "
262 "target link type not set.");
266 ptag
->link
.type
= -1;
267 for (i
= 0; (name
= text_link_type_name(i
)); i
++) {
268 if (0 == fc_strncasecmp(buf
, name
, strlen(name
))) {
273 if (ptag
->link
.type
== -1) {
274 log_featured_text("text_tag_init_from_sequence(): "
275 "target link type not supported (\"%s\").", buf
);
279 switch (ptag
->link
.type
) {
282 if (!find_option(sequence
, "id", buf
, sizeof(buf
))) {
283 log_featured_text("text_tag_init_from_sequence(): "
284 "city link without id.");
287 if (!str_to_int(buf
, &ptag
->link
.id
)) {
288 log_featured_text("text_tag_init_from_sequence(): "
289 "city link without valid id (\"%s\").", buf
);
293 if (!find_option(sequence
, "name", ptag
->link
.name
,
294 sizeof(ptag
->link
.name
))) {
295 /* Set something as name. */
296 fc_snprintf(ptag
->link
.name
, sizeof(ptag
->link
.name
),
297 "CITY_ID%d", ptag
->link
.id
);
306 if (!find_option(sequence
, "x", buf
, sizeof(buf
))) {
307 log_featured_text("text_tag_init_from_sequence(): "
308 "tile link without x coordinate.");
311 if (!str_to_int(buf
, &x
)) {
312 log_featured_text("text_tag_init_from_sequence(): "
313 "tile link without valid x coordinate "
318 if (!find_option(sequence
, "y", buf
, sizeof(buf
))) {
319 log_featured_text("text_tag_init_from_sequence(): "
320 "tile link without y coordinate.");
323 if (!str_to_int(buf
, &y
)) {
324 log_featured_text("text_tag_init_from_sequence(): "
325 "tile link without valid y coordinate "
330 ptile
= map_pos_to_tile(x
, y
);
332 log_featured_text("text_tag_init_from_sequence(): "
333 "(%d, %d) are not valid coordinates "
334 "in this game.", x
, y
);
337 ptag
->link
.id
= tile_index(ptile
);
338 fc_snprintf(ptag
->link
.name
, sizeof(ptag
->link
.name
),
339 "(%d, %d)", TILE_XY(ptile
));
344 if (!find_option(sequence
, "id", buf
, sizeof(buf
))) {
345 log_featured_text("text_tag_init_from_sequence(): "
346 "unit link without id.");
349 if (!str_to_int(buf
, &ptag
->link
.id
)) {
350 log_featured_text("text_tag_init_from_sequence(): "
351 "unit link without valid id (\"%s\").", buf
);
355 if (!find_option(sequence
, "name", ptag
->link
.name
,
356 sizeof(ptag
->link
.name
))) {
357 /* Set something as name. */
358 fc_snprintf(ptag
->link
.name
, sizeof(ptag
->link
.name
),
359 "UNIT_ID%d", ptag
->link
.id
);
369 /**************************************************************************
370 Initialize a text_tag structure from a va_list.
372 What's should be in the va_list:
373 - If the text tag type is TTT_BOLD, TTT_ITALIC, TTT_STRIKE or
374 TTT_UNDERLINE, there shouldn't be any extra argument.
375 - If the text tag type is TTT_COLOR, then there should be 1 argument of
376 type 'struct ft_color'.
377 - If the text tag type is TTT_LINK, then there should be 2 extra arguments.
378 The first is type 'enum text_link_type' and will determine the type of the
380 - If the link type is TLT_CITY, last argument is typed 'struct city *'.
381 - If the link type is TLT_TILE, last argument is typed 'struct tile *'.
382 - If the link type is TLT_UNIT, last argument is typed 'struct unit *'.
384 Returns TRUE on success.
385 **************************************************************************/
386 static bool text_tag_initv(struct text_tag
*ptag
, enum text_tag_type type
,
387 ft_offset_t start_offset
, ft_offset_t stop_offset
,
391 ptag
->start_offset
= start_offset
;
392 ptag
->stop_offset
= stop_offset
;
402 const struct ft_color color
= va_arg(args
, struct ft_color
);
404 if ((NULL
== color
.foreground
|| '\0' == color
.foreground
[0])
405 && (NULL
== color
.background
|| '\0' == color
.background
[0])) {
406 return FALSE
; /* No color at all. */
409 if (NULL
!= color
.foreground
&& '\0' != color
.foreground
[0]) {
410 sz_strlcpy(ptag
->color
.foreground
, color
.foreground
);
412 ptag
->color
.foreground
[0] = '\0';
415 if (NULL
!= color
.background
&& '\0' != color
.background
[0]) {
416 sz_strlcpy(ptag
->color
.background
, color
.background
);
418 ptag
->color
.background
[0] = '\0';
424 ptag
->link
.type
= va_arg(args
, enum text_link_type
);
425 switch (ptag
->link
.type
) {
428 struct city
*pcity
= va_arg(args
, struct city
*);
433 ptag
->link
.id
= pcity
->id
;
434 sz_strlcpy(ptag
->link
.name
, city_name_get(pcity
));
439 struct tile
*ptile
= va_arg(args
, struct tile
*);
444 ptag
->link
.id
= tile_index(ptile
);
445 fc_snprintf(ptag
->link
.name
, sizeof(ptag
->link
.name
),
446 "(%d, %d)", TILE_XY(ptile
));
451 struct unit
*punit
= va_arg(args
, struct unit
*);
456 ptag
->link
.id
= punit
->id
;
457 sz_strlcpy(ptag
->link
.name
, unit_name_translation(punit
));
466 /**************************************************************************
467 Print in a string the start sequence of the tag.
468 **************************************************************************/
469 static size_t text_tag_start_sequence(const struct text_tag
*ptag
,
470 char *buf
, size_t len
)
472 switch (ptag
->type
) {
477 return fc_snprintf(buf
, len
, "%c%s%c", SEQ_START
,
478 text_tag_type_short_name(ptag
->type
), SEQ_STOP
);
481 size_t ret
= fc_snprintf(buf
, len
, "%c%s", SEQ_START
,
482 text_tag_type_short_name(ptag
->type
));
484 if (ptag
->color
.foreground
[0] != '\0') {
485 ret
+= fc_snprintf(buf
+ ret
, len
- ret
, " fg=\"%s\"",
486 ptag
->color
.foreground
);
488 if (ptag
->color
.background
[0] != '\0') {
489 ret
+= fc_snprintf(buf
+ ret
, len
- ret
, " bg=\"%s\"",
490 ptag
->color
.background
);
492 return ret
+ fc_snprintf(buf
+ ret
, len
- ret
, "%c", SEQ_STOP
);
496 size_t ret
= fc_snprintf(buf
, len
, "%c%s tgt=\"%s\"", SEQ_START
,
497 text_tag_type_short_name(ptag
->type
),
498 text_link_type_name(ptag
->link
.type
));
500 switch (ptag
->link
.type
) {
503 struct city
*pcity
= game_city_by_number(ptag
->link
.id
);
506 ret
+= fc_snprintf(buf
+ ret
, len
- ret
,
507 " id=%d name=\"%s\"",
508 pcity
->id
, city_name_get(pcity
));
510 ret
+= fc_snprintf(buf
+ ret
, len
- ret
,
511 " id=%d", ptag
->link
.id
);
517 struct tile
*ptile
= index_to_tile(ptag
->link
.id
);
520 ret
+= fc_snprintf(buf
+ ret
, len
- ret
,
521 " x=%d y=%d", TILE_XY(ptile
));
523 ret
+= fc_snprintf(buf
+ ret
, len
- ret
,
524 " id=%d", ptag
->link
.id
);
530 struct unit
*punit
= game_unit_by_number(ptag
->link
.id
);
533 ret
+= fc_snprintf(buf
+ ret
, len
- ret
,
534 " id=%d name=\"%s\"",
535 punit
->id
, unit_name_translation(punit
));
537 ret
+= fc_snprintf(buf
+ ret
, len
- ret
,
538 " id=%d", ptag
->link
.id
);
544 if (ptag
->stop_offset
== ptag
->start_offset
) {
545 /* This is a single sequence like [link ... /]. */
546 ret
+= fc_snprintf(buf
+ ret
, len
- ret
, "%c", SEQ_END
);
549 return ret
+ fc_snprintf(buf
+ ret
, len
- ret
, "%c", SEQ_STOP
);
555 /**************************************************************************
556 Print in a string the stop sequence of the tag.
557 **************************************************************************/
558 static size_t text_tag_stop_sequence(const struct text_tag
*ptag
,
559 char *buf
, size_t len
)
561 if (ptag
->type
== TTT_LINK
&& ptag
->stop_offset
== ptag
->start_offset
) {
562 /* Should be already finished. */
566 return fc_snprintf(buf
, len
, "%c%c%s%c", SEQ_START
, SEQ_END
,
567 text_tag_type_short_name(ptag
->type
), SEQ_STOP
);
570 /**************************************************************************
571 When the sequence looks like [sequence/] then we insert a string instead.
572 **************************************************************************/
573 static size_t text_tag_replace_text(const struct text_tag
*ptag
,
574 char *buf
, size_t len
,
575 bool replace_link_text
)
577 if (ptag
->type
!= TTT_LINK
) {
581 if (replace_link_text
) {
582 /* The client might check if this should be updated or translated. */
583 switch (ptag
->link
.type
) {
586 struct city
*pcity
= game_city_by_number(ptag
->link
.id
);
588 /* Note that if city_tile(pcity) is NULL, then it is probably an
589 * invisible city (see client/packhand.c). Then, we don't
590 * use the current city name which is usually not complete,
591 * a dumb string using the city id. */
592 if (NULL
!= pcity
&& NULL
!= city_tile(pcity
)) {
593 return fc_snprintf(buf
, len
, "%s", city_name_get(pcity
));
601 struct unit
*punit
= game_unit_by_number(ptag
->link
.id
);
604 return fc_snprintf(buf
, len
, "%s", unit_name_translation(punit
));
611 if (ptag
->link
.type
== TLT_UNIT
) {
612 /* Attempt to translate the link name (it should be a unit type name). */
613 return fc_snprintf(buf
, len
, "%s", _(ptag
->link
.name
));
615 return fc_snprintf(buf
, len
, "%s", ptag
->link
.name
);
619 /**************************************************************************
620 Returns a new text_tag or NULL on error.
623 - If tag_type is TTT_BOLD, TTT_ITALIC, TTT_STRIKE or TTT_UNDERLINE, there
624 shouldn't be any extra argument.
625 - If tag_type is TTT_COLOR:
626 struct text_tag *text_tag_new(..., const struct ft_color color);
627 - If tag_type is TTT_LINK and you want a city link:
628 struct text_tag *text_tag_new(..., TLT_CITY, struct city *pcity);
629 - If tag_type is TTT_LINK and you want a tile link:
630 struct text_tag *text_tag_new(..., TLT_TILE, struct tile *ptile);
631 - If tag_type is TTT_LINK and you want an unit link:
632 struct text_tag *text_tag_new(..., TLT_UNIT, struct unit *punit);
634 See also comment for text_tag_initv().
635 **************************************************************************/
636 struct text_tag
*text_tag_new(enum text_tag_type tag_type
,
637 ft_offset_t start_offset
,
638 ft_offset_t stop_offset
,
641 struct text_tag
*ptag
= fc_malloc(sizeof(struct text_tag
));
645 va_start(args
, stop_offset
);
646 ok
= text_tag_initv(ptag
, tag_type
, start_offset
, stop_offset
, args
);
657 /**************************************************************************
658 This function returns a new pointer to a text_tag which is similar
659 to the 'ptag' argument.
660 **************************************************************************/
661 struct text_tag
*text_tag_copy(const struct text_tag
*ptag
)
663 struct text_tag
*pnew_tag
;
669 pnew_tag
= fc_malloc(sizeof(struct text_tag
));
675 /**************************************************************************
676 Free a text_tag structure.
677 **************************************************************************/
678 void text_tag_destroy(struct text_tag
*ptag
)
683 /**************************************************************************
684 Return the type of this text tag.
685 **************************************************************************/
686 enum text_tag_type
text_tag_type(const struct text_tag
*ptag
)
691 /**************************************************************************
692 Return the start offset (in bytes) of this text tag.
693 **************************************************************************/
694 ft_offset_t
text_tag_start_offset(const struct text_tag
*ptag
)
696 return ptag
->start_offset
;
699 /**************************************************************************
700 Return the stop offset (in bytes) of this text tag.
701 **************************************************************************/
702 ft_offset_t
text_tag_stop_offset(const struct text_tag
*ptag
)
704 return ptag
->stop_offset
;
707 /**************************************************************************
708 Return the foreground color suggested by this text tag. This requires
709 the tag type to be TTT_COLOR. Returns NULL on error, "" if unset.
710 **************************************************************************/
711 const char *text_tag_color_foreground(const struct text_tag
*ptag
)
713 if (ptag
->type
!= TTT_COLOR
) {
714 log_error("text_tag_color_foreground(): incompatible tag type.");
718 return ptag
->color
.foreground
;
721 /**************************************************************************
722 Return the background color suggested by this text tag. This requires
723 the tag type to be TTT_COLOR. Returns NULL on error, "" if unset.
724 **************************************************************************/
725 const char *text_tag_color_background(const struct text_tag
*ptag
)
727 if (ptag
->type
!= TTT_COLOR
) {
728 log_error("text_tag_color_background(): incompatible tag type.");
732 return ptag
->color
.background
;
735 /**************************************************************************
736 Return the link target type suggested by this text tag. This requires
737 the tag type to be TTT_LINK. Returns -1 on error.
738 **************************************************************************/
739 enum text_link_type
text_tag_link_type(const struct text_tag
*ptag
)
741 if (ptag
->type
!= TTT_LINK
) {
742 log_error("text_tag_link_type(): incompatible tag type.");
746 return ptag
->link
.type
;
749 /**************************************************************************
750 Return the link target id suggested by this text tag (city id,
751 tile index or unit id). This requires the tag type to be TTT_LINK.
753 **************************************************************************/
754 int text_tag_link_id(const struct text_tag
*ptag
)
756 if (ptag
->type
!= TTT_LINK
) {
757 log_error("text_tag_link_id(): incompatible tag type.");
761 return ptag
->link
.id
;
764 /**************************************************************************
765 Extract a sequence from a string. Also, determine the type and the text
766 tag type of the sequence. Return 0 on error.
767 **************************************************************************/
768 static size_t extract_sequence_text(const char *featured_text
,
769 char *buf
, size_t len
,
770 enum sequence_type
*seq_type
,
771 enum text_tag_type
*type
)
773 const char *buf_in
= featured_text
;
774 const char *stop
= strchr(buf_in
, SEQ_STOP
);
775 const char *end
= stop
;
782 return 0; /* Not valid. */
785 /* Check sequence type. */
786 for (buf_in
++; fc_isspace(*buf_in
); buf_in
++);
788 if (*buf_in
== SEQ_END
) {
792 for (end
--; fc_isspace(*end
); end
--);
794 if (*end
== SEQ_END
) {
795 *seq_type
= ST_SINGLE
;
797 for (end
--; fc_isspace(*end
); end
--);
799 *seq_type
= ST_START
;
803 while (fc_isspace(*buf_in
)) {
807 /* Check the length of the type name. */
808 for (name
= buf_in
; name
< stop
; name
++) {
809 if (!fc_isalpha(*name
)) {
813 type_len
= name
- buf_in
;
816 for (i
= 0; (name
= text_tag_type_name(i
)); i
++) {
817 name_len
= strlen(name
);
818 if (name_len
== type_len
&& 0 == fc_strncasecmp(name
, buf_in
, name_len
)) {
825 /* Try with short names. */
826 for (i
= 0; (name
= text_tag_type_short_name(i
)); i
++) {
827 name_len
= strlen(name
);
828 if (name_len
== type_len
829 && 0 == fc_strncasecmp(name
, buf_in
, name_len
)) {
836 return 0; /* Not valid. */
840 while (fc_isspace(*buf_in
)) {
844 if (end
- buf_in
+ 2 > 0) {
845 fc_strlcpy(buf
, buf_in
, MIN(end
- buf_in
+ 2, len
));
849 return stop
- featured_text
+ 1;
853 /**************************************************************************
854 Separate the text from the text features. 'tags' can be NULL.
856 When 'replace_link_text' is set, the text used for the signal sequence
857 links will be overwritten. It is used on client side to have updated
858 links in chatline, to communicate when users don't know share the city
859 names, and avoid users making voluntary confusing names when editing
861 **************************************************************************/
862 size_t featured_text_to_plain_text(const char *featured_text
,
863 char *plain_text
, size_t plain_text_len
,
864 struct text_tag_list
**tags
,
865 bool replace_link_text
)
867 const char *text_in
= featured_text
;
868 char *text_out
= plain_text
;
869 size_t text_out_len
= plain_text_len
;
872 *tags
= text_tag_list_new();
875 while (*text_in
!= '\0' && text_out_len
> 1) {
876 if (SEQ_START
== *text_in
) {
877 /* Escape sequence... */
878 char buf
[text_out_len
];
879 enum sequence_type seq_type
;
880 enum text_tag_type type
;
881 size_t len
= extract_sequence_text(text_in
, buf
, text_out_len
,
885 /* Looks a valid sequence. */
890 /* Create a new tag. */
891 struct text_tag
*ptag
= fc_malloc(sizeof(struct text_tag
));
893 if (text_tag_init_from_sequence(ptag
, type
,
894 text_out
- plain_text
, buf
)) {
895 text_tag_list_append(*tags
, ptag
);
897 text_tag_destroy(ptag
);
898 log_featured_text("Couldn't create a text tag with \"%s\".",
905 /* Set the stop offset. */
906 struct text_tag
*ptag
= NULL
;
908 /* Look up on reversed order. */
909 text_tag_list_rev_iterate(*tags
, piter
) {
910 if (piter
->type
== type
911 && piter
->stop_offset
== FT_OFFSET_UNSET
) {
915 } text_tag_list_rev_iterate_end
;
918 ptag
->stop_offset
= text_out
- plain_text
;
920 log_featured_text("Extra text tag end for \"%s\".",
921 text_tag_type_name(type
));
927 /* In this case, we replace the sequence by some text. */
930 if (!text_tag_init_from_sequence(&tag
, type
,
931 text_out
- plain_text
, buf
)) {
932 log_featured_text("Couldn't create a text tag with \"%s\".",
935 len
= text_tag_replace_text(&tag
, text_out
, text_out_len
,
940 /* Set it in the list. */
941 struct text_tag
*ptag
= fc_malloc(sizeof(struct text_tag
));
944 ptag
->stop_offset
= text_out
- plain_text
;
945 text_tag_list_append(*tags
, ptag
);
952 *text_out
++ = *text_in
++;
956 *text_out
++ = *text_in
++;
962 return plain_text_len
- text_out_len
;
965 /**************************************************************************
966 Apply a tag to a text. This text can already containing escape
967 sequences. Returns 0 on error.
970 - If tag_type is TTT_BOLD, TTT_ITALIC, TTT_STRIKE or TTT_UNDERLINE, there
971 shouldn't be any extra argument.
972 - If tag_type is TTT_COLOR:
973 size_t featured_text_apply_tag(..., const struct ft_color color);
974 - If tag_type is TTT_LINK and you want a city link:
975 size_t featured_text_apply_tag(..., TLT_CITY, struct city *pcity);
976 - If tag_type is TTT_LINK and you want a tile link:
977 size_t featured_text_apply_tag(..., TLT_TILE, struct tile *ptile);
978 - If tag_type is TTT_LINK and you want an unit link:
979 size_t featured_text_apply_tag(..., TLT_UNIT, struct unit *punit);
981 See also comment for text_tag_initv().
982 **************************************************************************/
983 size_t featured_text_apply_tag(const char *text_source
,
984 char *featured_text
, size_t featured_text_len
,
985 enum text_tag_type tag_type
,
986 ft_offset_t start_offset
,
987 ft_offset_t stop_offset
,
991 size_t len
, total_len
= 0;
994 if (start_offset
== FT_OFFSET_UNSET
995 || start_offset
> strlen(text_source
)
996 || (stop_offset
!= FT_OFFSET_UNSET
997 && stop_offset
< start_offset
)) {
998 log_featured_text("featured_text_apply_tag(): invalid offsets.");
1002 va_start(args
, stop_offset
);
1003 if (!text_tag_initv(&tag
, tag_type
, start_offset
, stop_offset
, args
)) {
1009 if (start_offset
> 0) {
1010 /* First part: before the sequence. */
1012 while (len
< start_offset
1013 && *text_source
!= '\0'
1014 && featured_text_len
> 1) {
1015 *featured_text
++ = *text_source
++;
1016 featured_text_len
--;
1022 /* Start sequence. */
1023 len
= text_tag_start_sequence(&tag
, featured_text
, featured_text_len
);
1025 featured_text
+= len
;
1026 featured_text_len
-= len
;
1028 /* Second part: between the sequences. */
1030 while (len
< stop_offset
1031 && *text_source
!= '\0'
1032 && featured_text_len
> 1) {
1033 *featured_text
++ = *text_source
++;
1034 featured_text_len
--;
1039 /* Stop sequence. */
1040 len
= text_tag_stop_sequence(&tag
, featured_text
, featured_text_len
);
1042 featured_text
+= len
;
1043 featured_text_len
-= len
;
1045 /* Third part: after the sequence. */
1046 while (*text_source
!= '\0'
1047 && featured_text_len
> 1) {
1048 *featured_text
++ = *text_source
++;
1049 featured_text_len
--;
1052 *featured_text
= '\0';
1057 /**************************************************************************
1058 Get a text link to a city.
1059 N.B.: The returned string is static, so every call to this function
1060 overwrites the previous.
1061 **************************************************************************/
1062 const char *city_link(const struct city
*pcity
)
1064 static char buf
[MAX_LEN_LINK
];
1066 fc_snprintf(buf
, sizeof(buf
), "%c%s tgt=\"%s\" id=%d name=\"%s\" %c%c",
1067 SEQ_START
, text_tag_type_short_name(TTT_LINK
),
1068 text_link_type_name(TLT_CITY
), pcity
->id
,
1069 city_name_get(pcity
), SEQ_END
, SEQ_STOP
);
1073 /**************************************************************************
1074 Get a text link to a city tile (make a clickable link to a tile with the
1076 N.B.: The returned string is static, so every call to this function
1077 overwrites the previous.
1078 **************************************************************************/
1079 const char *city_tile_link(const struct city
*pcity
)
1081 static char buf
[MAX_LEN_LINK
];
1082 const char *tag_name
= text_tag_type_short_name(TTT_LINK
);
1084 fc_snprintf(buf
, sizeof(buf
), "%c%s tgt=\"%s\" x=%d y=%d%c%s%c%c%s%c",
1085 SEQ_START
, tag_name
, text_link_type_name(TLT_TILE
),
1086 TILE_XY(city_tile(pcity
)), SEQ_STOP
, city_name_get(pcity
),
1087 SEQ_START
, SEQ_END
, tag_name
, SEQ_STOP
);
1091 /**************************************************************************
1092 Get a text link to a tile.
1093 N.B.: The returned string is static, so every call to this function
1094 overwrites the previous.
1095 **************************************************************************/
1096 const char *tile_link(const struct tile
*ptile
)
1098 static char buf
[MAX_LEN_LINK
];
1100 fc_snprintf(buf
, sizeof(buf
), "%c%s tgt=\"%s\" x=%d y=%d %c%c",
1101 SEQ_START
, text_tag_type_short_name(TTT_LINK
),
1102 text_link_type_name(TLT_TILE
), TILE_XY(ptile
),
1107 /**************************************************************************
1108 Get a text link to an unit.
1109 N.B.: The returned string is static, so every call to this function
1110 overwrites the previous.
1111 **************************************************************************/
1112 const char *unit_link(const struct unit
*punit
)
1114 static char buf
[MAX_LEN_LINK
];
1116 fc_snprintf(buf
, sizeof(buf
), "%c%s tgt=\"%s\" id=%d name=\"%s\" %c%c",
1117 SEQ_START
, text_tag_type_short_name(TTT_LINK
),
1118 text_link_type_name(TLT_UNIT
), punit
->id
,
1119 unit_name_translation(punit
), SEQ_END
, SEQ_STOP
);
1123 /**************************************************************************
1124 Get a text link to a unit tile (make a clickable link to a tile with the
1125 unit type name as text).
1126 N.B.: The returned string is static, so every call to this function
1127 overwrites the previous.
1128 **************************************************************************/
1129 const char *unit_tile_link(const struct unit
*punit
)
1131 static char buf
[MAX_LEN_LINK
];
1132 const char *tag_name
= text_tag_type_short_name(TTT_LINK
);
1134 fc_snprintf(buf
, sizeof(buf
), "%c%s tgt=\"%s\" x=%d y=%d%c%s%c%c%s%c",
1135 SEQ_START
, tag_name
, text_link_type_name(TLT_TILE
),
1136 TILE_XY(unit_tile(punit
)), SEQ_STOP
,
1137 unit_name_translation(punit
),
1138 SEQ_START
, SEQ_END
, tag_name
, SEQ_STOP
);