1
0
mirror of https://git.zap.org.au/git/trader.git synced 2024-10-27 18:20:13 -04:00

Use wide-character functions for input routines and most strings

Convert all input routines, and most internal strings, to use
wide-character functions.  All extended characters are supported,
including those having column widths of zero (eg, combining characters),
one (eg, normal Western characters) and two (eg, many East Asian
characters).  This was quite a major undertaking!
This commit is contained in:
John Zaitseff 2011-08-25 13:19:31 +10:00
parent b951b21894
commit 20473ae409
9 changed files with 1261 additions and 805 deletions

View File

@ -97,7 +97,7 @@ void exchange_stock (void)
center(curwin, 1, 0, attr_title, 0, 0, 1, center(curwin, 1, 0, attr_title, 0, 0, 1,
_(" Interstellar Stock Exchange ")); _(" Interstellar Stock Exchange "));
center(curwin, 2, 0, attr_normal, attr_highlight, 0, 1, center(curwin, 2, 0, attr_normal, attr_highlight, 0, 1,
_("Player: ^{%s^}"), player[current_player].name); _("Player: ^{%ls^}"), player[current_player].name);
all_off_map = true; all_off_map = true;
for (i = 0; i < MAX_COMPANIES; i++) { for (i = 0; i < MAX_COMPANIES; i++) {
@ -143,19 +143,19 @@ void exchange_stock (void)
- SHARE_RETURN_COLS, attr_subtitle, 0, 0, 2, - SHARE_RETURN_COLS, attr_subtitle, 0, 0, 2,
/* TRANSLATORS: "Price per share" is a two-line column /* TRANSLATORS: "Price per share" is a two-line column
label in a table containing the price per share in label in a table containing the price per share in
any given company. %s is the currency symbol in the any given company. %ls is the currency symbol in
current locale. The maximum column width is 12 the current locale. The maximum column width is 12
characters INCLUDING the currency symbol (see characters INCLUDING the currency symbol (see
SHARE_PRICE_COLS in src/intf.h). */ SHARE_PRICE_COLS in src/intf.h). */
pgettext("subtitle", "Price per\nshare (%s)"), pgettext("subtitle", "Price per\nshare (%ls)"),
lconvinfo.currency_symbol); currency_symbol);
for (line = 6, i = 0; i < MAX_COMPANIES; i++) { for (line = 6, i = 0; i < MAX_COMPANIES; i++) {
if (company[i].on_map) { if (company[i].on_map) {
mvwaddch(curwin, line, 2, PRINTABLE_MAP_VAL(COMPANY_TO_MAP(i)) mvwaddch(curwin, line, 2, PRINTABLE_MAP_VAL(COMPANY_TO_MAP(i))
| attr_choice); | attr_choice);
left(curwin, line, 4, attr_normal, 0, 0, 1, "%s", left(curwin, line, 4, attr_normal, 0, 0, 1, "%ls",
company[i].name); company[i].name);
right(curwin, line, w - 2, attr_normal, 0, 0, 1, "%'ld ", right(curwin, line, w - 2, attr_normal, 0, 0, 1, "%'ld ",
@ -196,48 +196,61 @@ void exchange_stock (void)
// Get the actual selection made by the player // Get the actual selection made by the player
while (selection == SEL_NONE) { while (selection == SEL_NONE) {
bool found; wint_t key;
int key = gettxchar(curwin); if (gettxchar(curwin, &key) == OK) {
// Ordinary wide character
bool found;
if (isupper(*keycode_company)) { if (iswupper(*keycode_company)) {
key = toupper(key); key = towupper(key);
} else if (islower(*keycode_company)) { } else if (iswlower(*keycode_company)) {
key = tolower(key); key = towlower(key);
} }
for (i = 0, found = false; keycode_company[i] != '\0'; i++) { for (i = 0, found = false; keycode_company[i] != '\0'; i++) {
if (keycode_company[i] == key) { if (keycode_company[i] == key) {
found = true; found = true;
if (company[i].on_map) { if (company[i].on_map) {
selection = i; selection = i;
} else { } else {
beep();
}
break;
}
}
if (! found) {
switch (key) {
case '1':
curs_set(CURS_OFF);
show_status(current_player);
curs_set(CURS_ON);
break;
case '2':
curs_set(CURS_OFF);
show_map(true);
curs_set(CURS_ON);
break;
case '3':
selection = SEL_BANK;
break;
case '4':
case ' ':
selection = SEL_EXIT;
break;
default:
beep(); beep();
} }
break;
} }
} } else {
// Function or control key
if (! found) {
switch (key) { switch (key) {
case '1': case KEY_ESC:
curs_set(CURS_OFF);
show_status(current_player);
curs_set(CURS_ON);
break;
case '2':
curs_set(CURS_OFF);
show_map(true);
curs_set(CURS_ON);
break;
case '3':
selection = SEL_BANK;
break;
case '4':
case ' ':
case KEY_CANCEL: case KEY_CANCEL:
case KEY_EXIT: case KEY_EXIT:
case KEY_CTRL('C'): case KEY_CTRL('C'):
@ -287,7 +300,7 @@ void visit_bank (void)
{ {
double credit_limit; double credit_limit;
double val, max; double val, max;
int key; wint_t key;
bool done; bool done;
chtype *chbuf = xmalloc(BUFSIZE * sizeof(chtype)); chtype *chbuf = xmalloc(BUFSIZE * sizeof(chtype));
@ -363,27 +376,41 @@ void visit_bank (void)
done = false; done = false;
while (! done) { while (! done) {
key = gettxchar(curwin); if (gettxchar(curwin, &key) == OK) {
// Ordinary wide character
switch (key) {
case '1':
case '2':
case '3':
left(curwin, getcury(curwin), getcurx(curwin), A_BOLD,
0, 0, 1, "%lc", key);
wrefresh(curwin);
switch (key) { done = true;
case '1': break;
case '2':
case '3':
wechochar(curwin, key | A_BOLD);
done = true;
break;
case ' ': case ' ':
case KEY_CANCEL: done = true;
case KEY_EXIT: break;
case KEY_CTRL('C'):
case KEY_CTRL('G'):
case KEY_CTRL('\\'):
done = true;
break;
default: default:
beep(); beep();
}
} else {
// Function or control key
switch (key) {
case KEY_ESC:
case KEY_CANCEL:
case KEY_EXIT:
case KEY_CTRL('C'):
case KEY_CTRL('G'):
case KEY_CTRL('\\'):
done = true;
break;
default:
beep();
}
} }
} }
@ -410,8 +437,8 @@ void visit_bank (void)
n = (lconvinfo.p_sep_by_space == 1) ? 1 : 0; n = (lconvinfo.p_sep_by_space == 1) ? 1 : 0;
mkchstr(chbuf, BUFSIZE, attr_normal, attr_normal | A_BOLD, 0, 1, mkchstr(chbuf, BUFSIZE, attr_normal, attr_normal | A_BOLD, 0, 1,
getmaxx(curwin) / 2, &width_cursym, 1, "^{%s^}", getmaxx(curwin) / 2, &width_cursym, 1, "^{%ls^}",
lconvinfo.currency_symbol); currency_symbol);
chbuf_cursym = xchstrdup(chbuf); chbuf_cursym = xchstrdup(chbuf);
mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1, getmaxx(curwin) mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1, getmaxx(curwin)
@ -468,8 +495,8 @@ void visit_bank (void)
n = (lconvinfo.p_sep_by_space == 1) ? 1 : 0; n = (lconvinfo.p_sep_by_space == 1) ? 1 : 0;
mkchstr(chbuf, BUFSIZE, attr_normal, attr_normal | A_BOLD, 0, 1, mkchstr(chbuf, BUFSIZE, attr_normal, attr_normal | A_BOLD, 0, 1,
getmaxx(curwin) / 2, &width_cursym, 1, "^{%s^}", getmaxx(curwin) / 2, &width_cursym, 1, "^{%ls^}",
lconvinfo.currency_symbol); currency_symbol);
chbuf_cursym = xchstrdup(chbuf); chbuf_cursym = xchstrdup(chbuf);
mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1, getmaxx(curwin) mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1, getmaxx(curwin)
@ -527,11 +554,12 @@ void visit_bank (void)
void trade_shares (int num, bool *bid_used) void trade_shares (int num, bool *bid_used)
{ {
bool done; bool done;
int key, ret, w, x; int ret, w, x;
long int maxshares, val; long int maxshares, val;
double ownership; double ownership;
chtype *chbuf; chtype *chbuf;
int width; int width;
wint_t key;
assert(num >= 0 && num < MAX_COMPANIES); assert(num >= 0 && num < MAX_COMPANIES);
@ -548,8 +576,8 @@ void trade_shares (int num, bool *bid_used)
w = getmaxx(curwin); w = getmaxx(curwin);
center(curwin, 1, 0, attr_title, 0, 0, 1, center(curwin, 1, 0, attr_title, 0, 0, 1,
/* TRANSLATORS: %s represents the company name. */ /* TRANSLATORS: %ls represents the company name. */
_(" Stock Transaction in %s "), company[num].name); _(" Stock Transaction in %ls "), company[num].name);
mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1, w / 2, &width, 1, mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1, w / 2, &width, 1,
/* TRANSLATORS: "Shares issued" represents the number of /* TRANSLATORS: "Shares issued" represents the number of
@ -633,28 +661,42 @@ void trade_shares (int num, bool *bid_used)
done = false; done = false;
while (! done) { while (! done) {
key = gettxchar(curwin); if (gettxchar(curwin, &key) == OK) {
// Ordinary wide character
switch (key) {
case '1':
case '2':
case '3':
case '4':
left(curwin, getcury(curwin), getcurx(curwin), A_BOLD,
0, 0, 1, "%lc", key);
wrefresh(curwin);
switch (key) { done = true;
case '1': break;
case '2':
case '3':
case '4':
wechochar(curwin, key | A_BOLD);
done = true;
break;
case ' ': case ' ':
case KEY_CANCEL: done = true;
case KEY_EXIT: break;
case KEY_CTRL('C'):
case KEY_CTRL('G'):
case KEY_CTRL('\\'):
done = true;
break;
default: default:
beep(); beep();
}
} else {
// Function or control key
switch (key) {
case KEY_ESC:
case KEY_CANCEL:
case KEY_EXIT:
case KEY_CTRL('C'):
case KEY_CTRL('G'):
case KEY_CTRL('\\'):
done = true;
break;
default:
beep();
}
} }
} }
@ -755,16 +797,16 @@ void trade_shares (int num, bool *bid_used)
txdlgbox(MAX_DLG_LINES, 50, 8, WCENTER, attr_error_window, txdlgbox(MAX_DLG_LINES, 50, 8, WCENTER, attr_error_window,
attr_error_title, attr_error_highlight, 0, 0, attr_error_title, attr_error_highlight, 0, 0,
attr_error_waitforkey, _(" No Shares Issued "), attr_error_waitforkey, _(" No Shares Issued "),
/* TRANSLATORS: %s represents the company name. */ /* TRANSLATORS: %ls represents the company name. */
_("%s has refused\nto issue more shares."), _("%ls has refused\nto issue more shares."),
company[num].name); company[num].name);
} else { } else {
txdlgbox(MAX_DLG_LINES, 50, 8, WCENTER, attr_normal_window, txdlgbox(MAX_DLG_LINES, 50, 8, WCENTER, attr_normal_window,
attr_title, attr_normal, attr_highlight, 0, attr_title, attr_normal, attr_highlight, 0,
attr_waitforkey, _(" Shares Issued "), attr_waitforkey, _(" Shares Issued "),
/* TRANSLATORS: %s represents the company name. */ /* TRANSLATORS: %ls represents the company name. */
ngettext("%s has issued\n^{one^} more share.", ngettext("%ls has issued\n^{one^} more share.",
"%s has issued\n^{%'ld^} more shares.", "%ls has issued\n^{%'ld^} more shares.",
maxshares), company[num].name, maxshares); maxshares), company[num].name, maxshares);
} }
break; break;

View File

@ -83,7 +83,7 @@ static const unsigned char game_file_crypt_key[] = {
} while (0) } while (0)
#ifdef USE_UTF8_GAME_FILE #ifdef USE_UTF8_GAME_FILE
# define load_game_read_string(_var) \ # define load_game_read_string(_var, _var_utf8) \
do { \ do { \
char *s; \ char *s; \
int len; \ int len; \
@ -115,11 +115,14 @@ static const unsigned char game_file_crypt_key[] = {
s[len - 1] = '\0'; \ s[len - 1] = '\0'; \
} \ } \
\ \
xmbstowcs(wcbuf, s, BUFSIZE); \
(_var) = xwcsdup(wcbuf); \
(_var_utf8) = s; \
\
lineno++; \ lineno++; \
(_var) = s; \
} while (0) } while (0)
#else // ! USE_UTF8_GAME_FILE #else // ! USE_UTF8_GAME_FILE
# define load_game_read_string(_var) \ # define load_game_read_string(_var, _var_utf8) \
do { \ do { \
char *s; \ char *s; \
int len; \ int len; \
@ -140,8 +143,11 @@ static const unsigned char game_file_crypt_key[] = {
s[len - 1] = '\0'; \ s[len - 1] = '\0'; \
} \ } \
\ \
xmbstowcs(wcbuf, s, BUFSIZE); \
(_var) = xwcsdup(wcbuf); \
(_var_utf8) = s; \
\
lineno++; \ lineno++; \
(_var) = s; \
} while (0) } while (0)
#endif // ! USE_UTF8_GAME_FILE #endif // ! USE_UTF8_GAME_FILE
@ -165,27 +171,38 @@ static const unsigned char game_file_crypt_key[] = {
save_game_printf("%d", (int) _var) save_game_printf("%d", (int) _var)
#ifdef USE_UTF8_GAME_FILE #ifdef USE_UTF8_GAME_FILE
# define save_game_write_string(_var) \ # define save_game_write_string(_var, _var_utf8) \
do { \ do { \
if (need_icd) { \ if ((_var_utf8) != NULL) { \
char *s = str_cd_iconv(_var, icd); \ save_game_printf("%s", _var_utf8); \
if (s == NULL) { \
if (errno == EILSEQ) { \
err_exit(_("%s: could not convert string"), \
filename); \
} else { \
errno_exit("str_cd_iconv"); \
} \
} \
save_game_printf("%s", s); \
free(s); \
} else { \ } else { \
save_game_printf("%s", _var); \ if (need_icd) { \
snprintf(buf, BUFSIZE, "%ls", _var); \
char *s = str_cd_iconv(buf, icd); \
if (s == NULL) { \
if (errno == EILSEQ) { \
err_exit(_("%s: could not convert string"), \
filename); \
} else { \
errno_exit("str_cd_iconv"); \
} \
} \
save_game_printf("%s", s); \
free(s); \
} else { \
save_game_printf("%ls", _var); \
} \
} \ } \
} while (0) } while (0)
#else // ! USE_UTF8_GAME_FILE #else // ! USE_UTF8_GAME_FILE
# define save_game_write_string(_var) \ # define save_game_write_string(_var, _var_utf8) \
save_game_printf("%s", _var) do { \
if ((_var_utf8) != NULL) { \
save_game_printf("%s", _var_utf8); \
} else { \
save_game_printf("%ls", _var); \
} \
} while (0)
#endif // ! USE_UTF8_GAME_FILE #endif // ! USE_UTF8_GAME_FILE
@ -201,12 +218,15 @@ static const unsigned char game_file_crypt_key[] = {
bool load_game (int num) bool load_game (int num)
{ {
char *buf, *filename; char *filename;
FILE *file; FILE *file;
char *codeset, *codeset_nl; char *codeset, *codeset_nl;
int saved_errno, lineno; int saved_errno, lineno;
char *prev_locale; char *prev_locale;
char *buf;
wchar_t *wcbuf;
int crypt_key; int crypt_key;
int n, i, j; int n, i, j;
@ -219,6 +239,7 @@ bool load_game (int num)
assert(num >= 1 && num <= 9); assert(num >= 1 && num <= 9);
buf = xmalloc(BUFSIZE); buf = xmalloc(BUFSIZE);
wcbuf = xmalloc(BUFSIZE * sizeof(wchar_t));
filename = game_filename(num); filename = game_filename(num);
assert(filename != NULL); assert(filename != NULL);
@ -246,6 +267,7 @@ bool load_game (int num)
} }
free(buf); free(buf);
free(wcbuf);
free(filename); free(filename);
return false; return false;
} }
@ -332,7 +354,7 @@ bool load_game (int num)
// Read in player data // Read in player data
for (i = 0; i < number_players; i++) { for (i = 0; i < number_players; i++) {
load_game_read_string(player[i].name); load_game_read_string(player[i].name, player[i].name_utf8);
load_game_read_double(player[i].cash, player[i].cash >= 0.0); load_game_read_double(player[i].cash, player[i].cash >= 0.0);
load_game_read_double(player[i].debt, player[i].debt >= 0.0); load_game_read_double(player[i].debt, player[i].debt >= 0.0);
load_game_read_bool(player[i].in_game); load_game_read_bool(player[i].in_game);
@ -344,7 +366,8 @@ bool load_game (int num)
// Read in company data // Read in company data
for (i = 0; i < MAX_COMPANIES; i++) { for (i = 0; i < MAX_COMPANIES; i++) {
company[i].name = xstrdup(gettext(company_name[i])); xmbstowcs(wcbuf, gettext(company_name[i]), BUFSIZE);
company[i].name = xwcsdup(wcbuf);
load_game_read_double(company[i].share_price, company[i].share_price >= 0.0); load_game_read_double(company[i].share_price, company[i].share_price >= 0.0);
load_game_read_double(company[i].share_return, true); load_game_read_double(company[i].share_return, true);
load_game_read_long(company[i].stock_issued, company[i].stock_issued >= 0); load_game_read_long(company[i].stock_issued, company[i].stock_issued >= 0);
@ -391,6 +414,7 @@ bool load_game (int num)
#endif #endif
free(buf); free(buf);
free(wcbuf);
free(filename); free(filename);
free(prev_locale); free(prev_locale);
free(codeset_nl); free(codeset_nl);
@ -514,7 +538,7 @@ bool save_game (int num)
// Write out player data // Write out player data
for (i = 0; i < number_players; i++) { for (i = 0; i < number_players; i++) {
save_game_write_string(player[i].name); save_game_write_string(player[i].name, player[i].name_utf8);
save_game_write_double(player[i].cash); save_game_write_double(player[i].cash);
save_game_write_double(player[i].debt); save_game_write_double(player[i].debt);
save_game_write_bool(player[i].in_game); save_game_write_bool(player[i].in_game);

View File

@ -108,48 +108,6 @@ static int cmp_player (const void *a, const void *b);
void init_game (void) void init_game (void)
{ {
/* Initialise strings used for keycode input and map representations.
Each string must have an ASCII vertical line (U+007C) in the
correct position, followed by context information (such as
"input|Company" and "output|MapVals"). This is done to overcome a
limitation of gettext_noop() and N_() that does NOT allow context
IDs. This vertical line is replaced by a NUL character to
terminate the resulting string. The vertical line MAY appear in
other positions; if so, it is handled correctly. */
keycode_company = xstrdup(gettext(default_keycode_company));
if (strlen(keycode_company) < MAX_COMPANIES + 1
|| keycode_company[MAX_COMPANIES] != '|') {
err_exit(_("keycode string for companies has incorrect format: `%s'"),
keycode_company);
}
keycode_company[MAX_COMPANIES] = '\0';
keycode_game_move = xstrdup(gettext(default_keycode_game_move));
if (strlen(keycode_game_move) < NUMBER_MOVES + 1
|| keycode_game_move[NUMBER_MOVES] != '|') {
err_exit(_("keycode string for game moves has incorrect format: `%s'"),
keycode_game_move);
}
keycode_game_move[NUMBER_MOVES] = '\0';
printable_map_val = xstrdup(gettext(default_printable_map_val));
if (strlen(printable_map_val) < MAX_COMPANIES + 4
|| printable_map_val[MAX_COMPANIES + 3] != '|') {
err_exit(_("output string for companies has incorrect format: `%s'"),
printable_map_val);
}
printable_map_val[MAX_COMPANIES + 3] = '\0';
printable_game_move = xstrdup(gettext(default_printable_game_move));
if (strlen(printable_game_move) < NUMBER_MOVES + 1
|| printable_game_move[NUMBER_MOVES] != '|') {
err_exit(_("output string for game moves has incorrect format: `%s'"),
printable_game_move);
}
printable_game_move[NUMBER_MOVES] = '\0';
// Try to load an old game, if possible // Try to load an old game, if possible
if (game_num != 0) { if (game_num != 0) {
chtype *chbuf = xmalloc(BUFSIZE * sizeof(chtype)); chtype *chbuf = xmalloc(BUFSIZE * sizeof(chtype));
@ -213,6 +171,8 @@ void init_game (void)
} }
if (! game_loaded) { if (! game_loaded) {
wchar_t *buf = xmalloc(BUFSIZE * sizeof(wchar_t));
ask_player_names(); ask_player_names();
deltxwin(); // "Number of players" window deltxwin(); // "Number of players" window
@ -231,7 +191,8 @@ void init_game (void)
// Initialise company data // Initialise company data
for (int i = 0; i < MAX_COMPANIES; i++) { for (int i = 0; i < MAX_COMPANIES; i++) {
company[i].name = xstrdup(gettext(company_name[i])); xmbstowcs(buf, gettext(company_name[i]), BUFSIZE);
company[i].name = xwcsdup(buf);
company[i].share_price = 0.0; company[i].share_price = 0.0;
company[i].share_return = INITIAL_RETURN; company[i].share_return = INITIAL_RETURN;
company[i].stock_issued = 0; company[i].stock_issued = 0;
@ -263,10 +224,12 @@ void init_game (void)
txdlgbox(MAX_DLG_LINES, 50, 8, WCENTER, attr_normal_window, txdlgbox(MAX_DLG_LINES, 50, 8, WCENTER, attr_normal_window,
attr_title, attr_normal, attr_highlight, 0, attr_title, attr_normal, attr_highlight, 0,
attr_waitforkey, _(" First Player "), attr_waitforkey, _(" First Player "),
_("The first player to go is ^{%s^}."), _("The first player to go is ^{%ls^}."),
player[first_player].name); player[first_player].name);
txrefresh(); txrefresh();
} }
free(buf);
} }
} }
@ -280,15 +243,14 @@ void init_game (void)
static int ask_number_players (void) static int ask_number_players (void)
{ {
char *keycode_contgame; wchar_t *keycode_contgame = xmalloc(BUFSIZE * sizeof(wchar_t));
chtype *chbuf; chtype *chbuf = xmalloc(BUFSIZE * sizeof(chtype));
int lines, maxwidth; int lines, maxwidth;
int widthbuf[2]; int widthbuf[2];
int key, ret; int ret;
bool done; bool done;
chbuf = xmalloc(BUFSIZE * sizeof(chtype));
lines = mkchstr(chbuf, BUFSIZE, attr_normal, attr_keycode, 0, 2, lines = mkchstr(chbuf, BUFSIZE, attr_normal, attr_keycode, 0, 2,
WIN_COLS - 7, widthbuf, 2, WIN_COLS - 7, widthbuf, 2,
/* TRANSLATORS: The keycode <C> should be modified to /* TRANSLATORS: The keycode <C> should be modified to
@ -311,21 +273,33 @@ static int ask_number_players (void)
first character (keyboard input code) is used to print the user's first character (keyboard input code) is used to print the user's
response if one of those keys is pressed. Both upper and response if one of those keys is pressed. Both upper and
lower-case versions should be present. */ lower-case versions should be present. */
keycode_contgame = xstrdup(pgettext("input|ContinueGame", "Cc")); xmbstowcs(keycode_contgame, pgettext("input|ContinueGame", "Cc"), BUFSIZE);
done = false; done = false;
while (! done) { while (! done) {
key = gettxchar(curwin); wint_t key;
if (key >= '1' && key <= MAX_PLAYERS + '0') { if (gettxchar(curwin, &key) == OK) {
wechochar(curwin, key | A_BOLD); // Ordinary wide character
ret = key - '0'; if (key >= '1' && key <= MAX_PLAYERS + '0') {
done = true; left(curwin, getcury(curwin), getcurx(curwin), A_BOLD,
} else if (strchr(keycode_contgame, key) != NULL) { 0, 0, 1, "%lc", key);
wechochar(curwin, ((unsigned char) *keycode_contgame) | A_BOLD); wrefresh(curwin);
ret = 0;
done = true; ret = key - '0';
done = true;
} else if (wcschr(keycode_contgame, key) != NULL) {
left(curwin, getcury(curwin), getcurx(curwin), A_BOLD,
0, 0, 1, "%lc", *keycode_contgame);
wrefresh(curwin);
ret = 0;
done = true;
} else {
beep();
}
} else { } else {
// Function or control key
switch (key) { switch (key) {
case KEY_ESC: case KEY_ESC:
case KEY_CANCEL: case KEY_CANCEL:
@ -357,7 +331,7 @@ int ask_game_number (void)
chtype *chbuf; chtype *chbuf;
int lines, maxwidth; int lines, maxwidth;
int widthbuf[2]; int widthbuf[2];
int key, ret; int ret;
bool done; bool done;
@ -378,13 +352,22 @@ int ask_game_number (void)
done = false; done = false;
while (! done) { while (! done) {
key = gettxchar(curwin); wint_t key;
if (key >= '1' && key <= '9') { if (gettxchar(curwin, &key) == OK) {
wechochar(curwin, key | A_BOLD); // Ordinary wide character
ret = key - '0'; if (key >= '1' && key <= '9') {
done = true; left(curwin, getcury(curwin), getcurx(curwin), A_BOLD,
0, 0, 1, "%lc", key);
wrefresh(curwin);
ret = key - '0';
done = true;
} else {
beep();
}
} else { } else {
// Function or control key
switch (key) { switch (key) {
case KEY_ESC: case KEY_ESC:
case KEY_CANCEL: case KEY_CANCEL:
@ -426,10 +409,11 @@ void ask_player_names (void)
int w = getmaxx(curwin) - x - 2; int w = getmaxx(curwin) - x - 2;
player[0].name = NULL; player[0].name = NULL;
player[0].name_utf8 = NULL;
while (true) { while (true) {
int ret = gettxstr(curwin, &player[0].name, NULL, false, int ret = gettxstr(curwin, &player[0].name, NULL, false,
2, x, w, attr_input_field); 2, x, w, attr_input_field);
if (ret == OK && strlen(player[0].name) != 0) { if (ret == OK && wcslen(player[0].name) != 0) {
break; break;
} else { } else {
beep(); beep();
@ -460,6 +444,7 @@ void ask_player_names (void)
for (i = 0; i < number_players; i++) { for (i = 0; i < number_players; i++) {
player[i].name = NULL; player[i].name = NULL;
player[i].name_utf8 = NULL;
entered[i] = false; entered[i] = false;
left(curwin, i + 3, 2, attr_normal, 0, 0, 1, left(curwin, i + 3, 2, attr_normal, 0, 0, 1,
/* xgettext:c-format, range: 1..8 */ /* xgettext:c-format, range: 1..8 */
@ -478,7 +463,7 @@ void ask_player_names (void)
switch (ret) { switch (ret) {
case OK: case OK:
// Make sure name is not an empty string // Make sure name is not an empty string
len = strlen(player[cur].name); len = wcslen(player[cur].name);
entered[cur] = (len != 0); entered[cur] = (len != 0);
if (len == 0) { if (len == 0) {
beep(); beep();
@ -487,7 +472,7 @@ void ask_player_names (void)
// Make sure name has not been entered already // Make sure name has not been entered already
for (i = 0; i < number_players; i++) { for (i = 0; i < number_players; i++) {
if (i != cur && player[i].name != NULL if (i != cur && player[i].name != NULL
&& strcmp(player[i].name, player[cur].name) == 0) { && wcscmp(player[i].name, player[cur].name) == 0) {
entered[cur] = false; entered[cur] = false;
beep(); beep();
break; break;
@ -601,10 +586,10 @@ void end_game (void)
lines = mkchstr(chbuf, BUFSIZE, attr_normal, attr_highlight, lines = mkchstr(chbuf, BUFSIZE, attr_normal, attr_highlight,
attr_blink, 5, WIN_COLS - 8, widthbuf, 5, attr_blink, 5, WIN_COLS - 8, widthbuf, 5,
(player[0].sort_value == 0) ? (player[0].sort_value == 0) ?
_("The winner is ^{%s^}\n" _("The winner is ^{%ls^}\n"
"who is ^[*** BANKRUPT ***^]") : "who is ^[*** BANKRUPT ***^]") :
/* xgettext:c-format */ /* xgettext:c-format */
_("The winner is ^{%s^}\n" _("The winner is ^{%ls^}\n"
"with a value of ^{%N^}."), "with a value of ^{%N^}."),
player[0].name, player[0].sort_value); player[0].name, player[0].sort_value);
@ -622,16 +607,15 @@ void end_game (void)
pgettext("subtitle", "Player")); pgettext("subtitle", "Player"));
right(curwin, lines + 4, w - 4, attr_subtitle, 0, 0, 1, right(curwin, lines + 4, w - 4, attr_subtitle, 0, 0, 1,
/* TRANSLATORS: "Total Value" refers to the total worth /* TRANSLATORS: "Total Value" refers to the total worth
(shares, cash and debt) of any given player. %s is the (shares, cash and debt) of any given player. %ls is the
currency symbol of the current locale. */ currency symbol of the current locale. */
pgettext("subtitle", "Total Value (%s)"), pgettext("subtitle", "Total Value (%ls)"), currency_symbol);
lconvinfo.currency_symbol);
for (int i = 0; i < number_players; i++) { for (int i = 0; i < number_players; i++) {
right(curwin, i + lines + 5, ORDINAL_COLS + 2, attr_normal, 0, 0, right(curwin, i + lines + 5, ORDINAL_COLS + 2, attr_normal, 0, 0,
1, gettext(ordinal[i + 1])); 1, gettext(ordinal[i + 1]));
left(curwin, i + lines + 5, ORDINAL_COLS + 4, attr_normal, 0, 0, left(curwin, i + lines + 5, ORDINAL_COLS + 4, attr_normal, 0, 0,
1, "%s", player[i].name); 1, "%ls", player[i].name);
right(curwin, i + lines + 5, w - 2, attr_normal, 0, 0, right(curwin, i + lines + 5, w - 2, attr_normal, 0, 0,
1, " %!N ", player[i].sort_value); 1, " %!N ", player[i].sort_value);
} }
@ -659,7 +643,7 @@ void show_map (bool closewin)
// Display current player and turn number // Display current player and turn number
left(curwin, 1, 4, attr_mapwin_title, attr_mapwin_highlight, 0, 1, left(curwin, 1, 4, attr_mapwin_title, attr_mapwin_highlight, 0, 1,
_("Player: ^{%s^}"), player[current_player].name); _("Player: ^{%ls^}"), player[current_player].name);
right(curwin, 1, getmaxx(curwin) - 2, attr_mapwin_title, right(curwin, 1, getmaxx(curwin) - 2, attr_mapwin_title,
attr_mapwin_highlight, attr_mapwin_blink, 1, attr_mapwin_highlight, attr_mapwin_blink, 1,
(turn_number != max_turn) ? _(" Turn: ^{%d^} ") : (turn_number != max_turn) ? _(" Turn: ^{%d^} ") :
@ -669,26 +653,11 @@ void show_map (bool closewin)
for (int y = 0; y < MAX_Y; y++) { for (int y = 0; y < MAX_Y; y++) {
wmove(curwin, y + 3, 2); wmove(curwin, y + 3, 2);
for (int x = 0; x < MAX_X; x++) { for (int x = 0; x < MAX_X; x++) {
map_val_t m = galaxy_map[x][y]; chtype *mapstr = CHTYPE_MAP_VAL(galaxy_map[x][y]);
switch (m) { while (*mapstr != '\0') {
case MAP_EMPTY: waddch(curwin, *mapstr++);
waddch(curwin, PRINTABLE_MAP_VAL(m) | attr_map_empty);
break;
case MAP_OUTPOST:
waddch(curwin, PRINTABLE_MAP_VAL(m) | attr_map_outpost);
break;
case MAP_STAR:
waddch(curwin, PRINTABLE_MAP_VAL(m) | attr_map_star);
break;
default:
waddch(curwin, PRINTABLE_MAP_VAL(m) | attr_map_company);
break;
} }
waddch(curwin, ' ' | attr_map_empty);
} }
} }
@ -724,7 +693,7 @@ void show_status (int num)
attr_normal_window); attr_normal_window);
center(curwin, 1, 0, attr_title, 0, 0, 1, _(" Stock Portfolio ")); center(curwin, 1, 0, attr_title, 0, 0, 1, _(" Stock Portfolio "));
center(curwin, 2, 0, attr_normal, attr_highlight, 0, 1, center(curwin, 2, 0, attr_normal, attr_highlight, 0, 1,
_("Player: ^{%s^}"), player[num].name); _("Player: ^{%ls^}"), player[num].name);
val = total_value(num); val = total_value(num);
if (val == 0.0) { if (val == 0.0) {
@ -780,16 +749,16 @@ void show_status (int num)
- SHARE_RETURN_COLS, attr_subtitle, 0, 0, 2, - SHARE_RETURN_COLS, attr_subtitle, 0, 0, 2,
/* TRANSLATORS: "Price per share" is a two-line column /* TRANSLATORS: "Price per share" is a two-line column
label in a table containing the price per share in label in a table containing the price per share in
any given company. %s is the currency symbol in the any given company. %ls is the currency symbol in
current locale. The maximum column width is 12 the current locale. The maximum column width is 12
characters INCLUDING the currency symbol (see characters INCLUDING the currency symbol (see
SHARE_PRICE_COLS in src/intf.h). */ SHARE_PRICE_COLS in src/intf.h). */
pgettext("subtitle", "Price per\nshare (%s)"), pgettext("subtitle", "Price per\nshare (%ls)"),
lconvinfo.currency_symbol); currency_symbol);
for (line = 6, i = 0; i < MAX_COMPANIES; i++) { for (line = 6, i = 0; i < MAX_COMPANIES; i++) {
if (company[i].on_map) { if (company[i].on_map) {
left(curwin, line, 4, attr_normal, 0, 0, 1, "%s", left(curwin, line, 4, attr_normal, 0, 0, 1, "%ls",
company[i].name); company[i].name);
right(curwin, line, w - 2, attr_normal, 0, 0, 1, "%.2f ", right(curwin, line, w - 2, attr_normal, 0, 0, 1, "%.2f ",

View File

@ -128,10 +128,12 @@ player_info_t player[MAX_PLAYERS]; // Array of players
map_val_t galaxy_map[MAX_X][MAX_Y]; // Map of the galaxy map_val_t galaxy_map[MAX_X][MAX_Y]; // Map of the galaxy
move_rec_t game_move[NUMBER_MOVES]; // Current moves move_rec_t game_move[NUMBER_MOVES]; // Current moves
char *keycode_company; // Keycodes for each company wchar_t *keycode_company; // Keycodes for each company
char *keycode_game_move; // Keycodes for each game move wchar_t *keycode_game_move; // Keycodes for each game move
char *printable_map_val; // Printable output for each map value wchar_t *printable_map_val; // Printable output for each map value
char *printable_game_move; // Printable output for each game move wchar_t *printable_game_move; // Printable output for each game move
chtype *chtype_map_val[MAX_COMPANIES + 3]; // as chtype strings
chtype *chtype_game_move[NUMBER_MOVES]; // as chtype strings
int max_turn; // Max. number of turns in game int max_turn; // Max. number of turns in game
int turn_number; // Current turn (1 to max_turn) int turn_number; // Current turn (1 to max_turn)

View File

@ -32,7 +32,7 @@
#define included_GLOBALS_H 1 #define included_GLOBALS_H 1
#include <stdbool.h> #include <system.h>
/************************************************************************ /************************************************************************
@ -97,7 +97,7 @@
// Information about each company // Information about each company
typedef struct company_info { typedef struct company_info {
const char *name; // Company name wchar_t *name; // Company name
double share_price; // Share price double share_price; // Share price
double share_return; // Return per share double share_return; // Return per share
long int stock_issued; // Total stock sold to players long int stock_issued; // Total stock sold to players
@ -108,7 +108,8 @@ typedef struct company_info {
// Information about each player // Information about each player
typedef struct player_info { typedef struct player_info {
char *name; // Player name wchar_t *name; // Player name
char *name_utf8; // Player name (in UTF-8, for load/save)
double cash; // Cash available double cash; // Cash available
double debt; // Amount of debt double debt; // Amount of debt
long int stock_owned[MAX_COMPANIES]; // How much stock is owned long int stock_owned[MAX_COMPANIES]; // How much stock is owned
@ -130,11 +131,14 @@ typedef enum map_val {
#define MAP_TO_COMPANY(m) ((m) - MAP_A) #define MAP_TO_COMPANY(m) ((m) - MAP_A)
#define IS_MAP_COMPANY(m) ((m) >= MAP_A && (m) <= MAP_LAST) #define IS_MAP_COMPANY(m) ((m) >= MAP_A && (m) <= MAP_LAST)
#define PRINTABLE_MAP_VAL(m) \ #define MAP_TO_INDEX(m) \
(((m) == MAP_EMPTY) ? printable_map_val[0] : \ (((m) == MAP_EMPTY) ? 0 : \
(((m) == MAP_OUTPOST) ? printable_map_val[1] : \ (((m) == MAP_OUTPOST) ? 1 : \
(((m) == MAP_STAR) ? printable_map_val[2] : \ (((m) == MAP_STAR) ? 2 : \
printable_map_val[(m) - MAP_A + 3]))) ((m) - MAP_A + 3))))
#define PRINTABLE_MAP_VAL(m) printable_map_val[MAP_TO_INDEX(m)]
#define CHTYPE_MAP_VAL(m) chtype_map_val[MAP_TO_INDEX(m)]
// Information about a move // Information about a move
@ -144,6 +148,7 @@ typedef struct move_rec {
} move_rec_t; } move_rec_t;
#define PRINTABLE_GAME_MOVE(m) (printable_game_move[m]) #define PRINTABLE_GAME_MOVE(m) (printable_game_move[m])
#define CHTYPE_GAME_MOVE(m) (chtype_game_move[m])
// Player moves / selection values // Player moves / selection values
@ -193,10 +198,12 @@ extern player_info_t player[MAX_PLAYERS]; // Array of players
extern map_val_t galaxy_map[MAX_X][MAX_Y]; // Map of the galaxy extern map_val_t galaxy_map[MAX_X][MAX_Y]; // Map of the galaxy
extern move_rec_t game_move[NUMBER_MOVES]; // Current moves extern move_rec_t game_move[NUMBER_MOVES]; // Current moves
extern char *keycode_company; // Keycodes for each company extern wchar_t *keycode_company; // Keycodes for each company
extern char *keycode_game_move; // Keycodes for each game move extern wchar_t *keycode_game_move; // Keycodes for each game move
extern char *printable_map_val; // Printable output for each map value extern wchar_t *printable_map_val; // Printable output for each map value
extern char *printable_game_move; // Printable output for each game move extern wchar_t *printable_game_move; // Printable output for each game move
extern chtype *chtype_map_val[MAX_COMPANIES + 3]; // as chtype strings
extern chtype *chtype_game_move[NUMBER_MOVES]; // as chtype strings
extern int max_turn; // Max. number of turns in game extern int max_turn; // Max. number of turns in game
extern int turn_number; // Current turn (1 to max_turn) extern int turn_number; // Current turn (1 to max_turn)

View File

@ -279,7 +279,6 @@ void show_help (void)
char *cp; char *cp;
mbstate_t mbstate; mbstate_t mbstate;
chtype *outp; chtype *outp;
wchar_t c;
size_t i, n; size_t i, n;
int count = BIGBUFSIZE; int count = BIGBUFSIZE;
@ -387,36 +386,31 @@ 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
c = btowc(PRINTABLE_GAME_MOVE(*htxt - L'1')); wcbuf[0] = PRINTABLE_GAME_MOVE(*htxt - L'1');
wcbuf[0] = (c == WEOF) ? EILSEQ_REPL : c;
wcbuf[1] = '\0'; wcbuf[1] = '\0';
goto addwcbuf; goto addwcbuf;
case 'M': case 'M':
// Last choice of move, as a key press // Last choice of move, as a key press
c = btowc(PRINTABLE_GAME_MOVE(NUMBER_MOVES - 1)); wcbuf[0] = PRINTABLE_GAME_MOVE(NUMBER_MOVES - 1);
wcbuf[0] = (c == WEOF) ? EILSEQ_REPL : c;
wcbuf[1] = '\0'; wcbuf[1] = '\0';
goto addwcbuf; goto addwcbuf;
case '.': case '.':
// Map representation of empty space // Map representation of empty space
c = btowc(PRINTABLE_MAP_VAL(MAP_EMPTY)); wcbuf[0] = PRINTABLE_MAP_VAL(MAP_EMPTY);
wcbuf[0] = (c == WEOF) ? EILSEQ_REPL : c;
wcbuf[1] = '\0'; wcbuf[1] = '\0';
goto addwcbuf; goto addwcbuf;
case '+': case '+':
// Map representation of an outpost // Map representation of an outpost
c = btowc(PRINTABLE_MAP_VAL(MAP_OUTPOST)); wcbuf[0] = PRINTABLE_MAP_VAL(MAP_OUTPOST);
wcbuf[0] = (c == WEOF) ? EILSEQ_REPL : c;
wcbuf[1] = '\0'; wcbuf[1] = '\0';
goto addwcbuf; goto addwcbuf;
case '*': case '*':
// Map representation of a star // Map representation of a star
c = btowc(PRINTABLE_MAP_VAL(MAP_STAR)); wcbuf[0] = PRINTABLE_MAP_VAL(MAP_STAR);
wcbuf[0] = (c == WEOF) ? EILSEQ_REPL : c;
wcbuf[1] = '\0'; wcbuf[1] = '\0';
goto addwcbuf; goto addwcbuf;
@ -429,8 +423,8 @@ void show_help (void)
case 'G': case 'G':
case 'H': case 'H':
// Map representation of company // Map representation of company
c = btowc(PRINTABLE_MAP_VAL(COMPANY_TO_MAP(*htxt - L'A'))); assert((*htxt - L'A') < MAX_COMPANIES);
wcbuf[0] = (c == WEOF) ? EILSEQ_REPL : c; wcbuf[0] = PRINTABLE_MAP_VAL(COMPANY_TO_MAP(*htxt - L'A'));
wcbuf[1] = '\0'; wcbuf[1] = '\0';
goto addwcbuf; goto addwcbuf;
@ -486,35 +480,41 @@ void show_help (void)
"for the previous page ] ")); "for the previous page ] "));
wrefresh(curwin); wrefresh(curwin);
int key = gettxchar(curwin); wint_t key;
if (gettxchar(curwin, &key) == OK) {
switch (key) { // Ordinary wide character
case KEY_BS:
case KEY_BACKSPACE:
case KEY_DEL:
case KEY_PPAGE:
case KEY_UP:
case KEY_LEFT:
case KEY_BTAB:
if (curpage == 0) {
beep();
} else {
curpage--;
}
break;
case KEY_ESC:
case KEY_CANCEL:
case KEY_EXIT:
case KEY_CTRL('C'):
case KEY_CTRL('G'):
case KEY_CTRL('\\'):
done = true;
break;
default:
curpage++; curpage++;
done = (curpage == numpages); done = (curpage == numpages);
} else {
// Function or control character
switch (key) {
case KEY_BS:
case KEY_BACKSPACE:
case KEY_DEL:
case KEY_PPAGE:
case KEY_UP:
case KEY_LEFT:
case KEY_BTAB:
if (curpage == 0) {
beep();
} else {
curpage--;
}
break;
case KEY_ESC:
case KEY_CANCEL:
case KEY_EXIT:
case KEY_CTRL('C'):
case KEY_CTRL('G'):
case KEY_CTRL('\\'):
done = true;
break;
default:
curpage++;
done = (curpage == numpages);
}
} }
} }

1212
src/intf.c

File diff suppressed because it is too large Load Diff

View File

@ -105,8 +105,8 @@ typedef enum curs_type {
#define KEY_CTRL(x) ((x) - 0100) // ASCII control character #define KEY_CTRL(x) ((x) - 0100) // ASCII control character
// Keycodes for inserting the default value in input routines // Keycodes for inserting the default value in input routines
#define KEY_DEFAULTVAL1 '=' #define CHAR_DEFVAL1 '='
#define KEY_DEFAULTVAL2 ';' #define CHAR_DEFVAL2 ';'
// Control-arrow key combinations, as returned by Ncurses // Control-arrow key combinations, as returned by Ncurses
#ifndef KEY_CDOWN #ifndef KEY_CDOWN
@ -116,6 +116,11 @@ typedef enum curs_type {
# define KEY_CRIGHT 01052 // CTRL + Right Arrow # define KEY_CRIGHT 01052 // CTRL + Right Arrow
#endif #endif
// Function-key result, for Curses that do not define it
#ifndef KEY_CODE_YES
# define KEY_CODE_YES 0400
#endif
// Timeout value (in ms) for Meta-X-style keyboard input // Timeout value (in ms) for Meta-X-style keyboard input
#ifdef NCURSES_VERSION #ifdef NCURSES_VERSION
# define META_TIMEOUT ESCDELAY # define META_TIMEOUT ESCDELAY
@ -321,7 +326,7 @@ extern int txdlgbox (int maxlines, int ncols, int begin_y, int begin_x,
Returns: int - Number of lines actually used Returns: int - Number of lines actually used
This function converts the format string and following arguments into This function converts the format string and following arguments into
chbuf, a chtype buffer that can be used for calls to leftch(), centerch() chbuf, a chtype string that can be used for calls to leftch(), centerch()
and rightch(). At most maxlines lines are used, each with a maximum and rightch(). At most maxlines lines are used, each with a maximum
width of maxwidth. The actual widths of each resulting line are stored width of maxwidth. The actual widths of each resulting line are stored
in widthbuf (which must not be NULL). If maxlines is greater than 1, in widthbuf (which must not be NULL). If maxlines is greater than 1,
@ -534,26 +539,26 @@ extern int right (WINDOW *win, int y, int x, chtype attr_norm, chtype attr_alt1,
/* /*
Function: gettxchar - Read a character from the keyboard Function: gettxchar - Read a wide character from the keyboard
Parameters: win - Window to use (should be curwin) Parameters: win - Window to use (should be curwin)
Returns: int - The keyboard character wch - Pointer to keyboard wide character
Returns: int - OK or KEY_CODE_YES
This function reads a single character from the keyboard. The key is This function waits until the user presses a key on the keyboard, then
NOT echoed to the terminal display, nor is the cursor visibility reads that key as a single wide character. If it is a function key or
affected. a control key, it is stored in wch and KEY_CODE_YES is returned.
Otherwise, it is an ordinary key: it is also stored in wch and OK is
This implementation does not handle multibyte characters correctly: returned. ERR is never returned. The key is NOT echoed to the
each part of the multibyte character most likely appears as a separate terminal display, nor is the cursor visibility affected.
keyboard press.
*/ */
extern int gettxchar (WINDOW *win); extern int gettxchar (WINDOW *win, wint_t *wch);
/* /*
Function: gettxline - Read a line from the keyboard (low-level) Function: gettxline - Read a line of input from the keyboard (low-level)
Parameters: win - Window to use (should be curwin) Parameters: win - Window to use (should be curwin)
buf - Pointer to preallocated buffer buf - Pointer to preallocated buffer
bufsize - Size of buffer in bytes bufsize - Size of buffer (number of wchar_t elements)
modified - Pointer to modified status (result) modified - Pointer to modified status (result)
multifield - Allow <TAB>, etc, to exit this function multifield - Allow <TAB>, etc, to exit this function
emptyval - String used if input line is empty emptyval - String used if input line is empty
@ -561,14 +566,14 @@ extern int gettxchar (WINDOW *win);
allowed - Characters allowed in the input line allowed - Characters allowed in the input line
stripspc - True to strip leading/trailing spaces stripspc - True to strip leading/trailing spaces
y, x - Start of the input field (line, column) y, x - Start of the input field (line, column)
width - Width of the input field width - Width of the input field (column spaces)
attr - Character rendition to use for input field attr - Character rendition to use for input field
Returns: int - Status code: OK, ERR or KEY_ keycode Returns: int - Status code: OK, ERR or KEY_ keycode
This low-level function draws an input field width characters long This low-level function shows an input field width column spaces long
using attr as the character rendition, then reads a line of input from using attr as the character rendition, then reads a line of input from
the keyboard and places it into the preallocated buffer buf[] of size the keyboard and places it into the preallocated buffer buf[] of size
bufsize. On entry, buf[] must contain a valid C string; this string is bufsize. On entry, buf[] must contain a valid string; this string is
used as the initial contents of the input field. On exit, buf[] used as the initial contents of the input field. On exit, buf[]
contains the final string as edited or input by the user. This string contains the final string as edited or input by the user. This string
is printed in place of the input field using the original character is printed in place of the input field using the original character
@ -580,13 +585,13 @@ extern int gettxchar (WINDOW *win);
empty string is entered, the string pointed to by emptyval (if not empty string is entered, the string pointed to by emptyval (if not
NULL) is stored in buf[]. NULL) is stored in buf[].
If CANCEL, EXIT, ESC, ^C, ^\ or ^G is pressed, ERR is returned. In If ESC, CANCEL, EXIT, ^C, ^G or ^\ is pressed, ERR is returned. In
this case, buf[] contains the string as left by the user: emptyval is this case, buf[] contains the string as left by the user: emptyval is
NOT used, nor are leading and trailing spaces stripped. NOT used, nor are leading and trailing spaces stripped.
If multifield is true, the UP and DOWN arrow keys, as well as TAB, If multifield is true, the UP and DOWN arrow keys, as well as TAB,
Shift-TAB, ^P (Previous) and ^N (Next) return KEY_UP or KEY_DOWN as Shift-TAB, ^P (Previous) and ^N (Next) return either KEY_UP or KEY_DOWN
appropriate. As with CANCEL etc., emptyval is NOT used, nor are as appropriate. As with ESC etc., emptyval is NOT used, nor are
leading and trailing spaces stripped. leading and trailing spaces stripped.
In all of these cases, the boolean variable *modified (if modified is In all of these cases, the boolean variable *modified (if modified is
@ -594,7 +599,7 @@ extern int gettxchar (WINDOW *win);
way (including if the user made any changed, spaces were stripped or if way (including if the user made any changed, spaces were stripped or if
emptyval was copied into buf[]). emptyval was copied into buf[]).
If KEY_DEFAULTVAL1 or KEY_DEFAULTVAL2 is pressed when the input line is If either KEY_DEFVAL1 or KEY_DEFVAL2 is pressed when the input line is
empty, the string pointed to by defaultval (if not NULL) is placed in empty, the string pointed to by defaultval (if not NULL) is placed in
the buffer as if typed by the user. Editing is NOT terminated in this the buffer as if typed by the user. Editing is NOT terminated in this
case. case.
@ -607,17 +612,12 @@ extern int gettxchar (WINDOW *win);
Note that the character rendition (attributes) in attr may contain a Note that the character rendition (attributes) in attr may contain a
printing character. For example, A_BOLD | '_' is a valid rendition printing character. For example, A_BOLD | '_' is a valid rendition
that causes the input field to be a series of "_" characters in bold. that causes the input field to be a series of "_" characters in bold.
Note also that the cursor becomes invisible after calling this function.
This implementation does not handle multibyte characters correctly:
each part of the multibyte character most likely appears as a separate
keyboard press and is handled as a separate character, causing the
cursor position to be incorrect. In addition, allowed is compared on a
byte-by-byte basis, not character-by-character.
*/ */
extern int gettxline (WINDOW *win, char *buf, int bufsize, extern int gettxline (WINDOW *win, wchar_t *restrict buf, int bufsize,
bool *restrict modified, bool multifield, bool *restrict modified, bool multifield,
const char *emptyval, const char *defaultval, const wchar_t *emptyval, const wchar_t *defaultval,
const char *allowed, bool stripspc, int y, int x, const wchar_t *allowed, bool stripspc, int y, int x,
int width, chtype attr); int width, chtype attr);
@ -633,18 +633,19 @@ extern int gettxline (WINDOW *win, char *buf, int bufsize,
Returns: int - Status code: OK, ERR or KEY_ keycode Returns: int - Status code: OK, ERR or KEY_ keycode
This function calls gettxline() to allow the user to enter a string via This function calls gettxline() to allow the user to enter a string via
the keyboard. On entry, bufptr must be the address of a char * pointer the keyboard. On entry, bufptr must be the address of a wchar_t *
variable; that pointer (*bufptr) must either be NULL or contain the pointer variable; that pointer (*bufptr) must either be NULL or contain
address of a buffer previously allocated with gettxstr(). If *bufptr the address of a buffer previously allocated with gettxstr(). If
is NULL, a buffer of BUFSIZE is automatically allocated using malloc(); *bufptr is NULL, a buffer of BUFSIZE is automatically allocated using
this buffer is used to store and return the input line. malloc(); this buffer is used to store and return the input line.
Apart from bufptr, all parameters are as used for gettxline(). The Apart from bufptr, all parameters are as used for gettxline(). The
gettxline() parameters emptyval and defaultval are passed as "", gettxline() parameters emptyval and defaultval are passed as "",
allowed is NULL and stripspc is true. allowed is NULL and stripspc is true.
*/ */
extern int gettxstr (WINDOW *win, char **bufptr, bool *restrict modified, extern int gettxstr (WINDOW *win, wchar_t *restrict *restrict bufptr,
bool multifield, int y, int x, int width, chtype attr); bool *restrict modified, bool multifield,
int y, int x, int width, chtype attr);
/* /*
@ -656,7 +657,7 @@ extern int gettxstr (WINDOW *win, char **bufptr, bool *restrict modified,
emptyval - Value to use for empty input emptyval - Value to use for empty input
defaultval - Value to use for default input defaultval - Value to use for default input
y, x - Start of the input field (line, column) y, x - Start of the input field (line, column)
width - Width of the input field width - Width of the input field (column spaces)
attr - Character rendition to use for input field attr - Character rendition to use for input field
Returns: int - Status code: OK, ERR or KEY_ keycode Returns: int - Status code: OK, ERR or KEY_ keycode
@ -668,12 +669,11 @@ extern int gettxstr (WINDOW *win, char **bufptr, bool *restrict modified,
result from gettxline() is passed back to the caller. Note that the result from gettxline() is passed back to the caller. Note that the
low-level function gettxline() is called with multifield set to false. low-level function gettxline() is called with multifield set to false.
This function is locale-aware, although multibyte strings are not This function is locale-aware. In particular, the default value is
handled correctly. In particular, the default value is formatted using formatted using strfmon() and uses the locale monetary default decimal
strfmon() and uses the locale monetary default decimal places places (frac_digits). In addition, the user is allowed to use the
(frac_digits). In addition, the user is allowed to use the locale's locale's radix character (decimal point) and the thousands separator,
radix character (decimal point) and the thousands separator, as well as as well as the monetary versions of these.
the monetary versions of these.
*/ */
extern int gettxdouble (WINDOW *win, double *restrict result, double min, extern int gettxdouble (WINDOW *win, double *restrict result, double min,
double max, double emptyval, double defaultval, double max, double emptyval, double defaultval,
@ -696,9 +696,9 @@ extern int gettxdouble (WINDOW *win, double *restrict result, double min,
This function behaves almost exactly like gettxdouble(), except that This function behaves almost exactly like gettxdouble(), except that
only integer numbers are allowed to be entered. only integer numbers are allowed to be entered.
This function is locale-aware, although multibyte strings are not This function is locale-aware. In particular, the user is allowed to
handled correctly. In particular, the user is allowed to use the use the locale's thousands separator and the monetary thousands
locale's thousands separator and the monetary thousands separator. separator.
*/ */
extern int gettxlong (WINDOW *win, long int *restrict result, long int min, extern int gettxlong (WINDOW *win, long int *restrict result, long int min,
long int max, long int emptyval, long int defaultval, long int max, long int emptyval, long int defaultval,
@ -710,10 +710,11 @@ extern int gettxlong (WINDOW *win, long int *restrict result, long int min,
Parameters: win - Window to use (should be curwin) Parameters: win - Window to use (should be curwin)
Returns: bool - True if Yes was selected, false if No Returns: bool - True if Yes was selected, false if No
This function waits for the user to press either "Y" (for Yes) or "N" This function waits for the user to press either the locale-specific
(for No) on the keyboard, then prints the answer using A_BOLD. True is equivalent of "Y" (for Yes) or "N" (for No) on the keyboard, then
returned if "Y" was selected, false if "N". Note that the cursor prints the answer using A_BOLD. True is returned if "Y" was selected,
becomes invisible after calling this function. false if "N". Note that the cursor becomes invisible after calling
this function.
*/ */
extern bool answer_yesno (WINDOW *win); extern bool answer_yesno (WINDOW *win);
@ -730,10 +731,6 @@ extern bool answer_yesno (WINDOW *win);
The reason the user is not asked "Press any key to continue" is The reason the user is not asked "Press any key to continue" is
historical: many, many people used to ask "where is the <ANY> key?" :-) historical: many, many people used to ask "where is the <ANY> key?" :-)
The current implementation does not handle multibyte characters
correctly: only the first byte of the character is consumed, with
further bytes left in the keyboard queue.
*/ */
extern void wait_for_key (WINDOW *win, int y, chtype attr); extern void wait_for_key (WINDOW *win, int y, chtype attr);

View File

@ -219,8 +219,8 @@ selection_t get_move (void)
// Display current move choices on the galaxy map // Display current move choices on the galaxy map
for (int i = 0; i < NUMBER_MOVES; i++) { for (int i = 0; i < NUMBER_MOVES; i++) {
mvwaddch(curwin, game_move[i].y + 3, game_move[i].x * 2 + 2, mvwaddchstr(curwin, game_move[i].y + 3, game_move[i].x * 2 + 2,
PRINTABLE_GAME_MOVE(i) | attr_map_choice); CHTYPE_GAME_MOVE(i));
} }
wrefresh(curwin); wrefresh(curwin);
@ -242,7 +242,7 @@ selection_t get_move (void)
right(curwin, 1, getmaxx(curwin) / 2, attr_normal, attr_keycode, right(curwin, 1, getmaxx(curwin) / 2, attr_normal, attr_keycode,
attr_choice, 1, attr_choice, 1,
_("Select move [^[%c^]-^[%c^]/^{1^}-^{3^}/^{<CTRL><C>^}]: "), _("Select move [^[%lc^]-^[%lc^]/^{1^}-^{3^}/^{<CTRL><C>^}]: "),
PRINTABLE_GAME_MOVE(0), PRINTABLE_GAME_MOVE(NUMBER_MOVES - 1)); PRINTABLE_GAME_MOVE(0), PRINTABLE_GAME_MOVE(NUMBER_MOVES - 1));
curs_set(CURS_ON); curs_set(CURS_ON);
@ -250,60 +250,69 @@ selection_t get_move (void)
// Get the actual selection made by the player // Get the actual selection made by the player
while (selection == SEL_NONE) { while (selection == SEL_NONE) {
int i; wint_t key;
bool found;
int key = gettxchar(curwin); if (gettxchar(curwin, &key) == OK) {
// Ordinary wide character
int i;
bool found;
if (isupper(*keycode_game_move)) { if (iswupper(*keycode_game_move)) {
key = toupper(key); key = towupper(key);
} else if (islower(*keycode_game_move)) { } else if (iswlower(*keycode_game_move)) {
key = tolower(key); key = towlower(key);
}
for (i = 0, found = false; keycode_game_move[i] != '\0'; i++) {
if (keycode_game_move[i] == key) {
found = true;
selection = i;
curs_set(CURS_OFF);
left(curwin, 1, getmaxx(curwin) / 2, attr_normal,
attr_choice, 0, 1,
/* TRANSLATORS: "Move" refers to the choice of
moves made by the current player (out of a
selection of 20 moves). */
_("Move ^{%c^}"), PRINTABLE_GAME_MOVE(i));
break;
} }
}
if (! found) { for (i = 0, found = false; keycode_game_move[i] != '\0'; i++) {
if (keycode_game_move[i] == key) {
found = true;
selection = i;
curs_set(CURS_OFF);
left(curwin, 1, getmaxx(curwin) / 2, attr_normal,
attr_choice, 0, 1,
/* TRANSLATORS: "Move" refers to the choice of
moves made by the current player (out of a
selection of 20 moves). */
_("Move ^{%lc^}"), PRINTABLE_GAME_MOVE(i));
break;
}
}
if (! found) {
switch (key) {
case '1':
curs_set(CURS_OFF);
show_status(current_player);
curs_set(CURS_ON);
break;
case '2':
selection = SEL_BANKRUPT;
curs_set(CURS_OFF);
left(curwin, 1, getmaxx(curwin) / 2, attr_normal,
attr_normal | A_BOLD, 0, 1,
_("^{<2>^} (Declare bankruptcy)"));
break;
case '3':
selection = SEL_SAVE;
curs_set(CURS_OFF);
left(curwin, 1, getmaxx(curwin) / 2, attr_normal,
attr_normal | A_BOLD, 0, 1,
_("^{<3>^} (Save and end the game)"));
break;
default:
beep();
}
}
} else {
// Function or control key
switch (key) { switch (key) {
case '1':
curs_set(CURS_OFF);
show_status(current_player);
curs_set(CURS_ON);
break;
case '2':
selection = SEL_BANKRUPT;
curs_set(CURS_OFF);
left(curwin, 1, getmaxx(curwin) / 2, attr_normal,
attr_normal | A_BOLD, 0, 1,
_("^{<2>^} (Declare bankruptcy)"));
break;
case '3':
selection = SEL_SAVE;
curs_set(CURS_OFF);
left(curwin, 1, getmaxx(curwin) / 2, attr_normal,
attr_normal | A_BOLD, 0, 1,
_("^{<3>^} (Save and end the game)"));
break;
case KEY_ESC: case KEY_ESC:
case KEY_CANCEL: case KEY_CANCEL:
case KEY_EXIT: case KEY_EXIT:
@ -361,10 +370,10 @@ selection_t get_move (void)
if (! saved) { if (! saved) {
// Ask which game to save // Ask which game to save
int key;
bool done; bool done;
int widthbuf[2]; int widthbuf[2];
int lines, maxwidth; int lines, maxwidth;
int choice;
lines = mkchstr(chbuf, BUFSIZE, attr_normal, attr_keycode, 0, lines = mkchstr(chbuf, BUFSIZE, attr_normal, attr_keycode, 0,
2, WIN_COLS - 7, widthbuf, 2, 2, WIN_COLS - 7, widthbuf, 2,
@ -383,12 +392,22 @@ selection_t get_move (void)
done = false; done = false;
while (! done) { while (! done) {
key = gettxchar(curwin); wint_t key;
if (key >= '1' && key <= '9') { if (gettxchar(curwin, &key) == OK) {
wechochar(curwin, key | A_BOLD); // Ordinary wide character
done = true; if (key >= '1' && key <= '9') {
left(curwin, getcury(curwin), getcurx(curwin),
A_BOLD, 0, 0, 1, "%lc", key);
wrefresh(curwin);
choice = key - '0';
done = true;
} else {
beep();
}
} else { } else {
// Function or control key
switch (key) { switch (key) {
case KEY_ESC: case KEY_ESC:
case KEY_CANCEL: case KEY_CANCEL:
@ -396,7 +415,7 @@ selection_t get_move (void)
case KEY_CTRL('C'): case KEY_CTRL('C'):
case KEY_CTRL('G'): case KEY_CTRL('G'):
case KEY_CTRL('\\'): case KEY_CTRL('\\'):
key = KEY_CANCEL; choice = ERR;
done = true; done = true;
break; break;
@ -408,9 +427,10 @@ selection_t get_move (void)
curs_set(CURS_OFF); curs_set(CURS_OFF);
if (key != KEY_CANCEL) { if (choice != ERR) {
// Try to save the game, if possible // Try to save the game, if possible
game_num = key - '0';
game_num = choice;
mkchstr(chbuf, BUFSIZE, attr_status_window, 0, 0, 1, mkchstr(chbuf, BUFSIZE, attr_status_window, 0, 0, 1,
WIN_COLS - 7, &width, 1, WIN_COLS - 7, &width, 1,
@ -645,14 +665,14 @@ void bankrupt_player (bool forced)
txdlgbox(MAX_DLG_LINES, 50, 7, WCENTER, attr_error_window, txdlgbox(MAX_DLG_LINES, 50, 7, WCENTER, attr_error_window,
attr_error_title, attr_error_highlight, 0, 0, attr_error_title, attr_error_highlight, 0, 0,
attr_error_waitforkey, _(" Bankruptcy Court "), attr_error_waitforkey, _(" Bankruptcy Court "),
_("%s has been declared bankrupt " _("%ls has been declared bankrupt "
"by the Interstellar Trading Bank."), "by the Interstellar Trading Bank."),
player[current_player].name); player[current_player].name);
} else { } else {
txdlgbox(MAX_DLG_LINES, 50, 7, WCENTER, attr_error_window, txdlgbox(MAX_DLG_LINES, 50, 7, WCENTER, attr_error_window,
attr_error_title, attr_error_highlight, 0, 0, attr_error_title, attr_error_highlight, 0, 0,
attr_error_waitforkey, _(" Bankruptcy Court "), attr_error_waitforkey, _(" Bankruptcy Court "),
_("%s has declared bankruptcy."), _("%ls has declared bankruptcy."),
player[current_player].name); player[current_player].name);
} }
txrefresh(); txrefresh();
@ -721,7 +741,7 @@ void try_start_new_company (int x, int y)
txdlgbox(MAX_DLG_LINES, 50, 7, WCENTER, attr_normal_window, txdlgbox(MAX_DLG_LINES, 50, 7, WCENTER, attr_normal_window,
attr_title, attr_normal, attr_highlight, 0, attr_waitforkey, attr_title, attr_normal, attr_highlight, 0, attr_waitforkey,
_(" New Company "), _(" New Company "),
_("A new company has been formed!\nIts name is ^{%s^}."), _("A new company has been formed!\nIts name is ^{%ls^}."),
company[i].name); company[i].name);
txrefresh(); txrefresh();
@ -777,7 +797,7 @@ void merge_companies (map_val_t a, map_val_t b)
lines = mkchstr(chbuf, BUFSIZE, attr_normal, attr_highlight, 0, 4, lines = mkchstr(chbuf, BUFSIZE, attr_normal, attr_highlight, 0, 4,
WIN_COLS - 8, widthbuf, 4, WIN_COLS - 8, widthbuf, 4,
_("^{%s^} has just merged into ^{%s^}.\n" _("^{%ls^} has just merged into ^{%ls^}.\n"
"Please note the following transactions:\n"), "Please note the following transactions:\n"),
company[bb].name, company[aa].name); company[bb].name, company[aa].name);
@ -787,11 +807,11 @@ void merge_companies (map_val_t a, map_val_t b)
centerch(curwin, 3, 0, chbuf, lines, widthbuf); centerch(curwin, 3, 0, chbuf, lines, widthbuf);
mkchstr(chbuf, BUFSIZE, attr_highlight, 0, 0, 1, getmaxx(curwin) / 2, mkchstr(chbuf, BUFSIZE, attr_highlight, 0, 0, 1, getmaxx(curwin) / 2,
&width_aa, 1, "%s", company[aa].name); &width_aa, 1, "%ls", company[aa].name);
chbuf_aa = xchstrdup(chbuf); chbuf_aa = xchstrdup(chbuf);
mkchstr(chbuf, BUFSIZE, attr_highlight, 0, 0, 1, getmaxx(curwin) / 2, mkchstr(chbuf, BUFSIZE, attr_highlight, 0, 0, 1, getmaxx(curwin) / 2,
&width_bb, 1, "%s", company[bb].name); &width_bb, 1, "%ls", company[bb].name);
chbuf_bb = xchstrdup(chbuf); chbuf_bb = xchstrdup(chbuf);
mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1, getmaxx(curwin) / 2, mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1, getmaxx(curwin) / 2,
@ -824,12 +844,11 @@ void merge_companies (map_val_t a, map_val_t b)
pgettext("subtitle", "Player")); pgettext("subtitle", "Player"));
right(curwin, lines + 6, w - 4, attr_subtitle, 0, 0, 1, right(curwin, lines + 6, w - 4, attr_subtitle, 0, 0, 1,
/* TRANSLATORS: "Bonus" refers to the bonus cash amount paid to /* TRANSLATORS: "Bonus" refers to the bonus cash amount paid to
each player after two companies merge. %s is the currency each player after two companies merge. %ls is the currency
symbol in the current locale. The maximum column width is symbol in the current locale. The maximum column width is
12 characters INCLUDING the currency symbol (see 12 characters INCLUDING the currency symbol (see
MERGE_BONUS_COLS in src/intf.h). */ MERGE_BONUS_COLS in src/intf.h). */
pgettext("subtitle", "Bonus (%s)"), pgettext("subtitle", "Bonus (%ls)"), currency_symbol);
lconvinfo.currency_symbol);
right(curwin, lines + 6, w - 6 - MERGE_BONUS_COLS, attr_subtitle, 0, 0, 1, right(curwin, lines + 6, w - 6 - MERGE_BONUS_COLS, attr_subtitle, 0, 0, 1,
/* TRANSLATORS: "Total" refers to the total number of shares in /* TRANSLATORS: "Total" refers to the total number of shares in
the new company after a merger. The maximum column width is the new company after a merger. The maximum column width is
@ -868,7 +887,7 @@ void merge_companies (map_val_t a, map_val_t b)
mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1, w - 12 mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1, w - 12
- MERGE_BONUS_COLS - MERGE_TOTAL_STOCK_COLS - MERGE_BONUS_COLS - MERGE_TOTAL_STOCK_COLS
- MERGE_NEW_STOCK_COLS - MERGE_OLD_STOCK_COLS, - MERGE_NEW_STOCK_COLS - MERGE_OLD_STOCK_COLS,
&width, 1, "%s", player[i].name); &width, 1, "%ls", player[i].name);
leftch(curwin, ln, 4, chbuf, 1, &width); leftch(curwin, ln, 4, chbuf, 1, &width);
right(curwin, ln, w - 4, attr_normal, 0, 0, 1, "%!N", bonus); right(curwin, ln, w - 4, attr_normal, 0, 0, 1, "%!N", bonus);
@ -994,7 +1013,7 @@ void adjust_values (void)
attr_error_title, attr_error_highlight, attr_error_title, attr_error_highlight,
attr_error_normal, 0, attr_error_waitforkey, attr_error_normal, 0, attr_error_waitforkey,
_(" Bankruptcy Court "), _(" Bankruptcy Court "),
_("%s has been declared bankrupt " _("%ls has been declared bankrupt "
"by the Interstellar Trading Bank.\n\n" "by the Interstellar Trading Bank.\n\n"
"^{All assets have been taken " "^{All assets have been taken "
"to repay outstanding loans.^}"), "to repay outstanding loans.^}"),
@ -1016,7 +1035,7 @@ void adjust_values (void)
lines = mkchstr(chbuf, BUFSIZE, attr_error_highlight, lines = mkchstr(chbuf, BUFSIZE, attr_error_highlight,
attr_error_normal, 0, 6, 60 - 4, widthbuf, 6, attr_error_normal, 0, 6, 60 - 4, widthbuf, 6,
_("%s has been declared bankrupt by the " _("%ls has been declared bankrupt by the "
"Interstellar Trading Bank.\n\n" "Interstellar Trading Bank.\n\n"
"^{The Bank has agreed to pay stock holders ^}" "^{The Bank has agreed to pay stock holders ^}"
"%.2f%%^{ of the share value on each share " "%.2f%%^{ of the share value on each share "