1 #include "core/asking-player.h"
2 #include "cmd-io/macro-util.h"
3 #include "core/stuff-handler.h"
4 #include "core/window-redrawer.h"
5 #include "game-option/input-options.h"
6 #include "io/command-repeater.h"
7 #include "io/input-key-acceptor.h"
8 #include "io/input-key-requester.h" //!< @todo 相互依存している、後で何とかする.
9 #include "main/sound-of-music.h"
10 #include "system/player-type-definition.h"
11 #include "system/redrawing-flags-updater.h"
12 #include "term/gameterm.h"
13 #include "term/screen-processor.h"
14 #include "term/term-color-types.h"
15 #include "term/z-form.h"
16 #include "util/int-char-converter.h"
17 #include "util/string-processor.h"
18 #include "view/display-messages.h"
28 * Get some string input at the cursor location.
29 * Assume the buffer is initialized to a default string.
31 * The default buffer is in Overwrite mode and displayed in yellow at
32 * first. Normal chars clear the yellow text and append the char in
35 * LEFT (^B) and RIGHT (^F) movement keys move the cursor position.
36 * If the text is still displayed in yellow (Overwite mode), it will
37 * turns into white (Insert mode) when cursor moves.
39 * DELETE (^D) deletes a char at the cursor position.
40 * BACKSPACE (^H) deletes a char at the left of cursor position.
41 * ESCAPE clears the buffer and the window and returns FALSE.
42 * RETURN accepts the current buffer contents and returns TRUE.
44 bool askfor(char *buf, int len, bool numpad_cursor)
48 * TERM_YELLOW : Overwrite mode
49 * TERM_WHITE : Insert mode
51 auto color = TERM_YELLOW;
59 if ((x < 0) || (x >= MAIN_TERM_MIN_COLS)) {
63 if (x + len > MAIN_TERM_MIN_COLS) {
64 len = MAIN_TERM_MIN_COLS - x;
70 term_erase(x, y, len);
71 term_putstr(x, y, -1, color, buf);
72 term_gotoxy(x + pos, y);
73 const auto skey = inkey_special(numpad_cursor);
84 auto next_pos = i + 1;
86 if (iskanji(buf[i])) {
90 if (next_pos >= pos) {
103 if ('\0' == buf[pos]) {
108 if (iskanji(buf[pos])) {
125 pos = std::string_view(buf).length();
141 auto next_pos = i + 1;
143 if (iskanji(buf[i])) {
147 if (next_pos >= pos) {
160 if (buf[pos] == '\0') {
166 if (iskanji(buf[pos])) {
171 while ('\0' != (buf[dst++] = buf[src++])) {
179 if (skey & SKEY_MASK) {
183 const auto c = static_cast<char>(skey);
184 if (color == TERM_YELLOW) {
189 strcpy(tmp, buf + pos);
203 const auto is_print = _(isprint(c) || iskana(c), isprint(c));
204 if (pos < len && is_print) {
212 angband_strcat(buf, tmp, len + 1);
220 * Get a string from the user
222 * The "prompt" should take the form "Prompt: "
224 * Note that the initial contents of the string is used as
225 * the default response, so be sure to "clear" it if needed.
227 * We clear the input, and return FALSE, on "ESCAPE".
229 bool get_string(std::string_view prompt, char *buf, int len)
234 res = askfor(buf, len);
240 * Verify something with the user
242 * The "prompt" should take the form "Query? "
244 * Note that "[y/n]" is appended to the prompt.
246 bool get_check(std::string_view prompt)
248 return get_check_strict(p_ptr, prompt, 0);
252 * Verify something with the user strictly
254 * mode & CHECK_OKAY_CANCEL : force user to answer 'O'kay or 'C'ancel
255 * mode & CHECK_NO_ESCAPE : don't allow ESCAPE key
256 * mode & CHECK_NO_HISTORY : no message_add
257 * mode & CHECK_DEFAULT_Y : accept any key as y, except n and Esc.
259 bool get_check_strict(PlayerType *player_ptr, std::string_view prompt, BIT_FLAGS mode)
261 if (!rogue_like_commands) {
262 mode &= ~CHECK_OKAY_CANCEL;
265 std::stringstream ss;
267 if (mode & CHECK_OKAY_CANCEL) {
268 ss << "[(O)k/(C)ancel]";
269 } else if (mode & CHECK_DEFAULT_Y) {
274 const auto buf = ss.str();
276 auto &rfu = RedrawingFlagsUpdater::get_instance();
278 rfu.set_flag(SubWindowRedrawingFlag::MESSAGE);
279 handle_stuff(player_ptr);
286 if (!(mode & CHECK_NO_HISTORY) && player_ptr->playing) {
288 rfu.set_flag(SubWindowRedrawingFlag::MESSAGE);
289 handle_stuff(player_ptr);
296 if (!(mode & CHECK_NO_ESCAPE)) {
303 if (mode & CHECK_OKAY_CANCEL) {
304 if (i == 'o' || i == 'O') {
307 } else if (i == 'c' || i == 'C') {
312 if (i == 'y' || i == 'Y') {
315 } else if (i == 'n' || i == 'N') {
321 if (mode & CHECK_DEFAULT_Y) {
334 * Prompts for a keypress
336 * The "prompt" should take the form "Command: "
338 * Returns TRUE unless the character is "Escape"
340 bool get_com(std::string_view prompt, char *command, bool z_escape)
344 if (get_com_no_macros) {
345 *command = (char)inkey_special(false);
351 if (*command == ESCAPE) {
354 if (z_escape && ((*command == 'z') || (*command == 'Z'))) {
362 * Request a "quantity" from the user
364 * Hack -- allow "command_arg" to specify a quantity
366 QUANTITY get_quantity(std::optional<std::string_view> prompt_opt, QUANTITY max)
369 * @todo QUANTITY、COMMAND_CODE、その他の型サイズがまちまちな変数とのやり取りが多数ある.
370 * この処理での数の入力を0からSHRT_MAXに制限することで不整合の発生を回避する.
372 max = std::clamp<QUANTITY>(max, 0, SHRT_MAX);
390 bool result = repeat_pull(&code);
391 amt = (QUANTITY)code;
392 if ((max != 1) && result) {
403 std::string_view prompt;
404 if (prompt_opt.has_value()) {
405 prompt = prompt_opt.value();
407 strnfmt(tmp, sizeof(tmp), _("いくつですか (1-%d): ", "Quantity (1-%d): "), max);
414 strnfmt(buf, sizeof(buf), "%d", amt);
418 * Don't allow to use numpad as cursor key.
420 res = askfor(buf, 6, false);
427 if (isalpha(buf[0])) {
430 amt = std::clamp<int>(atoi(buf), 0, max);
434 repeat_push((COMMAND_CODE)amt);
441 * Pause for user response
443 void pause_line(int row)
446 put_str(_("[ 何かキーを押して下さい ]", "[Press any key to continue]"), row, _(26, 23));
452 std::optional<int> input_integer(std::string_view prompt, int min, int max, int initial_value)
454 std::stringstream ss;
455 char tmp_val[12] = "";
456 std::to_chars(std::begin(tmp_val), std::end(tmp_val) - 1, initial_value);
457 ss << prompt << "(" << min << "-" << max << "): ";
458 auto digit = std::max(std::to_string(min).length(), std::to_string(max).length());
460 if (!get_string(ss.str().data(), tmp_val, digit)) {
465 auto val = std::stoi(tmp_val);
466 if ((val < min) || (val > max)) {
467 msg_format(_("%dから%dの間で指定して下さい。", "It must be between %d to %d."), min, max);
472 } catch (std::invalid_argument const &) {
473 msg_print(_("数値を入力して下さい。", "Please input numeric value."));
475 } catch (std::out_of_range const &) {
476 msg_print(_("入力可能な数値の範囲を超えています。", "Input value overflows the maximum number."));