OSDN Git Service

[Refactor] #3499 input_quantity() の入力部をinput_string() に差し替えた
[hengbandforosx/hengbandosx.git] / src / core / asking-player.cpp
index 4adb163..c6596c7 100644 (file)
@@ -8,17 +8,20 @@
 #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 <algorithm>
 #include <charconv>
 #include <climits>
 #include <iostream>
 #include <sstream>
+#include <stdexcept>
 #include <string>
 
 /*
@@ -53,12 +56,12 @@ bool askfor(char *buf, int len, bool numpad_cursor)
         len = 1;
     }
 
-    if ((x < 0) || (x >= 80)) {
+    if ((x < 0) || (x >= MAIN_TERM_MIN_COLS)) {
         x = 0;
     }
 
-    if (x + len > 80) {
-        len = 80 - x;
+    if (x + len > MAIN_TERM_MIN_COLS) {
+        len = MAIN_TERM_MIN_COLS - x;
     }
 
     buf[len] = '\0';
@@ -111,6 +114,16 @@ bool askfor(char *buf, int len, bool numpad_cursor)
             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;
@@ -140,7 +153,7 @@ bool askfor(char *buf, int len, bool numpad_cursor)
 
             pos = i;
         }
-            /* Fall through */
+            [[fallthrough]];
         case 0x7F:
         case KTRL('d'): {
             color = TERM_WHITE;
@@ -213,14 +226,19 @@ bool askfor(char *buf, int len, bool numpad_cursor)
  *
  * 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;
 }
 
 /*
@@ -230,39 +248,48 @@ bool get_string(concptr prompt, char *buf, int len)
  *
  * Note that "[y/n]" is appended to the prompt.
  */
-bool get_check(concptr prompt)
+bool input_check(std::string_view prompt)
 {
-    return get_check_strict(p_ptr, prompt, 0);
+    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(PlayerType *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;
+        mode.reset(UserCheck::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]");
+    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;
     }
@@ -270,9 +297,9 @@ bool get_check_strict(PlayerType *player_ptr, concptr prompt, BIT_FLAGS mode)
     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);
     }
 
@@ -280,14 +307,14 @@ bool get_check_strict(PlayerType *player_ptr, concptr prompt, BIT_FLAGS mode)
     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;
@@ -305,7 +332,7 @@ bool get_check_strict(PlayerType *player_ptr, concptr prompt, BIT_FLAGS mode)
             }
         }
 
-        if (mode & CHECK_DEFAULT_Y) {
+        if (mode.has(UserCheck::DEFAULT_Y)) {
             flag = true;
             break;
         }
@@ -324,42 +351,35 @@ bool get_check_strict(PlayerType *player_ptr, concptr prompt, BIT_FLAGS mode)
  *
  * 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);
+    char command;
     if (get_com_no_macros) {
-        *command = (char)inkey_special(false);
+        command = static_cast<char>(inkey_special(false));
     } else {
-        *command = inkey();
+        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;
@@ -370,49 +390,46 @@ QUANTITY get_quantity(concptr prompt, QUANTITY max)
         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;
+            return max;
         }
+
         if (amt < 0) {
-            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(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);
+        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;
@@ -430,26 +447,31 @@ void pause_line(int row)
     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[12] = "";
-    std::to_chars(std::begin(tmp_val), std::end(tmp_val) - 1, *value);
-    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().data(), 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;
 }