First Commit
This commit is contained in:
commit
ae0fab9400
424 files changed
+71006
No files matched your search
@@ -0,0 +1,22 @@
|
||||
// 21 june 2016
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
|
||||
void *uiTableModelGiveInt(int i)
|
||||
{
|
||||
return (void *) ((intptr_t) i);
|
||||
}
|
||||
|
||||
int uiTableModelTakeInt(void *v)
|
||||
{
|
||||
return (int) ((intptr_t) v);
|
||||
}
|
||||
|
||||
uiTableColumn *uiTableAppendTextColumn(uiTable *t, const char *name, int modelColumn)
|
||||
{
|
||||
uiTableColumn *tc;
|
||||
|
||||
tc = uiTableAppendColumn(t, name);
|
||||
uiTableColumnAppendTextPart(tc, modelColumn, 1);
|
||||
return tc;
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
// 29 march 2014
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
|
||||
/*
|
||||
Windows and GTK+ have a limit of 2 and 3 clicks, respectively, natively supported. Fortunately, we can simulate the double/triple-click behavior to build higher-order clicks. We can use the same algorithm Windows uses on both:
|
||||
http://blogs.msdn.com/b/oldnewthing/archive/2004/10/18/243925.aspx
|
||||
For GTK+, we pull the double-click time and double-click distance, which work the same as the equivalents on Windows (so the distance is in all directions), from the GtkSettings system.
|
||||
|
||||
On GTK+ this will also allow us to discard the GDK_BUTTON_2PRESS and GDK_BUTTON_3PRESS events, so the button press stream will be just like on other platforms.
|
||||
|
||||
Thanks to mclasen, garnacho_, halfline, and tristan in irc.gimp.net/#gtk+.
|
||||
|
||||
TODO note the bits about asymmetry and g_rcClick initial value not mattering in the oldnewthing article
|
||||
*/
|
||||
|
||||
// x, y, xdist, ydist, and c.rect must have the same units
|
||||
// so must time, maxTime, and c.prevTime
|
||||
int uiprivClickCounterClick(uiprivClickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist)
|
||||
{
|
||||
// different button than before? if so, don't count
|
||||
if (button != c->curButton)
|
||||
c->count = 0;
|
||||
|
||||
// (x, y) in the allowed region for a double-click? if not, don't count
|
||||
if (x < c->rectX0)
|
||||
c->count = 0;
|
||||
if (y < c->rectY0)
|
||||
c->count = 0;
|
||||
if (x >= c->rectX1)
|
||||
c->count = 0;
|
||||
if (y >= c->rectY1)
|
||||
c->count = 0;
|
||||
|
||||
// too slow? if so, don't count
|
||||
// note the below expression; time > (c.prevTime + maxTime) can overflow!
|
||||
if ((time - c->prevTime) > maxTime) // too slow; don't count
|
||||
c->count = 0;
|
||||
|
||||
c->count++; // if either of the above ifs happened, this will make the click count 1; otherwise it will make the click count 2, 3, 4, 5, ...
|
||||
|
||||
// now we need to update the internal structures for the next test
|
||||
c->curButton = button;
|
||||
c->prevTime = time;
|
||||
c->rectX0 = x - xdist;
|
||||
c->rectY0 = y - ydist;
|
||||
c->rectX1 = x + xdist;
|
||||
c->rectY1 = y + ydist;
|
||||
|
||||
return c->count;
|
||||
}
|
||||
|
||||
void uiprivClickCounterReset(uiprivClickCounter *c)
|
||||
{
|
||||
c->curButton = 0;
|
||||
c->rectX0 = 0;
|
||||
c->rectY0 = 0;
|
||||
c->rectX1 = 0;
|
||||
c->rectY1 = 0;
|
||||
c->prevTime = 0;
|
||||
c->count = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
For position independence across international keyboard layouts, typewriter keys are read using scancodes (which are always set 1).
|
||||
Windows provides the scancodes directly in the LPARAM.
|
||||
GTK+ provides the scancodes directly from the underlying window system via GdkEventKey.hardware_keycode.
|
||||
On X11, this is scancode + 8 (because X11 keyboard codes have a range of [8,255]).
|
||||
Wayland is guaranteed to give the same result (thanks ebassi in irc.gimp.net/#gtk+).
|
||||
On Linux, where evdev is used instead of polling scancodes directly from the keyboard, evdev's typewriter section key code constants are the same as scancodes anyway, so the rules above apply.
|
||||
Typewriter section scancodes are the same across international keyboards with some exceptions that have been accounted for (see KeyEvent's documentation); see http://www.quadibloc.com/comp/scan.htm for details.
|
||||
Non-typewriter keys can be handled safely using constants provided by the respective backend API.
|
||||
|
||||
Because GTK+ keysyms may or may not obey Num Lock, we also handle the 0-9 and . keys on the numeric keypad with scancodes (they match too).
|
||||
*/
|
||||
|
||||
// use uintptr_t to be safe; the size of the scancode/hardware key code field on each platform is different
|
||||
static const struct {
|
||||
uintptr_t scancode;
|
||||
char equiv;
|
||||
} scancodeKeys[] = {
|
||||
{ 0x02, '1' },
|
||||
{ 0x03, '2' },
|
||||
{ 0x04, '3' },
|
||||
{ 0x05, '4' },
|
||||
{ 0x06, '5' },
|
||||
{ 0x07, '6' },
|
||||
{ 0x08, '7' },
|
||||
{ 0x09, '8' },
|
||||
{ 0x0A, '9' },
|
||||
{ 0x0B, '0' },
|
||||
{ 0x0C, '-' },
|
||||
{ 0x0D, '=' },
|
||||
{ 0x0E, '\b' },
|
||||
{ 0x0F, '\t' },
|
||||
{ 0x10, 'q' },
|
||||
{ 0x11, 'w' },
|
||||
{ 0x12, 'e' },
|
||||
{ 0x13, 'r' },
|
||||
{ 0x14, 't' },
|
||||
{ 0x15, 'y' },
|
||||
{ 0x16, 'u' },
|
||||
{ 0x17, 'i' },
|
||||
{ 0x18, 'o' },
|
||||
{ 0x19, 'p' },
|
||||
{ 0x1A, '[' },
|
||||
{ 0x1B, ']' },
|
||||
{ 0x1C, '\n' },
|
||||
{ 0x1E, 'a' },
|
||||
{ 0x1F, 's' },
|
||||
{ 0x20, 'd' },
|
||||
{ 0x21, 'f' },
|
||||
{ 0x22, 'g' },
|
||||
{ 0x23, 'h' },
|
||||
{ 0x24, 'j' },
|
||||
{ 0x25, 'k' },
|
||||
{ 0x26, 'l' },
|
||||
{ 0x27, ';' },
|
||||
{ 0x28, '\'' },
|
||||
{ 0x29, '`' },
|
||||
{ 0x2B, '\\' },
|
||||
{ 0x2C, 'z' },
|
||||
{ 0x2D, 'x' },
|
||||
{ 0x2E, 'c' },
|
||||
{ 0x2F, 'v' },
|
||||
{ 0x30, 'b' },
|
||||
{ 0x31, 'n' },
|
||||
{ 0x32, 'm' },
|
||||
{ 0x33, ',' },
|
||||
{ 0x34, '.' },
|
||||
{ 0x35, '/' },
|
||||
{ 0x39, ' ' },
|
||||
{ 0xFFFF, 0 },
|
||||
};
|
||||
|
||||
static const struct {
|
||||
uintptr_t scancode;
|
||||
uiExtKey equiv;
|
||||
} scancodeExtKeys[] = {
|
||||
{ 0x47, uiExtKeyN7 },
|
||||
{ 0x48, uiExtKeyN8 },
|
||||
{ 0x49, uiExtKeyN9 },
|
||||
{ 0x4B, uiExtKeyN4 },
|
||||
{ 0x4C, uiExtKeyN5 },
|
||||
{ 0x4D, uiExtKeyN6 },
|
||||
{ 0x4F, uiExtKeyN1 },
|
||||
{ 0x50, uiExtKeyN2 },
|
||||
{ 0x51, uiExtKeyN3 },
|
||||
{ 0x52, uiExtKeyN0 },
|
||||
{ 0x53, uiExtKeyNDot },
|
||||
{ 0xFFFF, 0 },
|
||||
};
|
||||
|
||||
int uiprivFromScancode(uintptr_t scancode, uiAreaKeyEvent *ke)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; scancodeKeys[i].scancode != 0xFFFF; i++)
|
||||
if (scancodeKeys[i].scancode == scancode) {
|
||||
ke->Key = scancodeKeys[i].equiv;
|
||||
return 1;
|
||||
}
|
||||
for (i = 0; scancodeExtKeys[i].scancode != 0xFFFF; i++)
|
||||
if (scancodeExtKeys[i].scancode == scancode) {
|
||||
ke->ExtKey = scancodeExtKeys[i].equiv;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
// 19 february 2018
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
#include "attrstr.h"
|
||||
|
||||
struct uiAttribute {
|
||||
int ownedByUser;
|
||||
size_t refcount;
|
||||
uiAttributeType type;
|
||||
union {
|
||||
char *family;
|
||||
double size;
|
||||
uiTextWeight weight;
|
||||
uiTextItalic italic;
|
||||
uiTextStretch stretch;
|
||||
struct {
|
||||
double r;
|
||||
double g;
|
||||
double b;
|
||||
double a;
|
||||
// put this here so we can reuse this structure
|
||||
uiUnderlineColor underlineColor;
|
||||
} color;
|
||||
uiUnderline underline;
|
||||
uiOpenTypeFeatures *features;
|
||||
} u;
|
||||
};
|
||||
|
||||
static uiAttribute *newAttribute(uiAttributeType type)
|
||||
{
|
||||
uiAttribute *a;
|
||||
|
||||
a = uiprivNew(uiAttribute);
|
||||
a->ownedByUser = 1;
|
||||
a->refcount = 0;
|
||||
a->type = type;
|
||||
return a;
|
||||
}
|
||||
|
||||
// returns a to allow expressions like b = uiprivAttributeRetain(a)
|
||||
// TODO would this allow us to copy attributes between strings in a foreach func, and if so, should that be allowed?
|
||||
uiAttribute *uiprivAttributeRetain(uiAttribute *a)
|
||||
{
|
||||
a->ownedByUser = 0;
|
||||
a->refcount++;
|
||||
return a;
|
||||
}
|
||||
|
||||
static void destroy(uiAttribute *a)
|
||||
{
|
||||
switch (a->type) {
|
||||
case uiAttributeTypeFamily:
|
||||
uiprivFree(a->u.family);
|
||||
break;
|
||||
case uiAttributeTypeFeatures:
|
||||
uiFreeOpenTypeFeatures(a->u.features);
|
||||
break;
|
||||
}
|
||||
uiprivFree(a);
|
||||
}
|
||||
|
||||
void uiprivAttributeRelease(uiAttribute *a)
|
||||
{
|
||||
if (a->ownedByUser)
|
||||
/* TODO implementation bug: we can't release an attribute we don't own */;
|
||||
a->refcount--;
|
||||
if (a->refcount == 0)
|
||||
destroy(a);
|
||||
}
|
||||
|
||||
void uiFreeAttribute(uiAttribute *a)
|
||||
{
|
||||
if (!a->ownedByUser)
|
||||
/* TODO user bug: you can't free an attribute you don't own */;
|
||||
destroy(a);
|
||||
}
|
||||
|
||||
uiAttributeType uiAttributeGetType(const uiAttribute *a)
|
||||
{
|
||||
return a->type;
|
||||
}
|
||||
|
||||
uiAttribute *uiNewFamilyAttribute(const char *family)
|
||||
{
|
||||
uiAttribute *a;
|
||||
|
||||
a = newAttribute(uiAttributeTypeFamily);
|
||||
a->u.family = (char *) uiprivAlloc((strlen(family) + 1) * sizeof (char), "char[] (uiAttribute)");
|
||||
strcpy(a->u.family, family);
|
||||
return a;
|
||||
}
|
||||
|
||||
const char *uiAttributeFamily(const uiAttribute *a)
|
||||
{
|
||||
return a->u.family;
|
||||
}
|
||||
|
||||
uiAttribute *uiNewSizeAttribute(double size)
|
||||
{
|
||||
uiAttribute *a;
|
||||
|
||||
a = newAttribute(uiAttributeTypeSize);
|
||||
a->u.size = size;
|
||||
return a;
|
||||
}
|
||||
|
||||
double uiAttributeSize(const uiAttribute *a)
|
||||
{
|
||||
return a->u.size;
|
||||
}
|
||||
|
||||
uiAttribute *uiNewWeightAttribute(uiTextWeight weight)
|
||||
{
|
||||
uiAttribute *a;
|
||||
|
||||
a = newAttribute(uiAttributeTypeWeight);
|
||||
a->u.weight = weight;
|
||||
return a;
|
||||
}
|
||||
|
||||
uiTextWeight uiAttributeWeight(const uiAttribute *a)
|
||||
{
|
||||
return a->u.weight;
|
||||
}
|
||||
|
||||
uiAttribute *uiNewItalicAttribute(uiTextItalic italic)
|
||||
{
|
||||
uiAttribute *a;
|
||||
|
||||
a = newAttribute(uiAttributeTypeItalic);
|
||||
a->u.italic = italic;
|
||||
return a;
|
||||
}
|
||||
|
||||
uiTextItalic uiAttributeItalic(const uiAttribute *a)
|
||||
{
|
||||
return a->u.italic;
|
||||
}
|
||||
|
||||
uiAttribute *uiNewStretchAttribute(uiTextStretch stretch)
|
||||
{
|
||||
uiAttribute *a;
|
||||
|
||||
a = newAttribute(uiAttributeTypeStretch);
|
||||
a->u.stretch = stretch;
|
||||
return a;
|
||||
}
|
||||
|
||||
uiTextStretch uiAttributeStretch(const uiAttribute *a)
|
||||
{
|
||||
return a->u.stretch;
|
||||
}
|
||||
|
||||
uiAttribute *uiNewColorAttribute(double r, double g, double b, double a)
|
||||
{
|
||||
uiAttribute *at;
|
||||
|
||||
at = newAttribute(uiAttributeTypeColor);
|
||||
at->u.color.r = r;
|
||||
at->u.color.g = g;
|
||||
at->u.color.b = b;
|
||||
at->u.color.a = a;
|
||||
return at;
|
||||
}
|
||||
|
||||
void uiAttributeColor(const uiAttribute *a, double *r, double *g, double *b, double *alpha)
|
||||
{
|
||||
*r = a->u.color.r;
|
||||
*g = a->u.color.g;
|
||||
*b = a->u.color.b;
|
||||
*alpha = a->u.color.a;
|
||||
}
|
||||
|
||||
uiAttribute *uiNewBackgroundAttribute(double r, double g, double b, double a)
|
||||
{
|
||||
uiAttribute *at;
|
||||
|
||||
at = newAttribute(uiAttributeTypeBackground);
|
||||
at->u.color.r = r;
|
||||
at->u.color.g = g;
|
||||
at->u.color.b = b;
|
||||
at->u.color.a = a;
|
||||
return at;
|
||||
}
|
||||
|
||||
uiAttribute *uiNewUnderlineAttribute(uiUnderline u)
|
||||
{
|
||||
uiAttribute *a;
|
||||
|
||||
a = newAttribute(uiAttributeTypeUnderline);
|
||||
a->u.underline = u;
|
||||
return a;
|
||||
}
|
||||
|
||||
uiUnderline uiAttributeUnderline(const uiAttribute *a)
|
||||
{
|
||||
return a->u.underline;
|
||||
}
|
||||
|
||||
uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double r, double g, double b, double a)
|
||||
{
|
||||
uiAttribute *at;
|
||||
|
||||
at = uiNewColorAttribute(r, g, b, a);
|
||||
at->type = uiAttributeTypeUnderlineColor;
|
||||
at->u.color.underlineColor = u;
|
||||
return at;
|
||||
}
|
||||
|
||||
void uiAttributeUnderlineColor(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha)
|
||||
{
|
||||
*u = a->u.color.underlineColor;
|
||||
uiAttributeColor(a, r, g, b, alpha);
|
||||
}
|
||||
|
||||
uiAttribute *uiNewFeaturesAttribute(const uiOpenTypeFeatures *otf)
|
||||
{
|
||||
uiAttribute *a;
|
||||
|
||||
a = newAttribute(uiAttributeTypeFeatures);
|
||||
a->u.features = uiOpenTypeFeaturesClone(otf);
|
||||
return a;
|
||||
}
|
||||
|
||||
const uiOpenTypeFeatures *uiAttributeFeatures(const uiAttribute *a)
|
||||
{
|
||||
return a->u.features;
|
||||
}
|
||||
|
||||
int uiprivAttributeEqual(const uiAttribute *a, const uiAttribute *b)
|
||||
{
|
||||
if (a == b)
|
||||
return 1;
|
||||
if (a->type != b->type)
|
||||
return 0;
|
||||
switch (a->type) {
|
||||
case uiAttributeTypeFamily:
|
||||
return uiprivStricmp(a->u.family, b->u.family);
|
||||
case uiAttributeTypeSize:
|
||||
// TODO is the use of == correct?
|
||||
return a->u.size == b->u.size;
|
||||
case uiAttributeTypeWeight:
|
||||
return a->u.weight == b->u.weight;
|
||||
case uiAttributeTypeItalic:
|
||||
return a->u.italic == b->u.italic;
|
||||
case uiAttributeTypeStretch:
|
||||
return a->u.stretch == b->u.stretch;
|
||||
case uiAttributeTypeUnderline:
|
||||
return a->u.underline == b->u.underline;
|
||||
case uiAttributeTypeUnderlineColor:
|
||||
if (a->u.color.underlineColor != b->u.color.underlineColor)
|
||||
return 0;
|
||||
// fall through
|
||||
case uiAttributeTypeColor:
|
||||
case uiAttributeTypeBackground:
|
||||
// TODO is the use of == correct?
|
||||
return (a->u.color.r == b->u.color.r) &&
|
||||
(a->u.color.g == b->u.color.g) &&
|
||||
(a->u.color.b == b->u.color.b) &&
|
||||
(a->u.color.a == b->u.color.a);
|
||||
case uiAttributeTypeFeatures:
|
||||
return uiprivOpenTypeFeaturesEqual(a->u.features, b->u.features);
|
||||
}
|
||||
// TODO should not be reached
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,612 @@
|
||||
// 16 december 2016
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
#include "attrstr.h"
|
||||
|
||||
/*
|
||||
An attribute list is a doubly linked list of attributes.
|
||||
Attribute start positions are inclusive and attribute end positions are exclusive (or in other words, [start, end)).
|
||||
The list is kept sorted in increasing order by start position. Whether or not the sort is stable is undefined, so no temporal information should be expected to stay.
|
||||
Overlapping attributes are not allowed; if an attribute is added that conflicts with an existing one, the existing one is removed.
|
||||
In addition, the linked list tries to reduce fragmentation: if an attribute is added that just expands another, then there will only be one entry in alist, not two. (TODO does it really?)
|
||||
The linked list is not a ring; alist->fist->prev == NULL and alist->last->next == NULL.
|
||||
TODO verify that this disallows attributes of length zero
|
||||
*/
|
||||
|
||||
struct attr {
|
||||
uiAttribute *val;
|
||||
size_t start;
|
||||
size_t end;
|
||||
struct attr *prev;
|
||||
struct attr *next;
|
||||
};
|
||||
|
||||
struct uiprivAttrList {
|
||||
struct attr *first;
|
||||
struct attr *last;
|
||||
};
|
||||
|
||||
// if before is NULL, add to the end of the list
|
||||
static void attrInsertBefore(uiprivAttrList *alist, struct attr *a, struct attr *before)
|
||||
{
|
||||
// if the list is empty, this is the first item
|
||||
if (alist->first == NULL) {
|
||||
alist->first = a;
|
||||
alist->last = a;
|
||||
return;
|
||||
}
|
||||
|
||||
// add to the end
|
||||
if (before == NULL) {
|
||||
struct attr *oldlast;
|
||||
|
||||
oldlast = alist->last;
|
||||
alist->last = a;
|
||||
a->prev = oldlast;
|
||||
oldlast->next = a;
|
||||
return;
|
||||
}
|
||||
|
||||
// add to the beginning
|
||||
if (before == alist->first) {
|
||||
struct attr *oldfirst;
|
||||
|
||||
oldfirst = alist->first;
|
||||
alist->first = a;
|
||||
oldfirst->prev = a;
|
||||
a->next = oldfirst;
|
||||
return;
|
||||
}
|
||||
|
||||
// add to the middle
|
||||
a->prev = before->prev;
|
||||
a->next = before;
|
||||
before->prev = a;
|
||||
a->prev->next = a;
|
||||
}
|
||||
|
||||
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 the old a->next, for forward iteration
|
||||
static struct attr *attrUnlink(uiprivAttrList *alist, struct attr *a)
|
||||
{
|
||||
struct attr *p, *n;
|
||||
|
||||
p = a->prev;
|
||||
n = a->next;
|
||||
a->prev = NULL;
|
||||
a->next = NULL;
|
||||
|
||||
// only item in list?
|
||||
if (p == NULL && n == NULL) {
|
||||
alist->first = NULL;
|
||||
alist->last = NULL;
|
||||
return NULL;
|
||||
}
|
||||
// start of list?
|
||||
if (p == NULL) {
|
||||
n->prev = NULL;
|
||||
alist->first = n;
|
||||
return n;
|
||||
}
|
||||
// end of list?
|
||||
if (n == NULL) {
|
||||
p->next = NULL;
|
||||
alist->last = p;
|
||||
return NULL;
|
||||
}
|
||||
// middle of list
|
||||
p->next = n;
|
||||
n->prev = p;
|
||||
return n;
|
||||
}
|
||||
|
||||
// returns the old a->next, for forward iteration
|
||||
static struct attr *attrDelete(uiprivAttrList *alist, struct attr *a)
|
||||
{
|
||||
struct attr *next;
|
||||
|
||||
next = attrUnlink(alist, a);
|
||||
uiprivAttributeRelease(a->val);
|
||||
uiprivFree(a);
|
||||
return next;
|
||||
}
|
||||
|
||||
// attrDropRange() removes attributes without deleting characters.
|
||||
//
|
||||
// If the attribute needs no change, then nothing is done.
|
||||
//
|
||||
// If the attribute needs to be deleted, it is deleted.
|
||||
//
|
||||
// If the attribute only needs to be resized at the end, it is adjusted.
|
||||
//
|
||||
// If the attribute only needs to be resized at the start, it is adjusted, unlinked, and returned in tail.
|
||||
//
|
||||
// Otherwise, the attribute needs to be split. The existing attribute is adjusted to make the left half and a new attribute with the right half. This attribute is kept unlinked and returned in tail.
|
||||
//
|
||||
// In all cases, the return value is the next attribute to look at in a forward sequential loop.
|
||||
static struct attr *attrDropRange(uiprivAttrList *alist, struct attr *a, size_t start, size_t end, struct attr **tail)
|
||||
{
|
||||
struct attr *b;
|
||||
|
||||
// always pre-initialize tail to NULL
|
||||
*tail = NULL;
|
||||
|
||||
if (!attrRangeIntersect(a, &start, &end))
|
||||
// out of range; nothing to do
|
||||
return a->next;
|
||||
|
||||
// just outright delete the attribute?
|
||||
// the inequalities handle attributes entirely inside the range
|
||||
// if both are equal, the attribute's range is equal to the range
|
||||
if (a->start >= start && a->end <= end)
|
||||
return attrDelete(alist, a);
|
||||
|
||||
// only chop off the start or end?
|
||||
if (a->start == start) { // chop off the start
|
||||
// we are dropping the left half, so set a->start and unlink
|
||||
a->start = end;
|
||||
*tail = a;
|
||||
return attrUnlink(alist, a);
|
||||
}
|
||||
if (a->end == end) { // chop off the end
|
||||
// we are dropping the right half, so just set a->end
|
||||
a->end = start;
|
||||
return a->next;
|
||||
}
|
||||
|
||||
// we'll need to split the attribute into two
|
||||
b = uiprivNew(struct attr);
|
||||
b->val = uiprivAttributeRetain(a->val);
|
||||
b->start = end;
|
||||
b->end = a->end;
|
||||
*tail = b;
|
||||
|
||||
a->end = start;
|
||||
return a->next;
|
||||
}
|
||||
|
||||
static void attrGrow(uiprivAttrList *alist, struct attr *a, size_t start, size_t end)
|
||||
{
|
||||
struct attr *before;
|
||||
|
||||
// adjusting the end is simple: if it ends before our new end, just set the new end
|
||||
if (a->end < end)
|
||||
a->end = end;
|
||||
|
||||
// adjusting the start is harder
|
||||
// if the start is before our new start, we are done
|
||||
// otherwise, we have to move the start back AND reposition the attribute to keep the sorted order
|
||||
if (a->start <= start)
|
||||
return;
|
||||
a->start = start;
|
||||
attrUnlink(alist, a);
|
||||
for (before = alist->first; before != NULL; before = before->next)
|
||||
if (before->start > a->start)
|
||||
break;
|
||||
attrInsertBefore(alist, a, before);
|
||||
}
|
||||
|
||||
// returns the right side of the split, which is unlinked, or NULL if no split was done
|
||||
static struct attr *attrSplitAt(uiprivAttrList *alist, struct attr *a, size_t at)
|
||||
{
|
||||
struct attr *b;
|
||||
|
||||
// no splittng needed?
|
||||
// note the equality: we don't need to split at start or end
|
||||
// in the end case, the last split point is at - 1; at itself is outside the range, and at - 1 results in the right hand side having length 1
|
||||
if (at <= a->start)
|
||||
return NULL;
|
||||
if (at >= a->end)
|
||||
return NULL;
|
||||
|
||||
b = uiprivNew(struct attr);
|
||||
b->val = uiprivAttributeRetain(a->val);
|
||||
b->start = at;
|
||||
b->end = a->end;
|
||||
|
||||
a->end = at;
|
||||
return b;
|
||||
}
|
||||
|
||||
// attrDeleteRange() removes attributes while deleting characters.
|
||||
//
|
||||
// If the attribute does not include the deleted range, then nothing is done (though the start and end are adjusted as necessary).
|
||||
//
|
||||
// If the attribute needs to be deleted, it is deleted.
|
||||
//
|
||||
// Otherwise, the attribute only needs the start or end deleted, and it is adjusted.
|
||||
//
|
||||
// In all cases, the return value is the next attribute to look at in a forward sequential loop.
|
||||
// TODO rewrite this comment
|
||||
static struct attr *attrDeleteRange(uiprivAttrList *alist, struct attr *a, size_t start, size_t end)
|
||||
{
|
||||
size_t ostart, oend;
|
||||
size_t count;
|
||||
|
||||
ostart = start;
|
||||
oend = end;
|
||||
count = oend - ostart;
|
||||
|
||||
if (!attrRangeIntersect(a, &start, &end)) {
|
||||
// out of range
|
||||
// adjust if necessary
|
||||
if (a->start >= ostart)
|
||||
a->start -= count;
|
||||
if (a->end >= oend)
|
||||
a->end -= count;
|
||||
return a->next;
|
||||
}
|
||||
|
||||
// just outright delete the attribute?
|
||||
// the inequalities handle attributes entirely inside the range
|
||||
// if both are equal, the attribute's range is equal to the range
|
||||
if (a->start >= start && a->end <= end)
|
||||
return attrDelete(alist, a);
|
||||
|
||||
// only chop off the start or end?
|
||||
if (a->start == start) { // chop off the start
|
||||
// if we weren't adjusting positions this would just be setting a->start to end
|
||||
// but since this is deleting from the start, we need to adjust both by count
|
||||
a->start = end - count;
|
||||
a->end -= count;
|
||||
return a->next;
|
||||
}
|
||||
if (a->end == end) { // chop off the end
|
||||
// a->start is already good
|
||||
a->end = start;
|
||||
return a->next;
|
||||
}
|
||||
|
||||
// in this case, the deleted range is inside the attribute
|
||||
// we can clear it by just removing count from a->end
|
||||
a->end -= count;
|
||||
return a->next;
|
||||
}
|
||||
|
||||
uiprivAttrList *uiprivNewAttrList(void)
|
||||
{
|
||||
return uiprivNew(uiprivAttrList);
|
||||
}
|
||||
|
||||
void uiprivFreeAttrList(uiprivAttrList *alist)
|
||||
{
|
||||
struct attr *a, *next;
|
||||
|
||||
a = alist->first;
|
||||
while (a != NULL) {
|
||||
next = a->next;
|
||||
uiprivAttributeRelease(a->val);
|
||||
uiprivFree(a);
|
||||
a = next;
|
||||
}
|
||||
uiprivFree(alist);
|
||||
}
|
||||
|
||||
void uiprivAttrListInsertAttribute(uiprivAttrList *alist, uiAttribute *val, size_t start, size_t end)
|
||||
{
|
||||
struct attr *a;
|
||||
struct attr *before;
|
||||
struct attr *tail = NULL;
|
||||
int split = 0;
|
||||
uiAttributeType valtype;
|
||||
|
||||
// first, figure out where in the list this should go
|
||||
// in addition, if this attribute overrides one that already exists, split that one apart so this one can take over
|
||||
before = alist->first;
|
||||
valtype = uiAttributeGetType(val);
|
||||
while (before != NULL) {
|
||||
size_t lstart, lend;
|
||||
|
||||
// once we get to the first point after start, we know where to insert
|
||||
if (before->start > start)
|
||||
break;
|
||||
|
||||
// if we have already split a prior instance of this attribute, don't bother doing it again
|
||||
if (split)
|
||||
goto next;
|
||||
|
||||
// should we split this attribute?
|
||||
if (uiAttributeGetType(before->val) != valtype)
|
||||
goto next;
|
||||
lstart = start;
|
||||
lend = end;
|
||||
if (!attrRangeIntersect(before, &lstart, &lend))
|
||||
goto next;
|
||||
|
||||
// okay so this might conflict; if the val is the same as the one we want, we need to expand the existing attribute, not fragment anything
|
||||
// TODO will this reduce fragmentation if we first add from 0 to 2 and then from 2 to 4? or do we have to do that separately?
|
||||
if (uiprivAttributeEqual(before->val, val)) {
|
||||
attrGrow(alist, before, start, end);
|
||||
return;
|
||||
}
|
||||
// okay the values are different; we need to split apart
|
||||
before = attrDropRange(alist, before, start, end, &tail);
|
||||
split = 1;
|
||||
continue;
|
||||
|
||||
next:
|
||||
before = before->next;
|
||||
}
|
||||
|
||||
// if we got here, we know we have to add the attribute before before
|
||||
a = uiprivNew(struct attr);
|
||||
a->val = uiprivAttributeRetain(val);
|
||||
a->start = start;
|
||||
a->end = end;
|
||||
attrInsertBefore(alist, a, before);
|
||||
|
||||
// and finally, if we split, insert the remainder
|
||||
if (tail == NULL)
|
||||
return;
|
||||
// note we start at before; it won't be inserted before that by the sheer nature of how the code above works
|
||||
for (; before != NULL; before = before->next)
|
||||
if (before->start > tail->start)
|
||||
break;
|
||||
attrInsertBefore(alist, tail, before);
|
||||
}
|
||||
|
||||
void uiprivAttrListInsertCharactersUnattributed(uiprivAttrList *alist, size_t start, size_t count)
|
||||
{
|
||||
struct attr *a;
|
||||
struct attr *tails = NULL;
|
||||
|
||||
// every attribute before the insertion point can either cross into the insertion point or not
|
||||
// if it does, we need to split that attribute apart at the insertion point, keeping only the old attribute in place, adjusting the new tail, and preparing it for being re-added later
|
||||
for (a = alist->first; a != NULL; a = a->next) {
|
||||
struct attr *tail;
|
||||
|
||||
// stop once we get to the insertion point
|
||||
if (a->start >= start)
|
||||
break;
|
||||
// only do something if overlapping
|
||||
if (!attrHasPos(a, start))
|
||||
continue;
|
||||
|
||||
tail = attrSplitAt(alist, a, start);
|
||||
// adjust the new tail for the insertion
|
||||
tail->start += count;
|
||||
tail->end += count;
|
||||
// and queue it for re-adding later
|
||||
// we can safely use tails as if it was singly-linked since it's just a temporary list; we properly merge them back in below and they'll be doubly-linked again then
|
||||
// TODO actually we could probably save some time by making then doubly-linked now and adding them in one fell swoop, but that would make things a bit more complicated...
|
||||
tail->next = tails;
|
||||
tails = tail;
|
||||
}
|
||||
|
||||
// at this point in the attribute list, we are at or after the insertion point
|
||||
// all the split-apart attributes will be at the insertion point
|
||||
// therefore, we can just add them all back now, and the list will still be sorted correctly
|
||||
while (tails != NULL) {
|
||||
struct attr *next;
|
||||
|
||||
// make all the links NULL before insertion, just to be safe
|
||||
next = tails->next;
|
||||
tails->next = NULL;
|
||||
attrInsertBefore(alist, tails, a);
|
||||
tails = next;
|
||||
}
|
||||
|
||||
// every remaining attribute will be either at or after the insertion point
|
||||
// we just need to move them ahead
|
||||
for (; a != NULL; a = a->next) {
|
||||
a->start += count;
|
||||
a->end += count;
|
||||
}
|
||||
}
|
||||
|
||||
// The attributes are those of character start - 1.
|
||||
// If start == 0, the attributes are those of character 0.
|
||||
/*
|
||||
This is an obtuse function. Here's some diagrams to help.
|
||||
|
||||
Given the input string
|
||||
abcdefghi (positions: 012345678 9)
|
||||
and attribute set
|
||||
red start 0 end 3
|
||||
bold start 2 end 6
|
||||
underline start 5 end 8
|
||||
or visually:
|
||||
012345678 9
|
||||
rrr------
|
||||
--bbbb---
|
||||
-----uuu-
|
||||
If we insert qwe to result in positions 0123456789AB C:
|
||||
|
||||
before 0, 1, 2 (grow the red part, move everything else down)
|
||||
red -> start 0 (no change) end 3+3=6
|
||||
bold -> start 2+3=5 end 6+3=9
|
||||
underline -> start 5+3=8 end 8+3=B
|
||||
before 3 (grow red and bold, move underline down)
|
||||
red -> start 0 (no change) end 3+3=6
|
||||
bold -> start 2 (no change) end 6+3=9
|
||||
underline -> start 5+3=8 end 8+3=B
|
||||
before 4, 5 (keep red, grow bold, move underline down)
|
||||
red -> start 0 (no change) end 3 (no change)
|
||||
bold -> start 2 (no change) end 6+3=9
|
||||
underline -> start 5+3=8 end 8+3=B
|
||||
before 6 (keep red, grow bold and underline)
|
||||
red -> start 0 (no change) end 3 (no change)
|
||||
bold -> start 2 (no change) end 6+3=9
|
||||
underline -> start 5 (no change) end 8+3=B
|
||||
before 7, 8 (keep red and bold, grow underline)
|
||||
red -> start 0 (no change) end 3 (no change)
|
||||
bold -> start 2 (no change) end 6 (no change)
|
||||
underline -> start 5 (no change) end 8+3=B
|
||||
before 9 (keep all three)
|
||||
red -> start 0 (no change) end 3 (no change)
|
||||
bold -> start 2 (no change) end 6 (no change)
|
||||
underline -> start 5 (no change) end 8 (no change)
|
||||
|
||||
result:
|
||||
0 1 2 3 4 5 6 7 8 9
|
||||
red E E E e n n n n n n
|
||||
bold s s S E E E e n n n
|
||||
underline s s s s s S E E e n
|
||||
N = none
|
||||
E = end only
|
||||
S = start and end
|
||||
uppercase = in original range, lowercase = not
|
||||
|
||||
which results in our algorithm:
|
||||
for each attribute
|
||||
if start < insertion point
|
||||
move start up
|
||||
else if start == insertion point
|
||||
if start != 0
|
||||
move start up
|
||||
if end <= insertion point
|
||||
move end up
|
||||
*/
|
||||
// TODO does this ensure the list remains sorted?
|
||||
void uiprivAttrListInsertCharactersExtendingAttributes(uiprivAttrList *alist, size_t start, size_t count)
|
||||
{
|
||||
struct attr *a;
|
||||
|
||||
for (a = alist->first; a != NULL; a = a->next) {
|
||||
if (a->start < start)
|
||||
a->start += count;
|
||||
else if (a->start == start && start != 0)
|
||||
a->start += count;
|
||||
if (a->end <= start)
|
||||
a->end += count;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO replace at point with — replaces with first character's attributes
|
||||
|
||||
void uiprivAttrListRemoveAttribute(uiprivAttrList *alist, uiAttributeType type, size_t start, size_t end)
|
||||
{
|
||||
struct attr *a;
|
||||
struct attr *tails = NULL; // see uiprivAttrListInsertCharactersUnattributed() above
|
||||
struct attr *tailsAt = NULL;
|
||||
|
||||
a = alist->first;
|
||||
while (a != NULL) {
|
||||
size_t lstart, lend;
|
||||
struct attr *tail;
|
||||
|
||||
// this defines where to re-attach the tails
|
||||
// (all the tails will have their start at end, so we can just insert them all before tailsAt)
|
||||
if (a->start >= end) {
|
||||
tailsAt = a;
|
||||
// and at this point we're done, so
|
||||
break;
|
||||
}
|
||||
if (uiAttributeGetType(a->val) != type)
|
||||
goto next;
|
||||
lstart = start;
|
||||
lend = end;
|
||||
if (!attrRangeIntersect(a, &lstart, &lend))
|
||||
goto next;
|
||||
a = attrDropRange(alist, a, start, end, &tail);
|
||||
if (tail != NULL) {
|
||||
tail->next = tails;
|
||||
tails = tail;
|
||||
}
|
||||
continue;
|
||||
|
||||
next:
|
||||
a = a->next;
|
||||
}
|
||||
|
||||
while (tails != NULL) {
|
||||
struct attr *next;
|
||||
|
||||
// make all the links NULL before insertion, just to be safe
|
||||
next = tails->next;
|
||||
tails->next = NULL;
|
||||
attrInsertBefore(alist, tails, a);
|
||||
tails = next;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO merge this with the above
|
||||
void uiprivAttrListRemoveAttributes(uiprivAttrList *alist, size_t start, size_t end)
|
||||
{
|
||||
struct attr *a;
|
||||
struct attr *tails = NULL; // see uiprivAttrListInsertCharactersUnattributed() above
|
||||
struct attr *tailsAt = NULL;
|
||||
|
||||
a = alist->first;
|
||||
while (a != NULL) {
|
||||
size_t lstart, lend;
|
||||
struct attr *tail;
|
||||
|
||||
// this defines where to re-attach the tails
|
||||
// (all the tails will have their start at end, so we can just insert them all before tailsAt)
|
||||
if (a->start >= end) {
|
||||
tailsAt = a;
|
||||
// and at this point we're done, so
|
||||
break;
|
||||
}
|
||||
lstart = start;
|
||||
lend = end;
|
||||
if (!attrRangeIntersect(a, &lstart, &lend))
|
||||
goto next;
|
||||
a = attrDropRange(alist, a, start, end, &tail);
|
||||
if (tail != NULL) {
|
||||
tail->next = tails;
|
||||
tails = tail;
|
||||
}
|
||||
continue;
|
||||
|
||||
next:
|
||||
a = a->next;
|
||||
}
|
||||
|
||||
while (tails != NULL) {
|
||||
struct attr *next;
|
||||
|
||||
// make all the links NULL before insertion, just to be safe
|
||||
next = tails->next;
|
||||
tails->next = NULL;
|
||||
attrInsertBefore(alist, tails, a);
|
||||
tails = next;
|
||||
}
|
||||
}
|
||||
|
||||
void uiprivAttrListRemoveCharacters(uiprivAttrList *alist, size_t start, size_t end)
|
||||
{
|
||||
struct attr *a;
|
||||
|
||||
a = alist->first;
|
||||
while (a != NULL)
|
||||
a = attrDeleteRange(alist, a, start, end);
|
||||
}
|
||||
|
||||
void uiprivAttrListForEach(const uiprivAttrList *alist, const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data)
|
||||
{
|
||||
struct attr *a;
|
||||
uiForEach ret;
|
||||
|
||||
for (a = alist->first; a != NULL; a = a->next) {
|
||||
ret = (*f)(s, a->val, a->start, a->end, data);
|
||||
if (ret == uiForEachStop)
|
||||
// TODO for all: break or return?
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,357 @@
|
||||
// 3 december 2016
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
#include "attrstr.h"
|
||||
|
||||
struct uiAttributedString {
|
||||
char *s;
|
||||
size_t len;
|
||||
|
||||
uiprivAttrList *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+)
|
||||
uint16_t *u16;
|
||||
size_t u16len;
|
||||
|
||||
size_t *u8tou16;
|
||||
size_t *u16tou8;
|
||||
|
||||
// this is lazily created to keep things from getting *too* slow
|
||||
uiprivGraphemes *graphemes;
|
||||
};
|
||||
|
||||
static void resize(uiAttributedString *s, size_t u8, size_t u16)
|
||||
{
|
||||
s->len = u8;
|
||||
s->s = (char *) uiprivRealloc(s->s, (s->len + 1) * sizeof (char), "char[] (uiAttributedString)");
|
||||
s->u8tou16 = (size_t *) uiprivRealloc(s->u8tou16, (s->len + 1) * sizeof (size_t), "size_t[] (uiAttributedString)");
|
||||
s->u16len = u16;
|
||||
s->u16 = (uint16_t *) uiprivRealloc(s->u16, (s->u16len + 1) * sizeof (uint16_t), "uint16_t[] (uiAttributedString)");
|
||||
s->u16tou8 = (size_t *) uiprivRealloc(s->u16tou8, (s->u16len + 1) * sizeof (size_t), "size_t[] (uiAttributedString)");
|
||||
}
|
||||
|
||||
uiAttributedString *uiNewAttributedString(const char *initialString)
|
||||
{
|
||||
uiAttributedString *s;
|
||||
|
||||
s = uiprivNew(uiAttributedString);
|
||||
s->attrs = uiprivNewAttrList();
|
||||
uiAttributedStringAppendUnattributed(s, initialString);
|
||||
return s;
|
||||
}
|
||||
|
||||
// TODO make sure that all implementations of uiprivNewGraphemes() work fine with empty strings; in particular, the Windows one might not
|
||||
static void recomputeGraphemes(uiAttributedString *s)
|
||||
{
|
||||
if (s->graphemes != NULL)
|
||||
return;
|
||||
if (uiprivGraphemesTakesUTF16()) {
|
||||
s->graphemes = uiprivNewGraphemes(s->u16, s->u16len);
|
||||
return;
|
||||
}
|
||||
s->graphemes = uiprivNewGraphemes(s->s, s->len);
|
||||
}
|
||||
|
||||
static void invalidateGraphemes(uiAttributedString *s)
|
||||
{
|
||||
if (s->graphemes == NULL)
|
||||
return;
|
||||
uiprivFree(s->graphemes->pointsToGraphemes);
|
||||
uiprivFree(s->graphemes->graphemesToPoints);
|
||||
uiprivFree(s->graphemes);
|
||||
s->graphemes = NULL;
|
||||
}
|
||||
|
||||
void uiFreeAttributedString(uiAttributedString *s)
|
||||
{
|
||||
uiprivFreeAttrList(s->attrs);
|
||||
invalidateGraphemes(s);
|
||||
uiprivFree(s->u16tou8);
|
||||
uiprivFree(s->u8tou16);
|
||||
uiprivFree(s->u16);
|
||||
uiprivFree(s->s);
|
||||
uiprivFree(s);
|
||||
}
|
||||
|
||||
const char *uiAttributedStringString(const uiAttributedString *s)
|
||||
{
|
||||
return s->s;
|
||||
}
|
||||
|
||||
size_t uiAttributedStringLen(const uiAttributedString *s)
|
||||
{
|
||||
return s->len;
|
||||
}
|
||||
|
||||
static void u8u16len(const char *str, size_t *n8, size_t *n16)
|
||||
{
|
||||
uint32_t rune;
|
||||
char buf[4];
|
||||
uint16_t buf16[2];
|
||||
|
||||
*n8 = 0;
|
||||
*n16 = 0;
|
||||
while (*str) {
|
||||
str = uiprivUTF8DecodeRune(str, 0, &rune);
|
||||
// TODO document the use of the function vs a pointer subtract here
|
||||
// TODO also we need to consider namespace collision with utf.h...
|
||||
*n8 += uiprivUTF8EncodeRune(rune, buf);
|
||||
*n16 += uiprivUTF16EncodeRune(rune, buf16);
|
||||
}
|
||||
}
|
||||
|
||||
void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str)
|
||||
{
|
||||
uiAttributedStringInsertAtUnattributed(s, str, s->len);
|
||||
}
|
||||
|
||||
// this works (and returns true, which is what we want) at s->len too because s->s[s->len] is always going to be 0 due to us allocating s->len + 1 bytes and because uiprivRealloc() always zero-fills allocated memory
|
||||
static int onCodepointBoundary(uiAttributedString *s, size_t at)
|
||||
{
|
||||
uint8_t c;
|
||||
|
||||
// for uiNewAttributedString()
|
||||
if (s->s == NULL && at == 0)
|
||||
return 1;
|
||||
c = (uint8_t) (s->s[at]);
|
||||
return c < 0x80 || c >= 0xC0;
|
||||
}
|
||||
|
||||
// TODO note that at must be on a codeoint boundary
|
||||
void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at)
|
||||
{
|
||||
uint32_t rune;
|
||||
char buf[4];
|
||||
uint16_t buf16[2];
|
||||
size_t n8, n16; // TODO make loop-local? to avoid using them in the wrong place again
|
||||
size_t old, old16;
|
||||
size_t oldn8, oldn16;
|
||||
size_t oldlen, old16len;
|
||||
size_t at16;
|
||||
size_t i;
|
||||
|
||||
if (!onCodepointBoundary(s, at)) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
at16 = 0;
|
||||
if (s->u8tou16 != NULL)
|
||||
at16 = s->u8tou16[at];
|
||||
|
||||
// do this first to reclaim memory
|
||||
invalidateGraphemes(s);
|
||||
|
||||
// first figure out how much we need to grow by
|
||||
// this includes post-validated UTF-8
|
||||
u8u16len(str, &n8, &n16);
|
||||
|
||||
// and resize
|
||||
old = at;
|
||||
old16 = at16;
|
||||
oldlen = s->len;
|
||||
old16len = s->u16len;
|
||||
resize(s, s->len + n8, s->u16len + n16);
|
||||
|
||||
// move existing characters out of the way
|
||||
// note the use of memmove(): https://twitter.com/rob_pike/status/737797688217894912
|
||||
memmove(
|
||||
s->s + at + n8,
|
||||
s->s + at,
|
||||
(oldlen - at) * sizeof (char));
|
||||
memmove(
|
||||
s->u16 + at16 + n16,
|
||||
s->u16 + at16,
|
||||
(old16len - at16) * sizeof (uint16_t));
|
||||
// note the + 1 for these; we want to copy the terminating null too
|
||||
memmove(
|
||||
s->u8tou16 + at + n8,
|
||||
s->u8tou16 + at,
|
||||
(oldlen - at + 1) * sizeof (size_t));
|
||||
memmove(
|
||||
s->u16tou8 + at16 + n16,
|
||||
s->u16tou8 + at16,
|
||||
(old16len - at16 + 1) * sizeof (size_t));
|
||||
oldn8 = n8;
|
||||
oldn16 = n16;
|
||||
|
||||
// and copy
|
||||
while (*str) {
|
||||
size_t n;
|
||||
|
||||
str = uiprivUTF8DecodeRune(str, 0, &rune);
|
||||
n = uiprivUTF8EncodeRune(rune, buf);
|
||||
n16 = uiprivUTF16EncodeRune(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
|
||||
// TODO is this done by the below?
|
||||
//TODO s->u8tou16[old] = old16;
|
||||
//TODO s->u16tou8[old16] = old;
|
||||
|
||||
// and adjust the prior values in the conversion tables
|
||||
// use <= so the terminating 0 gets updated too
|
||||
for (i = 0; i <= oldlen - at; i++)
|
||||
s->u8tou16[at + oldn8 + i] += s->u16len - old16len;
|
||||
for (i = 0; i <= old16len - at16; i++)
|
||||
s->u16tou8[at16 + oldn16 + i] += s->len - oldlen;
|
||||
|
||||
// and finally do the attributes
|
||||
uiprivAttrListInsertCharactersUnattributed(s->attrs, at, n8);
|
||||
}
|
||||
|
||||
// TODO document that end is the first index that will be maintained
|
||||
void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end)
|
||||
{
|
||||
size_t start16, end16;
|
||||
size_t count, count16;
|
||||
size_t i;
|
||||
|
||||
if (!onCodepointBoundary(s, start)) {
|
||||
// TODO
|
||||
}
|
||||
if (!onCodepointBoundary(s, end)) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
count = end - start;
|
||||
start16 = s->u8tou16[start];
|
||||
end16 = s->u8tou16[end];
|
||||
count16 = end16 - start16;
|
||||
|
||||
invalidateGraphemes(s);
|
||||
|
||||
// overwrite old characters
|
||||
memmove(
|
||||
s->s + start,
|
||||
s->s + end,
|
||||
(s->len - end) * sizeof (char));
|
||||
memmove(
|
||||
s->u16 + start16,
|
||||
s->u16 + end16,
|
||||
(s->u16len - end16) * sizeof (uint16_t));
|
||||
// note the + 1 for these; we want to copy the terminating null too
|
||||
memmove(
|
||||
s->u8tou16 + start,
|
||||
s->u8tou16 + end,
|
||||
(s->len - end + 1) * sizeof (size_t));
|
||||
memmove(
|
||||
s->u16tou8 + start16,
|
||||
s->u16tou8 + end16,
|
||||
(s->u16len - end16 + 1) * sizeof (size_t));
|
||||
|
||||
// update the conversion tables
|
||||
// note the use of <= to include the null terminator
|
||||
for (i = 0; i <= (s->len - end); i++)
|
||||
s->u8tou16[start + i] -= count16;
|
||||
for (i = 0; i <= (s->u16len - end16); i++)
|
||||
s->u16tou8[start16 + i] -= count;
|
||||
|
||||
// null-terminate the string
|
||||
s->s[start + (s->len - end)] = 0;
|
||||
s->u16[start16 + (s->u16len - end16)] = 0;
|
||||
|
||||
// fix up attributes
|
||||
uiprivAttrListRemoveCharacters(s->attrs, start, end);
|
||||
|
||||
// and finally resize
|
||||
resize(s, s->len - count, s->u16len - count16);
|
||||
}
|
||||
|
||||
void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute *a, size_t start, size_t end)
|
||||
{
|
||||
uiprivAttrListInsertAttribute(s->attrs, a, start, end);
|
||||
}
|
||||
|
||||
// LONGTERM introduce an iterator object instead?
|
||||
void uiAttributedStringForEachAttribute(const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data)
|
||||
{
|
||||
uiprivAttrListForEach(s->attrs, s, f, data);
|
||||
}
|
||||
|
||||
// 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 (uiprivGraphemesTakesUTF16())
|
||||
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 (uiprivGraphemesTakesUTF16())
|
||||
pos = s->u16tou8[pos];
|
||||
return pos;
|
||||
}
|
||||
|
||||
// helpers for platform-specific code
|
||||
|
||||
const uint16_t *uiprivAttributedStringUTF16String(const uiAttributedString *s)
|
||||
{
|
||||
return s->u16;
|
||||
}
|
||||
|
||||
size_t uiprivAttributedStringUTF16Len(const uiAttributedString *s)
|
||||
{
|
||||
return s->u16len;
|
||||
}
|
||||
|
||||
// TODO is this still needed given the below?
|
||||
size_t uiprivAttributedStringUTF8ToUTF16(const uiAttributedString *s, size_t n)
|
||||
{
|
||||
return s->u8tou16[n];
|
||||
}
|
||||
|
||||
size_t *uiprivAttributedStringCopyUTF8ToUTF16Table(const uiAttributedString *s, size_t *n)
|
||||
{
|
||||
size_t *out;
|
||||
size_t nbytes;
|
||||
|
||||
nbytes = (s->len + 1) * sizeof (size_t);
|
||||
*n = s->len;
|
||||
out = (size_t *) uiprivAlloc(nbytes, "size_t[] (uiAttributedString)");
|
||||
memmove(out, s->u8tou16, nbytes);
|
||||
return out;
|
||||
}
|
||||
|
||||
size_t *uiprivAttributedStringCopyUTF16ToUTF8Table(const uiAttributedString *s, size_t *n)
|
||||
{
|
||||
size_t *out;
|
||||
size_t nbytes;
|
||||
|
||||
nbytes = (s->u16len + 1) * sizeof (size_t);
|
||||
*n = s->u16len;
|
||||
out = (size_t *) uiprivAlloc(nbytes, "size_t[] (uiAttributedString)");
|
||||
memmove(out, s->u16tou8, nbytes);
|
||||
return out;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// 19 february 2018
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// attribute.c
|
||||
extern uiAttribute *uiprivAttributeRetain(uiAttribute *a);
|
||||
extern void uiprivAttributeRelease(uiAttribute *a);
|
||||
extern int uiprivAttributeEqual(const uiAttribute *a, const uiAttribute *b);
|
||||
|
||||
// opentype.c
|
||||
extern int uiprivOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b);
|
||||
|
||||
// attrlist.c
|
||||
typedef struct uiprivAttrList uiprivAttrList;
|
||||
extern uiprivAttrList *uiprivNewAttrList(void);
|
||||
extern void uiprivFreeAttrList(uiprivAttrList *alist);
|
||||
extern void uiprivAttrListInsertAttribute(uiprivAttrList *alist, uiAttribute *val, size_t start, size_t end);
|
||||
extern void uiprivAttrListInsertCharactersUnattributed(uiprivAttrList *alist, size_t start, size_t count);
|
||||
extern void uiprivAttrListInsertCharactersExtendingAttributes(uiprivAttrList *alist, size_t start, size_t count);
|
||||
extern void uiprivAttrListRemoveAttribute(uiprivAttrList *alist, uiAttributeType type, size_t start, size_t end);
|
||||
extern void uiprivAttrListRemoveAttributes(uiprivAttrList *alist, size_t start, size_t end);
|
||||
extern void uiprivAttrListRemoveCharacters(uiprivAttrList *alist, size_t start, size_t end);
|
||||
extern void uiprivAttrListForEach(const uiprivAttrList *alist, const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data);
|
||||
|
||||
// attrstr.c
|
||||
extern const uint16_t *uiprivAttributedStringUTF16String(const uiAttributedString *s);
|
||||
extern size_t uiprivAttributedStringUTF16Len(const uiAttributedString *s);
|
||||
extern size_t uiprivAttributedStringUTF8ToUTF16(const uiAttributedString *s, size_t n);
|
||||
extern size_t *uiprivAttributedStringCopyUTF8ToUTF16Table(const uiAttributedString *s, size_t *n);
|
||||
extern size_t *uiprivAttributedStringCopyUTF16ToUTF8Table(const uiAttributedString *s, size_t *n);
|
||||
|
||||
// per-OS graphemes.c/graphemes.cpp/graphemes.m/etc.
|
||||
typedef struct uiprivGraphemes uiprivGraphemes;
|
||||
struct uiprivGraphemes {
|
||||
size_t len;
|
||||
size_t *pointsToGraphemes;
|
||||
size_t *graphemesToPoints;
|
||||
};
|
||||
extern int uiprivGraphemesTakesUTF16(void);
|
||||
extern uiprivGraphemes *uiprivNewGraphemes(void *s, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,101 @@
|
||||
// 26 may 2015
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
|
||||
void uiControlDestroy(uiControl *c)
|
||||
{
|
||||
(*(c->Destroy))(c);
|
||||
}
|
||||
|
||||
uintptr_t uiControlHandle(uiControl *c)
|
||||
{
|
||||
return (*(c->Handle))(c);
|
||||
}
|
||||
|
||||
uiControl *uiControlParent(uiControl *c)
|
||||
{
|
||||
return (*(c->Parent))(c);
|
||||
}
|
||||
|
||||
void uiControlSetParent(uiControl *c, uiControl *parent)
|
||||
{
|
||||
(*(c->SetParent))(c, parent);
|
||||
}
|
||||
|
||||
int uiControlToplevel(uiControl *c)
|
||||
{
|
||||
return (*(c->Toplevel))(c);
|
||||
}
|
||||
|
||||
int uiControlVisible(uiControl *c)
|
||||
{
|
||||
return (*(c->Visible))(c);
|
||||
}
|
||||
|
||||
void uiControlShow(uiControl *c)
|
||||
{
|
||||
(*(c->Show))(c);
|
||||
}
|
||||
|
||||
void uiControlHide(uiControl *c)
|
||||
{
|
||||
(*(c->Hide))(c);
|
||||
}
|
||||
|
||||
int uiControlEnabled(uiControl *c)
|
||||
{
|
||||
return (*(c->Enabled))(c);
|
||||
}
|
||||
|
||||
void uiControlEnable(uiControl *c)
|
||||
{
|
||||
(*(c->Enable))(c);
|
||||
}
|
||||
|
||||
void uiControlDisable(uiControl *c)
|
||||
{
|
||||
(*(c->Disable))(c);
|
||||
}
|
||||
|
||||
#define uiprivControlSignature 0x7569436F
|
||||
|
||||
uiControl *uiAllocControl(size_t size, uint32_t OSsig, uint32_t typesig, const char *typenamestr)
|
||||
{
|
||||
uiControl *c;
|
||||
|
||||
c = (uiControl *) uiprivAlloc(size, typenamestr);
|
||||
c->Signature = uiprivControlSignature;
|
||||
c->OSSignature = OSsig;
|
||||
c->TypeSignature = typesig;
|
||||
return c;
|
||||
}
|
||||
|
||||
void uiFreeControl(uiControl *c)
|
||||
{
|
||||
if (uiControlParent(c) != NULL)
|
||||
uiprivUserBug("You cannot destroy a uiControl while it still has a parent. (control: %p)", c);
|
||||
uiprivFree(c);
|
||||
}
|
||||
|
||||
void uiControlVerifySetParent(uiControl *c, uiControl *parent)
|
||||
{
|
||||
uiControl *curParent;
|
||||
|
||||
if (uiControlToplevel(c))
|
||||
uiprivUserBug("You cannot give a toplevel uiControl a parent. (control: %p)", c);
|
||||
curParent = uiControlParent(c);
|
||||
if (parent != NULL && curParent != NULL)
|
||||
uiprivUserBug("You cannot give a uiControl a parent while it already has one. (control: %p; current parent: %p; new parent: %p)", c, curParent, parent);
|
||||
if (parent == NULL && curParent == NULL)
|
||||
uiprivImplBug("attempt to double unparent uiControl %p", c);
|
||||
}
|
||||
|
||||
int uiControlEnabledToUser(uiControl *c)
|
||||
{
|
||||
while (c != NULL) {
|
||||
if (!uiControlEnabled(c))
|
||||
return 0;
|
||||
c = uiControlParent(c);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// 24 april 2016
|
||||
|
||||
// LONGTERM if I don't decide to remove these outright, should they be renamed uiprivTypeNameSignature? these aren't real symbols, so...
|
||||
|
||||
#define uiAreaSignature 0x41726561
|
||||
#define uiBoxSignature 0x426F784C
|
||||
#define uiButtonSignature 0x42746F6E
|
||||
#define uiCheckboxSignature 0x43686B62
|
||||
#define uiColorButtonSignature 0x436F6C42
|
||||
#define uiComboboxSignature 0x436F6D62
|
||||
#define uiDateTimePickerSignature 0x44545069
|
||||
#define uiEditableComboboxSignature 0x45644362
|
||||
#define uiEntrySignature 0x456E7472
|
||||
#define uiFontButtonSignature 0x466F6E42
|
||||
#define uiFormSignature 0x466F726D
|
||||
#define uiGridSignature 0x47726964
|
||||
#define uiGroupSignature 0x47727062
|
||||
#define uiLabelSignature 0x4C61626C
|
||||
#define uiMultilineEntrySignature 0x4D6C6E45
|
||||
#define uiProgressBarSignature 0x50426172
|
||||
#define uiRadioButtonsSignature 0x5264696F
|
||||
#define uiSeparatorSignature 0x53657061
|
||||
#define uiSliderSignature 0x536C6964
|
||||
#define uiSpinboxSignature 0x5370696E
|
||||
#define uiTabSignature 0x54616273
|
||||
#define uiTableSignature 0x5461626C
|
||||
#define uiWindowSignature 0x57696E64
|
||||
@@ -0,0 +1,21 @@
|
||||
// 13 may 2016
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
|
||||
void uiprivDoImplBug(const char *file, const char *line, const char *func, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
uiprivRealBug(file, line, func, "POSSIBLE IMPLEMENTATION BUG; CONTACT ANDLABS:\n", format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void uiprivDoUserBug(const char *file, const char *line, const char *func, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
uiprivRealBug(file, line, func, "You have a bug: ", format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// 11 october 2015
|
||||
#include <math.h>
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
|
||||
void uiDrawMatrixSetIdentity(uiDrawMatrix *m)
|
||||
{
|
||||
m->M11 = 1;
|
||||
m->M12 = 0;
|
||||
m->M21 = 0;
|
||||
m->M22 = 1;
|
||||
m->M31 = 0;
|
||||
m->M32 = 0;
|
||||
}
|
||||
|
||||
// The rest of this file provides basic utilities in case the platform doesn't provide any of its own for these tasks.
|
||||
// Keep these as minimal as possible. They should generally not call other fallbacks.
|
||||
|
||||
// see https://msdn.microsoft.com/en-us/library/windows/desktop/ff684171%28v=vs.85%29.aspx#skew_transform
|
||||
// TODO see if there's a way we can avoid the multiplication
|
||||
void uiprivFallbackSkew(uiDrawMatrix *m, double x, double y, double xamount, double yamount)
|
||||
{
|
||||
uiDrawMatrix n;
|
||||
|
||||
uiDrawMatrixSetIdentity(&n);
|
||||
// TODO explain this
|
||||
n.M12 = tan(yamount);
|
||||
n.M21 = tan(xamount);
|
||||
n.M31 = -y * tan(xamount);
|
||||
n.M32 = -x * tan(yamount);
|
||||
uiDrawMatrixMultiply(m, &n);
|
||||
}
|
||||
|
||||
void uiprivScaleCenter(double xCenter, double yCenter, double *x, double *y)
|
||||
{
|
||||
*x = xCenter - (*x * xCenter);
|
||||
*y = yCenter - (*y * yCenter);
|
||||
}
|
||||
|
||||
// the basic algorithm is from cairo
|
||||
// but it's the same algorithm as the transform point, just without M31 and M32 taken into account, so let's just do that instead
|
||||
void uiprivFallbackTransformSize(uiDrawMatrix *m, double *x, double *y)
|
||||
{
|
||||
uiDrawMatrix m2;
|
||||
|
||||
m2 = *m;
|
||||
m2.M31 = 0;
|
||||
m2.M32 = 0;
|
||||
uiDrawMatrixTransformPoint(&m2, x, y);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
# 23 march 2019
|
||||
|
||||
libui_sources += [
|
||||
'common/attribute.c',
|
||||
'common/attrlist.c',
|
||||
'common/attrstr.c',
|
||||
'common/areaevents.c',
|
||||
'common/control.c',
|
||||
'common/debug.c',
|
||||
'common/matrix.c',
|
||||
'common/opentype.c',
|
||||
'common/shouldquit.c',
|
||||
'common/tablemodel.c',
|
||||
'common/tablevalue.c',
|
||||
'common/userbugs.c',
|
||||
'common/utf.c',
|
||||
]
|
||||
@@ -0,0 +1,165 @@
|
||||
// 25 february 2018
|
||||
#include <stdlib.h>
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
#include "attrstr.h"
|
||||
|
||||
struct feature {
|
||||
char a;
|
||||
char b;
|
||||
char c;
|
||||
char d;
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
struct uiOpenTypeFeatures {
|
||||
struct feature *data;
|
||||
size_t len;
|
||||
size_t cap;
|
||||
};
|
||||
|
||||
#define bytecount(n) ((n) * sizeof (struct feature))
|
||||
|
||||
uiOpenTypeFeatures *uiNewOpenTypeFeatures(void)
|
||||
{
|
||||
uiOpenTypeFeatures *otf;
|
||||
|
||||
otf = uiprivNew(uiOpenTypeFeatures);
|
||||
otf->cap = 16;
|
||||
otf->data = (struct feature *) uiprivAlloc(bytecount(otf->cap), "struct feature[]");
|
||||
otf->len = 0;
|
||||
return otf;
|
||||
}
|
||||
|
||||
void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf)
|
||||
{
|
||||
uiprivFree(otf->data);
|
||||
uiprivFree(otf);
|
||||
}
|
||||
|
||||
uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf)
|
||||
{
|
||||
uiOpenTypeFeatures *ret;
|
||||
|
||||
ret = uiprivNew(uiOpenTypeFeatures);
|
||||
ret->len = otf->len;
|
||||
ret->cap = otf->cap;
|
||||
ret->data = (struct feature *) uiprivAlloc(bytecount(ret->cap), "struct feature[]");
|
||||
memset(ret->data, 0, bytecount(ret->cap));
|
||||
memmove(ret->data, otf->data, bytecount(ret->len));
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define intdiff(a, b) (((int) (a)) - ((int) (b)))
|
||||
|
||||
static int featurecmp(const void *a, const void *b)
|
||||
{
|
||||
const struct feature *f = (const struct feature *) a;
|
||||
const struct feature *g = (const struct feature *) b;
|
||||
|
||||
if (f->a != g->a)
|
||||
return intdiff(f->a, g->a);
|
||||
if (f->b != g->b)
|
||||
return intdiff(f->b, g->b);
|
||||
if (f->c != g->c)
|
||||
return intdiff(f->c, g->c);
|
||||
return intdiff(f->d, g->d);
|
||||
}
|
||||
|
||||
static struct feature mkkey(char a, char b, char c, char d)
|
||||
{
|
||||
struct feature f;
|
||||
|
||||
f.a = a;
|
||||
f.b = b;
|
||||
f.c = c;
|
||||
f.d = d;
|
||||
return f;
|
||||
}
|
||||
|
||||
#define find(pkey, otf) bsearch(pkey, otf->data, otf->len, sizeof (struct feature), featurecmp)
|
||||
|
||||
void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value)
|
||||
{
|
||||
struct feature *f;
|
||||
struct feature key;
|
||||
|
||||
// replace existing value if any
|
||||
key = mkkey(a, b, c, d);
|
||||
f = (struct feature *) find(&key, otf);
|
||||
if (f != NULL) {
|
||||
f->value = value;
|
||||
return;
|
||||
}
|
||||
|
||||
// if we got here, the tag is new
|
||||
if (otf->len == otf->cap) {
|
||||
otf->cap *= 2;
|
||||
otf->data = (struct feature *) uiprivRealloc(otf->data, bytecount(otf->cap), "struct feature[]");
|
||||
}
|
||||
f = otf->data + otf->len;
|
||||
f->a = a;
|
||||
f->b = b;
|
||||
f->c = c;
|
||||
f->d = d;
|
||||
f->value = value;
|
||||
// LONGTERM qsort here is overkill
|
||||
otf->len++;
|
||||
qsort(otf->data, otf->len, sizeof (struct feature), featurecmp);
|
||||
}
|
||||
|
||||
void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d)
|
||||
{
|
||||
struct feature *f;
|
||||
struct feature key;
|
||||
ptrdiff_t index;
|
||||
size_t count;
|
||||
|
||||
key = mkkey(a, b, c, d);
|
||||
f = (struct feature *) find(&key, otf);
|
||||
if (f == NULL)
|
||||
return;
|
||||
|
||||
index = f - otf->data;
|
||||
count = otf->len - index - 1;
|
||||
memmove(f + 1, f, bytecount(count));
|
||||
otf->len--;
|
||||
}
|
||||
|
||||
int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value)
|
||||
{
|
||||
const struct feature *f;
|
||||
struct feature key;
|
||||
|
||||
key = mkkey(a, b, c, d);
|
||||
f = (const struct feature *) find(&key, otf);
|
||||
if (f == NULL)
|
||||
return 0;
|
||||
*value = f->value;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data)
|
||||
{
|
||||
size_t n;
|
||||
const struct feature *p;
|
||||
uiForEach ret;
|
||||
|
||||
p = otf->data;
|
||||
for (n = 0; n < otf->len; n++) {
|
||||
ret = (*f)(otf, p->a, p->b, p->c, p->d, p->value, data);
|
||||
// TODO for all: require exact match?
|
||||
if (ret == uiForEachStop)
|
||||
return;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
int uiprivOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b)
|
||||
{
|
||||
if (a == b)
|
||||
return 1;
|
||||
if (a->len != b->len)
|
||||
return 0;
|
||||
return memcmp(a->data, b->data, bytecount(a->len)) == 0;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// 9 may 2015
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
|
||||
static int defaultOnShouldQuit(void *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int (*onShouldQuit)(void *) = defaultOnShouldQuit;
|
||||
static void *onShouldQuitData = NULL;
|
||||
|
||||
void uiOnShouldQuit(int (*f)(void *), void *data)
|
||||
{
|
||||
onShouldQuit = f;
|
||||
onShouldQuitData = data;
|
||||
}
|
||||
|
||||
int uiprivShouldQuit(void)
|
||||
{
|
||||
return (*onShouldQuit)(onShouldQuitData);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// 23 june 2018
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// tablemodel.c
|
||||
extern uiTableModelHandler *uiprivTableModelHandler(uiTableModel *m);
|
||||
extern int uiprivTableModelNumColumns(uiTableModel *m);
|
||||
extern uiTableValueType uiprivTableModelColumnType(uiTableModel *m, int column);
|
||||
extern int uiprivTableModelNumRows(uiTableModel *m);
|
||||
extern uiTableValue *uiprivTableModelCellValue(uiTableModel *m, int row, int column);
|
||||
extern void uiprivTableModelSetCellValue(uiTableModel *m, int row, int column, const uiTableValue *value);
|
||||
extern const uiTableTextColumnOptionalParams uiprivDefaultTextColumnOptionalParams;
|
||||
extern int uiprivTableModelCellEditable(uiTableModel *m, int row, int column);
|
||||
extern int uiprivTableModelColorIfProvided(uiTableModel *m, int row, int column, double *r, double *g, double *b, double *a);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,81 @@
|
||||
// 23 june 2018
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
#include "table.h"
|
||||
|
||||
int uiprivTableModelNumColumns(uiTableModel *m)
|
||||
{
|
||||
uiTableModelHandler *mh;
|
||||
|
||||
mh = uiprivTableModelHandler(m);
|
||||
return (*(mh->NumColumns))(mh, m);
|
||||
}
|
||||
|
||||
uiTableValueType uiprivTableModelColumnType(uiTableModel *m, int column)
|
||||
{
|
||||
uiTableModelHandler *mh;
|
||||
|
||||
mh = uiprivTableModelHandler(m);
|
||||
return (*(mh->ColumnType))(mh, m, column);
|
||||
}
|
||||
|
||||
int uiprivTableModelNumRows(uiTableModel *m)
|
||||
{
|
||||
uiTableModelHandler *mh;
|
||||
|
||||
mh = uiprivTableModelHandler(m);
|
||||
return (*(mh->NumRows))(mh, m);
|
||||
}
|
||||
|
||||
uiTableValue *uiprivTableModelCellValue(uiTableModel *m, int row, int column)
|
||||
{
|
||||
uiTableModelHandler *mh;
|
||||
|
||||
mh = uiprivTableModelHandler(m);
|
||||
return (*(mh->CellValue))(mh, m, row, column);
|
||||
}
|
||||
|
||||
void uiprivTableModelSetCellValue(uiTableModel *m, int row, int column, const uiTableValue *value)
|
||||
{
|
||||
uiTableModelHandler *mh;
|
||||
|
||||
mh = uiprivTableModelHandler(m);
|
||||
(*(mh->SetCellValue))(mh, m, row, column, value);
|
||||
|
||||
uiTableModelRowChanged(m, row);
|
||||
}
|
||||
|
||||
const uiTableTextColumnOptionalParams uiprivDefaultTextColumnOptionalParams = {
|
||||
.ColorModelColumn = -1,
|
||||
};
|
||||
|
||||
int uiprivTableModelCellEditable(uiTableModel *m, int row, int column)
|
||||
{
|
||||
uiTableValue *value;
|
||||
int editable;
|
||||
|
||||
switch (column) {
|
||||
case uiTableModelColumnNeverEditable:
|
||||
return 0;
|
||||
case uiTableModelColumnAlwaysEditable:
|
||||
return 1;
|
||||
}
|
||||
value = uiprivTableModelCellValue(m, row, column);
|
||||
editable = uiTableValueInt(value);
|
||||
uiFreeTableValue(value);
|
||||
return editable;
|
||||
}
|
||||
|
||||
int uiprivTableModelColorIfProvided(uiTableModel *m, int row, int column, double *r, double *g, double *b, double *a)
|
||||
{
|
||||
uiTableValue *value;
|
||||
|
||||
if (column == -1)
|
||||
return 0;
|
||||
value = uiprivTableModelCellValue(m, row, column);
|
||||
if (value == NULL)
|
||||
return 0;
|
||||
uiTableValueColor(value, r, g, b, a);
|
||||
uiFreeTableValue(value);
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
// 3 june 2018
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
#include "table.h"
|
||||
|
||||
struct uiTableValue {
|
||||
uiTableValueType type;
|
||||
union {
|
||||
char *str;
|
||||
uiImage *img;
|
||||
int i;
|
||||
struct {
|
||||
double r;
|
||||
double g;
|
||||
double b;
|
||||
double a;
|
||||
} color;
|
||||
} u;
|
||||
};
|
||||
|
||||
static uiTableValue *newTableValue(uiTableValueType type)
|
||||
{
|
||||
uiTableValue *v;
|
||||
|
||||
v = uiprivNew(uiTableValue);
|
||||
v->type = type;
|
||||
return v;
|
||||
}
|
||||
|
||||
void uiFreeTableValue(uiTableValue *v)
|
||||
{
|
||||
switch (v->type) {
|
||||
case uiTableValueTypeString:
|
||||
uiprivFree(v->u.str);
|
||||
break;
|
||||
}
|
||||
uiprivFree(v);
|
||||
}
|
||||
|
||||
uiTableValueType uiTableValueGetType(const uiTableValue *v)
|
||||
{
|
||||
return v->type;
|
||||
}
|
||||
|
||||
uiTableValue *uiNewTableValueString(const char *str)
|
||||
{
|
||||
uiTableValue *v;
|
||||
|
||||
v = newTableValue(uiTableValueTypeString);
|
||||
v->u.str = (char *) uiprivAlloc((strlen(str) + 1) * sizeof (char), "char[] (uiTableValue)");
|
||||
strcpy(v->u.str, str);
|
||||
return v;
|
||||
}
|
||||
|
||||
const char *uiTableValueString(const uiTableValue *v)
|
||||
{
|
||||
return v->u.str;
|
||||
}
|
||||
|
||||
uiTableValue *uiNewTableValueImage(uiImage *img)
|
||||
{
|
||||
uiTableValue *v;
|
||||
|
||||
v = newTableValue(uiTableValueTypeImage);
|
||||
v->u.img = img;
|
||||
return v;
|
||||
}
|
||||
|
||||
uiImage *uiTableValueImage(const uiTableValue *v)
|
||||
{
|
||||
return v->u.img;
|
||||
}
|
||||
|
||||
uiTableValue *uiNewTableValueInt(int i)
|
||||
{
|
||||
uiTableValue *v;
|
||||
|
||||
v = newTableValue(uiTableValueTypeInt);
|
||||
v->u.i = i;
|
||||
return v;
|
||||
}
|
||||
|
||||
int uiTableValueInt(const uiTableValue *v)
|
||||
{
|
||||
return v->u.i;
|
||||
}
|
||||
|
||||
uiTableValue *uiNewTableValueColor(double r, double g, double b, double a)
|
||||
{
|
||||
uiTableValue *v;
|
||||
|
||||
v = newTableValue(uiTableValueTypeColor);
|
||||
v->u.color.r = r;
|
||||
v->u.color.g = g;
|
||||
v->u.color.b = b;
|
||||
v->u.color.a = a;
|
||||
return v;
|
||||
}
|
||||
|
||||
void uiTableValueColor(const uiTableValue *v, double *r, double *g, double *b, double *a)
|
||||
{
|
||||
*r = v->u.color.r;
|
||||
*g = v->u.color.g;
|
||||
*b = v->u.color.b;
|
||||
*a = v->u.color.a;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// 6 april 2015
|
||||
// note: this file should not include ui.h, as the OS-specific ui_*.h files are included between that one and this one in the OS-specific uipriv_*.h* files
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include "controlsigs.h"
|
||||
#include "utf.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// OS-specific init.* or main.* files
|
||||
extern uiInitOptions uiprivOptions;
|
||||
|
||||
// OS-specific alloc.* files
|
||||
extern void *uiprivAlloc(size_t, const char *);
|
||||
#define uiprivNew(T) ((T *) uiprivAlloc(sizeof (T), #T))
|
||||
extern void *uiprivRealloc(void *, size_t, const char *);
|
||||
extern void uiprivFree(void *);
|
||||
|
||||
// debug.c and OS-specific debug.* files
|
||||
// TODO get rid of this mess...
|
||||
// ugh, __func__ was only introduced in MSVC 2015...
|
||||
#ifdef _MSC_VER
|
||||
#define uiprivMacro__func__ __FUNCTION__
|
||||
#else
|
||||
#define uiprivMacro__func__ __func__
|
||||
#endif
|
||||
extern void uiprivRealBug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap);
|
||||
#define uiprivMacro_ns2(s) #s
|
||||
#define uiprivMacro_ns(s) uiprivMacro_ns2(s)
|
||||
extern void uiprivDoImplBug(const char *file, const char *line, const char *func, const char *format, ...);
|
||||
#define uiprivImplBug(...) uiprivDoImplBug(__FILE__, uiprivMacro_ns(__LINE__), uiprivMacro__func__, __VA_ARGS__)
|
||||
extern void uiprivDoUserBug(const char *file, const char *line, const char *func, const char *format, ...);
|
||||
#define uiprivUserBug(...) uiprivDoUserBug(__FILE__, uiprivMacro_ns(__LINE__), uiprivMacro__func__, __VA_ARGS__)
|
||||
|
||||
// shouldquit.c
|
||||
extern int uiprivShouldQuit(void);
|
||||
|
||||
// areaevents.c
|
||||
typedef struct uiprivClickCounter uiprivClickCounter;
|
||||
// you should call Reset() to zero-initialize a new instance
|
||||
// it doesn't matter that all the non-count fields are zero: the first click will fail the curButton test straightaway, so it'll return 1 and set the rest of the structure accordingly
|
||||
struct uiprivClickCounter {
|
||||
int curButton;
|
||||
int rectX0;
|
||||
int rectY0;
|
||||
int rectX1;
|
||||
int rectY1;
|
||||
uintptr_t prevTime;
|
||||
int count;
|
||||
};
|
||||
extern int uiprivClickCounterClick(uiprivClickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist);
|
||||
extern void uiprivClickCounterReset(uiprivClickCounter *);
|
||||
extern int uiprivFromScancode(uintptr_t, uiAreaKeyEvent *);
|
||||
|
||||
// matrix.c
|
||||
extern void uiprivFallbackSkew(uiDrawMatrix *, double, double, double, double);
|
||||
extern void uiprivScaleCenter(double, double, double *, double *);
|
||||
extern void uiprivFallbackTransformSize(uiDrawMatrix *, double *, double *);
|
||||
|
||||
// OS-specific text.* files
|
||||
extern int uiprivStricmp(const char *a, const char *b);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
// 22 may 2016
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
|
||||
void uiUserBugCannotSetParentOnToplevel(const char *type)
|
||||
{
|
||||
uiprivUserBug("You cannot make a %s a child of another uiControl,", type);
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
// utf by pietro gagliardi (andlabs) — https://github.com/andlabs/utf/
|
||||
// 10 november 2016
|
||||
// function names have been altered to avoid namespace collisions in libui static builds (see utf.h)
|
||||
#include "utf.h"
|
||||
|
||||
// this code imitates Go's unicode/utf8 and unicode/utf16
|
||||
// the biggest difference is that a rune is unsigned instead of signed (because Go guarantees what a right shift on a signed number will do, whereas C does not)
|
||||
// it is also an imitation so we can license it under looser terms than the Go source
|
||||
#define badrune 0xFFFD
|
||||
|
||||
// encoded must be at most 4 bytes
|
||||
// TODO clean this code up somehow
|
||||
size_t uiprivUTF8EncodeRune(uint32_t rune, char *encoded)
|
||||
{
|
||||
uint8_t b, c, d, e;
|
||||
size_t n;
|
||||
|
||||
// not in the valid range for Unicode
|
||||
if (rune > 0x10FFFF)
|
||||
rune = badrune;
|
||||
// surrogate runes cannot be encoded
|
||||
if (rune >= 0xD800 && rune < 0xE000)
|
||||
rune = badrune;
|
||||
|
||||
if (rune < 0x80) { // ASCII bytes represent themselves
|
||||
b = (uint8_t) (rune & 0xFF);
|
||||
n = 1;
|
||||
goto done;
|
||||
}
|
||||
if (rune < 0x800) { // two-byte encoding
|
||||
c = (uint8_t) (rune & 0x3F);
|
||||
c |= 0x80;
|
||||
rune >>= 6;
|
||||
b = (uint8_t) (rune & 0x1F);
|
||||
b |= 0xC0;
|
||||
n = 2;
|
||||
goto done;
|
||||
}
|
||||
if (rune < 0x10000) { // three-byte encoding
|
||||
d = (uint8_t) (rune & 0x3F);
|
||||
d |= 0x80;
|
||||
rune >>= 6;
|
||||
c = (uint8_t) (rune & 0x3F);
|
||||
c |= 0x80;
|
||||
rune >>= 6;
|
||||
b = (uint8_t) (rune & 0x0F);
|
||||
b |= 0xE0;
|
||||
n = 3;
|
||||
goto done;
|
||||
}
|
||||
// otherwise use a four-byte encoding
|
||||
e = (uint8_t) (rune & 0x3F);
|
||||
e |= 0x80;
|
||||
rune >>= 6;
|
||||
d = (uint8_t) (rune & 0x3F);
|
||||
d |= 0x80;
|
||||
rune >>= 6;
|
||||
c = (uint8_t) (rune & 0x3F);
|
||||
c |= 0x80;
|
||||
rune >>= 6;
|
||||
b = (uint8_t) (rune & 0x07);
|
||||
b |= 0xF0;
|
||||
n = 4;
|
||||
|
||||
done:
|
||||
encoded[0] = b;
|
||||
if (n > 1)
|
||||
encoded[1] = c;
|
||||
if (n > 2)
|
||||
encoded[2] = d;
|
||||
if (n > 3)
|
||||
encoded[3] = e;
|
||||
return n;
|
||||
}
|
||||
|
||||
const char *uiprivUTF8DecodeRune(const char *s, size_t nElem, uint32_t *rune)
|
||||
{
|
||||
uint8_t b, c;
|
||||
uint8_t lowestAllowed, highestAllowed;
|
||||
size_t i, expected;
|
||||
int bad;
|
||||
|
||||
b = (uint8_t) (*s);
|
||||
if (b < 0x80) { // ASCII bytes represent themselves
|
||||
*rune = b;
|
||||
s++;
|
||||
return s;
|
||||
}
|
||||
// 0xC0 and 0xC1 cover 2-byte overlong equivalents
|
||||
// 0xF5 to 0xFD cover values > 0x10FFFF
|
||||
// 0xFE and 0xFF were never defined (always illegal)
|
||||
if (b < 0xC2 || b > 0xF4) { // invalid
|
||||
*rune = badrune;
|
||||
s++;
|
||||
return s;
|
||||
}
|
||||
|
||||
// this determines the range of allowed first continuation bytes
|
||||
lowestAllowed = 0x80;
|
||||
highestAllowed = 0xBF;
|
||||
switch (b) {
|
||||
case 0xE0:
|
||||
// disallow 3-byte overlong equivalents
|
||||
lowestAllowed = 0xA0;
|
||||
break;
|
||||
case 0xED:
|
||||
// disallow surrogate characters
|
||||
highestAllowed = 0x9F;
|
||||
break;
|
||||
case 0xF0:
|
||||
// disallow 4-byte overlong equivalents
|
||||
lowestAllowed = 0x90;
|
||||
break;
|
||||
case 0xF4:
|
||||
// disallow values > 0x10FFFF
|
||||
highestAllowed = 0x8F;
|
||||
break;
|
||||
}
|
||||
|
||||
// and this determines how many continuation bytes are expected
|
||||
expected = 1;
|
||||
if (b >= 0xE0)
|
||||
expected++;
|
||||
if (b >= 0xF0)
|
||||
expected++;
|
||||
if (nElem != 0) { // are there enough bytes?
|
||||
nElem--;
|
||||
if (nElem < expected) { // nope
|
||||
*rune = badrune;
|
||||
s++;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure that everything is correct
|
||||
// if not, **only** consume the initial byte
|
||||
bad = 0;
|
||||
for (i = 0; i < expected; i++) {
|
||||
c = (uint8_t) (s[1 + i]);
|
||||
if (c < lowestAllowed || c > highestAllowed) {
|
||||
bad = 1;
|
||||
break;
|
||||
}
|
||||
// the old lowestAllowed and highestAllowed is only for the first continuation byte
|
||||
lowestAllowed = 0x80;
|
||||
highestAllowed = 0xBF;
|
||||
}
|
||||
if (bad) {
|
||||
*rune = badrune;
|
||||
s++;
|
||||
return s;
|
||||
}
|
||||
|
||||
// now do the topmost bits
|
||||
if (b < 0xE0)
|
||||
*rune = b & 0x1F;
|
||||
else if (b < 0xF0)
|
||||
*rune = b & 0x0F;
|
||||
else
|
||||
*rune = b & 0x07;
|
||||
s++; // we can finally move on
|
||||
|
||||
// now do the continuation bytes
|
||||
for (; expected; expected--) {
|
||||
c = (uint8_t) (*s);
|
||||
s++;
|
||||
c &= 0x3F; // strip continuation bits
|
||||
*rune <<= 6;
|
||||
*rune |= c;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
// encoded must have at most 2 elements
|
||||
size_t uiprivUTF16EncodeRune(uint32_t rune, uint16_t *encoded)
|
||||
{
|
||||
uint16_t low, high;
|
||||
|
||||
// not in the valid range for Unicode
|
||||
if (rune > 0x10FFFF)
|
||||
rune = badrune;
|
||||
// surrogate runes cannot be encoded
|
||||
if (rune >= 0xD800 && rune < 0xE000)
|
||||
rune = badrune;
|
||||
|
||||
if (rune < 0x10000) {
|
||||
encoded[0] = (uint16_t) rune;
|
||||
return 1;
|
||||
}
|
||||
|
||||
rune -= 0x10000;
|
||||
low = (uint16_t) (rune & 0x3FF);
|
||||
rune >>= 10;
|
||||
high = (uint16_t) (rune & 0x3FF);
|
||||
encoded[0] = high | 0xD800;
|
||||
encoded[1] = low | 0xDC00;
|
||||
return 2;
|
||||
}
|
||||
|
||||
// TODO see if this can be cleaned up somehow
|
||||
const uint16_t *uiprivUTF16DecodeRune(const uint16_t *s, size_t nElem, uint32_t *rune)
|
||||
{
|
||||
uint16_t high, low;
|
||||
|
||||
if (*s < 0xD800 || *s >= 0xE000) {
|
||||
// self-representing character
|
||||
*rune = *s;
|
||||
s++;
|
||||
return s;
|
||||
}
|
||||
if (*s >= 0xDC00) {
|
||||
// out-of-order surrogates
|
||||
*rune = badrune;
|
||||
s++;
|
||||
return s;
|
||||
}
|
||||
if (nElem == 1) { // not enough elements
|
||||
*rune = badrune;
|
||||
s++;
|
||||
return s;
|
||||
}
|
||||
high = *s;
|
||||
high &= 0x3FF;
|
||||
if (s[1] < 0xDC00 || s[1] >= 0xE000) {
|
||||
// bad surrogate pair
|
||||
*rune = badrune;
|
||||
s++;
|
||||
return s;
|
||||
}
|
||||
s++;
|
||||
low = *s;
|
||||
s++;
|
||||
low &= 0x3FF;
|
||||
*rune = high;
|
||||
*rune <<= 10;
|
||||
*rune |= low;
|
||||
*rune += 0x10000;
|
||||
return s;
|
||||
}
|
||||
|
||||
// TODO find a way to reduce the code in all of these somehow
|
||||
// TODO find a way to remove u as well
|
||||
size_t uiprivUTF8RuneCount(const char *s, size_t nElem)
|
||||
{
|
||||
size_t len;
|
||||
uint32_t rune;
|
||||
|
||||
if (nElem != 0) {
|
||||
const char *t, *u;
|
||||
|
||||
len = 0;
|
||||
t = s;
|
||||
while (nElem != 0) {
|
||||
u = uiprivUTF8DecodeRune(t, nElem, &rune);
|
||||
len++;
|
||||
nElem -= u - t;
|
||||
t = u;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
len = 0;
|
||||
while (*s) {
|
||||
s = uiprivUTF8DecodeRune(s, nElem, &rune);
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t uiprivUTF8UTF16Count(const char *s, size_t nElem)
|
||||
{
|
||||
size_t len;
|
||||
uint32_t rune;
|
||||
uint16_t encoded[2];
|
||||
|
||||
if (nElem != 0) {
|
||||
const char *t, *u;
|
||||
|
||||
len = 0;
|
||||
t = s;
|
||||
while (nElem != 0) {
|
||||
u = uiprivUTF8DecodeRune(t, nElem, &rune);
|
||||
len += uiprivUTF16EncodeRune(rune, encoded);
|
||||
nElem -= u - t;
|
||||
t = u;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
len = 0;
|
||||
while (*s) {
|
||||
s = uiprivUTF8DecodeRune(s, nElem, &rune);
|
||||
len += uiprivUTF16EncodeRune(rune, encoded);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t uiprivUTF16RuneCount(const uint16_t *s, size_t nElem)
|
||||
{
|
||||
size_t len;
|
||||
uint32_t rune;
|
||||
|
||||
if (nElem != 0) {
|
||||
const uint16_t *t, *u;
|
||||
|
||||
len = 0;
|
||||
t = s;
|
||||
while (nElem != 0) {
|
||||
u = uiprivUTF16DecodeRune(t, nElem, &rune);
|
||||
len++;
|
||||
nElem -= u - t;
|
||||
t = u;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
len = 0;
|
||||
while (*s) {
|
||||
s = uiprivUTF16DecodeRune(s, nElem, &rune);
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t uiprivUTF16UTF8Count(const uint16_t *s, size_t nElem)
|
||||
{
|
||||
size_t len;
|
||||
uint32_t rune;
|
||||
char encoded[4];
|
||||
|
||||
if (nElem != 0) {
|
||||
const uint16_t *t, *u;
|
||||
|
||||
len = 0;
|
||||
t = s;
|
||||
while (nElem != 0) {
|
||||
u = uiprivUTF16DecodeRune(t, nElem, &rune);
|
||||
len += uiprivUTF8EncodeRune(rune, encoded);
|
||||
nElem -= u - t;
|
||||
t = u;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
len = 0;
|
||||
while (*s) {
|
||||
s = uiprivUTF16DecodeRune(s, nElem, &rune);
|
||||
len += uiprivUTF8EncodeRune(rune, encoded);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
// utf by pietro gagliardi (andlabs) — https://github.com/andlabs/utf/
|
||||
// 10 november 2016
|
||||
|
||||
// note the overridden names with uipriv at the beginning; this avoids potential symbol clashes when building libui as a static library
|
||||
// LONGTERM find a way to encode the name overrides directly into the utf library
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// TODO (for utf itself as well) should this go outside the extern "C" block or not
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// if nElem == 0, assume the buffer has no upper limit and is '\0' terminated
|
||||
// otherwise, assume buffer is NOT '\0' terminated but is bounded by nElem *elements*
|
||||
|
||||
extern size_t uiprivUTF8EncodeRune(uint32_t rune, char *encoded);
|
||||
extern const char *uiprivUTF8DecodeRune(const char *s, size_t nElem, uint32_t *rune);
|
||||
extern size_t uiprivUTF16EncodeRune(uint32_t rune, uint16_t *encoded);
|
||||
extern const uint16_t *uiprivUTF16DecodeRune(const uint16_t *s, size_t nElem, uint32_t *rune);
|
||||
|
||||
extern size_t uiprivUTF8RuneCount(const char *s, size_t nElem);
|
||||
extern size_t uiprivUTF8UTF16Count(const char *s, size_t nElem);
|
||||
extern size_t uiprivUTF16RuneCount(const uint16_t *s, size_t nElem);
|
||||
extern size_t uiprivUTF16UTF8Count(const uint16_t *s, size_t nElem);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
// TODO sync this back to upstream (need copyright clearance first)
|
||||
|
||||
// On Windows, wchar_t is equivalent to uint16_t, but C++ requires
|
||||
// wchar_t to be a completely distinct type. These overloads allow
|
||||
// passing wchar_t pointers directly into these functions from C++
|
||||
// on Windows. Otherwise, you'd need to cast to pass a wchar_t
|
||||
// pointer, WCHAR pointer, or equivalent to these functions.
|
||||
//
|
||||
// This does not apply to MSVC because the situation there is
|
||||
// slightly more complicated; see below.
|
||||
#if defined(_WIN32) && !defined(_MSC_VER)
|
||||
|
||||
inline size_t uiprivUTF16EncodeRune(uint32_t rune, wchar_t *encoded)
|
||||
{
|
||||
return uiprivUTF16EncodeRune(rune, reinterpret_cast<uint16_t *>(encoded));
|
||||
}
|
||||
|
||||
inline const wchar_t *uiprivUTF16DecodeRune(const wchar_t *s, size_t nElem, uint32_t *rune)
|
||||
{
|
||||
const uint16_t *ret;
|
||||
|
||||
ret = uiprivUTF16DecodeRune(reinterpret_cast<const uint16_t *>(s), nElem, rune);
|
||||
return reinterpret_cast<const wchar_t *>(ret);
|
||||
}
|
||||
|
||||
inline size_t uiprivUTF16RuneCount(const wchar_t *s, size_t nElem)
|
||||
{
|
||||
return uiprivUTF16RuneCount(reinterpret_cast<const uint16_t *>(s), nElem);
|
||||
}
|
||||
|
||||
inline size_t uiprivUTF16UTF8Count(const wchar_t *s, size_t nElem)
|
||||
{
|
||||
return uiprivUTF16UTF8Count(reinterpret_cast<const uint16_t *>(s), nElem);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// This is the same as the above, except that with MSVC, whether
|
||||
// wchar_t is a keyword or not is controlled by a compiler option!
|
||||
// (At least with gcc, this is not the case; thanks redi in
|
||||
// irc.freenode.net/#gcc.) We use __wchar_t to be independent of
|
||||
// the option; see https://blogs.msdn.microsoft.com/oldnewthing/20161201-00/?p=94836
|
||||
// (ironically posted one day after I initially wrote this code!).
|
||||
// TODO should defined(_WIN32) be used too?
|
||||
// TODO check this under /Wall
|
||||
// TODO are C-style casts enough? or will that fail in /Wall?
|
||||
// TODO same for UniChar/unichar on Mac? if both are unsigned then we have nothing to worry about
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
inline size_t uiprivUTF16EncodeRune(uint32_t rune, __wchar_t *encoded)
|
||||
{
|
||||
return uiprivUTF16EncodeRune(rune, reinterpret_cast<uint16_t *>(encoded));
|
||||
}
|
||||
|
||||
inline const __wchar_t *uiprivUTF16DecodeRune(const __wchar_t *s, size_t nElem, uint32_t *rune)
|
||||
{
|
||||
const uint16_t *ret;
|
||||
|
||||
ret = uiprivUTF16DecodeRune(reinterpret_cast<const uint16_t *>(s), nElem, rune);
|
||||
return reinterpret_cast<const __wchar_t *>(ret);
|
||||
}
|
||||
|
||||
inline size_t uiprivUTF16RuneCount(const __wchar_t *s, size_t nElem)
|
||||
{
|
||||
return uiprivUTF16RuneCount(reinterpret_cast<const uint16_t *>(s), nElem);
|
||||
}
|
||||
|
||||
inline size_t uiprivUTF16UTF8Count(const __wchar_t *s, size_t nElem)
|
||||
{
|
||||
return uiprivUTF16UTF8Count(reinterpret_cast<const uint16_t *>(s), nElem);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Reference in new issue
Block a user