2016-12-16 22:31:04 -06:00
// 16 december 2016
2016-12-22 23:31:11 -06:00
# include "../ui.h"
# include "uipriv.h"
2018-03-03 20:27:01 -06:00
# include "attrstr.h"
2016-12-16 22:31:04 -06:00
2016-12-18 13:11:12 -06:00
/*
An attribute list is a doubly linked list of attributes .
2016-12-22 12:05:36 -06:00
Attribute start positions are inclusive and attribute end positions are exclusive ( or in other words , [ start , end ) ) .
2016-12-18 13:11:12 -06:00
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 .
2017-11-04 19:47:09 -05:00
TODO verify that this disallows attributes of length zero
2016-12-18 13:11:12 -06:00
*/
2018-03-03 20:32:18 -06:00
struct attr {
2018-03-03 20:27:01 -06:00
uiAttribute * val ;
2016-12-16 22:31:04 -06:00
size_t start ;
size_t end ;
struct attr * prev ;
struct attr * next ;
} ;
2018-03-03 20:32:18 -06:00
struct uiprivAttrList {
2016-12-16 22:31:04 -06:00
struct attr * first ;
struct attr * last ;
} ;
2016-12-17 11:19:33 -06:00
// if before is NULL, add to the end of the list
2018-03-03 20:27:01 -06:00
static void attrInsertBefore ( uiprivAttrList * alist , struct attr * a , struct attr * before )
2016-12-17 11:19:33 -06:00
{
// 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 ;
}
2016-12-17 22:07:48 -06:00
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 ;
}
2016-12-19 10:02:33 -06:00
// returns the old a->next, for forward iteration
2018-03-03 20:27:01 -06:00
static struct attr * attrUnlink ( uiprivAttrList * alist , struct attr * a )
2016-12-16 22:31:04 -06:00
{
2016-12-18 10:49:54 -06:00
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 ;
2016-12-19 10:02:33 -06:00
return NULL ;
2016-12-18 10:49:54 -06:00
}
// start of list?
if ( p = = NULL ) {
n - > prev = NULL ;
alist - > first = n ;
2016-12-19 10:02:33 -06:00
return n ;
2016-12-18 10:49:54 -06:00
}
// end of list?
if ( n = = NULL ) {
p - > next = NULL ;
alist - > last = p ;
2016-12-19 10:02:33 -06:00
return NULL ;
2016-12-18 10:49:54 -06:00
}
// middle of list
p - > next = n ;
n - > prev = p ;
2016-12-19 10:02:33 -06:00
return n ;
2016-12-18 10:49:54 -06:00
}
2016-12-16 22:31:04 -06:00
2016-12-19 10:02:33 -06:00
// returns the old a->next, for forward iteration
2018-03-03 20:27:01 -06:00
static struct attr * attrDelete ( uiprivAttrList * alist , struct attr * a )
2016-12-18 10:49:54 -06:00
{
2016-12-19 10:02:33 -06:00
struct attr * next ;
next = attrUnlink ( alist , a ) ;
2018-03-03 20:27:01 -06:00
uiprivAttributeRelease ( a - > val ) ;
uiprivFree ( a ) ;
2016-12-19 10:02:33 -06:00
return next ;
2016-12-18 10:49:54 -06:00
}
2016-12-16 22:31:04 -06:00
2016-12-18 10:49:54 -06:00
// attrDropRange() removes attributes without deleting characters.
//
2016-12-19 10:02:33 -06:00
// If the attribute needs no change, then nothing is done.
2016-12-18 10:49:54 -06:00
//
2016-12-19 10:02:33 -06:00
// If the attribute needs to be deleted, it is deleted.
2016-12-18 10:49:54 -06:00
//
2016-12-19 10:02:33 -06:00
// If the attribute only needs to be resized at the end, it is adjusted.
2016-12-18 10:49:54 -06:00
//
2016-12-19 10:02:33 -06:00
// If the attribute only needs to be resized at the start, it is adjusted, unlinked, and returned in tail.
2016-12-18 10:49:54 -06:00
//
2016-12-19 10:02:33 -06:00
// 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.
2016-12-18 10:49:54 -06:00
//
2016-12-19 10:02:33 -06:00
// In all cases, the return value is the next attribute to look at in a forward sequential loop.
2018-03-03 20:27:01 -06:00
static struct attr * attrDropRange ( uiprivAttrList * alist , struct attr * a , size_t start , size_t end , struct attr * * tail )
2016-12-18 10:49:54 -06:00
{
struct attr * b ;
2016-12-17 22:07:48 -06:00
2016-12-18 10:49:54 -06:00
// always pre-initialize tail to NULL
* tail = NULL ;
2016-12-19 10:02:33 -06:00
if ( ! attrRangeIntersect ( a , & start , & end ) )
2016-12-18 10:49:54 -06:00
// out of range; nothing to do
2016-12-19 10:02:33 -06:00
return a - > next ;
2016-12-18 10:49:54 -06:00
// just outright delete the attribute?
2016-12-22 13:22:01 -06:00
// 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 )
2016-12-19 10:02:33 -06:00
return attrDelete ( alist , a ) ;
2016-12-18 10:49:54 -06:00
// only chop off the start or end?
2016-12-22 13:22:01 -06:00
if ( a - > start = = start ) { // chop off the start
// we are dropping the left half, so set a->start and unlink
a - > start = end ;
2016-12-18 10:49:54 -06:00
* tail = a ;
2017-01-17 12:05:40 -06:00
return attrUnlink ( alist , a ) ;
2016-12-18 10:49:54 -06:00
}
2016-12-22 13:22:01 -06:00
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 ;
}
2016-12-18 10:49:54 -06:00
// we'll need to split the attribute into two
2018-03-03 20:27:01 -06:00
b = uiprivNew ( struct attr ) ;
b - > val = uiprivAttributeRetain ( a - > val ) ;
2016-12-18 10:49:54 -06:00
b - > start = end ;
b - > end = a - > end ;
* tail = b ;
a - > end = start ;
2016-12-19 10:02:33 -06:00
return a - > next ;
2016-12-16 22:31:04 -06:00
}
2016-12-17 22:07:48 -06:00
2018-03-03 20:27:01 -06:00
static void attrGrow ( uiprivAttrList * alist , struct attr * a , size_t start , size_t end )
2016-12-18 13:06:37 -06:00
{
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 ;
2016-12-19 10:02:33 -06:00
attrUnlink ( alist , a ) ;
2016-12-18 13:06:37 -06:00
for ( before = alist - > first ; before ! = NULL ; before = before - > next )
if ( before - > start > a - > start )
break ;
attrInsertBefore ( alist , a , before ) ;
}
2016-12-19 12:46:20 -06:00
// returns the right side of the split, which is unlinked, or NULL if no split was done
2018-03-03 20:27:01 -06:00
static struct attr * attrSplitAt ( uiprivAttrList * alist , struct attr * a , size_t at )
2016-12-19 12:46:20 -06:00
{
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 ;
2018-03-03 20:27:01 -06:00
b = uiprivNew ( struct attr ) ;
b - > val = uiprivAttributeRetain ( a - > val ) ;
2016-12-19 12:46:20 -06:00
b - > start = at ;
b - > end = a - > end ;
a - > end = at ;
return b ;
}
2016-12-22 13:22:01 -06:00
// 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
2018-03-03 20:27:01 -06:00
static struct attr * attrDeleteRange ( uiprivAttrList * alist , struct attr * a , size_t start , size_t end )
2016-12-22 13:22:01 -06:00
{
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 ;
}
2018-03-03 20:27:01 -06:00
uiprivAttrList * uiprivNewAttrList ( void )
2017-02-12 13:11:25 -06:00
{
2018-03-03 20:27:01 -06:00
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 ;
2017-02-12 13:11:25 -06:00
}
2018-03-03 20:27:01 -06:00
uiprivFree ( alist ) ;
2017-02-12 13:11:25 -06:00
}
2018-03-03 20:27:01 -06:00
void uiprivAttrListInsertAttribute ( uiprivAttrList * alist , uiAttribute * val , size_t start , size_t end )
2016-12-17 22:07:48 -06:00
{
struct attr * a ;
struct attr * before ;
struct attr * tail = NULL ;
2016-12-18 10:49:54 -06:00
int split = 0 ;
2018-03-03 20:27:01 -06:00
uiAttributeType valtype ;
2016-12-17 22:07:48 -06:00
// 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 ;
2018-03-03 20:27:01 -06:00
valtype = uiAttributeGetType ( val ) ;
2016-12-17 22:07:48 -06:00
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
2016-12-18 10:49:54 -06:00
if ( split )
2016-12-17 22:07:48 -06:00
goto next ;
2018-03-03 20:27:01 -06:00
// should we split this attribute?
if ( uiAttributeGetType ( before - > val ) ! = valtype )
2016-12-17 22:07:48 -06:00
goto next ;
lstart = start ;
lend = end ;
2016-12-22 12:05:36 -06:00
if ( ! attrRangeIntersect ( before , & lstart , & lend ) )
2016-12-17 22:07:48 -06:00
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
2016-12-18 13:11:12 -06:00
// 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?
2018-03-03 20:27:01 -06:00
if ( uiprivAttributeEqual ( before - > val , val ) ) {
2017-01-17 12:05:40 -06:00
attrGrow ( alist , before , start , end ) ;
2016-12-18 10:49:54 -06:00
return ;
2016-12-17 22:07:48 -06:00
}
// okay the values are different; we need to split apart
2017-01-17 12:05:40 -06:00
before = attrDropRange ( alist , before , start , end , & tail ) ;
2016-12-18 10:49:54 -06:00
split = 1 ;
continue ;
2016-12-17 22:07:48 -06:00
next :
before = before - > next ;
}
// if we got here, we know we have to add the attribute before before
2018-03-03 20:27:01 -06:00
a = uiprivNew ( struct attr ) ;
a - > val = uiprivAttributeRetain ( val ) ;
2016-12-17 22:07:48 -06:00
a - > start = start ;
a - > end = end ;
2016-12-18 13:06:37 -06:00
attrInsertBefore ( alist , a , before ) ;
2016-12-17 22:07:48 -06:00
// 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 ;
2016-12-18 13:06:37 -06:00
attrInsertBefore ( alist , tail , before ) ;
2016-12-17 22:07:48 -06:00
}
2016-12-19 10:07:52 -06:00
2018-03-03 20:27:01 -06:00
void uiprivAttrListInsertCharactersUnattributed ( uiprivAttrList * alist , size_t start , size_t count )
2016-12-19 10:07:52 -06:00
{
2016-12-19 12:46:20 -06:00
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 ;
}
2016-12-19 10:07:52 -06:00
}
// The attributes are those of character start - 1.
// If start == 0, the attributes are those of character 0.
2016-12-20 15:16:52 -06:00
/*
This is an obtuse function . Here ' s some diagrams to help .
Given the input string
abcdefghi ( positions : 01234567 8 9 )
and attribute set
red start 0 end 3
bold start 2 end 6
underline start 5 end 8
or visually :
01234567 8 9
rrr - - - - - -
- - bbbb - - -
- - - - - uuu -
If we insert qwe to result in positions 01234567 89 AB C :
2016-12-20 13:24:26 -06:00
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 )
2016-12-20 15:16:52 -06:00
2016-12-20 13:47:11 -06:00
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
2016-12-20 15:16:52 -06:00
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
2016-12-20 13:47:11 -06:00
*/
2016-12-20 15:16:52 -06:00
// TODO does this ensure the list remains sorted?
2018-03-03 20:27:01 -06:00
void uiprivAttrListInsertCharactersExtendingAttributes ( uiprivAttrList * alist , size_t start , size_t count )
2016-12-20 15:16:52 -06:00
{
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 ;
}
2016-12-19 10:07:52 -06:00
}
2016-12-22 12:05:36 -06:00
// TODO replace at point with — replaces with first character's attributes
2018-03-03 20:27:01 -06:00
void uiprivAttrListRemoveAttribute ( uiprivAttrList * alist , uiAttributeType type , size_t start , size_t end )
2016-12-19 10:07:52 -06:00
{
2016-12-22 12:05:36 -06:00
struct attr * a ;
2018-03-03 20:27:01 -06:00
struct attr * tails = NULL ; // see uiprivAttrListInsertCharactersUnattributed() above
2016-12-22 12:05:36 -06:00
struct attr * tailsAt = NULL ;
2017-01-17 12:05:40 -06:00
a = alist - > first ;
2016-12-22 12:05:36 -06:00
while ( a ! = NULL ) {
size_t lstart , lend ;
2017-01-17 12:05:40 -06:00
struct attr * tail ;
2016-12-22 12:05:36 -06:00
// 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 ;
}
2018-03-03 20:27:01 -06:00
if ( uiAttributeGetType ( a - > val ) ! = type )
2016-12-22 12:05:36 -06:00
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 ;
}
2016-12-19 10:07:52 -06:00
}
2016-12-22 13:22:01 -06:00
// TODO merge this with the above
2018-03-03 20:27:01 -06:00
void uiprivAttrListRemoveAttributes ( uiprivAttrList * alist , size_t start , size_t end )
2016-12-19 10:07:52 -06:00
{
2016-12-22 13:22:01 -06:00
struct attr * a ;
2018-03-03 20:27:01 -06:00
struct attr * tails = NULL ; // see uiprivAttrListInsertCharactersUnattributed() above
2016-12-22 13:22:01 -06:00
struct attr * tailsAt = NULL ;
2017-01-17 12:05:40 -06:00
a = alist - > first ;
2016-12-22 13:22:01 -06:00
while ( a ! = NULL ) {
size_t lstart , lend ;
2017-01-17 12:05:40 -06:00
struct attr * tail ;
2016-12-22 13:22:01 -06:00
// 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 ;
}
2016-12-19 10:07:52 -06:00
}
2018-03-03 20:27:01 -06:00
void uiprivAttrListRemoveCharacters ( uiprivAttrList * alist , size_t start , size_t end )
2016-12-19 10:07:52 -06:00
{
2016-12-22 13:22:01 -06:00
struct attr * a ;
a = alist - > first ;
while ( a ! = NULL )
a = attrDeleteRange ( alist , a , start , end ) ;
2016-12-19 10:07:52 -06:00
}
2016-12-22 13:33:30 -06:00
2018-03-03 20:27:01 -06:00
void uiprivAttrListForEach ( uiprivAttrList * alist , uiAttributedString * s , uiAttributedStringForEachAttributeFunc f , void * data )
2016-12-23 13:01:09 -06:00
{
struct attr * a ;
2017-06-06 11:47:07 -05:00
uiForEach ret ;
2016-12-23 13:01:09 -06:00
2017-06-06 11:47:07 -05:00
for ( a = alist - > first ; a ! = NULL ; a = a - > next ) {
2018-03-03 20:27:01 -06:00
ret = ( * f ) ( s , a - > val , a - > start , a - > end , data ) ;
2017-06-06 11:47:07 -05:00
if ( ret = = uiForEachStop )
// TODO for all: break or return?
2016-12-23 13:01:09 -06:00
break ;
2017-06-06 11:47:07 -05:00
}
2016-12-23 13:01:09 -06:00
}