diff --git a/binaries/data/mods/public/gui/session/session.xml b/binaries/data/mods/public/gui/session/session.xml index f7ab18788d..5492bb9810 100644 --- a/binaries/data/mods/public/gui/session/session.xml +++ b/binaries/data/mods/public/gui/session/session.xml @@ -532,7 +532,7 @@ - + diff --git a/source/gui/GUIbase.cpp b/source/gui/GUIbase.cpp index 2cab7f9df6..3a65bd1193 100644 --- a/source/gui/GUIbase.cpp +++ b/source/gui/GUIbase.cpp @@ -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 @@ GUI base #include "precompiled.h" -#include - +#include "ps/CLogger.h" #include "GUI.h" //-------------------------------------------------------- @@ -62,118 +61,86 @@ CRect CClientArea::GetClientArea(const CRect &parent) const 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 + // 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++) + { + switch (input[i]) + { + 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; + } + } - // 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) + if (coord < 3) + { + LOGWARNING(L"Too few CClientArea parameters (4 min). Your input: '%s'", Value.c_str()); 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) - { - std::string str; - line.GetArgString(i, str); - if (str == "delim") - { - 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; - } } - // Iterate argument - - // This is the scheme: - // 1 argument = Just pixel value - // 2 arguments = Just percent value - // 3 arguments = percent and pixel - // 4 arguments = INVALID + // Now that we're at the end of the string, flush the remaining buffer. + pixels[coord] += buffer.ToFloat(); - // 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 (arg_count[v] == 1) - { - std::string str; - line.GetArgString(arg_start[v], str); - - 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; - - } - 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; } diff --git a/source/gui/GUIutil.cpp b/source/gui/GUIutil.cpp index bcee7f250d..c1efceadc3 100644 --- a/source/gui/GUIutil.cpp +++ b/source/gui/GUIutil.cpp @@ -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 @@ GUI utilities #include "GUI.h" #include "GUIManager.h" #include "maths/Matrix3D.h" -#include "ps/Parser.h" extern int g_xres, g_yres; @@ -61,28 +60,35 @@ bool __ParseString(const CStrW& Value, float &Output) template <> bool __ParseString(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,57 +125,69 @@ bool __ParseString(const CStrW& Value, CColor &Output) template <> bool __ParseString(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 + 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; + } + } + + Output.cx = coords[0]; + Output.cy = coords[1]; + + if (!stream.eof()) + { + LOGWARNING(L"Too many CSize parameters (max %i). Your input: '%s'", NUM_COORDS, Value.ToUTF8().c_str()); return false; } - float x, y; - - // x - if (!line.GetArgFloat(0, x)) - { - // TODO Gee: Parsing failed - return false; - } - - // y - if (!line.GetArgFloat(1, y)) - { - // TODO Gee: Parsing failed - return false; - } - - Output.cx = x; - Output.cy = y; - return true; } template <> bool __ParseString(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; } diff --git a/source/gui/GUIutil.h b/source/gui/GUIutil.h index 448d137e5d..32cd980823 100644 --- a/source/gui/GUIutil.h +++ b/source/gui/GUIutil.h @@ -37,7 +37,6 @@ GUI util // Includes / Compiler directives //-------------------------------------------------------- #include "GUIbase.h" -#include "ps/Parser.h" // TODO Gee: New #include "ps/Overlay.h" #include "CGUI.h" diff --git a/source/gui/IGUIObject.cpp b/source/gui/IGUIObject.cpp index c280ff86c1..e7de78a35e 100644 --- a/source/gui/IGUIObject.cpp +++ b/source/gui/IGUIObject.cpp @@ -22,8 +22,6 @@ IGUIObject #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" diff --git a/source/gui/tests/test_ParseString.h b/source/gui/tests/test_ParseString.h index dbea409981..2e2bd7d64c 100644 --- a/source/gui/tests/test_ParseString.h +++ b/source/gui/tests/test_ParseString.h @@ -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,26 +18,87 @@ #include "lib/self_test.h" #include "gui/GUIbase.h" +#include "gui/GUIutil.h" +#include "ps/CLogger.h" class TestGuiParseString : public CxxTest::TestSuite { 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)); } };