2012-01-21 21:57:41 +01:00
|
|
|
#include "text.hpp"
|
|
|
|
|
|
|
|
static const wchar_t *g_whitespaces = L" \f\n\r\t\v";
|
|
|
|
|
|
|
|
// Simplified use of sprintf
|
|
|
|
const char *fmt(const char *format, ...)
|
|
|
|
{
|
|
|
|
static int currentStr = 0;
|
|
|
|
currentStr = (currentStr + 1) % MAX_USES;
|
|
|
|
|
|
|
|
va_list va;
|
|
|
|
va_start(va, format);
|
|
|
|
static char buffer[MAX_USES][MAX_MSG_SIZE];
|
|
|
|
vsnprintf(buffer[currentStr], MAX_MSG_SIZE, format, va);
|
|
|
|
buffer[currentStr][MAX_MSG_SIZE - 1] = '\0';
|
|
|
|
va_end(va);
|
|
|
|
|
|
|
|
return buffer[currentStr];
|
|
|
|
}
|
|
|
|
|
|
|
|
string sfmt(const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list va;
|
|
|
|
va_start(va, format);
|
|
|
|
u32 length = vsnprintf(0, 0, format, va) + 1;
|
|
|
|
va_end(va);
|
|
|
|
char *tmp = new char[length + 1];
|
|
|
|
va_start(va, format);
|
|
|
|
vsnprintf(tmp, length, format, va);
|
|
|
|
va_end(va);
|
|
|
|
string s = tmp;
|
|
|
|
delete[] tmp;
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool fmtCount(const wstringEx &format, int &i, int &s)
|
|
|
|
{
|
|
|
|
int state = 0;
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
s = 0;
|
|
|
|
for (u32 k = 0; k < format.size(); ++k)
|
|
|
|
{
|
|
|
|
if (state == 0)
|
|
|
|
{
|
|
|
|
if (format[k] == L'%')
|
|
|
|
state = 1;
|
|
|
|
}
|
|
|
|
else if (state == 1)
|
|
|
|
{
|
|
|
|
switch (format[k])
|
|
|
|
{
|
|
|
|
case L'%':
|
|
|
|
state = 0;
|
|
|
|
break;
|
|
|
|
case L'i':
|
|
|
|
case L'd':
|
|
|
|
state = 0;
|
|
|
|
++i;
|
|
|
|
break;
|
|
|
|
case L's':
|
|
|
|
state = 0;
|
|
|
|
++s;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only handles the cases i need for translations : plain %i and %s
|
|
|
|
bool checkFmt(const wstringEx &ref, const wstringEx &format)
|
|
|
|
{
|
|
|
|
int s;
|
|
|
|
int i;
|
|
|
|
int refs;
|
|
|
|
int refi;
|
|
|
|
if (!fmtCount(ref, refi, refs))
|
|
|
|
return false;
|
|
|
|
if (!fmtCount(format, i, s))
|
|
|
|
return false;
|
|
|
|
return i == refi && s == refs;
|
|
|
|
}
|
|
|
|
|
|
|
|
wstringEx wfmt(const wstringEx &format, ...)
|
|
|
|
{
|
|
|
|
// Don't care about performance
|
|
|
|
va_list va;
|
|
|
|
string f(format.toUTF8());
|
|
|
|
va_start(va, format);
|
|
|
|
u32 length = vsnprintf(0, 0, f.c_str(), va) + 1;
|
|
|
|
va_end(va);
|
|
|
|
char *tmp = new char[length + 1];
|
|
|
|
va_start(va, format);
|
|
|
|
vsnprintf(tmp, length, f.c_str(), va);
|
|
|
|
va_end(va);
|
|
|
|
wstringEx ws;
|
|
|
|
ws.fromUTF8(tmp);
|
|
|
|
delete[] tmp;
|
|
|
|
return ws;
|
|
|
|
}
|
|
|
|
|
2012-05-06 14:03:43 +02:00
|
|
|
string vectorToString(const vector<string> &vect, string sep)
|
2012-01-21 21:57:41 +01:00
|
|
|
{
|
|
|
|
string s;
|
|
|
|
for (u32 i = 0; i < vect.size(); ++i)
|
|
|
|
{
|
|
|
|
if (i > 0)
|
|
|
|
s.append(sep);
|
|
|
|
s.append(vect[i]);
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2012-05-06 14:03:43 +02:00
|
|
|
wstringEx vectorToString(const vector<wstringEx> &vect, char sep)
|
2012-01-21 21:57:41 +01:00
|
|
|
{
|
|
|
|
wstringEx s;
|
|
|
|
for (u32 i = 0; i < vect.size(); ++i)
|
|
|
|
{
|
|
|
|
if (i > 0)
|
|
|
|
s.push_back(sep);
|
|
|
|
s.append(vect[i]);
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2012-05-06 14:03:43 +02:00
|
|
|
vector<string> stringToVector(const string &text, char sep)
|
2012-01-21 21:57:41 +01:00
|
|
|
{
|
2012-05-06 14:03:43 +02:00
|
|
|
vector<string> v;
|
2012-01-21 21:57:41 +01:00
|
|
|
if (text.empty()) return v;
|
|
|
|
u32 count = 1;
|
|
|
|
for (u32 i = 0; i < text.size(); ++i)
|
|
|
|
if (text[i] == sep)
|
|
|
|
++count;
|
|
|
|
v.reserve(count);
|
|
|
|
string::size_type off = 0;
|
|
|
|
string::size_type i = 0;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
i = text.find_first_of(sep, off);
|
|
|
|
if (i != string::npos)
|
|
|
|
{
|
|
|
|
string ws(text.substr(off, i - off));
|
|
|
|
v.push_back(ws);
|
|
|
|
off = i + 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
v.push_back(text.substr(off));
|
|
|
|
} while (i != string::npos);
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2012-05-06 14:03:43 +02:00
|
|
|
vector<wstringEx> stringToVector(const wstringEx &text, char sep)
|
2012-01-21 21:57:41 +01:00
|
|
|
{
|
2012-05-06 14:03:43 +02:00
|
|
|
vector<wstringEx> v;
|
2012-01-21 21:57:41 +01:00
|
|
|
if (text.empty()) return v;
|
|
|
|
u32 count = 1;
|
|
|
|
for (u32 i = 0; i < text.size(); ++i)
|
|
|
|
if (text[i] == sep)
|
|
|
|
++count;
|
|
|
|
v.reserve(count);
|
|
|
|
wstringEx::size_type off = 0;
|
|
|
|
wstringEx::size_type i = 0;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
i = text.find_first_of(sep, off);
|
|
|
|
if (i != wstringEx::npos)
|
|
|
|
{
|
|
|
|
wstringEx ws(text.substr(off, i - off));
|
|
|
|
v.push_back(ws);
|
|
|
|
off = i + 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
v.push_back(text.substr(off));
|
|
|
|
} while (i != wstringEx::npos);
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2012-09-07 20:13:04 +02:00
|
|
|
bool SFont::fromBuffer(u8 *buffer, u32 bufferSize, u32 size, u32 lspacing, u32 w, u32 idx, const char *)
|
2012-01-21 21:57:41 +01:00
|
|
|
{
|
|
|
|
if (!buffer || !font) return false;
|
|
|
|
|
|
|
|
size = min(max(6u, size), 1000u);
|
|
|
|
lineSpacing = min(max(6u, lspacing), 1000u);
|
|
|
|
weight = min(w, 32u);
|
|
|
|
index = idx;
|
|
|
|
|
2012-05-13 17:13:33 +02:00
|
|
|
if(data.get())
|
|
|
|
data.release();
|
2012-01-21 21:57:41 +01:00
|
|
|
data = smartMem2Alloc(bufferSize);
|
|
|
|
if(!data) return false;
|
|
|
|
|
2012-09-07 20:13:04 +02:00
|
|
|
memcpy(data.get(), buffer, bufferSize);
|
2012-01-21 21:57:41 +01:00
|
|
|
dataSize = bufferSize;
|
|
|
|
|
|
|
|
font->loadFont(data.get(), dataSize, size, weight, index, false);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SFont::fromFile(const char *filename, u32 size, u32 lspacing, u32 w, u32 idx)
|
|
|
|
{
|
|
|
|
if (!font) return false;
|
|
|
|
size = min(max(6u, size), 1000u);
|
|
|
|
weight = min(w, 32u);
|
|
|
|
index = idx = 0;
|
|
|
|
|
|
|
|
lineSpacing = min(max(6u, lspacing), 1000u);
|
|
|
|
|
|
|
|
FILE *file = fopen(filename, "rb");
|
|
|
|
if (file == NULL) return false;
|
|
|
|
fseek(file, 0, SEEK_END);
|
|
|
|
u32 fileSize = ftell(file);
|
|
|
|
fseek(file, 0, SEEK_SET);
|
|
|
|
if (fileSize == 0) return false;
|
2012-05-13 17:13:33 +02:00
|
|
|
|
|
|
|
if(data.get())
|
|
|
|
data.release();
|
2012-01-21 21:57:41 +01:00
|
|
|
data = smartMem2Alloc(fileSize);
|
|
|
|
if (!data)
|
|
|
|
{
|
2012-05-13 17:13:33 +02:00
|
|
|
fclose(file);
|
2012-01-21 21:57:41 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
fread(data.get(), 1, fileSize, file);
|
|
|
|
|
|
|
|
dataSize = fileSize;
|
|
|
|
|
|
|
|
font->loadFont(data.get(), dataSize, size, weight, index, false);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CText::setText(SFont font, const wstringEx &t)
|
|
|
|
{
|
|
|
|
CText::SWord w;
|
|
|
|
m_lines.clear();
|
|
|
|
if (!!font.font) m_font = font;
|
|
|
|
if (!m_font.font) return;
|
|
|
|
|
|
|
|
firstLine = 0;
|
|
|
|
// Don't care about performance
|
2012-05-06 14:03:43 +02:00
|
|
|
vector<wstringEx> lines = stringToVector(t, L'\n');
|
2012-01-21 21:57:41 +01:00
|
|
|
m_lines.reserve(lines.size());
|
|
|
|
//
|
|
|
|
for (u32 k = 0; k < lines.size(); ++k)
|
|
|
|
{
|
|
|
|
wstringEx &l = lines[k];
|
|
|
|
m_lines.push_back(CText::CLine());
|
|
|
|
m_lines.back().reserve(32);
|
|
|
|
wstringEx::size_type i = l.find_first_not_of(g_whitespaces);
|
|
|
|
wstringEx::size_type j;
|
|
|
|
while (i != wstringEx::npos)
|
|
|
|
{
|
|
|
|
j = l.find_first_of(g_whitespaces, i);
|
|
|
|
if (j != wstringEx::npos && j > i)
|
|
|
|
{
|
|
|
|
w.text.assign(l, i, j - i);
|
|
|
|
m_lines.back().push_back(w);
|
|
|
|
i = l.find_first_not_of(g_whitespaces, j);
|
|
|
|
}
|
|
|
|
else if (j == wstringEx::npos)
|
|
|
|
{
|
|
|
|
w.text.assign(l, i, l.size() - i);
|
|
|
|
m_lines.back().push_back(w);
|
|
|
|
i = wstringEx::npos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CText::setText(SFont font, const wstringEx &t, u32 startline)
|
|
|
|
{
|
|
|
|
CText::SWord w;
|
|
|
|
totalHeight = 0;
|
|
|
|
|
|
|
|
m_lines.clear();
|
|
|
|
if (!!font.font) m_font = font;
|
|
|
|
if (!m_font.font) return;
|
|
|
|
|
|
|
|
firstLine = startline;
|
|
|
|
// Don't care about performance
|
2012-05-06 14:03:43 +02:00
|
|
|
vector<wstringEx> lines = stringToVector(t, L'\n');
|
2012-01-21 21:57:41 +01:00
|
|
|
m_lines.reserve(lines.size());
|
|
|
|
//
|
|
|
|
for (u32 k = 0; k < lines.size(); ++k)
|
|
|
|
{
|
|
|
|
wstringEx &l = lines[k];
|
|
|
|
m_lines.push_back(CText::CLine());
|
|
|
|
m_lines.back().reserve(32);
|
|
|
|
wstringEx::size_type i = l.find_first_not_of(g_whitespaces);
|
|
|
|
wstringEx::size_type j;
|
|
|
|
while (i != wstringEx::npos)
|
|
|
|
{
|
|
|
|
j = l.find_first_of(g_whitespaces, i);
|
|
|
|
if (j != wstringEx::npos && j > i)
|
|
|
|
{
|
|
|
|
w.text.assign(l, i, j - i);
|
|
|
|
m_lines.back().push_back(w);
|
|
|
|
i = l.find_first_not_of(g_whitespaces, j);
|
|
|
|
}
|
|
|
|
else if (j == wstringEx::npos)
|
|
|
|
{
|
|
|
|
w.text.assign(l, i, l.size() - i);
|
|
|
|
m_lines.back().push_back(w);
|
|
|
|
i = wstringEx::npos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CText::setFrame(float width, u16 style, bool ignoreNewlines, bool instant)
|
|
|
|
{
|
|
|
|
float shift;
|
|
|
|
|
|
|
|
totalHeight = 0;
|
|
|
|
|
|
|
|
if (!m_font.font) return;
|
|
|
|
|
|
|
|
float space = m_font.font->getWidth(L" ");
|
|
|
|
float posX = 0.f;
|
|
|
|
float posY = 0.f;
|
|
|
|
u32 lineBeg = 0;
|
|
|
|
|
|
|
|
if(firstLine > m_lines.size()) firstLine = 0;
|
|
|
|
|
|
|
|
for (u32 k = firstLine; k < m_lines.size(); ++k)
|
|
|
|
{
|
|
|
|
CText::CLine &words = m_lines[k];
|
|
|
|
if (words.empty())
|
|
|
|
{
|
|
|
|
posY += (float)m_font.lineSpacing;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (u32 i = 0; i < words.size(); ++i)
|
|
|
|
{
|
|
|
|
float wordWidth = m_font.font->getWidth(words[i].text.c_str());
|
|
|
|
if (posX == 0.f || posX + (float)wordWidth + space * 2 <= width)
|
|
|
|
{
|
|
|
|
words[i].targetPos = Vector3D(posX, posY, 0.f);
|
|
|
|
posX += wordWidth + space;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
posY += (float)m_font.lineSpacing;
|
|
|
|
words[i].targetPos = Vector3D(0.f, posY, 0.f);
|
|
|
|
if ((style & (FTGX_JUSTIFY_CENTER | FTGX_JUSTIFY_RIGHT)) != 0)
|
|
|
|
{
|
|
|
|
posX -= space;
|
|
|
|
shift = (style & FTGX_JUSTIFY_CENTER) != 0 ? -posX * 0.5f : -posX;
|
|
|
|
for (u32 j = lineBeg; j < i; ++j)
|
|
|
|
words[j].targetPos.x += shift;
|
|
|
|
}
|
|
|
|
posX = wordWidth + space;
|
|
|
|
lineBeg = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Quick patch for newline support
|
|
|
|
if (!ignoreNewlines && k + 1 < m_lines.size())
|
|
|
|
posX = 9999999.f;
|
|
|
|
}
|
|
|
|
totalHeight = posY + m_font.lineSpacing;
|
|
|
|
|
|
|
|
if ((style & (FTGX_JUSTIFY_CENTER | FTGX_JUSTIFY_RIGHT)) != 0)
|
|
|
|
{
|
|
|
|
posX -= space;
|
|
|
|
shift = (style & FTGX_JUSTIFY_CENTER) != 0 ? -posX * 0.5f : -posX;
|
|
|
|
for (u32 k = firstLine; k < m_lines.size(); ++k)
|
|
|
|
for (u32 j = lineBeg; j < m_lines[k].size(); ++j)
|
|
|
|
m_lines[k][j].targetPos.x += shift;
|
|
|
|
}
|
|
|
|
if ((style & (FTGX_ALIGN_MIDDLE | FTGX_ALIGN_BOTTOM)) != 0)
|
|
|
|
{
|
|
|
|
posY += (float)m_font.lineSpacing;
|
|
|
|
shift = (style & FTGX_ALIGN_MIDDLE) != 0 ? -posY * 0.5f : -posY;
|
|
|
|
for (u32 k = firstLine; k < m_lines.size(); ++k)
|
|
|
|
for (u32 j = 0; j < m_lines[k].size(); ++j)
|
|
|
|
m_lines[k][j].targetPos.y += shift;
|
|
|
|
}
|
|
|
|
if (instant)
|
|
|
|
for (u32 k = firstLine; k < m_lines.size(); ++k)
|
|
|
|
for (u32 i = 0; i < m_lines[k].size(); ++i)
|
|
|
|
m_lines[k][i].pos = m_lines[k][i].targetPos;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CText::setColor(const CColor &c)
|
|
|
|
{
|
|
|
|
m_color = c;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CText::tick(void)
|
|
|
|
{
|
|
|
|
for (u32 k = 0; k < m_lines.size(); ++k)
|
|
|
|
for (u32 i = 0; i < m_lines[k].size(); ++i)
|
|
|
|
m_lines[k][i].pos += (m_lines[k][i].targetPos - m_lines[k][i].pos) * 0.05f;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CText::draw(void)
|
|
|
|
{
|
|
|
|
if (!m_font.font) return;
|
|
|
|
|
|
|
|
for (u32 k = firstLine; k < m_lines.size(); ++k)
|
|
|
|
for (u32 i = 0; i < m_lines[k].size(); ++i)
|
|
|
|
{
|
|
|
|
m_font.font->setX(m_lines[k][i].pos.x);
|
|
|
|
m_font.font->setY(m_lines[k][i].pos.y);
|
|
|
|
m_font.font->drawText(0, m_font.lineSpacing, m_lines[k][i].text.c_str(), m_color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int CText::getTotalHeight(void)
|
|
|
|
{
|
|
|
|
return totalHeight;
|
|
|
|
}
|
|
|
|
|
|
|
|
string upperCase(string text)
|
|
|
|
{
|
|
|
|
char c;
|
|
|
|
for (string::size_type i = 0; i < text.size(); ++i)
|
|
|
|
{
|
|
|
|
c = text[i];
|
|
|
|
if (c >= 'a' && c <= 'z')
|
|
|
|
text[i] = c & 0xDF;
|
|
|
|
}
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
string lowerCase(string text)
|
|
|
|
{
|
|
|
|
char c;
|
|
|
|
for (string::size_type i = 0; i < text.size(); ++i)
|
|
|
|
{
|
|
|
|
c = text[i];
|
|
|
|
if (c >= 'A' && c <= 'Z')
|
|
|
|
text[i] = c | 0x20;
|
|
|
|
}
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
|
|
|
// trim from start
|
|
|
|
string ltrim(string s)
|
|
|
|
{
|
|
|
|
s.erase(s.begin(), find_if(s.begin(), s.end(), not1(ptr_fun<int, int>(isspace))));
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
// trim from end
|
|
|
|
string rtrim(string s)
|
|
|
|
{
|
|
|
|
s.erase(find_if(s.rbegin(), s.rend(), not1(ptr_fun<int, int>(isspace))).base(), s.end());
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Asciify( wchar_t *str )
|
|
|
|
{
|
|
|
|
const wchar_t *ptr = str;
|
|
|
|
wchar_t *ctr = str;
|
|
|
|
|
|
|
|
while(*ptr != '\0')
|
|
|
|
{
|
|
|
|
switch(*ptr)
|
|
|
|
{
|
|
|
|
case 0x14c:
|
|
|
|
*ctr = 0x4f;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
*ctr = *ptr;
|
|
|
|
++ptr;
|
|
|
|
++ctr;
|
|
|
|
}
|
|
|
|
*ctr = '\0';
|
|
|
|
}
|
2012-02-17 03:35:42 +01:00
|
|
|
|
|
|
|
void Asciify2( char *str )
|
|
|
|
{
|
2012-02-17 14:26:57 +01:00
|
|
|
u8 i=0;
|
2012-02-17 03:35:42 +01:00
|
|
|
for( i=0; i < strlen(str); ++i )
|
|
|
|
{
|
|
|
|
if( str[i] < 0x20 || str[i] > 0x7F )
|
|
|
|
str[i] = '_';
|
|
|
|
else {
|
|
|
|
switch( str[i] )
|
|
|
|
{
|
|
|
|
case '*':
|
|
|
|
case '\"':
|
|
|
|
case '|':
|
|
|
|
case '<':
|
|
|
|
case '>':
|
|
|
|
case '?':
|
2012-08-19 11:45:57 +02:00
|
|
|
case ':':
|
2012-02-17 03:35:42 +01:00
|
|
|
str[i] = '_';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|