| | 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.c - input dialog - formatted text-only version |
| | 15 | Function |
| | 16 | Implements the input dialog function using formatted text |
| | 17 | Notes |
| | 18 | Only one of indlg_tx or indlg_os should be included in a given |
| | 19 | executable. The choice depends on whether a system-level dialog |
| | 20 | is available or not. If no system-level dialog is available, |
| | 21 | include indlg_tx, which provides an implementation using formatted |
| | 22 | text. If a system-level dialog is available, use indlg_os, which |
| | 23 | calls os_input_dialog(). |
| | 24 | |
| | 25 | This file exists in the portable layer, rather than in the OS layer, |
| | 26 | so that we can provide an implementation using formatted text. An |
| | 27 | OS-layer implementation could not provide formatted text. |
| | 28 | Modified |
| | 29 | 09/27/99 MJRoberts - Creation |
| | 30 | */ |
| | 31 | |
| | 32 | #include <stdio.h> |
| | 33 | #include <string.h> |
| | 34 | #include <ctype.h> |
| | 35 | |
| | 36 | #include "std.h" |
| | 37 | #include "os.h" |
| | 38 | #include "tio.h" |
| | 39 | |
| | 40 | /* ------------------------------------------------------------------------ */ |
| | 41 | /* |
| | 42 | * Text-mode os_input_dialog implementation |
| | 43 | */ |
| | 44 | int tio_input_dialog(int icon_id, const char *prompt, |
| | 45 | int standard_button_set, |
| | 46 | const char **buttons, int button_count, |
| | 47 | int default_index, int cancel_index) |
| | 48 | { |
| | 49 | /* ignore the icon ID - we can't display an icon in text mode */ |
| | 50 | VARUSED(icon_id); |
| | 51 | |
| | 52 | /* keep going until we get a valid response */ |
| | 53 | for (;;) |
| | 54 | { |
| | 55 | int i; |
| | 56 | char buf[256]; |
| | 57 | const char *p; |
| | 58 | const char *cur; |
| | 59 | char *resp; |
| | 60 | int match_cnt; |
| | 61 | int last_found; |
| | 62 | static const struct |
| | 63 | { |
| | 64 | const char *buttons[3]; |
| | 65 | int button_count; |
| | 66 | } std_btns[] = |
| | 67 | { |
| | 68 | { { "&OK" }, 1 }, |
| | 69 | { { "&OK", "&Cancel" }, 2 }, |
| | 70 | { { "&Yes", "&No" }, 2 }, |
| | 71 | { { "&Yes", "&No", "&Cancel" }, 3 } |
| | 72 | }; |
| | 73 | |
| | 74 | /* |
| | 75 | * if we have a standard button set selected, get our button |
| | 76 | * labels |
| | 77 | */ |
| | 78 | switch(standard_button_set) |
| | 79 | { |
| | 80 | case 0: |
| | 81 | /* use the explicit buttons provided */ |
| | 82 | break; |
| | 83 | |
| | 84 | case OS_INDLG_OK: |
| | 85 | i = 0; |
| | 86 | |
| | 87 | use_std_btns: |
| | 88 | /* use the selected standard button set */ |
| | 89 | buttons = (const char **)std_btns[i].buttons; |
| | 90 | button_count = std_btns[i].button_count; |
| | 91 | break; |
| | 92 | |
| | 93 | case OS_INDLG_OKCANCEL: |
| | 94 | i = 1; |
| | 95 | goto use_std_btns; |
| | 96 | |
| | 97 | case OS_INDLG_YESNO: |
| | 98 | i = 2; |
| | 99 | goto use_std_btns; |
| | 100 | |
| | 101 | case OS_INDLG_YESNOCANCEL: |
| | 102 | i = 3; |
| | 103 | goto use_std_btns; |
| | 104 | |
| | 105 | default: |
| | 106 | /* |
| | 107 | * we don't recognize other standard button sets - return an |
| | 108 | * error |
| | 109 | */ |
| | 110 | return 0; |
| | 111 | } |
| | 112 | |
| | 113 | /* |
| | 114 | * if there are no buttons defined, they'll never be able to |
| | 115 | * respond, so we'd just loop forever - rather than let that |
| | 116 | * happen, return failure |
| | 117 | */ |
| | 118 | if (button_count == 0) |
| | 119 | return 0; |
| | 120 | |
| | 121 | /* display a newline and the prompt string */ |
| | 122 | outformat("\\n"); |
| | 123 | outformat((char *)prompt); |
| | 124 | outformat(" "); |
| | 125 | |
| | 126 | /* display the response */ |
| | 127 | for (i = 0 ; i < button_count ; ++i) |
| | 128 | { |
| | 129 | /* |
| | 130 | * display a slash to separate responses, if this isn't the |
| | 131 | * first one |
| | 132 | */ |
| | 133 | if (i != 0) |
| | 134 | outformat("/"); |
| | 135 | |
| | 136 | /* get the current button */ |
| | 137 | cur = buttons[i]; |
| | 138 | |
| | 139 | /* |
| | 140 | * Look for a "&" in the response string. If we find it, |
| | 141 | * remove the "&" and enclose the shortcut key in parens. |
| | 142 | */ |
| | 143 | for (p = cur ; *p != '&' && *p != '\0' ; ++p) ; |
| | 144 | |
| | 145 | /* if we found the "&", put the next character in parens */ |
| | 146 | if (*p == '&') |
| | 147 | { |
| | 148 | /* reformat the response string */ |
| | 149 | sprintf(buf, "%.*s(%c)%s", (int)(p - cur), cur, *(p+1), p+2); |
| | 150 | |
| | 151 | /* display it */ |
| | 152 | outformat(buf); |
| | 153 | } |
| | 154 | else |
| | 155 | { |
| | 156 | /* no '&' - just display the response string as-is */ |
| | 157 | outformat((char *)cur); |
| | 158 | } |
| | 159 | } |
| | 160 | |
| | 161 | /* if we're in HTML mode, switch to input font */ |
| | 162 | if (tio_is_html_mode()) |
| | 163 | outformat("<font face='TADS-Input'>"); |
| | 164 | |
| | 165 | /* read the response */ |
| | 166 | getstring(" >", buf, sizeof(buf)); |
| | 167 | |
| | 168 | /* if we're in HTML mode, close the input font tag */ |
| | 169 | if (tio_is_html_mode()) |
| | 170 | outformat("</font>"); |
| | 171 | |
| | 172 | /* skip any leading spaces in the reply */ |
| | 173 | for (resp = buf ; t_isspace(*resp) ; ++resp) ; |
| | 174 | |
| | 175 | /* if it's one character, check it against the shortcut keys */ |
| | 176 | if (strlen(resp) == 1) |
| | 177 | { |
| | 178 | /* scan the responses */ |
| | 179 | for (i = 0 ; i < button_count ; ++i) |
| | 180 | { |
| | 181 | /* look for a '&' in this button */ |
| | 182 | for (p = buttons[i] ; *p != '&' && *p != '\0' ; ++p) ; |
| | 183 | |
| | 184 | /* if we found the '&', check the shortcut */ |
| | 185 | if (*p == '&' && toupper(*(p+1)) == toupper(*resp)) |
| | 186 | { |
| | 187 | /* |
| | 188 | * this is the one - return the current index |
| | 189 | * (bumping it by one to get a 1-based value) |
| | 190 | */ |
| | 191 | return i + 1; |
| | 192 | } |
| | 193 | } |
| | 194 | } |
| | 195 | |
| | 196 | /* |
| | 197 | * Either it's not a one-character reply, or it didn't match a |
| | 198 | * short-cut - check it against the leading substrings of the |
| | 199 | * responses. If it matches exactly one of the responses in its |
| | 200 | * leading substring, use that response. |
| | 201 | */ |
| | 202 | for (i = 0, match_cnt = 0 ; i < button_count ; ++i) |
| | 203 | { |
| | 204 | const char *p1; |
| | 205 | const char *p2; |
| | 206 | |
| | 207 | /* |
| | 208 | * compare this response to the user's response; skip any |
| | 209 | * '&' in the button label |
| | 210 | */ |
| | 211 | for (p1 = resp, p2 = buttons[i] ; *p1 != '\0' && *p2 != '\0' ; |
| | 212 | ++p1, ++p2) |
| | 213 | { |
| | 214 | /* if this is a '&' in the button label, skip it */ |
| | 215 | if (*p2 == '&') |
| | 216 | ++p2; |
| | 217 | |
| | 218 | /* if these characters don't match, it's no match */ |
| | 219 | if (toupper(*p1) != toupper(*p2)) |
| | 220 | break; |
| | 221 | } |
| | 222 | |
| | 223 | /* |
| | 224 | * if we reached the end of the user's response, we have a |
| | 225 | * match in the leading substring - count it and remember |
| | 226 | * this as the last one, but keep looking, since we need to |
| | 227 | * make sure we don't have any other matches |
| | 228 | */ |
| | 229 | if (*p1 == '\0') |
| | 230 | { |
| | 231 | ++match_cnt; |
| | 232 | last_found = i; |
| | 233 | } |
| | 234 | } |
| | 235 | |
| | 236 | /* |
| | 237 | * if we found exactly one match, return it (adjusting to a |
| | 238 | * 1-based index); if we found more or less than one match, it's |
| | 239 | * not a valid response, so start over with a new prompt |
| | 240 | */ |
| | 241 | if (match_cnt == 1) |
| | 242 | return last_found + 1; |
| | 243 | } |
| | 244 | } |
| | 245 | |