sources/interface.cpp

changeset 31
b5b5a6a96d91
parent 30
21fba5183768
child 32
ee770597a281
--- 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;

mercurial