Got rid of the old attribute code and integrated exp_attrdll.c into attrstr.c. I'll rename the file to attrlist.c later.
This commit is contained in:
parent
ab8aa9266e
commit
5c1dfbd86f
311
common/attrstr.c
311
common/attrstr.c
|
@ -6,9 +6,7 @@ struct uiAttributedString {
|
|||
char *s;
|
||||
size_t len;
|
||||
|
||||
size_t nAttrs; // TODO this needs to be maintained; is it necessary?
|
||||
struct attr *attrs;
|
||||
struct attr *lastattr;
|
||||
struct attrlist *attrs;
|
||||
|
||||
// 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+)
|
||||
|
@ -22,299 +20,6 @@ struct uiAttributedString {
|
|||
struct graphemes *graphemes;
|
||||
};
|
||||
|
||||
struct attr {
|
||||
uiAttribute type;
|
||||
uintptr_t val;
|
||||
size_t start;
|
||||
size_t end;
|
||||
struct attr *next;
|
||||
};
|
||||
|
||||
// if new entries types are added to the end of the uiAttribute enumeration, this MUST be updated!
|
||||
#define nAttrTypes (TODO + 1)
|
||||
|
||||
static int attrHasPos(struct attr *a, size_t pos)
|
||||
{
|
||||
if (pos < a->start)
|
||||
return 0;
|
||||
return pos < a->end;
|
||||
}
|
||||
|
||||
// returns 1 if there was an intersection and 0 otherwise
|
||||
static int attrRangeIntersect(struct attr *a, size_t *start, size_t *end)
|
||||
{
|
||||
// is the range outside a entirely?
|
||||
if (*start >= a->end)
|
||||
return 0;
|
||||
if (*end < a->start)
|
||||
return 0;
|
||||
|
||||
// okay, so there is an overlap
|
||||
// compute the intersection
|
||||
if (*start < a->start)
|
||||
*start = a->start;
|
||||
if (*end > a->end)
|
||||
*end = a->end;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// returns:
|
||||
// - 0 if no change needed
|
||||
// - 1 if the attribute was split
|
||||
static int attrSplitAt(uiAttributedString *s, struct attr *a, size_t at)
|
||||
{
|
||||
struct attr *b;
|
||||
|
||||
// no splittng needed?
|
||||
if (at == a->start)
|
||||
return 0;
|
||||
if ((at + 1) == a->end)
|
||||
return 0;
|
||||
|
||||
b = uiNew(struct attr);
|
||||
b->what = a->what;
|
||||
b->val = a->val;
|
||||
b->start = at;
|
||||
b->end = a->end;
|
||||
b->next = a->next;
|
||||
|
||||
a->end = at;
|
||||
a->next = b;
|
||||
if (a == s->lastattr)
|
||||
s->lastattr = a->next;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// removes attributes without deleting characters
|
||||
// returns:
|
||||
// - 0 if the attribute needs to be deleted
|
||||
// - 1 if the attribute was changed or no change needed
|
||||
// - 2 if the attribute was split
|
||||
static int attrDropRange(uiAttributedString *s, struct attr *a, size_t start, size_t end)
|
||||
{
|
||||
struct attr *b;
|
||||
|
||||
if (!attrRangeIntersect(a, &start, &end))
|
||||
// out of range; nothing to do
|
||||
return 1;
|
||||
|
||||
// just outright delete the attribute?
|
||||
if (a->start == start && a->end == end)
|
||||
return 0;
|
||||
|
||||
// only chop off the start or end?
|
||||
if (a->start == start) { // chop off the end
|
||||
a->end = end;
|
||||
return 1;
|
||||
}
|
||||
if (a->end == end) { // chop off the start
|
||||
a->start = start;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// we'll need to split the attribute into two
|
||||
b = uiNew(struct attr);
|
||||
b->what = a->what;
|
||||
b->val = a->val;
|
||||
b->start = end;
|
||||
b->end = a->end;
|
||||
b->next = a->next;
|
||||
|
||||
a->end = start;
|
||||
a->next = b;
|
||||
if (a == s->lastattr)
|
||||
s->lastattr = a->next;
|
||||
return 2;
|
||||
}
|
||||
|
||||
// removes attributes while deleting characters
|
||||
// returns:
|
||||
// - 0 if the attribute needs to be deleted
|
||||
// - 1 if the attribute was changed or no change needed
|
||||
// - 2 if the attribute was split
|
||||
static int attrDeleteRange(uiAttributedString *s, struct attr *a, size_t start, size_t end)
|
||||
{
|
||||
struct attr *b;
|
||||
struct attr tmp;
|
||||
size_t count, acount;
|
||||
|
||||
if (!attrRangeIntersect(a, &start, &end))
|
||||
// out of range; nothing to do
|
||||
return 1;
|
||||
|
||||
// just outright delete the attribute?
|
||||
if (a->start == start && a->end == end)
|
||||
return 0;
|
||||
|
||||
acount = a->end - a->start;
|
||||
count = end - start;
|
||||
|
||||
// only the start or end deleted?
|
||||
if (a->start == start) { // start deleted
|
||||
a->end = a->start + (acount - count);
|
||||
return 1;
|
||||
}
|
||||
if (a->end == end) { // end deleted
|
||||
a->end = a->start + count;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// something in the middle deleted
|
||||
// we ened to split the attribute into *three*
|
||||
// first, split at the start of he deleted range
|
||||
tmp.what = a->what;
|
||||
tmp.val = a->val;
|
||||
tmp.start = start;
|
||||
tmp.end = a->end;
|
||||
tmp.next = a->next;
|
||||
|
||||
a->end = start;
|
||||
a->next = &tmp;
|
||||
|
||||
// now split at the end
|
||||
b = uiNew(struct attr);
|
||||
b->what = a->what;
|
||||
b->val = a->val;
|
||||
b->start = end;
|
||||
b->end = a->end;
|
||||
b->next = tmp.next;
|
||||
|
||||
tmp.end = end;
|
||||
tmp.next = b;
|
||||
|
||||
// and now push b back to overwrite the deleted stuff
|
||||
a->next = b;
|
||||
b->start -= count;
|
||||
b->end -= count;
|
||||
if (a == s->lastattr)
|
||||
s->lastattr = a->next;
|
||||
return 2;
|
||||
}
|
||||
|
||||
// returns the attribute to continue with
|
||||
static struct attr *attrDelete(uiAttributedString *s, struct attr *a, struct attr *prev)
|
||||
{
|
||||
if (a == s->attrs) {
|
||||
s->attrs = a->next;
|
||||
uiFree(a);
|
||||
return s->attrs;
|
||||
}
|
||||
if (a == s->lastattr)
|
||||
s->lastattr = prev;
|
||||
prev->next = a->next;
|
||||
uiFree(a);
|
||||
return prev->next;
|
||||
}
|
||||
|
||||
static void attrAppend(uiAttributedString *s, int type, uintptr_t val, size_t start, size_t end)
|
||||
{
|
||||
struct attr *a;
|
||||
|
||||
a = uiNew(struct attr);
|
||||
a->type = type;
|
||||
a->val = val;
|
||||
a->start = start;
|
||||
a->end = end;
|
||||
if (s->attrs == NULL) {
|
||||
s->attrs = a;
|
||||
s->lastattr = a;
|
||||
return;
|
||||
}
|
||||
s->lastattr->next = a;
|
||||
s->lastattr = a;
|
||||
}
|
||||
|
||||
// alist should be struct attr *alist[nAttrTypes]
|
||||
static void attrsGetFor(uiAttributedString *s, struct attr **alist, size_t at)
|
||||
{
|
||||
int i;
|
||||
struct attr *a;
|
||||
|
||||
// we want the attributes for at
|
||||
// these are the attributes of at - 1
|
||||
// if at == 0. then these are the attributes at 0
|
||||
if (at != 0)
|
||||
at--;
|
||||
|
||||
// make usre unset attributes are NULL
|
||||
for (i = 0; i < nAttrTypes; i++)
|
||||
alist[i] = NULL;
|
||||
|
||||
for (a = s->attrs; a != NULL; a = a->next) {
|
||||
if (!attrHasPos(a, at))
|
||||
continue;
|
||||
alist[a->type] = a;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO have a routine that prunes overridden attributes for a given character from the list? merge it with the above?
|
||||
|
||||
static void attrAdjustPostInsert(uiAttributedString *s, size_t start, size_t n, size_t oldlen)
|
||||
{
|
||||
struct attr *a;
|
||||
|
||||
for (a = s->attrs; a != NULL; a = a->next) {
|
||||
size_t astart, aend;
|
||||
int splitNeeded;
|
||||
|
||||
// do we need to adjust this, and where?
|
||||
astart = start;
|
||||
aend = oldlen;
|
||||
if (!attrRangeIntersect(a, &astart, &aend))
|
||||
continue;
|
||||
|
||||
// if only part of the attribute falls in the moved area, we need to split at the insertion point and adjust both resultant attributes
|
||||
// otherwise, we only adjust the original attribute
|
||||
// split *before* adjusting so that the split is correct
|
||||
splitNeeded = attrSplit(s, a, astart);
|
||||
if (a->start >= start)
|
||||
a->start += n;
|
||||
if (a->end >= end)
|
||||
a->end += n;
|
||||
if (splitNeeded == 1) {
|
||||
a = a->next;
|
||||
if (a->start >= start)
|
||||
a->start += n;
|
||||
if (a->end >= end)
|
||||
a->end += n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void attrAdjustPostDelete(uiAttributedString *s, size_t start, size_t end)
|
||||
{
|
||||
struct attr *a, *prev;
|
||||
|
||||
a = s->attrs;
|
||||
prev = NULL;
|
||||
while (a != NULL) {
|
||||
size_t astart, aend;
|
||||
|
||||
// do we need to adjust this, and where?
|
||||
astart = start;
|
||||
aend = end;
|
||||
if (!attrRangeIntersect(a, &astart, &aend)) {
|
||||
prev = a;
|
||||
a = a->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (attrDeleteRange(s, a, astart, aend)) {
|
||||
case 0: // delete
|
||||
a = attrDelete(s, a, prev);
|
||||
// keep prev unchanged
|
||||
break;
|
||||
case 2: // split
|
||||
a = a->next;
|
||||
// fall through
|
||||
case 1: // existing only needed adjustment
|
||||
prev = a;
|
||||
a = a->next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void resize(uiAttributedString *s, size_t u8, size_t u16)
|
||||
{
|
||||
s->len = u8;
|
||||
|
@ -330,6 +35,7 @@ uiAttributedString *uiNewAttributedString(const char *initialString)
|
|||
uiAttributedString *s;
|
||||
|
||||
s = uiNew(uiAttributedString);
|
||||
s->attrs = attrlistNew();
|
||||
uiAttributedStringAppendUnattributed(s, initialString);
|
||||
return s;
|
||||
}
|
||||
|
@ -358,14 +64,7 @@ static void invalidateGraphemes(uiAttributedString *s)
|
|||
|
||||
void uiFreeAttributedString(uiAttributedString *s)
|
||||
{
|
||||
struct attr *a, *b;
|
||||
|
||||
a = s->attrs;
|
||||
while (a != NULL) {
|
||||
b = a->next;
|
||||
uiFree(a);
|
||||
a = b;
|
||||
}
|
||||
attrlistFree(s->attrs);
|
||||
invalidateGraphemes(s);
|
||||
uiFree(s->u16tou8);
|
||||
uiFree(s->u8tou16);
|
||||
|
@ -511,7 +210,7 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s
|
|||
s->u16tou8[at16 + n16 + i] += n8;
|
||||
|
||||
// and finally do the attributes
|
||||
attrAdjustPostInsert(s, at, n8, oldlen);
|
||||
attrlistInsertCharactersUnattributed(s->attrs, at, n8);
|
||||
}
|
||||
|
||||
// TODO document that end is the first index that will be maintained
|
||||
|
@ -566,7 +265,7 @@ void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end)
|
|||
s->u16[start16 + count16] = 0;
|
||||
|
||||
// fix up attributes
|
||||
attrAdjustPostDelete(s, start, end);
|
||||
attrlistRemoveCharacters(s->attrs, start, end);
|
||||
|
||||
// and finally resize
|
||||
resize(s, start + count, start16 + count16);
|
||||
|
|
|
@ -285,7 +285,7 @@ static struct attr *attrDeleteRange(struct attrlist *alist, struct attr *a, size
|
|||
return a->next;
|
||||
}
|
||||
|
||||
void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end)
|
||||
void attrlistInsertAttribute(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end)
|
||||
{
|
||||
struct attr *a;
|
||||
struct attr *before;
|
||||
|
@ -575,3 +575,21 @@ void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end)
|
|||
while (a != NULL)
|
||||
a = attrDeleteRange(alist, a, start, end);
|
||||
}
|
||||
|
||||
struct attrlist *attrlistNew(void)
|
||||
{
|
||||
return uiNew(struct attrlist);
|
||||
}
|
||||
|
||||
void attrlistFree(struct attrlist *alist)
|
||||
{
|
||||
struct attr *a, *next;
|
||||
|
||||
a = alist->first;
|
||||
while (a != NULL) {
|
||||
next = a->next;
|
||||
uiFree(a);
|
||||
a = next;
|
||||
}
|
||||
uiFree(alist);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue