| | 1 | #ifdef RCSID |
| | 2 | static char RCSid[] = |
| | 3 | "$Header$"; |
| | 4 | #endif |
| | 5 | |
| | 6 | /* |
| | 7 | * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. |
| | 8 | * |
| | 9 | * Please see the accompanying license file, LICENSE.TXT, for information |
| | 10 | * on using and copying this software. |
| | 11 | */ |
| | 12 | /* |
| | 13 | Name |
| | 14 | indlg_tx.cpp - formatted text implementation of input_dialog |
| | 15 | Function |
| | 16 | Implements the input dialog using formatted text |
| | 17 | Notes |
| | 18 | Only one of indlg_tx.c or indlg_os.c should be included in a given |
| | 19 | executable. For a text-only version, include indlg_tx. For a version |
| | 20 | where os_input_dialog() provides a system dialog, use indlg_os instead. |
| | 21 | |
| | 22 | We provide a choice of input_dialog() implementations in the portable |
| | 23 | code (rather than only through the OS code) so that we can call |
| | 24 | the formatted text output routines in this version. An OS-layer |
| | 25 | implementation could not call the formatted output routines (it would |
| | 26 | have to call os_printf directly), which would result in poor prompt |
| | 27 | formatting any time a prompt exceeded a single line of text. |
| | 28 | Modified |
| | 29 | 09/27/99 MJRoberts - Creation |
| | 30 | */ |
| | 31 | |
| | 32 | |
| | 33 | #include "os.h" |
| | 34 | #include "t3std.h" |
| | 35 | #include "vmglob.h" |
| | 36 | #include "vmconsol.h" |
| | 37 | #include "charmap.h" |
| | 38 | |
| | 39 | /* |
| | 40 | * formatted text-only file prompt |
| | 41 | */ |
| | 42 | int CVmConsole::input_dialog(VMG_ int /*icon_id*/, |
| | 43 | const char *prompt, int standard_button_set, |
| | 44 | const char **buttons, int button_count, |
| | 45 | int default_index, int cancel_index) |
| | 46 | { |
| | 47 | /* keep going until we get a valid response */ |
| | 48 | for (;;) |
| | 49 | { |
| | 50 | int i; |
| | 51 | char buf[256]; |
| | 52 | const char *p; |
| | 53 | const char *cur; |
| | 54 | char *resp; |
| | 55 | int match_cnt; |
| | 56 | int last_found; |
| | 57 | static const struct |
| | 58 | { |
| | 59 | const char *buttons[3]; |
| | 60 | int button_count; |
| | 61 | } std_btns[] = |
| | 62 | { |
| | 63 | { { "&OK" }, 1 }, |
| | 64 | { { "&OK", "&Cancel" }, 2 }, |
| | 65 | { { "&Yes", "&No" }, 2 }, |
| | 66 | { { "&Yes", "&No", "&Cancel" }, 3 } |
| | 67 | }; |
| | 68 | |
| | 69 | /* |
| | 70 | * if we have a standard button set selected, get our button |
| | 71 | * labels |
| | 72 | */ |
| | 73 | switch(standard_button_set) |
| | 74 | { |
| | 75 | case 0: |
| | 76 | /* use the explicit buttons provided */ |
| | 77 | break; |
| | 78 | |
| | 79 | case OS_INDLG_OK: |
| | 80 | i = 0; |
| | 81 | |
| | 82 | use_std_btns: |
| | 83 | /* use the selected standard button set */ |
| | 84 | buttons = (const char **)std_btns[i].buttons; |
| | 85 | button_count = std_btns[i].button_count; |
| | 86 | break; |
| | 87 | |
| | 88 | case OS_INDLG_OKCANCEL: |
| | 89 | i = 1; |
| | 90 | goto use_std_btns; |
| | 91 | |
| | 92 | case OS_INDLG_YESNO: |
| | 93 | i = 2; |
| | 94 | goto use_std_btns; |
| | 95 | |
| | 96 | case OS_INDLG_YESNOCANCEL: |
| | 97 | i = 3; |
| | 98 | goto use_std_btns; |
| | 99 | |
| | 100 | default: |
| | 101 | /* |
| | 102 | * we don't recognize other standard button sets - return an |
| | 103 | * error |
| | 104 | */ |
| | 105 | return 0; |
| | 106 | } |
| | 107 | |
| | 108 | /* |
| | 109 | * if there are no buttons defined, they'll never be able to |
| | 110 | * respond, so we'd just loop forever - rather than let that |
| | 111 | * happen, return failure |
| | 112 | */ |
| | 113 | if (button_count == 0) |
| | 114 | return 0; |
| | 115 | |
| | 116 | /* display a newline and the prompt string */ |
| | 117 | format_text(vmg_ "\n"); |
| | 118 | format_text(vmg_ prompt); |
| | 119 | format_text(vmg_ " "); |
| | 120 | |
| | 121 | /* display the response */ |
| | 122 | for (i = 0 ; i < button_count ; ++i) |
| | 123 | { |
| | 124 | /* |
| | 125 | * display a slash to separate responses, if this isn't the |
| | 126 | * first one |
| | 127 | */ |
| | 128 | if (i != 0) |
| | 129 | format_text(vmg_ "/"); |
| | 130 | |
| | 131 | /* get the current button */ |
| | 132 | cur = buttons[i]; |
| | 133 | |
| | 134 | /* |
| | 135 | * Look for a "&" in the response string. If we find it, |
| | 136 | * remove the "&" and enclose the shortcut key in parens. |
| | 137 | */ |
| | 138 | for (p = cur ; *p != '\0' && *p != '&' ; |
| | 139 | p = utf8_ptr::s_inc((char *)p)) ; |
| | 140 | |
| | 141 | /* if we found the "&", put the next character in parens */ |
| | 142 | if (*p != '\0') |
| | 143 | { |
| | 144 | size_t pre_len; |
| | 145 | size_t post_len; |
| | 146 | |
| | 147 | /* |
| | 148 | * note the length of the part up to the '&', but limit it |
| | 149 | * to avoid overflowing the buffer |
| | 150 | */ |
| | 151 | pre_len = p - cur; |
| | 152 | if (pre_len > sizeof(buf) - 5) |
| | 153 | pre_len = sizeof(buf) - 5; |
| | 154 | |
| | 155 | /* |
| | 156 | * note the length of the part after the '&', limiting it |
| | 157 | * as well |
| | 158 | */ |
| | 159 | post_len = strlen(p+2); |
| | 160 | if (post_len > sizeof(buf) - 5 - pre_len) |
| | 161 | post_len = sizeof(buf) - 5 - pre_len; |
| | 162 | |
| | 163 | /* reformat the response string */ |
| | 164 | sprintf(buf, "%.*s(%c)%.*s", |
| | 165 | (int)pre_len, cur, *(p + 1), (int)post_len, p + 2); |
| | 166 | |
| | 167 | /* display it */ |
| | 168 | format_text(vmg_ buf); |
| | 169 | } |
| | 170 | else |
| | 171 | { |
| | 172 | /* no '&' - just display the response string as-is */ |
| | 173 | format_text(vmg_ cur); |
| | 174 | } |
| | 175 | } |
| | 176 | |
| | 177 | /* switch to input font */ |
| | 178 | format_text(vmg_ "<font face='TADS-Input'>"); |
| | 179 | |
| | 180 | /* read the response */ |
| | 181 | format_text(vmg_ " >"); |
| | 182 | read_line(vmg_ buf, sizeof(buf)); |
| | 183 | |
| | 184 | /* close the input font tag */ |
| | 185 | format_text(vmg_ "</font>"); |
| | 186 | |
| | 187 | /* skip any leading spaces in the reply */ |
| | 188 | for (resp = buf ; isspace(*resp) ; ++resp) ; |
| | 189 | |
| | 190 | /* if it's one character, check it against the shortcut keys */ |
| | 191 | if (strlen(resp) == 1) |
| | 192 | { |
| | 193 | /* scan the responses */ |
| | 194 | for (i = 0 ; i < button_count ; ++i) |
| | 195 | { |
| | 196 | /* look for a '&' in this button */ |
| | 197 | for (p = buttons[i] ; *p != '&' && *p != '\0' ; ++p) ; |
| | 198 | |
| | 199 | /* if we found the '&', check the shortcut */ |
| | 200 | if (*p == '&' && toupper(*(p+1)) == toupper(*resp)) |
| | 201 | { |
| | 202 | /* |
| | 203 | * this is the one - return the current index |
| | 204 | * (bumping it by one to get a 1-based value) |
| | 205 | */ |
| | 206 | return i + 1; |
| | 207 | } |
| | 208 | } |
| | 209 | } |
| | 210 | |
| | 211 | /* |
| | 212 | * Either it's not a one-character reply, or it didn't match a |
| | 213 | * short-cut - check it against the leading substrings of the |
| | 214 | * responses. If it matches exactly one of the responses in its |
| | 215 | * leading substring, use that response. |
| | 216 | */ |
| | 217 | for (i = 0, match_cnt = 0 ; i < button_count ; ++i) |
| | 218 | { |
| | 219 | const char *p1; |
| | 220 | const char *p2; |
| | 221 | |
| | 222 | /* |
| | 223 | * compare this response to the user's response; skip any |
| | 224 | * '&' in the button label |
| | 225 | */ |
| | 226 | for (p1 = resp, p2 = buttons[i] ; *p1 != '\0' && *p2 != '\0' ; |
| | 227 | ++p1, ++p2) |
| | 228 | { |
| | 229 | /* if this is a '&' in the button label, skip it */ |
| | 230 | if (*p2 == '&') |
| | 231 | ++p2; |
| | 232 | |
| | 233 | /* if these characters don't match, it's no match */ |
| | 234 | if (toupper(*p1) != toupper(*p2)) |
| | 235 | break; |
| | 236 | } |
| | 237 | |
| | 238 | /* |
| | 239 | * if we reached the end of the user's response, we have a |
| | 240 | * match in the leading substring - count it and remember |
| | 241 | * this as the last one, but keep looking, since we need to |
| | 242 | * make sure we don't have any other matches |
| | 243 | */ |
| | 244 | if (*p1 == '\0') |
| | 245 | { |
| | 246 | ++match_cnt; |
| | 247 | last_found = i; |
| | 248 | } |
| | 249 | } |
| | 250 | |
| | 251 | /* |
| | 252 | * if we found exactly one match, return it (adjusting to a |
| | 253 | * 1-based index); if we found more or less than one match, it's |
| | 254 | * not a valid response, so start over with a new prompt |
| | 255 | */ |
| | 256 | if (match_cnt == 1) |
| | 257 | return last_found + 1; |
| | 258 | } |
| | 259 | } |
| | 260 | |