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);
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);
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()) {
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(
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(
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
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
}
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 != "")
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(
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);
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(
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++) {
((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);
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);
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) &&
m_default_tooltip_bgcolor,
m_default_tooltip_color);
- label = unescape_string(label);
FieldSpec spec(
name,
utf8_to_wide(label),
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;
}
}
// 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();
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());
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;
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)) {
#include "modalMenu.h"
#include "guiTable.h"
#include "network/networkprotocol.h"
+#include "util/string.h"
class IGameDef;
class InventoryManager;
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;
}
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;
};
}
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;
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;
#include "hud.h"
#include "settings.h"
#include "util/numeric.h"
+#include "util/string.h"
#include "log.h"
#include "gamedef.h"
#include "itemdef.h"
(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));
(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; }
void testStringAllowed();
void testAsciiPrintableHelper();
void testUTF8();
+ void testRemoveEscapes();
void testWrapRows();
void testIsNumber();
void testIsPowerOfTwo();
TEST(testStringAllowed);
TEST(testAsciiPrintableHelper);
TEST(testUTF8);
+ TEST(testRemoveEscapes);
TEST(testWrapRows);
TEST(testIsNumber);
TEST(testIsPowerOfTwo);
== "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");
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);
}
}
-/**
- * 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.
*
* 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;
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.