Skip to content

Commit

Permalink
Rework the listbox so that it can contain symbols with quoted spaces.
Browse files Browse the repository at this point in the history
Get rid of that awful atom_quote_spaces kludge. Instead, we store the
list in its unquoted form in the symbol and use a special delimiter
ALIST_DELIM instead of spaces to separate the list items.

ALIST_DELIM is ASCII character 127 at present, which seems to be the
perfect choice because it is a non-printable character which doesn't
normally occur in symbol values. ALIST_DELIM is only used internally, in
the external representation (rtext_retext et al) it is replaced by the
space character.

There are some complications with this approach (e.g., we need special
variants of binbuf_text, binbuf_gettext, and atom_string to deal with
these atoms, and mapping click actions on float atoms in a list becomes
more difficult because of the differences in internal and external
representations of list values). But overall this seems to be a much
better approach which gets rid of pretty much all previous limitations
in our "fake" listbox implementation, so that for all practical purposes
our listboxes now look and behave exactly as they do in vanilla Pd.

One might ask why we go to all these lengths to store the listbox atom
as a single atom instead of a list, as it is done in vanilla Pd. The
answer is that Purr Data offers some special editing operations on
gatoms which vanilla Pd doesn't have, and which rest on the assumption
that gatom values are always just single atoms. Implementing listboxes
as list objects would make this much more difficult. The code is already
complicated enough as it is, so I don't want to go there.
  • Loading branch information
agraef committed Sep 18, 2024
1 parent e6a6c0f commit d7847bb
Show file tree
Hide file tree
Showing 4 changed files with 244 additions and 48 deletions.
59 changes: 31 additions & 28 deletions pd/src/g_text.c
Original file line number Diff line number Diff line change
Expand Up @@ -922,20 +922,10 @@ static t_symbol *gatom_unescapit(t_symbol *s)
else return (iemgui_raute2dollar(s));
}

static int quote_spaces(int flag)
{
extern int atom_quote_spaces;
int save_flag = atom_quote_spaces;
atom_quote_spaces = flag;
return save_flag;
}

static void gatom_redraw(t_gobj *client, t_glist *glist)
{
t_gatom *x = (t_gatom *)client;
int save_flag = quote_spaces(x->a_flavor != A_LIST);
glist_retext(x->a_glist, &x->a_text);
quote_spaces(save_flag);
}

/* recolor option offers 0 ignore recolor
Expand Down Expand Up @@ -977,11 +967,10 @@ static void gatom_set(t_gatom *x, t_symbol *s, int argc, t_atom *argv)
int size;
binbuf_add(b, argc, argv);
// our "listbox" is really just a symbol gatom masquerading as a list
// gatom, so internally we store the list as a symbol -- our code can't
// really deal with proper list gatoms right now
int save_flag = quote_spaces(0);
binbuf_gettext(b, &buf, &size);
quote_spaces(save_flag);
// gatom, so internally we store the list as a symbol, with elements
// delimited by ALIST_DELIM (a non-printing character which shouldn't
// normally occur in symbols)
binbuf_gettext_s(b, &buf, &size);
buf = (char *)t_resizebytes(buf, size, size+1);
buf[size] = 0;
x->a_atom.a_w.w_symbol = gensym(buf);
Expand Down Expand Up @@ -1047,7 +1036,7 @@ static void gatom_bang(t_gatom *x)
// proper list again
t_binbuf *b = binbuf_new();
char *buf = x->a_atom.a_w.w_symbol->s_name;
binbuf_text(b, buf, strlen(buf));
binbuf_text_s(b, buf, strlen(buf));
int argc = binbuf_getnatom(b);
t_atom *argv = binbuf_getvec(b);
for (int i = 0; i < argc; i++)
Expand Down Expand Up @@ -1124,18 +1113,32 @@ static char *clicked_number(t_gatom *a, double *d, int *p, int *q)
{
static char token[MAXPDSTRING];
if (a->a_flavor == A_LIST) {
char *buf = a->a_atom.a_w.w_symbol->s_name;
char buf[MAXPDSTRING];
// make sure to get the fully expanded atom representation here, since
// number positions may be different from the symbol itself
atom_string(&a->a_atom, buf, MAXPDSTRING);
int size = strlen(buf), pos = a->a_click_pos;
if (pos <= size) {
// find the token preceding pos
while (pos > 0 && !isspace(buf[pos-1])) pos--;
while (pos > 0 &&
(!isspace(buf[pos-1]) ||
// check for and ignore escaped whitespace
(pos > 1 && buf[pos-2] == '\\'))) pos--;
int endpos = pos;
while (endpos < size && !isspace(buf[endpos])) endpos++;
while (endpos < size &&
(!isspace(buf[endpos]) ||
// check for and ignore escaped whitespace
(endpos > 0 && buf[endpos-1] == '\\'))) endpos++;
if (endpos > pos) {
strncpy(token, buf+pos, endpos-pos);
token[endpos-pos] = 0;
int n;
if (sscanf(token, "%lg%n", d, &n) == 1 && n == endpos-pos) {
// We need to account for backslash quoting in order to
// translate the token position back to the original
// unquoted symbol.
for (char *s = buf; *s; s++)
if (*s == '\\') pos--, endpos--;
*p = pos; *q = endpos;
return token;
}
Expand Down Expand Up @@ -1208,7 +1211,7 @@ static void gatom_motion(void *z, t_floatarg dx, t_floatarg dy)
{
double nval2 = float_increment(nval, dy);
if (nval2 != nval)
change_clicked(x, nval2, p, q, 1);
change_clicked(x, nval2, p, q, 1);
}
}
else if (x->a_atom.a_type == A_FLOAT)
Expand All @@ -1235,6 +1238,7 @@ static void gatom_key(void *z, t_floatarg f)
int len = strlen(x->a_buf);
t_atom at;
char sbuf[ATOMBUFSIZE + 4];
int escaped = 0;
if (len > 0 && x->a_buf[len-1] == '\\') {
// ag 20240916: Get rid of a trailing backslash. We don't keep the
// backslash itself in the buffer after it has served its purpose of
Expand All @@ -1243,6 +1247,7 @@ static void gatom_key(void *z, t_floatarg f)
// needed, when the gatom gets sent to the GUI, which happens in
// rtext_retext().
x->a_buf[--len] = 0;
escaped = 1;
}
if (c == 0)
{
Expand Down Expand Up @@ -1312,6 +1317,12 @@ static void gatom_key(void *z, t_floatarg f)
}
else if (len < (ATOMBUFSIZE-1))
{
if (c == ' ' && x->a_flavor == A_LIST && !escaped) {
// special listbox delimiter, this will be shown as ' '
x->a_buf[len++] = ALIST_DELIM;
x->a_buf[len] = 0;
goto redraw;
}
/* for numbers, only let reasonable characters through */
if ((x->a_atom.a_type == A_SYMBOL) ||
(c >= '0' && c <= '9' || c == '.' || c == '-'
Expand Down Expand Up @@ -1343,9 +1354,7 @@ static void gatom_key(void *z, t_floatarg f)
SETSYMBOL(&at, gensym(sbuf));
binbuf_clear(x->a_text.te_binbuf);
binbuf_add(x->a_text.te_binbuf, 1, &at);
int save_flag = quote_spaces(x->a_flavor != A_LIST);
glist_retext(x->a_glist, &x->a_text);
quote_spaces(save_flag);
}

// Ico's special keyboard bindings for gatoms. This was originally mangled up
Expand Down Expand Up @@ -1446,9 +1455,7 @@ static void gatom_click(t_gatom *x,
SETSYMBOL(&at, gensym(sbuf));
binbuf_clear(x->a_text.te_binbuf);
binbuf_add(x->a_text.te_binbuf, 1, &at);
int save_flag = quote_spaces(x->a_flavor != A_LIST);
glist_retext(x->a_glist, &x->a_text);
quote_spaces(save_flag);
}
glist_grabx(x->a_glist, &x->a_text.te_g, gatom_motion, gatom_key,
(t_glistkeynameafn)gatom_keyhandler, xpos, ypos);
Expand Down Expand Up @@ -1518,9 +1525,7 @@ static void gatom_param(t_gatom *x, t_symbol *sel, int argc, t_atom *argv)
canvas_realizedollar(x->a_glist, x->a_symfrom));
x->a_symto = symto;
x->a_expanded_to = canvas_realizedollar(x->a_glist, x->a_symto);
int save_flag = quote_spaces(x->a_flavor != A_LIST);
gobj_vis(&x->a_text.te_g, x->a_glist, 1);
quote_spaces(save_flag);
gobj_select(&x->a_text.te_g, x->a_glist, 1);
canvas_dirty(x->a_glist, 1);
canvas_getscroll(x->a_glist);
Expand Down Expand Up @@ -1568,9 +1573,7 @@ static void gatom_vis(t_gobj *z, t_glist *glist, int vis)
{
//post("gatom_vis");
t_gatom *x = (t_gatom*)z;
int save_flag = quote_spaces(x->a_flavor != A_LIST);
text_vis(z, glist, vis);
quote_spaces(save_flag);
if (*x->a_label->s_name)
{
if (vis)
Expand Down
65 changes: 51 additions & 14 deletions pd/src/m_atom.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,6 @@ t_symbol *atom_getsymbolarg(int which, int argc, t_atom *argv)
* the '$' only gets quoted if followed by a digit.
*/

// ag 20240916 XXXFIXME: Option to suppress escaping spaces. This flag is
// normally 1 (enable backslash-quoting of space characters), but at present
// we need to disable it for listboxes because these use spaces as a
// delimiter. It's a kludge to have this as a global variable, but turning it
// into a parameter would be even clumsier and require changes in public APIs,
// which we don't want. Thus, this will have to do until we fix our listbox
// implementation to make it work with symbols containing spaces.
int atom_quote_spaces = 1;

void atom_string(const t_atom *a, char *buf, unsigned int bufsize)
{
char tbuf[30];
Expand Down Expand Up @@ -117,8 +108,8 @@ void atom_string(const t_atom *a, char *buf, unsigned int bufsize)
else
{
for (sp = a->a_w.w_symbol->s_name, len = 0, quote = 0; *sp; sp++, len++)
if (*sp == ';' || *sp == ',' || *sp == '\\' ||
(atom_quote_spaces && *sp == ' ') ||
if (*sp == ';' || *sp == ',' || *sp == '\\' || *sp == ' ' ||
(a_sym && *sp == ALIST_DELIM) ||
(a_sym && *sp == '$' && sp[1] >= '0' && sp[1] <= '9'))
quote = 1;
}
Expand All @@ -128,11 +119,12 @@ void atom_string(const t_atom *a, char *buf, unsigned int bufsize)
sp = a->a_w.w_symbol->s_name;
while (bp < ep && *sp)
{
if (*sp == ';' || *sp == ',' || *sp == '\\' ||
(atom_quote_spaces && *sp == ' ') ||
if (*sp == ';' || *sp == ',' || *sp == '\\' || *sp == ' ' ||
(a_sym && *sp == '$' && ((sp[1] >= '0' && sp[1] <= '9') || sp[1]=='@')))
*bp++ = '\\';
*bp++ = *sp++;
// replace the special non-printable listbox delim character
// with a blank
*bp++ = a_sym && *sp == ALIST_DELIM ? ' ' : *sp; sp++;
}
if (*sp) *bp++ = '*';
*bp = 0;
Expand Down Expand Up @@ -164,3 +156,48 @@ void atom_string(const t_atom *a, char *buf, unsigned int bufsize)
bug("atom_string");
}
}

// same as atom_string, but without the auto-quoting
void atom_string_s(const t_atom *a, char *buf, unsigned int bufsize)
{
char tbuf[30];
switch(a->a_type)
{
case A_SEMI: strcpy(buf, ";"); break;
case A_COMMA: strcpy(buf, ","); break;
case A_POINTER:
strcpy(buf, "(pointer)");
break;
case A_FLOAT:
sprintf(tbuf, M_ATOM_FLOAT_SPECIFIER, a->a_w.w_float);
if (strlen(tbuf) < bufsize-1) strcpy(buf, tbuf);
else if (a->a_w.w_float < 0) strcpy(buf, "-");
else strcat(buf, "+");
break;
case A_SYMBOL:
case A_DOLLSYM:
{
char *sp = a->a_w.w_symbol->s_name;
unsigned int len = strlen(sp);
if (len < bufsize-1) strcpy(buf, sp);
else {
strncpy(buf, sp, bufsize - 2);
strcpy(buf + (bufsize - 2), "*");
}
}
break;
case A_DOLLAR:
if(a->a_w.w_index == DOLLARALL)
{
/* JMZ: $@ expansion */
sprintf(buf, "$@");
}
else
{
sprintf(buf, "$%d", a->a_w.w_index);
}
break;
default:
bug("atom_string_s");
}
}
Loading

0 comments on commit d7847bb

Please sign in to comment.