#include "io/input-key-requester.h" //!< @todo 相互依存している、後で何とかする.
#include "main/sound-of-music.h"
#include "system/player-type-definition.h"
+#include "system/redrawing-flags-updater.h"
+#include "term/gameterm.h"
#include "term/screen-processor.h"
#include "term/term-color-types.h"
+#include "term/z-form.h"
#include "util/int-char-converter.h"
#include "util/string-processor.h"
#include "view/display-messages.h"
-
-#include <climits>
#include <algorithm>
+#include <charconv>
+#include <climits>
#include <iostream>
-#include <string>
#include <sstream>
+#include <stdexcept>
+#include <string>
/*
* Get some string input at the cursor location.
* ESCAPE clears the buffer and the window and returns FALSE.
* RETURN accepts the current buffer contents and returns TRUE.
*/
-bool askfor_aux(char *buf, int len, bool numpad_cursor)
+bool askfor(char *buf, int len, bool numpad_cursor)
{
/*
* Text color
* TERM_YELLOW : Overwrite mode
* TERM_WHITE : Insert mode
*/
- byte color = TERM_YELLOW;
+ auto color = TERM_YELLOW;
int y, x;
term_locate(&x, &y);
- if (len < 1)
+ if (len < 1) {
len = 1;
- if ((x < 0) || (x >= 80))
+ }
+
+ if ((x < 0) || (x >= MAIN_TERM_MIN_COLS)) {
x = 0;
- if (x + len > 80)
- len = 80 - x;
+ }
- buf[len] = '\0';
+ if (x + len > MAIN_TERM_MIN_COLS) {
+ len = MAIN_TERM_MIN_COLS - x;
+ }
- int pos = 0;
+ buf[len] = '\0';
+ auto pos = 0;
while (true) {
term_erase(x, y, len);
term_putstr(x, y, -1, color, buf);
-
term_gotoxy(x + pos, y);
- int skey = inkey_special(numpad_cursor);
-
+ const auto skey = inkey_special(numpad_cursor);
switch (skey) {
case SKEY_LEFT:
case KTRL('b'): {
- int i = 0;
+ auto i = 0;
color = TERM_WHITE;
-
- if (0 == pos)
+ if (0 == pos) {
break;
+ }
+
while (true) {
- int next_pos = i + 1;
+ auto next_pos = i + 1;
#ifdef JP
- if (iskanji(buf[i]))
+ if (iskanji(buf[i])) {
next_pos++;
+ }
#endif
- if (next_pos >= pos)
+ if (next_pos >= pos) {
break;
+ }
i = next_pos;
}
pos = i;
break;
}
-
case SKEY_RIGHT:
case KTRL('f'):
color = TERM_WHITE;
- if ('\0' == buf[pos])
+ if ('\0' == buf[pos]) {
break;
+ }
#ifdef JP
- if (iskanji(buf[pos]))
+ if (iskanji(buf[pos])) {
pos += 2;
- else
+ } else {
pos++;
+ }
#else
pos++;
#endif
break;
-
+ case SKEY_TOP:
+ case KTRL('a'):
+ color = TERM_WHITE;
+ pos = 0;
+ break;
+ case SKEY_BOTTOM:
+ case KTRL('e'):
+ color = TERM_WHITE;
+ pos = std::string_view(buf).length();
+ break;
case ESCAPE:
buf[0] = '\0';
return false;
-
case '\n':
case '\r':
return true;
-
case '\010': {
- int i = 0;
+ auto i = 0;
color = TERM_WHITE;
- if (0 == pos)
+ if (pos == 0) {
break;
+ }
+
while (true) {
- int next_pos = i + 1;
+ auto next_pos = i + 1;
#ifdef JP
- if (iskanji(buf[i]))
+ if (iskanji(buf[i])) {
next_pos++;
+ }
#endif
- if (next_pos >= pos)
+ if (next_pos >= pos) {
break;
+ }
i = next_pos;
}
pos = i;
}
- /* Fall through */
-
+ [[fallthrough]];
case 0x7F:
case KTRL('d'): {
color = TERM_WHITE;
- if ('\0' == buf[pos])
+ if (buf[pos] == '\0') {
break;
- int src = pos + 1;
+ }
+
+ auto src = pos + 1;
#ifdef JP
- if (iskanji(buf[pos]))
+ if (iskanji(buf[pos])) {
src++;
+ }
#endif
-
- int dst = pos;
- while ('\0' != (buf[dst++] = buf[src++]))
+ auto dst = pos;
+ while ('\0' != (buf[dst++] = buf[src++])) {
;
+ }
+
break;
}
-
default: {
char tmp[100];
- if (skey & SKEY_MASK)
+ if (skey & SKEY_MASK) {
break;
- char c = (char)skey;
+ }
+ const auto c = static_cast<char>(skey);
if (color == TERM_YELLOW) {
buf[0] = '\0';
color = TERM_WHITE;
} else
#endif
{
-#ifdef JP
- if (pos < len && (isprint(c) || iskana(c)))
-#else
- if (pos < len && isprint(c))
-#endif
- {
+ const auto is_print = _(isprint(c) || iskana(c), isprint(c));
+ if (pos < len && is_print) {
buf[pos++] = c;
} else {
bell();
buf[pos] = '\0';
angband_strcat(buf, tmp, len + 1);
-
break;
}
}
}
/*
- * Get some string input at the cursor location.
- *
- * Allow to use numpad keys as cursor keys.
- */
-bool askfor(char *buf, int len) { return askfor_aux(buf, len, true); }
-
-/*
* Get a string from the user
*
* The "prompt" should take the form "Prompt: "
*
* We clear the input, and return FALSE, on "ESCAPE".
*/
-bool get_string(concptr prompt, char *buf, int len)
+std::optional<std::string> input_string(std::string_view prompt, int len, std::string_view initial_value)
{
- bool res;
msg_print(nullptr);
prt(prompt, 0, 0);
- res = askfor(buf, len);
+ char buf[1024]{};
+ angband_strcpy(buf, initial_value, sizeof(buf));
+ const auto res = askfor(buf, len);
prt("", 0, 0);
- return (res);
+ if (!res) {
+ return std::nullopt;
+ }
+
+ return buf;
}
/*
*
* Note that "[y/n]" is appended to the prompt.
*/
-bool get_check(concptr prompt) { return get_check_strict(p_ptr, prompt, 0); }
+bool input_check(std::string_view prompt)
+{
+ return input_check_strict(p_ptr, prompt, UserCheck::NONE);
+}
+
+/*!
+ * @details initializer_list を使うと再帰呼び出し扱いになるので一旦FlagGroup で受ける
+ */
+bool input_check_strict(PlayerType *player_ptr, std::string_view prompt, UserCheck one_mode)
+{
+ EnumClassFlagGroup<UserCheck> mode = { one_mode };
+ return input_check_strict(player_ptr, prompt, mode);
+}
/*
* Verify something with the user strictly
*
- * mode & CHECK_OKAY_CANCEL : force user to answer 'O'kay or 'C'ancel
- * mode & CHECK_NO_ESCAPE : don't allow ESCAPE key
- * mode & CHECK_NO_HISTORY : no message_add
- * mode & CHECK_DEFAULT_Y : accept any key as y, except n and Esc.
+ * OKAY_CANCEL : force user to answer 'O'kay or 'C'ancel
+ * NO_ESCAPE : don't allow ESCAPE key
+ * NO_HISTORY : no message_add
+ * DEFAULT_Y : accept any key as y, except n and Esc.
*/
-bool get_check_strict(player_type *player_ptr, concptr prompt, BIT_FLAGS mode)
+bool input_check_strict(PlayerType *player_ptr, std::string_view prompt, EnumClassFlagGroup<UserCheck> mode)
{
- char buf[80];
- if (!rogue_like_commands)
- mode &= ~CHECK_OKAY_CANCEL;
-
- if (mode & CHECK_OKAY_CANCEL) {
- angband_strcpy(buf, prompt, sizeof(buf) - 15);
- strcat(buf, "[(O)k/(C)ancel]");
- } else if (mode & CHECK_DEFAULT_Y) {
- angband_strcpy(buf, prompt, sizeof(buf) - 5);
- strcat(buf, "[Y/n]");
+ if (!rogue_like_commands) {
+ mode.reset(UserCheck::OKAY_CANCEL);
+ }
+
+ std::stringstream ss;
+ ss << prompt;
+ if (mode.has(UserCheck::OKAY_CANCEL)) {
+ ss << "[(O)k/(C)ancel]";
+ } else if (mode.has(UserCheck::DEFAULT_Y)) {
+ ss << "[Y/n]";
} else {
- angband_strcpy(buf, prompt, sizeof(buf) - 5);
- strcat(buf, "[y/n]");
+ ss << "[y/n]";
}
+ const auto buf = ss.str();
+ auto &rfu = RedrawingFlagsUpdater::get_instance();
if (auto_more) {
- player_ptr->window_flags |= PW_MESSAGE;
+ rfu.set_flag(SubWindowRedrawingFlag::MESSAGE);
handle_stuff(player_ptr);
num_more = 0;
}
msg_print(nullptr);
prt(buf, 0, 0);
- if (!(mode & CHECK_NO_HISTORY) && player_ptr->playing) {
+ if (mode.has_not(UserCheck::NO_HISTORY) && player_ptr->playing) {
message_add(buf);
- player_ptr->window_flags |= (PW_MESSAGE);
+ rfu.set_flag(SubWindowRedrawingFlag::MESSAGE);
handle_stuff(player_ptr);
}
while (true) {
int i = inkey();
- if (!(mode & CHECK_NO_ESCAPE)) {
+ if (mode.has_not(UserCheck::NO_ESCAPE)) {
if (i == ESCAPE) {
flag = false;
break;
}
}
- if (mode & CHECK_OKAY_CANCEL) {
+ if (mode.has(UserCheck::OKAY_CANCEL)) {
if (i == 'o' || i == 'O') {
flag = true;
break;
}
}
- if (mode & CHECK_DEFAULT_Y) {
+ if (mode.has(UserCheck::DEFAULT_Y)) {
flag = true;
break;
}
*
* Returns TRUE unless the character is "Escape"
*/
-bool get_com(concptr prompt, char *command, bool z_escape)
+std::optional<char> input_command(std::string_view prompt, bool z_escape)
{
msg_print(nullptr);
prt(prompt, 0, 0);
- if (get_com_no_macros)
- *command = (char)inkey_special(false);
- else
- *command = inkey();
+ char command;
+ if (get_com_no_macros) {
+ command = static_cast<char>(inkey_special(false));
+ } else {
+ command = inkey();
+ }
prt("", 0, 0);
- if (*command == ESCAPE)
- return false;
- if (z_escape && ((*command == 'z') || (*command == 'Z')))
- return false;
+ const auto is_z = (command == 'z') || (command == 'Z');
+ if ((command == ESCAPE) || (z_escape && is_z)) {
+ return std::nullopt;
+ }
- return true;
+ return command;
}
/*
- * Request a "quantity" from the user
- *
- * Hack -- allow "command_arg" to specify a quantity
+ * @brief 数量をユーザ入力する
+ * @param max 最大値 (売出し商品の個数等)
+ * @param initial_prompt 初期値
+ * @details 数値でない値 ('a'等)を入力したら最大数を選択したとみなす.
*/
-QUANTITY get_quantity(concptr prompt, QUANTITY max)
+int input_quantity(int max, std::string_view initial_prompt)
{
- // FIXME : QUANTITY、COMMAND_CODE、その他の型サイズがまちまちな変数とのやり取りが多数ある。この処理での数の入力を0からSHRT_MAXに制限することで不整合の発生を回避する。
- max = std::clamp<QUANTITY>(max, 0, SHRT_MAX);
-
- bool res;
- char tmp[80];
- char buf[80];
-
- QUANTITY amt;
+ int amt;
if (command_arg) {
amt = command_arg;
command_arg = 0;
- if (amt > max)
+ if (amt > max) {
amt = max;
+ }
- return (amt);
+ return amt;
}
- COMMAND_CODE code;
- bool result = repeat_pull(&code);
- amt = (QUANTITY)code;
+ short code;
+ auto result = repeat_pull(&code);
+ amt = code;
if ((max != 1) && result) {
- if (amt > max)
- amt = max;
- if (amt < 0)
- amt = 0;
+ if (amt > max) {
+ return max;
+ }
- return (amt);
+ if (amt < 0) {
+ return 0;
+ }
+
+ return amt;
}
- if (!prompt) {
- sprintf(tmp, _("いくつですか (1-%d): ", "Quantity (1-%d): "), max);
- prompt = tmp;
+ std::string prompt;
+ if (!initial_prompt.empty()) {
+ prompt = initial_prompt;
+ } else {
+ prompt = format(_("いくつですか (1-%d): ", "Quantity (1-%d): "), max);
}
msg_print(nullptr);
- prt(prompt, 0, 0);
- amt = 1;
- sprintf(buf, "%d", amt);
-
- /*
- * Ask for a quantity
- * Don't allow to use numpad as cursor key.
- */
- res = askfor_aux(buf, 6, false);
-
- prt("", 0, 0);
- if (!res)
+ const auto input_amount = input_string(prompt, 6, "1");
+ if (!input_amount) {
return 0;
+ }
- if (isalpha(buf[0]))
+ if (isalpha((*input_amount)[0])) {
amt = max;
- else
- amt = std::clamp<int>(atoi(buf), 0, max);
+ } else {
+ try {
+ amt = std::clamp<int>(std::stoi(*input_amount), 0, max);
+ } catch (const std::exception &) {
+ amt = 0;
+ }
+ }
- if (amt)
- repeat_push((COMMAND_CODE)amt);
+ if (amt > 0) {
+ repeat_push(static_cast<short>(amt));
+ }
- return (amt);
+ return amt;
}
/*
prt("", row, 0);
}
-bool get_value(const char *text, int min, int max, int *value)
+std::optional<int> input_integer(std::string_view prompt, int min, int max, int initial_value)
{
- std::stringstream st;
- int val;
- char tmp_val[10] = "";
- st << text << "(" << min << "-" << max << "): ";
- int digit = std::max(std::to_string(min).length(), std::to_string(max).length());
+ std::stringstream ss;
+ ss << prompt << "(" << min << "-" << max << "): ";
+ auto digit = std::max(std::to_string(min).length(), std::to_string(max).length());
while (true) {
- if (!get_string(st.str().c_str(), tmp_val, digit))
- return false;
+ const auto input_str = input_string(ss.str(), digit, std::to_string(initial_value));
+ if (!input_str.has_value()) {
+ return std::nullopt;
+ }
- val = atoi(tmp_val);
+ try {
+ auto val = std::stoi(input_str.value());
+ if ((val < min) || (val > max)) {
+ msg_format(_("%dから%dの間で指定して下さい。", "It must be between %d to %d."), min, max);
+ continue;
+ }
- if (min <= val && max >= val) {
- break;
+ return val;
+ } catch (std::invalid_argument const &) {
+ msg_print(_("数値を入力して下さい。", "Please input numeric value."));
+ continue;
+ } catch (std::out_of_range const &) {
+ msg_print(_("入力可能な数値の範囲を超えています。", "Input value overflows the maximum number."));
+ continue;
}
- msg_format(_("%dから%dの間で指定して下さい。", "It must be between %d to %d."), min, max);
}
- *value = val;
- return true;
}