161 lines
3.7 KiB
C
161 lines
3.7 KiB
C
// 3 december 2016
|
|
#include "../ui.h"
|
|
#include "uipriv.h"
|
|
|
|
struct uiAttributedString {
|
|
char *s;
|
|
size_t len;
|
|
|
|
// TODO attributes
|
|
|
|
// indiscriminately keep a UTF-16 copy of the string on all platforms so we can hand this off to the grapheme calculator
|
|
// this ensures no one platform has a speed advantage (sorry GTK+)
|
|
uint16_t *u16;
|
|
size_t u16len;
|
|
|
|
size_t *u8tou16;
|
|
size_t *u16tou8;
|
|
|
|
// this is lazily created to keep things from getting *too* slow
|
|
struct graphemes *graphemes;
|
|
};
|
|
|
|
static void resize(uiAttributedString *s, size_t u8, size_t u16)
|
|
{
|
|
s->len = u8;
|
|
s->s = (char *) uiRealloc(s->s, (s->len + 1) * sizeof (char), "char[] (uiAttributedString)");
|
|
s->u8tou16 = (size_t *) uiRealloc(s->u8tou16, (s->len + 1) * sizeof (size_t), "size_t[] (uiAttributedString)");
|
|
s->u16len = u16;
|
|
s->u16 = (uint16_t *) uiRealloc(s->u16, (s->u16len + 1) * sizeof (uint16_t), "uint16_t[] (uiAttributedString)");
|
|
s->u16tou8 = (size_t *) uiRealloc(s->u16tou8, (s->u16len + 1) * sizeof (size_t), "size_t[] (uiAttributedString)");
|
|
}
|
|
|
|
uiAttributedString *uiNewAttributedString(const char *initialString)
|
|
{
|
|
uiAttributedString *s;
|
|
|
|
s = uiNew(uiAttributedString);
|
|
uiAttributedStringAppendUnattributed(s, initialString);
|
|
return s;
|
|
}
|
|
|
|
static void recomputeGraphemes(uiAttributedString *s)
|
|
{
|
|
if (s->graphemes != NULL)
|
|
return;
|
|
if (graphemesTakesUTF16()) {
|
|
s->graphemes = graphemes(s->u16, s->u16len);
|
|
return;
|
|
}
|
|
s->graphemes = graphemes(s->s, s->len);
|
|
}
|
|
|
|
static void invalidateGraphemes(uiAttributedString *s)
|
|
{
|
|
if (s->graphemes == NULL)
|
|
return;
|
|
uiFree(s->graphemes->pointsToGraphemes);
|
|
uiFree(s->graphemes->graphemesToPoints);
|
|
uiFree(s->graphemes);
|
|
s->graphemes = NULL;
|
|
}
|
|
|
|
void uiFreeAttributedString(uiAttributedString *s)
|
|
{
|
|
invalidateGraphemes(s);
|
|
uiFree(s->u16tou8);
|
|
uiFree(s->u8tou16);
|
|
uiFree(s->u16);
|
|
uiFree(s->s);
|
|
uiFree(s);
|
|
}
|
|
|
|
const char *uiAttributedStringString(uiAttributedString *s)
|
|
{
|
|
return s->s;
|
|
}
|
|
|
|
void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str)
|
|
{
|
|
const char *t;
|
|
uint32_t rune;
|
|
char buf[4];
|
|
uint16_t u16buf[2];
|
|
size_t n, n16;
|
|
size_t old, old;
|
|
|
|
// first figure out how much we need to grow by
|
|
// this includes post-validated UTF-8
|
|
t = str;
|
|
n = 0;
|
|
n16 = 0;
|
|
while (*t) {
|
|
t = utf8DecodeRune(t, 0, &rune);
|
|
n += utf8EncodeRune(rune, buf);
|
|
n16 += utf16EncodeRune(rune, buf16);
|
|
}
|
|
|
|
// and resize
|
|
old = s->len;
|
|
old16 = s->len16;
|
|
resize(s, s->len + n, s->u16len + n16);
|
|
|
|
// and copy
|
|
while (*str) {
|
|
str = utf8DecodeRune(str, 0, &rune);
|
|
n = utf8EncodeRune(rune, buf);
|
|
n16 = utf16EncodeRune(rune, buf16);
|
|
s->s[old] = buf[0];
|
|
s->u8tou16[old] = old16;
|
|
if (n > 1) {
|
|
s->s[old + 1] = buf[1];
|
|
s->u8tou16[old + 1] = old16;
|
|
}
|
|
if (n > 2) {
|
|
s->s[old + 2] = buf[2];
|
|
s->u8tou16[old + 2] = old16;
|
|
}
|
|
if (n > 3) {
|
|
s->s[old + 3] = buf[3];
|
|
s->u8tou16[old + 3] = old16;
|
|
}
|
|
s->u16[old16] = buf16[0];
|
|
s->u16tou8[old16] = old;
|
|
if (n16 > 1) {
|
|
s->u16[old16 + 1] = buf16[1];
|
|
s->u16tou8[old16 + 1] = old;
|
|
}
|
|
old += n;
|
|
old16 += n16;
|
|
}
|
|
// and have an index for the end of the string
|
|
s->u8tou16[old] = old16;
|
|
s->u16tou8[old16] = old;
|
|
|
|
invalidateGraphemes(s);
|
|
}
|
|
|
|
// TODO figure out if we should count the grapheme past the end
|
|
size_t uiAttributedStringNumGraphemes(uiAttributedString *s)
|
|
{
|
|
recomputeGraphemes(s);
|
|
return s->graphemes->len;
|
|
}
|
|
|
|
size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos)
|
|
{
|
|
recomputeGraphemes(s);
|
|
if (graphemesTakesUTF16())
|
|
pos = s->u8tou16[pos];
|
|
return s->graphemes->pointsToGraphemes[pos];
|
|
}
|
|
|
|
size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos)
|
|
{
|
|
recomputeGraphemes(s);
|
|
pos = s->graphemes->graphemesToPoints[pos];
|
|
if (graphemesTakesUTF16())
|
|
pos = s->u16tou8[pos];
|
|
return pos;
|
|
}
|