1
0
mirror of https://git.zap.org.au/git/trader.git synced 2025-02-02 15:08:13 -05:00

Make the show_help() function multibyte-aware

All text in the show_help() function is now processed as wide characters.
This commit is contained in:
John Zaitseff 2011-08-20 13:27:26 +10:00
parent 7b544bbd35
commit 4d145813ff
2 changed files with 147 additions and 81 deletions

View File

@ -35,19 +35,24 @@
* Help text definition * * Help text definition *
************************************************************************/ ************************************************************************/
static const char *help_text[] = { #define HELP_TEXT_PAGES 10
static const char *help_text[HELP_TEXT_PAGES] = {
/* /*
TRANSLATORS: The help text for Star Traders is marked up using a TRANSLATORS: The help text for Star Traders is marked up using a
custom mark-up format NOT used anywhere else in the source code. custom mark-up format NOT used anywhere else in the source code.
Each string is a single page of text that is displayed in an area 76 Each string is a single page of text that is displayed in an area 76
columns wide by 16 lines high. Ideally, each line within the string columns wide by 16 lines high. Each line is delimited by "\n". NO
should be (manually) space-justified or centred; each line is word-wrapping is performed: you must place the "\n" characters in the
separated by "\n". TAB characters and other control codes must NOT appropriate place. Ideally, each line within the string should be
be used. If a string starts with "@" as the very first character, also (manually) space-justified or centred. TAB characters and other
that string (and all strings following) are ignored: this allows a control codes must NOT be used. If a string starts with "@" as the
variable number of help text pages (from one to twelve). very first character, that string is ignored (as are all strings
following): this allows a variable number of help text pages (from
one to ten). Multibyte strings are handled correctly (even those
requiring shift sequences!).
The ASCII circumflex accent character "^" switches to a different The ASCII circumflex accent character "^" switches to a different
character rendition (also called attributes), depending on the character rendition (also called attributes), depending on the
@ -74,23 +79,26 @@ static const char *help_text[] = {
~m - Print the number of moves available (NUMBER_MOVES) [**] ~m - Print the number of moves available (NUMBER_MOVES) [**]
~c - Print the maximum number of companies that can be formed (MAX_COMPANIES) [*] ~c - Print the maximum number of companies that can be formed (MAX_COMPANIES) [*]
~t - Prints the default number of turns in the game (DEFAULT_MAX_TURN) [**] ~t - Prints the default number of turns in the game (DEFAULT_MAX_TURN) [**]
~1 to ~9 - Print the keycode for the N-th choice of move [*] ~1 to ~9 - Print the keycode for the N-th choice of move [***]
~M - Print the keycode for the last choice of move [*] ~M - Print the keycode for the last choice of move [***]
~A to ~H - Print the character used to represent the company on the galaxy map [*] ~A to ~H - Print the character used to represent the company on the galaxy map [***]
~. - Print the character used to represent empty space on the map [*] ~. - Print the character used to represent empty space on the map [***]
~+ - Print the character used to represent outposts on the map [*] ~+ - Print the character used to represent outposts on the map [***]
~* - Print the character used to represent stars on the map [*] ~* - Print the character used to represent stars on the map [***]
[*] Takes one character space in the output [*] Takes one character space (column space) in the output
[**] Takes two character spaces in the output [**] Takes two column spaces in the output
[***] Takes one or two column spaces in the output, depending on the
appropriate strings in the current PO file.
Note that all keycodes and map representation characters use locale- Note that all keycodes and map representation characters use locale-
specific characters. Note also that the tilde value escapes do NOT specific characters; double-width characters ARE supported. Note
change the current character rendition: a circumflex accent escape is also that the tilde value escapes do NOT change the current character
needed for that. For example, to display the first choice of move as rendition: a circumflex accent escape is needed for that. For
it would be shown on the galaxy map, use something like "^k~1^N" (a example, to display the first choice of move as it would be shown on
six-character sequence that would translate to just one character in the galaxy map, use something like "^k~1^N" (a six-character sequence
the output text). that would translate to just one character (or maybe two) in the
output text).
*/ */
N_("" N_(""
"^BStar Traders^N is a simple game of interstellar trading. The object of the\n" "^BStar Traders^N is a simple game of interstellar trading. The object of the\n"
@ -201,18 +209,14 @@ static const char *help_text[] = {
"\n" "\n"
"The ^Bwinner of the game^N is the person with the greatest net worth (total\n" "The ^Bwinner of the game^N is the person with the greatest net worth (total\n"
"value of cash, stock and debt). ^HGood luck^N and may the best person win!\n" "value of cash, stock and debt). ^HGood luck^N and may the best person win!\n"
""), "")
#ifdef ENABLE_NLS #ifdef ENABLE_NLS
N_("@ Help text, page 7"), , N_("@ Help text, page 7")
N_("@ Help text, page 8"), , N_("@ Help text, page 8")
N_("@ Help text, page 9"), , N_("@ Help text, page 9")
N_("@ Help text, page 10"), , N_("@ Help text, page 10")
N_("@ Help text, page 11"),
N_("@ Help text, page 12"),
#endif #endif
NULL
}; };
@ -228,21 +232,31 @@ static const char *help_text[] = {
void show_help (void) void show_help (void)
{ {
wchar_t *wchelp_text[HELP_TEXT_PAGES];
wchar_t *wcbuf = xmalloc(BIGBUFSIZE * sizeof(wchar_t));
chtype *outbuf = xmalloc(BIGBUFSIZE * sizeof(chtype));
int curpage = 0; int curpage = 0;
int numpages = 0; int numpages = 0;
bool done = false; bool done = false;
// Count how many pages appear in the (translated) help text // Count how many pages appear in the (translated) help text
while (help_text[numpages] != NULL) { while (numpages < HELP_TEXT_PAGES) {
const char *s = gettext(help_text[numpages]); const char *s = gettext(help_text[numpages]);
if (s == NULL || *s == '\0' || *s == '@') if (s == NULL || *s == '\0' || *s == '@')
break; break;
xmbstowcs(wcbuf, s, BIGBUFSIZE);
wchelp_text[numpages] = xwcsdup(wcbuf);
numpages++; numpages++;
} }
if (numpages == 0) if (numpages == 0) {
free(outbuf);
free(wcbuf);
return; return;
}
newtxwin(WIN_LINES - 1, WIN_COLS, 1, WCENTER, false, 0); newtxwin(WIN_LINES - 1, WIN_COLS, 1, WCENTER, false, 0);
@ -260,100 +274,108 @@ void show_help (void)
// Process the help text string // Process the help text string
const char *s = gettext(help_text[curpage]); const wchar_t *htxt = wchelp_text[curpage];
char convbuf[MB_LEN_MAX + 1];
char *cp;
mbstate_t mbstate;
chtype *outp;
wchar_t c;
size_t i, n;
int count = BIGBUFSIZE;
int maxchar = MB_CUR_MAX;
int curattr = attr_normal; int curattr = attr_normal;
while (*s != '\0') { memset(&mbstate, 0, sizeof(mbstate));
switch (*s) { outp = outbuf;
while (*htxt != '\0' && count > maxchar * 2) {
switch (*htxt) {
case '\n': case '\n':
// Start a new line, suitably indented // Start a new line
wmove(curwin, getcury(curwin) + 1, 2); *outp++ = '\n';
count--;
break; break;
case '^': case '^':
// Switch to a different character rendition // Switch to a different character rendition
switch (*++s) { switch (*++htxt) {
case '^': case '^':
waddch(curwin, *s | curattr); wcbuf[0] = *htxt;
break; wcbuf[1] = '\0';
goto addwcbuf;
case 'N': case 'N':
curattr = attr_normal; curattr = attr_normal;
wattrset(curwin, curattr);
break; break;
case 'B': case 'B':
curattr = attr_normal | A_BOLD; curattr = attr_normal | A_BOLD;
wattrset(curwin, curattr);
break; break;
case 'H': case 'H':
curattr = attr_highlight; curattr = attr_highlight;
wattrset(curwin, curattr);
break; break;
case 'K': case 'K':
curattr = attr_keycode; curattr = attr_keycode;
wattrset(curwin, curattr);
break; break;
case 'e': case 'e':
curattr = attr_map_empty; curattr = attr_map_empty;
wattrset(curwin, curattr);
break; break;
case 'o': case 'o':
curattr = attr_map_outpost; curattr = attr_map_outpost;
wattrset(curwin, curattr);
break; break;
case 's': case 's':
curattr = attr_map_star; curattr = attr_map_star;
wattrset(curwin, curattr);
break; break;
case 'c': case 'c':
curattr = attr_map_company; curattr = attr_map_company;
wattrset(curwin, curattr);
break; break;
case 'k': case 'k':
curattr = attr_map_choice; curattr = attr_map_choice;
wattrset(curwin, curattr);
break; break;
default: default:
waddch(curwin, '^' | curattr); wcbuf[0] = '^';
waddch(curwin, *s | curattr); wcbuf[1] = *htxt;
wcbuf[2] = '\0';
goto addwcbuf;
} }
break; break;
case '~': case '~':
// Print a global constant // Print a global constant
switch (*++s) { switch (*++htxt) {
case '~': case '~':
waddch(curwin, *s | curattr); wcbuf[0] = *htxt;
break; wcbuf[1] = '\0';
goto addwcbuf;
case 'x': case 'x':
wprintw(curwin, "%2d", MAX_X); swprintf(wcbuf, BIGBUFSIZE, L"%2d", MAX_X);
break; goto addwcbuf;
case 'y': case 'y':
wprintw(curwin, "%2d", MAX_Y); swprintf(wcbuf, BIGBUFSIZE, L"%2d", MAX_Y);
break; goto addwcbuf;
case 'm': case 'm':
wprintw(curwin, "%2d", NUMBER_MOVES); swprintf(wcbuf, BIGBUFSIZE, L"%2d", NUMBER_MOVES);
break; goto addwcbuf;
case 'c': case 'c':
wprintw(curwin, "%d", MAX_COMPANIES); swprintf(wcbuf, BIGBUFSIZE, L"%d", MAX_COMPANIES);
break; goto addwcbuf;
case 't': case 't':
wprintw(curwin, "%2d", DEFAULT_MAX_TURN); swprintf(wcbuf, BIGBUFSIZE, L"%2d", DEFAULT_MAX_TURN);
break; goto addwcbuf;
case '1': case '1':
case '2': case '2':
@ -365,28 +387,38 @@ void show_help (void)
case '8': case '8':
case '9': case '9':
// N-th choice of move, as a key press // N-th choice of move, as a key press
wprintw(curwin, "%c", PRINTABLE_GAME_MOVE(*s - '1')); c = btowc(PRINTABLE_GAME_MOVE(*htxt - L'1'));
break; wcbuf[0] = (c == WEOF) ? EILSEQ_REPL : c;
wcbuf[1] = '\0';
goto addwcbuf;
case 'M': case 'M':
// Last choice of move, as a key press // Last choice of move, as a key press
wprintw(curwin, "%c", PRINTABLE_GAME_MOVE(NUMBER_MOVES - 1)); c = btowc(PRINTABLE_GAME_MOVE(NUMBER_MOVES - 1));
break; wcbuf[0] = (c == WEOF) ? EILSEQ_REPL : c;
wcbuf[1] = '\0';
goto addwcbuf;
case '.': case '.':
// Map representation of empty space // Map representation of empty space
wprintw(curwin, "%c", PRINTABLE_MAP_VAL(MAP_EMPTY)); c = btowc(PRINTABLE_MAP_VAL(MAP_EMPTY));
break; wcbuf[0] = (c == WEOF) ? EILSEQ_REPL : c;
wcbuf[1] = '\0';
goto addwcbuf;
case '+': case '+':
// Map representation of an outpost // Map representation of an outpost
wprintw(curwin, "%c", PRINTABLE_MAP_VAL(MAP_OUTPOST)); c = btowc(PRINTABLE_MAP_VAL(MAP_OUTPOST));
break; wcbuf[0] = (c == WEOF) ? EILSEQ_REPL : c;
wcbuf[1] = '\0';
goto addwcbuf;
case '*': case '*':
// Map representation of a star // Map representation of a star
wprintw(curwin, "%c", PRINTABLE_MAP_VAL(MAP_STAR)); c = btowc(PRINTABLE_MAP_VAL(MAP_STAR));
break; wcbuf[0] = (c == WEOF) ? EILSEQ_REPL : c;
wcbuf[1] = '\0';
goto addwcbuf;
case 'A': case 'A':
case 'B': case 'B':
@ -397,22 +429,49 @@ void show_help (void)
case 'G': case 'G':
case 'H': case 'H':
// Map representation of company // Map representation of company
wprintw(curwin, "%c", c = btowc(PRINTABLE_MAP_VAL(COMPANY_TO_MAP(*htxt - L'A')));
PRINTABLE_MAP_VAL(COMPANY_TO_MAP(*s - 'A'))); wcbuf[0] = (c == WEOF) ? EILSEQ_REPL : c;
break; wcbuf[1] = '\0';
goto addwcbuf;
default: default:
waddch(curwin, '~' | curattr); wcbuf[0] = '~';
waddch(curwin, *s | curattr); wcbuf[1] = *htxt;
goto addwcbuf;
} }
break; break;
default: default:
// Print the character // Print the character
waddch(curwin, *s | curattr); wcbuf[0] = *htxt;
wcbuf[1] = '\0';
addwcbuf:
for (wchar_t *p = wcbuf; *p != '\0' && count > maxchar * 2; p++) {
n = xwcrtomb(convbuf, *p, &mbstate);
for (i = 0, cp = convbuf; i < n; i++, cp++, outp++, count--) {
*outp = (unsigned char) *cp | curattr;
}
}
} }
s++; htxt++;
}
// Add the terminating NUL (possibly with a preceding shift sequence)
n = xwcrtomb(convbuf, '\0', &mbstate);
for (i = 0, cp = convbuf; i < n; i++, cp++, outp++, count--) {
*outp = (unsigned char) *cp;
}
assert(count >= 0);
// Display the formatted text in outbuf
for (outp = outbuf; *outp != '\0'; outp++) {
if (*outp == '\n') {
wmove(curwin, getcury(curwin) + 1, 2);
} else {
waddch(curwin, *outp);
}
} }
center(curwin, getmaxy(curwin) - 2, 0, attr_waitforkey, 0, 0, 1, center(curwin, getmaxy(curwin) - 2, 0, attr_waitforkey, 0, 0, 1,
@ -461,4 +520,10 @@ void show_help (void)
deltxwin(); deltxwin();
txrefresh(); txrefresh();
for (curpage = 0; curpage < numpages; curpage++) {
free(wchelp_text[curpage]);
}
free(outbuf);
free(wcbuf);
} }

View File

@ -61,6 +61,7 @@
#endif #endif
#define BUFSIZE 1024 // For various string buffers #define BUFSIZE 1024 // For various string buffers
#define BIGBUFSIZE 2048 // For buffers known to be larger
#endif /* included_TRADER_H */ #endif /* included_TRADER_H */