Index: source/gui/GUIbase.cpp
===================================================================
--- source/gui/GUIbase.cpp	(revision 15212)
+++ source/gui/GUIbase.cpp	(working copy)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2009 Wildfire Games.
+/* Copyright (C) 2014 Wildfire Games.
  * This file is part of 0 A.D.
  *
  * 0 A.D. is free software: you can redistribute it and/or modify
@@ -21,8 +21,7 @@
 
 #include "precompiled.h"
 
-#include <string>
-
+#include "ps/CLogger.h"
 #include "GUI.h"
 
 //--------------------------------------------------------
@@ -62,118 +61,86 @@
 
 bool CClientArea::SetClientArea(const CStr& Value)
 {
-	// This might lack incredible speed, but since all XML files
-	//  are read at startup, reading 100 client areas will be
-	//  negligible in the loading time.
+	/* ClientAreas contain a left, top, right, and bottom
+	 *  for example: "50%-150 10%+9 50%+150 10%+25" means
+	 *  the left edge is at 50% minus 150 pixels, the top
+	 *  edge is at 10% plus 9 pixels, the right edge is at
+	 *  50% plus 150 pixels, and the bottom edge is at 10%
+	 *  plus 25 pixels.
+	 * All four coordinates are required and can be
+	 *  defined only in pixels, only in percents, or some
+	 *  combination of both.
+	 */
 
-	// Setup parser to parse the value
-
-	// One of the four values:
-	//  will give outputs like (in argument):
-	//  (200) <== no percent, just the first $value
-	//  (200) (percent) <== just the percent
-	//  (200) (percent) (100) <== percent PLUS pixel
-	//  (200) (percent) (-100) <== percent MINUS pixel
-	//  (200) (percent) (100) (-100) <== Both PLUS and MINUS are used, INVALID
-	/*
-	string one_value = "_[-_$arg(_minus)]$value[$arg(percent)%_[+_$value]_[-_$arg(_minus)$value]_]";
-	string four_values = one_value + "$arg(delim)" + 
-						 one_value + "$arg(delim)" + 
-						 one_value + "$arg(delim)" + 
-						 one_value + "$arg(delim)_"; // it's easier to just end with another delimiter
-	*/
-	// Don't use the above strings, because they make this code go very slowly
-	const char* four_values =
-		"_[-_$arg(_minus)]$value[$arg(percent)%_[+_$value]_[-_$arg(_minus)$value]_]" "$arg(delim)"
-		"_[-_$arg(_minus)]$value[$arg(percent)%_[+_$value]_[-_$arg(_minus)$value]_]" "$arg(delim)"
-		"_[-_$arg(_minus)]$value[$arg(percent)%_[+_$value]_[-_$arg(_minus)$value]_]" "$arg(delim)"
-		"_[-_$arg(_minus)]$value[$arg(percent)%_[+_$value]_[-_$arg(_minus)$value]_]" "$arg(delim)"
-		"_";
-	CParser& parser (CParserCache::Get(four_values));
-
-	CParserLine line;
-	line.ParseString(parser, Value);
-
-	if (!line.m_ParseOK)
-		return false;
-
-	int arg_count[4] = {0,0,0,0}; // argument counts for the four values
-	int arg_start[4] = {0,0,0,0}; // location of first argument, [0] is always 0
-
-	// Divide into the four piles (delimiter is an argument named "delim")
-	for (int i=0, valuenr=0; i<(int)line.GetArgCount(); ++i)
+	// Check the input is only numeric
+	const char* input = Value.c_str();
+	CStr buffer = "";
+	unsigned int coord = 0;
+	float pixels[4] = {0, 0, 0, 0};
+	float percents[4] = {0, 0, 0, 0};
+	for (unsigned int i = 0; i < Value.length(); i++)
 	{
-		std::string str;
-		line.GetArgString(i, str);
-		if (str == "delim")
+		switch (input[i])
 		{
-			if (valuenr==0)
-			{
-				arg_count[0] = i;
-				arg_start[1] = i+1;
-			}
-			else
-			{
-				if (valuenr!=3)
-				{
-					ENSURE(valuenr <= 2);
-					arg_start[valuenr+1] = i+1;
-					arg_count[valuenr] = arg_start[valuenr+1] - arg_start[valuenr] - 1;
-				}
-				else
-					arg_count[3] = (int)line.GetArgCount() - arg_start[valuenr] - 1;
-			}
-
-			++valuenr;
+		case '.':
+		case '0':
+		case '1':
+		case '2':
+		case '3':
+		case '4':
+		case '5':
+		case '6':
+		case '7':
+		case '8':
+		case '9':
+			buffer.push_back(input[i]);
+			break;
+		case '+':
+			pixels[coord] += buffer.ToFloat();
+			buffer = "+";
+			break;
+		case '-':
+			pixels[coord] += buffer.ToFloat();
+			buffer = "-";
+			break;
+		case '%':
+			percents[coord] += buffer.ToFloat();
+			buffer = "";
+			break;
+		case ' ':
+			pixels[coord] += buffer.ToFloat();
+			buffer = "";
+			coord++;
+			break;
+		default:
+			LOGWARNING(L"ClientArea definitions may only contain numerics. Your input: '%s'", Value.c_str());
+			return false;
 		}
+		if (coord > 3)
+		{
+			LOGWARNING(L"Too many CClientArea parameters (4 max). Your input: '%s'", Value.c_str());
+			return false;
+		}
 	}
 
-	// Iterate argument
-	
-	// This is the scheme:
-	// 1 argument = Just pixel value
-	// 2 arguments = Just percent value
-	// 3 arguments = percent and pixel
-	// 4 arguments = INVALID
-
-  	// Default to 0
-	float values[4][2] = {{0.f,0.f},{0.f,0.f},{0.f,0.f},{0.f,0.f}};
-	for (int v=0; v<4; ++v)
+	if (coord < 3)
 	{
-		if (arg_count[v] == 1)
-		{
-			std::string str;
-			line.GetArgString(arg_start[v], str);
+		LOGWARNING(L"Too few CClientArea parameters (4 min). Your input: '%s'", Value.c_str());
+		return false;
+	}
 
-			if (!line.GetArgFloat(arg_start[v], values[v][1]))
-				return false;
-		}
-		else
-		if (arg_count[v] == 2)
-		{
-			if (!line.GetArgFloat(arg_start[v], values[v][0]))
-				return false;
-		}
-		else
-		if (arg_count[v] == 3)
-		{
-			if (!line.GetArgFloat(arg_start[v], values[v][0]) ||
-				!line.GetArgFloat(arg_start[v]+2, values[v][1]))
-				return false;
+	// Now that we're at the end of the string, flush the remaining buffer.
+	pixels[coord] += buffer.ToFloat();
 
-		}
-		else return false;
-	}
-
-	// Now store the values[][] in the right place
-	pixel.left =		values[0][1];
-	pixel.top =			values[1][1];
-	pixel.right =		values[2][1];
-	pixel.bottom =		values[3][1];
-	percent.left =		values[0][0];
-	percent.top =		values[1][0];
-	percent.right =		values[2][0];
-	percent.bottom =	values[3][0];
+	// Now store the coords in the right place
+	pixel.left =		pixels[0];
+	pixel.top =			pixels[1];
+	pixel.right =		pixels[2];
+	pixel.bottom =		pixels[3];
+	percent.left =		percents[0];
+	percent.top =		percents[1];
+	percent.right =		percents[2];
+	percent.bottom =	percents[3];
 	return true;
 }
 
Index: source/gui/GUIutil.cpp
===================================================================
--- source/gui/GUIutil.cpp	(revision 15212)
+++ source/gui/GUIutil.cpp	(working copy)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2012 Wildfire Games.
+/* Copyright (C) 2014 Wildfire Games.
  * This file is part of 0 A.D.
  *
  * 0 A.D. is free software: you can redistribute it and/or modify
@@ -23,7 +23,6 @@
 #include "GUI.h"
 #include "GUIManager.h"
 #include "maths/Matrix3D.h"
-#include "ps/Parser.h"
 
 extern int g_xres, g_yres;
 
@@ -61,28 +60,35 @@
 template <>
 bool __ParseString<CRect>(const CStrW& Value, CRect &Output)
 {
-	// Use the parser to parse the values
-	CParser& parser (CParserCache::Get("_$value_$value_$value_$value_"));
-
-	CParserLine line;
-	line.ParseString(parser, Value.ToUTF8());
-	if (!line.m_ParseOK)
+	const unsigned int NUM_COORDS = 4;
+	float coords[NUM_COORDS];
+	std::wstringstream stream;
+	stream.str(Value);
+	// Parse each coordinate
+	for (unsigned int i = 0; i < NUM_COORDS; i++)
 	{
-		// Parsing failed
-		return false;
-	}
-	float values[4];
-	for (int i=0; i<4; ++i)
-	{
-		if (!line.GetArgFloat(i, values[i]))
+		if (stream.eof())
 		{
-			// Parsing failed
+			LOGWARNING(L"Too few CRect parameters (min %i). Your input: '%s'", NUM_COORDS, Value.ToUTF8().c_str());
 			return false;
 		}
+		stream >> coords[i];
+		if ((stream.rdstate() & std::wstringstream::failbit) != 0)
+		{
+			LOGWARNING(L"Unable to parse CRect parameters. Your input: '%s'", Value.ToUTF8().c_str());
+			return false;
+		}
 	}
 
+	if (!stream.eof())
+	{
+		LOGWARNING(L"Too many CRect parameters (max %i). Your input: '%s'", NUM_COORDS, Value.ToUTF8().c_str());
+		return false;
+	}
+
 	// Finally the rectangle values
-	Output = CRect(values[0], values[1], values[2], values[3]);
+	Output = CRect(coords[0], coords[1], coords[2], coords[3]);
+
 	return true;
 }
 
@@ -119,36 +125,35 @@
 template <>
 bool __ParseString<CSize>(const CStrW& Value, CSize &Output)
 {
-	// Use the parser to parse the values
-	CParser& parser (CParserCache::Get("_$value_$value_"));
-
-	CParserLine line;
-	line.ParseString(parser, Value.ToUTF8());
-	if (!line.m_ParseOK)
+	const unsigned int NUM_COORDS = 2;
+	float coords[NUM_COORDS];
+	std::wstringstream stream;
+	stream.str(Value);
+	// Parse each coordinate
+	for (unsigned int i = 0; i < NUM_COORDS; i++)
 	{
-		// Parsing failed
-		return false;
+		if (stream.eof())
+		{
+			LOGWARNING(L"Too few CSize parameters (min %i). Your input: '%s'", NUM_COORDS, Value.ToUTF8().c_str());
+			return false;
+		}
+		stream >> coords[i];
+		if ((stream.rdstate() & std::wstringstream::failbit) != 0)
+		{
+			LOGWARNING(L"Unable to parse CSize parameters. Your input: '%s'", Value.ToUTF8().c_str());
+			return false;
+		}
 	}
 
-	float x, y;
+	Output.cx = coords[0];
+	Output.cy = coords[1];
 
-	// x
-	if (!line.GetArgFloat(0, x))
+	if (!stream.eof())
 	{
-		// TODO Gee: Parsing failed
+		LOGWARNING(L"Too many CSize parameters (max %i). Your input: '%s'", NUM_COORDS, Value.ToUTF8().c_str());
 		return false;
 	}
 
-	// y
-	if (!line.GetArgFloat(1, y))
-	{
-		// TODO Gee: Parsing failed
-		return false;
-	}
-
-	Output.cx = x;
-	Output.cy = y;
-	
 	return true;
 }
 
@@ -155,22 +160,35 @@
 template <>
 bool __ParseString<CPos>(const CStrW& Value, CPos &Output)
 {
-	CParser& parser (CParserCache::Get("_[-$arg(_minus)]$value_[-$arg(_minus)]$value_"));
+	const unsigned int NUM_COORDS = 2;
+	float coords[NUM_COORDS];
+	std::wstringstream stream;
+	stream.str(Value);
+	// Parse each coordinate
+	for (unsigned int i = 0; i < NUM_COORDS; i++)
+	{
+		if (stream.eof())
+		{
+			LOGWARNING(L"Too few CPos parameters (min %i). Your input: '%s'", NUM_COORDS, Value.ToUTF8().c_str());
+			return false;
+		}
+		stream >> coords[i];
+		if ((stream.rdstate() & std::wstringstream::failbit) != 0)
+		{
+			LOGWARNING(L"Unable to parse CPos parameters. Your input: '%s'", Value.ToUTF8().c_str());
+			return false;
+		}
+	}
 
-	CParserLine line;
-	line.ParseString(parser, Value.ToUTF8());
-	if (!line.m_ParseOK)
-		return false;
+	Output.x = coords[0];
+	Output.y = coords[1];
 
-	float x, y;
-	if (!line.GetArgFloat(0, x))
+	if (!stream.eof())
+	{
+		LOGWARNING(L"Too many CPos parameters (max %i). Your input: '%s'", NUM_COORDS, Value.ToUTF8().c_str());
 		return false;
-	if (!line.GetArgFloat(1, y))
-		return false;
+	}
 
-	Output.x = x;
-	Output.y = y;
-
 	return true;
 }
 
Index: source/gui/GUIutil.h
===================================================================
--- source/gui/GUIutil.h	(revision 15212)
+++ source/gui/GUIutil.h	(working copy)
@@ -37,7 +37,6 @@
 //  Includes / Compiler directives
 //--------------------------------------------------------
 #include "GUIbase.h"
-#include "ps/Parser.h"
 // TODO Gee: New
 #include "ps/Overlay.h"
 #include "CGUI.h"
Index: source/gui/IGUIObject.cpp
===================================================================
--- source/gui/IGUIObject.cpp	(revision 15212)
+++ source/gui/IGUIObject.cpp	(working copy)
@@ -22,8 +22,6 @@
 #include "precompiled.h"
 #include "GUI.h"
 
-#include "ps/Parser.h"
-
 #include "gui/scripting/JSInterface_IGUIObject.h"
 #include "gui/scripting/JSInterface_GUITypes.h"
 #include "scriptinterface/ScriptInterface.h"
Index: source/gui/tests/test_ParseString.h
===================================================================
--- source/gui/tests/test_ParseString.h	(revision 15212)
+++ source/gui/tests/test_ParseString.h	(working copy)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2013 Wildfire Games.
+/* Copyright (C) 2014 Wildfire Games.
  * This file is part of 0 A.D.
  *
  * 0 A.D. is free software: you can redistribute it and/or modify
@@ -18,6 +18,8 @@
 #include "lib/self_test.h"
 
 #include "gui/GUIbase.h"
+#include "gui/GUIutil.h"
+#include "ps/CLogger.h"
 
 class TestGuiParseString : public CxxTest::TestSuite 
 {
@@ -24,20 +26,79 @@
 public:
 	void test_clientarea()
 	{
+		TestLogger nolog;
 		CClientArea ca;
-		TS_ASSERT(ca.SetClientArea("0 1 2 3"));
-		TS_ASSERT_EQUALS(ca, CClientArea(CRect(0, 1, 2, 3), CRect(0, 0, 0, 0)));
 
-		TS_ASSERT(ca.SetClientArea("5% 10%+1 20%-2 3"));
-		TS_ASSERT_EQUALS(ca, CClientArea(CRect(0, 1, -2, 3), CRect(5, 10, 20, 0)));
+		// Test only pixels
+		TS_ASSERT(ca.SetClientArea("0.0 -10 20.0 -30"));
+		TS_ASSERT_EQUALS(ca, CClientArea(CRect(0, -10, 20, -30), CRect(0, 0, 0, 0)));
 
-		TS_ASSERT(!ca.SetClientArea("0+5% 1 2 3"));
+		// Test only pixels, but with math
+		TS_ASSERT(ca.SetClientArea("0 -100-10+100 20+200-200 -30"));
+		TS_ASSERT_EQUALS(ca, CClientArea(CRect(0, -10, 20, -30), CRect(0, 0, 0, 0)));
 
-		TS_ASSERT(!ca.SetClientArea("5%+10-10 1 2 3"));
+		// Test only percent
+		TS_ASSERT(ca.SetClientArea("-5% 10.0% -20% 30.0%"));
+		TS_ASSERT_EQUALS(ca, CClientArea(CRect(0, 0, 0, 0), CRect(-5, 10, -20, 30)));
 
-		TS_ASSERT(ca.SetClientArea("5% 10%-1 -20%-2 3"));
-		TS_ASSERT_EQUALS(ca, CClientArea(CRect(0, -1, -2, 3), CRect(5, 10, -20, 0)));
+		// Test only percent, but with math
+		TS_ASSERT(ca.SetClientArea("15%-5%-15% 10% -20% 30%+500%-500%"));
+		TS_ASSERT_EQUALS(ca, CClientArea(CRect(0, 0, 0, 0), CRect(-5, 10, -20, 30)));
 
-		TS_ASSERT(!ca.SetClientArea("5% 10%+1 -20%-2 3")); // parser bug
+		// Test mixed
+		TS_ASSERT(ca.SetClientArea("5% -10 -20% 30"));
+		TS_ASSERT_EQUALS(ca, CClientArea(CRect(0, -10, 0, 30), CRect(5, 0, -20, 0)));
+
+		// Test mixed with math
+		TS_ASSERT(ca.SetClientArea("5%+10%-10% 30%-10-30% 50-20%-50 30-100+100"));
+		TS_ASSERT_EQUALS(ca, CClientArea(CRect(0, -10, 0, 30), CRect(5, 0, -20, 0)));
+
+		// Test for fail with too many/few parameters
+		TS_ASSERT(!ca.SetClientArea("10 20 30 40 50"));
+		TS_ASSERT(!ca.SetClientArea("10 20 30"));
+
+		// Test for fail with garbage data
+		TS_ASSERT(!ca.SetClientArea("Hello world!"));
+		TS_ASSERT(!ca.SetClientArea("abc 123 xyz 789"));
+		TS_ASSERT(!ca.SetClientArea("300 wide, 400 high"));
 	}
+
+	void test_rect()
+	{
+		TestLogger nolog;
+		CRect test;
+
+		TS_ASSERT(__ParseString(CStrW(L"0.0 10.0 20.0 30.0"), test));
+		TS_ASSERT_EQUALS(CRect(0.0, 10.0, 20.0, 30.0), test);
+
+		TS_ASSERT(!__ParseString(CStrW(L"0 10 20"), test));
+		TS_ASSERT(!__ParseString(CStrW(L"0 10 20 30 40"), test));
+		TS_ASSERT(!__ParseString(CStrW(L"0,0 10,0 20,0 30,0"), test));
+	}
+
+	void test_size()
+	{
+		TestLogger nolog;
+		CSize test;
+
+		TS_ASSERT(__ParseString(CStrW(L"0.0 10.0"), test));
+		TS_ASSERT_EQUALS(CSize(0.0, 10.0), test);
+
+		TS_ASSERT(!__ParseString(CStrW(L"0"), test));
+		TS_ASSERT(!__ParseString(CStrW(L"0 10 20"), test));
+		TS_ASSERT(!__ParseString(CStrW(L"0,0 10,0"), test));
+	}
+
+	void test_pos()
+	{
+		TestLogger nolog;
+		CPos test;
+
+		TS_ASSERT(__ParseString(CStrW(L"0.0 10.0"), test));
+		TS_ASSERT_EQUALS(CPos(0.0, 10.0), test);
+
+		TS_ASSERT(!__ParseString(CStrW(L"0"), test));
+		TS_ASSERT(!__ParseString(CStrW(L"0 10 20"), test));
+		TS_ASSERT(!__ParseString(CStrW(L"0,0 10,0"), test));
+	}
 };
Index: binaries/data/mods/public/gui/session/session.xml
===================================================================
--- binaries/data/mods/public/gui/session/session.xml	(revision 15212)
+++ binaries/data/mods/public/gui/session/session.xml	(working copy)
@@ -532,7 +532,7 @@
 		</object>
 
 		<object name="tradeStatistics" size="20 90 100%-20 168">
-			<object name="landTraders" size="0 0   100%  50%" type="text" style="ModernLabelText" text_align="left" ghost="true" />
+			<object name="landTraders" size="0 0 100% 50%" type="text" style="ModernLabelText" text_align="left" ghost="true" />
 			<object name="shipTraders" size="0 50% 100% 100%" type="text" style="ModernLabelText" text_align="left" ghost="true" />
 		</object>
 
