--- a/sources/interface.cpp Mon Dec 15 02:15:09 2014 +0200 +++ b/sources/interface.cpp Mon Dec 15 03:09:26 2014 +0200 @@ -43,7 +43,8 @@ INPUTSTATE_CONFIRM_DISCONNECTION, }; -static String g_input; +static StringList g_input; +static int g_inputCursor = 0; static int g_cursor = 0; static int g_pan = 0; static bool g_needRefresh = false; @@ -70,6 +71,60 @@ // ------------------------------------------------------------------------------------------------- // static FUNCTION +current_input() -> const String& +{ + return g_input[g_inputCursor]; +} + +// ------------------------------------------------------------------------------------------------- +// +// Makes current_input() the lastmost input (so that we won't modify history) +// +static FUNCTION +detach_input() -> void +{ + if (g_inputCursor > 0) + { + g_input[0] = current_input(); + g_inputCursor = 0; + } +} + +// ------------------------------------------------------------------------------------------------- +// A version of current_input() that allows changing the contents of it. +// +static FUNCTION +mutable_current_input() -> String& +{ + detach_input(); + return g_input[g_inputCursor]; +} + +// ------------------------------------------------------------------------------------------------- +// +static FUNCTION +move_input_cursor (int delta) -> void +{ + // No input history when inputting addresses or passwords + if (g_inputState != INPUTSTATE_NORMAL) + { + g_inputCursor = 0; + return; + } + + int oldcursor = g_inputCursor; + g_inputCursor = clamp (g_inputCursor + delta, 0, g_input.size() - 1); + + if (g_inputCursor != oldcursor) + { + g_cursor = current_input().length(); + g_needInputRender = true; + } +} + +// ------------------------------------------------------------------------------------------------- +// +static FUNCTION interface_prompt_string() -> String { String prompt; @@ -94,14 +149,15 @@ if (newstate != INPUTSTATE_CONFIRM_DISCONNECTION and g_inputState != INPUTSTATE_CONFIRM_DISCONNECTION) { - g_input.clear(); + g_inputCursor = 0; + mutable_current_input().clear(); } switch (newstate) { case INPUTSTATE_ADDRESS: if (g_address.host != 0) - g_input = g_address.to_string (IP_WITH_PORT); + mutable_current_input() = g_address.to_string (IP_WITH_PORT); break; default: @@ -125,6 +181,8 @@ ::refresh(); ::timeout (0); ::use_default_colors(); + g_input.clear(); + g_input << ""; g_title = format (APPNAME " %1 (%2)", full_version_string(), changeset_date_string()); for (int i = 0; i < NUM_COLORS; ++i) @@ -138,7 +196,6 @@ render_full(); refresh(); g_needRefresh = false; - print ("Interface initialized.\n"); } // ------------------------------------------------------------------------------------------------- @@ -175,9 +232,7 @@ static FUNCTION safe_disconnect (Function<void()> afterwards) -> void { - RCONSession* session = RCONSession::get_session(); - - if (session and session->state() != RCON_DISCONNECTED) + if (RCONSession::get_session()->is_active()) { g_disconnectConfirmFunction = afterwards; set_input_state (INPUTSTATE_CONFIRM_DISCONNECTION); @@ -232,35 +287,34 @@ } String prompt = interface_prompt_string(); - int displaylength = COLS - prompt.length() - 2; + int displayLength = COLS - prompt.length() - 2; + String displayString = current_input(); int y = LINES - 2; + // If we're inputting a password, replace it with asterisks + if (g_inputState == INPUTSTATE_PASSWORD) + { + for (char& ch : displayString) + ch = '*'; + } + // Ensure the cursor is within bounds - g_cursor = clamp (g_cursor, 0, g_input.length()); + g_cursor = clamp (g_cursor, 0, displayString.length()); // Ensure that the cursor is always in view, adjust panning if this is not the case - if (g_cursor > g_pan + displaylength) - g_pan = g_cursor - displaylength; // cursor went too far right + if (g_cursor > g_pan + displayLength) + g_pan = g_cursor - displayLength; // cursor went too far right else if (g_cursor < g_pan) g_pan = g_cursor; // cursor went past the pan value to the left // What part of the string to draw? int start = g_pan; - int end = min<int> (g_input.length(), start + displaylength); + int end = min<int> (displayString.length(), start + displayLength); assert (g_cursor >= start and g_cursor <= end); - String displayinput = g_input; - - // If we're inputting a password, replace it with asterisks - if (g_inputState == INPUTSTATE_PASSWORD) - { - for (char& ch : displayinput) - ch = '*'; - } - // Render the input string mvhline (LINES - 2, 0, ' ', COLS); - mvprintw (y, prompt.length() + 1, "%s", displayinput.chars()); + mvprintw (y, prompt.length() + 1, "%s", displayString.chars()); // Render the prompt attron (promptColor); @@ -269,7 +323,7 @@ // Store in memory where the cursor is now (so that we can re-draw it to position the terminal // cursor). - g_cursorChar.ch = g_cursor != 0 ? displayinput[g_cursor - 1] : '\0'; + g_cursorChar.ch = g_cursor != 0 ? displayString[g_cursor - 1] : '\0'; g_cursorChar.x = prompt.length() + (g_cursor - g_pan); g_needRefresh = true; g_needInputRender = false; @@ -298,32 +352,25 @@ String text; RCONSession* session = RCONSession::get_session(); - if (session == nullptr) - { - text = ""; - } - else + switch (session->state()) { - switch (session->state()) - { - case RCON_DISCONNECTED: - text = "Disconnected."; - break; + case RCON_DISCONNECTED: + text = "Disconnected."; + break; - case RCON_CONNECTING: - case RCON_AUTHENTICATING: - text = "Connecting to " + session->address().to_string (IP_WITH_PORT) + "..."; - break; + case RCON_CONNECTING: + case RCON_AUTHENTICATING: + text = "Connecting to " + session->address().to_string (IP_WITH_PORT) + "..."; + break; - case RCON_CONNECTED: - { - String adminText = (session->num_admins() == 0) ? "No other admins" - : format ("%1 other admin%s1", session->num_admins()); - text = format ("%1 | %2 | %3", session->address().to_string (IP_WITH_PORT), - session->level(), adminText); - } - break; + case RCON_CONNECTED: + { + String adminText = (session->num_admins() == 0) ? "No other admins" + : format ("%1 other admin%s1", session->num_admins()); + text = format ("%1 | %2 | %3", session->address().to_string (IP_WITH_PORT), + session->level(), adminText); } + break; } if (not text.is_empty()) @@ -389,7 +436,7 @@ if (ch >= 0x20 and ch <= 0x7E) { - g_input.insert (g_cursor++, char (ch)); + mutable_current_input().insert (g_cursor++, char (ch)); g_needInputRender = true; } else switch (ch) @@ -403,8 +450,18 @@ case INPUTSTATE_NORMAL: safe_disconnect ([]() { - endwin(); - throw Exitception(); + RCONSession* session = RCONSession::get_session(); + + if (session->is_active()) + { + session->disconnect(); + set_input_state (INPUTSTATE_NORMAL); + } + else + { + endwin(); + throw Exitception(); + } }); break; @@ -433,13 +490,18 @@ break; case KEY_RIGHT: - if (g_cursor < g_input.length()) + if (g_cursor < current_input().length()) { g_cursor++; g_needInputRender = true; } break; + case KEY_DOWN: + case KEY_UP: + move_input_cursor (ch == KEY_DOWN ? -1 : 1); + break; + case KEY_HOME: if (g_cursor != 0) { @@ -449,9 +511,9 @@ break; case KEY_END: - if (g_cursor != g_input.length()) + if (g_cursor != current_input().length()) { - g_cursor = g_input.length(); + g_cursor = current_input().length(); g_needInputRender = true; } break; @@ -459,15 +521,15 @@ case KEY_BACKSPACE: if (g_cursor > 0) { - g_input.remove_at (--g_cursor); + mutable_current_input().remove_at (--g_cursor); g_needInputRender = true; } break; case KEY_DC: - if (g_cursor < g_input.length()) + if (g_cursor < current_input().length()) { - g_input.remove_at (g_cursor); + mutable_current_input().remove_at (g_cursor); g_needInputRender = true; } break; @@ -492,7 +554,7 @@ case INPUTSTATE_ADDRESS: try { - g_address = IPAddress::from_string (g_input); + g_address = IPAddress::from_string (current_input()); } catch (std::exception& e) { @@ -507,20 +569,19 @@ break; case INPUTSTATE_PASSWORD: - if (g_inputState == INPUTSTATE_PASSWORD and not g_input.is_empty()) + if (g_inputState == INPUTSTATE_PASSWORD and not current_input().is_empty()) { RCONSession* session = RCONSession::new_session(); - session->set_password (g_input); + session->set_password (current_input()); session->connect (g_address); set_input_state (INPUTSTATE_NORMAL); } break; case INPUTSTATE_NORMAL: - if (RCONSession::get_session() != nullptr - and RCONSession::get_session()->send_command (g_input)) + if (RCONSession::get_session()->send_command (current_input())) { - g_input.clear(); + g_input.insert (0, ""); g_needInputRender = true; } break;