OSDN Git Service

[Refactor] #3499 input_quantity() の入力部をinput_string() に差し替えた
[hengbandforosx/hengbandosx.git] / src / core / asking-player.cpp
index 4c489ce..c6596c7 100644 (file)
@@ -8,17 +8,21 @@
 #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;
             }
@@ -88,75 +97,90 @@ bool askfor_aux(char *buf, int len, bool numpad_cursor)
             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;
@@ -176,12 +200,8 @@ bool askfor_aux(char *buf, int len, bool numpad_cursor)
             } 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();
@@ -190,7 +210,6 @@ bool askfor_aux(char *buf, int len, bool numpad_cursor)
 
             buf[pos] = '\0';
             angband_strcat(buf, tmp, len + 1);
-
             break;
         }
         }
@@ -198,13 +217,6 @@ bool askfor_aux(char *buf, int len, bool numpad_cursor)
 }
 
 /*
- * 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: "
@@ -214,14 +226,19 @@ bool askfor(char *buf, int len) { return askfor_aux(buf, len, true); }
  *
  * 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;
 }
 
 /*
@@ -231,35 +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) { 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;
     }
@@ -267,9 +297,9 @@ bool get_check_strict(player_type *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);
     }
 
@@ -277,14 +307,14 @@ bool get_check_strict(player_type *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;
@@ -302,7 +332,7 @@ bool get_check_strict(player_type *player_ptr, concptr prompt, BIT_FLAGS mode)
             }
         }
 
-        if (mode & CHECK_DEFAULT_Y) {
+        if (mode.has(UserCheck::DEFAULT_Y)) {
             flag = true;
             break;
         }
@@ -321,89 +351,88 @@ bool get_check_strict(player_type *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);
-    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;
 }
 
 /*
@@ -418,24 +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[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;
 }