Escape more strings: formspecs, item descriptions, infotexts...

Also, change the escape character to the more standard \x1b
Thus, it can be used in the future for translation or colored text,
for example.
This commit is contained in:
Ekdohibs 2016-04-04 18:31:00 +02:00 committed by Craig Robbins
parent 21079cc8eb
commit 48939df9a5
8 changed files with 105 additions and 83 deletions

View File

@ -679,8 +679,8 @@ ChatBackend::~ChatBackend()
void ChatBackend::addMessage(std::wstring name, std::wstring text)
{
name = removeChatEscapes(name);
text = removeChatEscapes(text);
name = unescape_enriched(name);
text = unescape_enriched(text);
// Note: A message may consist of multiple lines, for example the MOTD.
WStrfnd fnd(text);

View File

@ -3731,7 +3731,7 @@ void Game::handlePointingAtNode(GameRunData *runData,
NodeMetadata *meta = map.getNodeMetadata(nodepos);
if (meta) {
infotext = utf8_to_wide(meta->getString("infotext"));
infotext = unescape_enriched(utf8_to_wide(meta->getString("infotext")));
} else {
MapNode n = map.getNodeNoEx(nodepos);
@ -3807,13 +3807,15 @@ void Game::handlePointingAtObject(GameRunData *runData,
const v3f &player_position,
bool show_debug)
{
infotext = utf8_to_wide(runData->selected_object->infoText());
infotext = unescape_enriched(
utf8_to_wide(runData->selected_object->infoText()));
if (show_debug) {
if (infotext != L"") {
infotext += L"\n";
}
infotext += utf8_to_wide(runData->selected_object->debugInfoText());
infotext += unescape_enriched(utf8_to_wide(
runData->selected_object->debugInfoText()));
}
if (input->getLeftState()) {

View File

@ -609,8 +609,6 @@ void GUIFormSpecMenu::parseButton(parserData* data,std::string element,
if(!data->explicit_size)
warningstream<<"invalid use of button without a size[] element"<<std::endl;
label = unescape_string(label);
std::wstring wlabel = utf8_to_wide(label);
FieldSpec spec(
@ -733,7 +731,6 @@ void GUIFormSpecMenu::parseTable(parserData* data,std::string element)
geom.X = stof(v_geom[0]) * (float)spacing.X;
geom.Y = stof(v_geom[1]) * (float)spacing.Y;
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
FieldSpec spec(
@ -746,7 +743,7 @@ void GUIFormSpecMenu::parseTable(parserData* data,std::string element)
spec.ftype = f_Table;
for (unsigned int i = 0; i < items.size(); ++i) {
items[i] = unescape_string(items[i]);
items[i] = unescape_string(unescape_enriched(items[i]));
}
//now really show table
@ -818,7 +815,7 @@ void GUIFormSpecMenu::parseTextList(parserData* data,std::string element)
spec.ftype = f_Table;
for (unsigned int i = 0; i < items.size(); ++i) {
items[i] = unescape_string(items[i]);
items[i] = unescape_string(unescape_enriched(items[i]));
}
//now really show list
@ -889,7 +886,8 @@ void GUIFormSpecMenu::parseDropDown(parserData* data,std::string element)
}
for (unsigned int i=0; i < items.size(); i++) {
e->addItem(utf8_to_wide(items[i]).c_str());
e->addItem(unescape_string(unescape_enriched(
utf8_to_wide(items[i]))).c_str());
}
if (str_initial_selection != "")
@ -930,8 +928,6 @@ void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element)
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
label = unescape_string(label);
std::wstring wlabel = utf8_to_wide(label);
FieldSpec spec(
@ -995,8 +991,6 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data,
if(m_form_src)
default_val = m_form_src->resolveText(default_val);
default_val = unescape_string(default_val);
label = unescape_string(label);
std::wstring wlabel = utf8_to_wide(label);
@ -1094,9 +1088,6 @@ void GUIFormSpecMenu::parseTextArea(parserData* data,
default_val = m_form_src->resolveText(default_val);
default_val = unescape_string(default_val);
label = unescape_string(label);
std::wstring wlabel = utf8_to_wide(label);
FieldSpec spec(
@ -1197,7 +1188,6 @@ void GUIFormSpecMenu::parseLabel(parserData* data,std::string element)
if(!data->explicit_size)
warningstream<<"invalid use of label without a size[] element"<<std::endl;
text = unescape_string(text);
std::vector<std::string> lines = split(text, '\n');
for (unsigned int i = 0; i != lines.size(); i++) {
@ -1243,7 +1233,8 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element)
((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION)))
{
std::vector<std::string> v_pos = split(parts[0],',');
std::wstring text = utf8_to_wide(unescape_string(parts[1]));
std::wstring text = unescape_string(
unescape_enriched(utf8_to_wide(parts[1])));
MY_CHECKPOS("vertlabel",1);
@ -1330,7 +1321,6 @@ void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element,
image_name = unescape_string(image_name);
pressed_image_name = unescape_string(pressed_image_name);
label = unescape_string(label);
std::wstring wlabel = utf8_to_wide(label);
@ -1430,7 +1420,8 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element)
e->setNotClipped(true);
for (unsigned int i = 0; i < buttons.size(); i++) {
e->addTab(utf8_to_wide(buttons[i]).c_str(), -1);
e->addTab(unescape_string(unescape_enriched(
utf8_to_wide(buttons[i]))).c_str(), -1);
}
if ((tab_index >= 0) &&
@ -1489,7 +1480,6 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element)
m_default_tooltip_bgcolor,
m_default_tooltip_color);
label = unescape_string(label);
FieldSpec spec(
name,
utf8_to_wide(label),
@ -1604,14 +1594,14 @@ void GUIFormSpecMenu::parseTooltip(parserData* data, std::string element)
std::vector<std::string> parts = split(element,';');
if (parts.size() == 2) {
std::string name = parts[0];
m_tooltips[name] = TooltipSpec(unescape_string(parts[1]),
m_tooltips[name] = TooltipSpec(parts[1],
m_default_tooltip_bgcolor, m_default_tooltip_color);
return;
} else if (parts.size() == 4) {
std::string name = parts[0];
video::SColor tmp_color1, tmp_color2;
if ( parseColorString(parts[2], tmp_color1, false) && parseColorString(parts[3], tmp_color2, false) ) {
m_tooltips[name] = TooltipSpec(unescape_string(parts[1]),
m_tooltips[name] = TooltipSpec(parts[1],
tmp_color1, tmp_color2);
return;
}
@ -2242,16 +2232,18 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase,
}
// Draw tooltip
std::string tooltip_text = "";
if (hovering && !m_selected_item)
tooltip_text = item.getDefinition(m_gamedef->idef()).description;
if (tooltip_text != "") {
std::vector<std::string> tt_rows = str_split(tooltip_text, '\n');
std::wstring tooltip_text = L"";
if (hovering && !m_selected_item) {
tooltip_text = utf8_to_wide(item.getDefinition(m_gamedef->idef()).description);
tooltip_text = unescape_enriched(tooltip_text);
}
if (tooltip_text != L"") {
std::vector<std::wstring> tt_rows = str_split(tooltip_text, L'\n');
m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor);
m_tooltip_element->setOverrideColor(m_default_tooltip_color);
m_tooltip_element->setVisible(true);
this->bringToFront(m_tooltip_element);
m_tooltip_element->setText(utf8_to_wide(tooltip_text).c_str());
m_tooltip_element->setText(tooltip_text.c_str());
s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
v2u32 screenSize = driver->getScreenSize();
@ -2504,7 +2496,7 @@ void GUIFormSpecMenu::drawMenu()
u32 delta = 0;
if (id == -1) {
m_old_tooltip_id = id;
m_old_tooltip = "";
m_old_tooltip = L"";
} else {
if (id == m_old_tooltip_id) {
delta = porting::getDeltaMs(m_hovered_time, getTimeMs());
@ -2517,11 +2509,11 @@ void GUIFormSpecMenu::drawMenu()
if (id != -1 && delta >= m_tooltip_show_delay) {
for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
iter != m_fields.end(); ++iter) {
if ( (iter->fid == id) && (m_tooltips[iter->fname].tooltip != "") ){
if (iter->fid == id && m_tooltips[iter->fname].tooltip != L"") {
if (m_old_tooltip != m_tooltips[iter->fname].tooltip) {
m_old_tooltip = m_tooltips[iter->fname].tooltip;
m_tooltip_element->setText(utf8_to_wide(m_tooltips[iter->fname].tooltip).c_str());
std::vector<std::string> tt_rows = str_split(m_tooltips[iter->fname].tooltip, '\n');
m_tooltip_element->setText(m_tooltips[iter->fname].tooltip.c_str());
std::vector<std::wstring> tt_rows = str_split(m_tooltips[iter->fname].tooltip, L'\n');
s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5;
int tooltip_offset_x = m_btn_height;
@ -2875,7 +2867,7 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
core::position2d<s32>(x, y));
if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
m_old_tooltip_id = -1;
m_old_tooltip = "";
m_old_tooltip = L"";
}
if (!isChild(hovered,this)) {
if (DoubleClickDetection(event)) {

View File

@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "modalMenu.h"
#include "guiTable.h"
#include "network/networkprotocol.h"
#include "util/string.h"
class IGameDef;
class InventoryManager;
@ -191,18 +192,26 @@ class GUIFormSpecMenu : public GUIModalMenu
bool scale;
};
/* The responsibility of unescaping the strings has been shifted
* from the formspec parsing methods to the draw methods.
* There still are a few exceptions:
* - Vertical label, because it modifies the string by inserting
* '\n' between each character,
* - Tab header, because it gives the string immediately to
* Irrlicht and we can't unescape it later.
*/
struct FieldSpec
{
FieldSpec()
{
}
FieldSpec(const std::string &name, const std::wstring &label,
const std::wstring &fdeflt, int id) :
const std::wstring &default_text, int id) :
fname(name),
flabel(label),
fdefault(fdeflt),
fid(id)
{
flabel = unescape_string(unescape_enriched(label));
fdefault = unescape_string(unescape_enriched(default_text));
send = false;
ftype = f_Unknown;
is_exit = false;
@ -235,12 +244,12 @@ class GUIFormSpecMenu : public GUIModalMenu
}
TooltipSpec(std::string a_tooltip, irr::video::SColor a_bgcolor,
irr::video::SColor a_color):
tooltip(a_tooltip),
bgcolor(a_bgcolor),
color(a_color)
{
tooltip = unescape_string(unescape_enriched(utf8_to_wide(a_tooltip)));
}
std::string tooltip;
std::wstring tooltip;
irr::video::SColor bgcolor;
irr::video::SColor color;
};
@ -252,18 +261,18 @@ class GUIFormSpecMenu : public GUIModalMenu
}
StaticTextSpec(const std::wstring &a_text,
const core::rect<s32> &a_rect):
text(a_text),
rect(a_rect),
parent_button(NULL)
{
text = unescape_string(unescape_enriched(a_text));
}
StaticTextSpec(const std::wstring &a_text,
const core::rect<s32> &a_rect,
gui::IGUIButton *a_parent_button):
text(a_text),
rect(a_rect),
parent_button(a_parent_button)
{
text = unescape_string(unescape_enriched(a_text));
}
std::wstring text;
core::rect<s32> rect;
@ -406,7 +415,7 @@ protected:
u32 m_tooltip_show_delay;
s32 m_hovered_time;
s32 m_old_tooltip_id;
std::string m_old_tooltip;
std::wstring m_old_tooltip;
bool m_rmouse_auto_place;

View File

@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "hud.h"
#include "settings.h"
#include "util/numeric.h"
#include "util/string.h"
#include "log.h"
#include "gamedef.h"
#include "itemdef.h"
@ -319,7 +320,7 @@ void Hud::drawLuaElements(const v3s16 &camera_offset)
(e->number >> 8) & 0xFF,
(e->number >> 0) & 0xFF);
core::rect<s32> size(0, 0, e->scale.X, text_height * e->scale.Y);
std::wstring text = utf8_to_wide(e->text);
std::wstring text = unescape_enriched(utf8_to_wide(e->text));
core::dimension2d<u32> textsize = font->getDimension(text.c_str());
v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2),
(e->align.Y - 1.0) * (textsize.Height / 2));
@ -355,11 +356,11 @@ void Hud::drawLuaElements(const v3s16 &camera_offset)
(e->number >> 8) & 0xFF,
(e->number >> 0) & 0xFF);
core::rect<s32> size(0, 0, 200, 2 * text_height);
std::wstring text = utf8_to_wide(e->name);
std::wstring text = unescape_enriched(utf8_to_wide(e->name));
font->draw(text.c_str(), size + pos, color);
std::ostringstream os;
os << distance << e->text;
text = utf8_to_wide(os.str());
text = unescape_enriched(utf8_to_wide(os.str()));
pos.Y += text_height;
font->draw(text.c_str(), size + pos, color);
break; }

View File

@ -45,6 +45,7 @@ public:
void testStringAllowed();
void testAsciiPrintableHelper();
void testUTF8();
void testRemoveEscapes();
void testWrapRows();
void testIsNumber();
void testIsPowerOfTwo();
@ -71,6 +72,7 @@ void TestUtilities::runTests(IGameDef *gamedef)
TEST(testStringAllowed);
TEST(testAsciiPrintableHelper);
TEST(testUTF8);
TEST(testRemoveEscapes);
TEST(testWrapRows);
TEST(testIsNumber);
TEST(testIsPowerOfTwo);
@ -253,6 +255,23 @@ void TestUtilities::testUTF8()
== "the shovel dug a crumbly node!");
}
void TestUtilities::testRemoveEscapes()
{
UASSERT(unescape_enriched<wchar_t>(
L"abc\x1bXdef") == L"abcdef");
UASSERT(unescape_enriched<wchar_t>(
L"abc\x1b(escaped)def") == L"abcdef");
UASSERT(unescape_enriched<wchar_t>(
L"abc\x1b((escaped with parenthesis\\))def") == L"abcdef");
UASSERT(unescape_enriched<wchar_t>(
L"abc\x1b(incomplete") == L"abc");
UASSERT(unescape_enriched<wchar_t>(
L"escape at the end\x1b") == L"escape at the end");
// Nested escapes not supported
UASSERT(unescape_enriched<wchar_t>(
L"abc\x1b(outer \x1b(inner escape)escape)def") == L"abcescape)def");
}
void TestUtilities::testWrapRows()
{
UASSERT(wrap_rows("12345678",4) == "1234\n5678");

View File

@ -729,33 +729,6 @@ static bool parseNamedColorString(const std::string &value, video::SColor &color
return true;
}
std::wstring removeChatEscapes(const std::wstring &s) {
std::wstring output;
size_t i = 0;
while (i < s.length()) {
if (s[i] == L'\v') {
++i;
if (i == s.length()) continue;
if (s[i] == L'(') {
++i;
while (i < s.length() && s[i] != L')') {
if (s[i] == L'\\') {
++i;
}
++i;
}
++i;
} else {
++i;
}
continue;
}
output += s[i];
++i;
}
return output;
}
void str_replace(std::string &str, char from, char to)
{
std::replace(str.begin(), str.end(), from, to);

View File

@ -386,14 +386,6 @@ inline void str_replace(std::string &str, const std::string &pattern,
}
}
/**
* Remove all chat escape sequences in \p s.
*
* @param s The string in which to remove escape sequences.
* @return \p s, with escape sequences removed.
*/
std::wstring removeChatEscapes(const std::wstring &s);
/**
* Replace all occurrences of the character \p from in \p str with \p to.
*
@ -476,7 +468,7 @@ inline std::string wrap_rows(const std::string &from,
* Removes backslashes from an escaped string (FormSpec strings)
*/
template <typename T>
inline std::basic_string<T> unescape_string(std::basic_string<T> &s)
inline std::basic_string<T> unescape_string(const std::basic_string<T> &s)
{
std::basic_string<T> res;
@ -492,6 +484,40 @@ inline std::basic_string<T> unescape_string(std::basic_string<T> &s)
return res;
}
/**
* Remove all escape sequences in \p s.
*
* @param s The string in which to remove escape sequences.
* @return \p s, with escape sequences removed.
*/
template <typename T>
std::basic_string<T> unescape_enriched(const std::basic_string<T> &s)
{
std::basic_string<T> output;
size_t i = 0;
while (i < s.length()) {
if (s[i] == '\x1b') {
++i;
if (i == s.length()) continue;
if (s[i] == '(') {
++i;
while (i < s.length() && s[i] != ')') {
if (s[i] == '\\') {
++i;
}
++i;
}
++i;
} else {
++i;
}
continue;
}
output += s[i];
++i;
}
return output;
}
/**
* Checks that all characters in \p to_check are a decimal digits.