Skip to content
Snippets Groups Projects
g_text.c 92 KiB
Newer Older
Miller Puckette's avatar
Miller Puckette committed
/* Copyright (c) 1997-1999 Miller Puckette.
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution.  */

/* changes by Thomas Musil IEM KUG Graz Austria 2001 */
/* the methods for calling the gui-objects from menu are implemented */
/* all changes are labeled with      iemlib      */

#include <stdlib.h>
#include "m_pd.h"
#include "m_imp.h"
#include "s_stuff.h"
#include "g_canvas.h"
#include <stdio.h>
#include <string.h>
#include <math.h>
Miller Puckette's avatar
Miller Puckette committed
t_class *text_class;
t_class *message_class;
static t_class *gatom_class;
Miller Puckette's avatar
Miller Puckette committed
static void text_vis(t_gobj *z, t_glist *glist, int vis);
static void text_displace(t_gobj *z, t_glist *glist,
    int dx, int dy);
static void text_getrect(t_gobj *z, t_glist *glist,
    int *xp1, int *yp1, int *xp2, int *yp2);
void canvas_howputnew(t_canvas *x, int *connectp, int *xpixp, int *ypixp,
    int *indexp, int *totalp);
Miller Puckette's avatar
Miller Puckette committed

void canvas_startmotion(t_canvas *x);
t_widgetbehavior text_widgetbehavior;

static char *invalid_fill = "$::pd_colors(dash_fill)";
extern void canvas_displaceselection(t_canvas *x, int dx, int dy);
extern void canvas_apply_setundo(t_canvas *x, t_gobj *y);
extern void canvas_setundo(t_canvas *x, t_undofn undofn, void *buf,
    const char *name);
extern void *canvas_undo_set_create(t_canvas *x);
extern void canvas_undo_create(t_canvas *x, void *z, int action);
extern void glob_preset_node_list_check_loc_and_update(void);
extern void glob_preset_node_list_seek_hub(void);
Miller Puckette's avatar
Miller Puckette committed
/* ----------------- the "text" object.  ------------------ */

    /* add a "text" object (comment) to a glist.  While this one goes for any
    glist, the other 3 below are for canvases only.  (why?)  This is called
    without args if invoked from the GUI; otherwise at least x and y
    are provided.  */

void glist_text(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
{
Miller Puckette's avatar
Miller Puckette committed
    t_text *x = (t_text *)pd_new(text_class);
    t_atom at;
    x->te_width = 0;                            /* don't know it yet. */
    x->te_type = T_TEXT;
Miller Puckette's avatar
Miller Puckette committed
    x->te_binbuf = binbuf_new();
    if (argc > 1)
    {
        x->te_xpix = atom_getfloatarg(0, argc, argv);
        x->te_ypix = atom_getfloatarg(1, argc, argv);
            binbuf_restore(x->te_binbuf, argc-2, argv+2);
        }
Miller Puckette's avatar
Miller Puckette committed
        else
        {
            SETSYMBOL(&at, gensym("comment"));
            binbuf_restore(x->te_binbuf, 1, &at);
        }
        glist_add(gl, &x->te_g);
    }
    else
    {
        //int xpix, ypix;
        int connectme, xpix, ypix, indx, nobj;
        canvas_howputnew(gl, &connectme, &xpix, &ypix, &indx, &nobj);
Miller Puckette's avatar
Miller Puckette committed
        pd_vmess((t_pd *)glist_getcanvas(gl), gensym("editmode"), "i", 1);
        SETSYMBOL(&at, gensym("comment"));
        glist_noselect(gl);
        //glist_getnextxy(gl, &xpix, &ypix);
        x->te_xpix = xpix;
        x->te_ypix = ypix;
Miller Puckette's avatar
Miller Puckette committed
        binbuf_restore(x->te_binbuf, 1, &at);
        glist_add(gl, &x->te_g);
        glist_noselect(gl);
        glist_select(gl, &x->te_g);
            /* it would be nice to "activate" here, but then the second,
            "put-me-down" click changes the text selection, which is quite
            irritating, so I took this back out.  It's OK in messages
            and objects though since there's no text in them at menu
            creation. */
            /* gobj_activate(&x->te_g, gl, 1); */
        if (!we_are_undoing)
            canvas_undo_add(glist_getcanvas(gl), 9, "create",
                (void *)canvas_undo_set_create(glist_getcanvas(gl)));
            canvas_displaceselection(glist_getcanvas(gl), -8, -8);
            canvas_startmotion(glist_getcanvas(gl));
        }
    glob_preset_node_list_seek_hub();
    glob_preset_node_list_check_loc_and_update();
Miller Puckette's avatar
Miller Puckette committed
}

/* ----------------- the "object" object.  ------------------ */

extern t_pd *newest;
extern void glist_scalar(t_glist *canvas, t_symbol *s, int argc, t_atom *argv);
Miller Puckette's avatar
Miller Puckette committed
void canvas_getargs(int *argcp, t_atom **argvp);

static void canvas_objtext(t_glist *gl, int xpix, int ypix,
    int width, int selected,
Miller Puckette's avatar
Miller Puckette committed
    t_binbuf *b)
{
    //fprintf(stderr,"canvas_objtext\n");
Miller Puckette's avatar
Miller Puckette committed
    t_text *x;
    int argc;
    t_atom *argv;
    // for hiding arguments
    t_atom *vec;
    int len, i, hidden;
    t_binbuf *hide;
Miller Puckette's avatar
Miller Puckette committed
    newest = 0;
    canvas_setcurrent((t_canvas *)gl);
    canvas_getargs(&argc, &argv);
    binbuf_eval(b, &pd_objectmaker, argc, argv);
    if (binbuf_getnatom(b))
    {
        if (!newest)
        {
            /* let's see if there's a scalar by this name... */
            t_atom *scalar_at = binbuf_getvec(b);
            if (scalar_at->a_type == A_SYMBOL)
            {
                t_symbol *templatesym = 
                    canvas_makebindsym(atom_getsymbol(scalar_at));
                t_template *tmpl = template_findbyname(templatesym);
                if (template_findbyname(templatesym) &&
                    template_cancreate(tmpl) &&
                    template_hasxy(tmpl))
                    //post("Hmm, found a scalar from struct %s... ",
                    //    templatesym->s_name);
                    t_binbuf *scalarbuf = binbuf_new();
                    t_atom coords_at[2];
                    SETFLOAT(coords_at, (t_float)xpix);
                    SETFLOAT(coords_at+1, (t_float)ypix);
                    binbuf_add(scalarbuf, 1, scalar_at);
                    binbuf_add(scalarbuf, 2, coords_at);
                    binbuf_add(scalarbuf, binbuf_getnatom(b)-1, scalar_at+1);
                    t_atom *scalar_create_at = binbuf_getvec(scalarbuf);
                    glist_scalar(gl, gensym("scalar_from_canvas_objtext"),
                        binbuf_getnatom(b)+2, scalar_create_at);
Miller Puckette's avatar
Miller Puckette committed
            binbuf_print(b);
            post("... couldn't create");
            x = 0;
        }
        else if (!(x = pd_checkobject(newest)))
        {
            binbuf_print(b);
            post("... didn't return a patchable object");
        }
    }
    else x = 0;
    if (!x)
    {
        /* LATER make the color reflect this */
        x = (t_text *)pd_new(text_class);
        //fprintf(stderr,"creating blank object %lx\n", x);
    }
    /* special case: an object, like preset_hub, hides its arguments
       beyond the first n, so we modify its binbuf here */
    vec = binbuf_getvec(b);
    len = binbuf_getnatom(b);
    hidden = 0;
    for (i = 0; i < len; i++)
    {
        if (!strcmp("%hidden%", atom_getsymbol(&vec[i])->s_name))
        {
            //fprintf(stderr,"found hidden %d %s\n",
            //    i, atom_getsymbol(&vec[i])->s_name);
            hidden = i;
            break;
        }
    }
    if (hidden)
    {
        hide = binbuf_new();
        binbuf_add(hide, hidden, vec);
        binbuf_free(b);
        b = hide;
    }
    /* done special case */
Miller Puckette's avatar
Miller Puckette committed
    x->te_binbuf = b;
Miller Puckette's avatar
Miller Puckette committed
    x->te_xpix = xpix;
    x->te_ypix = ypix;
Miller Puckette's avatar
Miller Puckette committed
    x->te_type = T_OBJECT;
    /* let's see if iemgui objects did not already set the value to 1,
       otherwise set it explicitly to 0 */
    if (x->te_iemgui != 1)
        x->te_iemgui = 0;
Miller Puckette's avatar
Miller Puckette committed
    glist_add(gl, &x->te_g);
Miller Puckette's avatar
Miller Puckette committed
    if (selected)
    {
            /* this is called if we've been created from the menu. */
        glist_select(gl, &x->te_g);
        gobj_activate(&x->te_g, gl,
            2); // <-- hack to signal that we're a new object
Miller Puckette's avatar
Miller Puckette committed
    }
    if (pd_class(&x->ob_pd) == vinlet_class)
        canvas_resortinlets(glist_getcanvas(gl));
    if (pd_class(&x->ob_pd) == voutlet_class)
        canvas_resortoutlets(glist_getcanvas(gl));
    canvas_unsetcurrent((t_canvas *)gl);

    glob_preset_node_list_seek_hub();
    glob_preset_node_list_check_loc_and_update();

    // here we recreate data buffer inside previously created undo snapshot
    //canvas_undo_create(glist_getcanvas(gl),
    //    glist_getcanvas(gl)->u_last->data, UNDO_FREE);
    //glist_getcanvas(gl)->u_last->data =
    //    canvas_undo_set_create(glist_getcanvas(gl));
    /*if (binbuf_getnatom(x->te_binbuf) && !we_are_undoing) {
        fprintf(stderr,"canvas_objtext calls create undo\n");
        //glist_select(gl, &x->te_g);
        canvas_undo_add(glist_getcanvas(gl), 9, "create",
            (void *)canvas_undo_set_create(glist_getcanvas(gl)));
    }*/
    if ( glist_isvisible( ((t_canvas *)gl) ) )
    {
extern t_gobj *glist_nth(t_glist *x, int n);
extern int glist_getindex(t_glist *x, t_gobj *y);
Miller Puckette's avatar
Miller Puckette committed
    /* utility routine to figure out where to put a new text box from menu
    and whether to connect to it automatically */
void canvas_howputnew(t_canvas *x, int *connectp, int *xpixp, int *ypixp,
Miller Puckette's avatar
Miller Puckette committed
    int *indexp, int *totalp)
{
Mathieu L Bouchard's avatar
Mathieu L Bouchard committed
    int indx = 0, nobj = 0, n2, x1, x2, y1, y2;
    /*
    int connectme = 0;
    if (x->gl_editor->e_selection &&
        !x->gl_editor->e_selection->sel_next &&
        !sys_noautopatch)
    {
        selected = x->gl_editor->e_selection->sel_what;
        t_object *ob = pd_checkobject(&selected->g_pd);
        connectme = (obj_noutlets(ob) ? 1 : 0);
    }*/
    int connectme = (x->gl_editor->e_selection &&
        !x->gl_editor->e_selection->sel_next);
Miller Puckette's avatar
Miller Puckette committed
    if (connectme)
    {
Miller Puckette's avatar
Miller Puckette committed
        t_gobj *g, *selected = x->gl_editor->e_selection->sel_what;
        t_text *t = (t_text *)selected;
        int recreate = 0;
        // if selected object has not yet been activated we need to recreate it first
        if (pd_class(&t->te_pd) == text_class && t->te_type != T_TEXT)
Miller Puckette's avatar
Miller Puckette committed
        {
            glist_noselect(x); // we do this to explicitly activate object
            glist_select(x, glist_nth(x, glist_getindex(x, 0)-1)); // then reselect it
            selected = x->gl_editor->e_selection->sel_what;
            recreate = 1;
Miller Puckette's avatar
Miller Puckette committed
        }
        for (g = x->gl_list, nobj = 0; g; g = g->g_next, nobj++)
            if (g == selected)
            {
                gobj_getrect(g, x, &x1, &y1, &x2, &y2);
                indx = nobj;
                *xpixp = x1;
                *ypixp = y2 + 5;
            }
Miller Puckette's avatar
Miller Puckette committed
        glist_noselect(x);
            /* search back for 'selected' and if it isn't on the list, 
                plan just to connect from the last item on the list. */
        for (g = x->gl_list, n2 = 0; g; g = g->g_next, n2++)
        {
            if (g == selected)
            {
                indx = n2;
                break;
            }
            else if (!g->g_next)
                indx = nobj-1;
        }
        x->gl_editor->e_onmotion = MA_NONE;
Miller Puckette's avatar
Miller Puckette committed
    }
    else
    {
        glist_getnextxy(x, xpixp, ypixp);
        glist_noselect(x);
Miller Puckette's avatar
Miller Puckette committed
    }
    if (sys_noautopatch)
    {
        if (connectme == 1) connectme = -1;
        else connectme = 0;
    }
Miller Puckette's avatar
Miller Puckette committed
    *connectp = connectme;
    *indexp = indx;
    *totalp = nobj;
}

Miller Puckette's avatar
Miller Puckette committed
    /* object creation routine.  These are called without any arguments if
Miller Puckette's avatar
Miller Puckette committed
    they're invoked from the gui; when pasting or restoring from a file, we
    get at least x and y. */
Miller Puckette's avatar
Miller Puckette committed
void canvas_obj(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
{
    //fprintf(stderr,"canvas_obj\n");
Miller Puckette's avatar
Miller Puckette committed
    if (argc >= 2)
    {
        t_binbuf *b = binbuf_new();
        binbuf_restore(b, argc-2, argv+2);
        canvas_objtext(gl, atom_getintarg(0, argc, argv),
            atom_getintarg(1, argc, argv), 0, 0, b);
Miller Puckette's avatar
Miller Puckette committed
        /* JMZ: don't go into interactive mode in a closed canvas */
    else if (!glist_isvisible(gl))
        post("unable to create stub object in closed canvas!");
    else
    {
            /* interactively create new obect */
Miller Puckette's avatar
Miller Puckette committed
        t_binbuf *b = binbuf_new();
Miller Puckette's avatar
Miller Puckette committed
        int connectme, xpix, ypix, indx, nobj;
        canvas_howputnew(gl, &connectme, &xpix, &ypix, &indx, &nobj);
Miller Puckette's avatar
Miller Puckette committed
        pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1);
        canvas_objtext(gl,
            connectme ? xpix : xpix - 8,
            connectme ? ypix : ypix - 8,
            0, 1, b);
        if (connectme == 1)
        {
            //fprintf(stderr,"canvas_obj calls canvas_connect\n");
            connect_exception = 1;
Miller Puckette's avatar
Miller Puckette committed
            canvas_connect(gl, indx, 0, nobj, 0);
            connect_exception = 0;
        }
        else if (connectme == 0)
        {
            //fprintf(stderr,"canvas_obj calls canvas_startmotion\n");
//            canvas_displaceselection(glist_getcanvas(gl), -8, -8);
            canvas_startmotion(glist_getcanvas(gl));
        }
        //canvas_setundo(glist_getcanvas(gl),
        //    canvas_undo_create, canvas_undo_set_create(gl), "create");
        if (!we_are_undoing)
            canvas_undo_add(glist_getcanvas(gl), 9, "create",
                (void *)canvas_undo_set_create(glist_getcanvas(gl)));
extern void glist_setlastxy(t_glist *gl, int xval, int yval);

/* invoked from tcl/tk: abstraction_name x_offset y_offset */
void canvas_obj_abstraction_from_menu(t_glist *gl, t_symbol *s,
    int argc, t_atom *argv)
    //fprintf(stderr,"canvas_abstraction_from_menu\n");
    int connectme, xpix, ypix, indx, nobj;
    canvas_howputnew(gl, &connectme, &xpix, &ypix, &indx, &nobj);
    pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1);
    if (sys_k12_mode)
        pd_vmess (&gl->gl_pd, gensym("tooltips"), "i", 1);
    canvas_objtext(gl, xpix+atom_getintarg(1, argc, argv),
        ypix+atom_getintarg(2, argc, argv), 0, 1, b);

    // the object is now the last on the glist so we locate it
    // and send it loadbang
    // we know we have at least one object since we just created one
    // so we don't check for y being valid
    y = gl->gl_list;
    while (y->g_next)
        y = y->g_next;
    canvas_loadbang((t_canvas *)y);
        canvas_connect(gl, indx, 0, nobj, 0);
        //glist_setlastxy(glist_getcanvas(gl), xpix, ypix);
        canvas_startmotion(glist_getcanvas(gl));
    }
    canvas_undo_add(glist_getcanvas(gl), 9, "create",
        (void *)canvas_undo_set_create(glist_getcanvas(gl)));
Miller Puckette's avatar
Miller Puckette committed
/* make an object box for an object that's already there. */

/* iemlib */
void canvas_iemguis(t_glist *gl, t_symbol *guiobjname)
{
    //fprintf(stderr,"canvas_iemguis\n");
Miller Puckette's avatar
Miller Puckette committed
    t_atom at;
    t_binbuf *b = binbuf_new();
    if(!strcmp(guiobjname->s_name, "cnv"))
        glist_noselect(gl);
    int connectme, xpix, ypix, indx, nobj;
    canvas_howputnew(gl, &connectme, &xpix, &ypix, &indx, &nobj);

    /* NOT NECESSARY ANY MORE: compensate for the iemgui sliders' xyoffset
       in case of autopatch
    if (connectme)
    {
        if(!strcmp(guiobjname->s_name, "hsl"))
            xpix = xpix + 3;
        else if(!strcmp(guiobjname->s_name, "vsl"))
            ypix = ypix + 2;
        else if(!strcmp(guiobjname->s_name, "vu"))
        {
            xpix = xpix + 1;
            ypix = ypix + 2;
        }
    }*/
    
Miller Puckette's avatar
Miller Puckette committed
    pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1);
    glist_noselect(gl);
    SETSYMBOL(&at, guiobjname);
    binbuf_restore(b, 1, &at);
    canvas_objtext(gl, xpix, ypix, 0, 1, b);
        canvas_connect(gl, indx, 0, nobj, 0);
    //glist_getnextxy(gl, &xpix, &ypix);
    //canvas_objtext(gl, xpix, ypix, 1, b);
        canvas_displaceselection(glist_getcanvas(gl), -8, -8);
        canvas_startmotion(glist_getcanvas(gl));
    }
    //canvas_setundo(glist_getcanvas(gl),
    //    canvas_undo_create, canvas_undo_set_create(gl), "create");
    canvas_undo_add(glist_getcanvas(gl), 9, "create",
        (void *)canvas_undo_set_create(glist_getcanvas(gl)));
Miller Puckette's avatar
Miller Puckette committed
}

void canvas_bng(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
{
    canvas_iemguis(gl, gensym("bng"));
}

void canvas_toggle(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
{
    canvas_iemguis(gl, gensym("tgl"));
}

void canvas_vslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
{
    canvas_iemguis(gl, gensym("vsl"));
}

void canvas_hslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
{
    canvas_iemguis(gl, gensym("hsl"));
}

void canvas_hdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
{
    canvas_iemguis(gl, gensym("hdl"));
}

void canvas_vdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
{
    canvas_iemguis(gl, gensym("vdl"));
}

void canvas_hradio(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
{
    canvas_iemguis(gl, gensym("hradio"));
}

void canvas_vradio(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
{
    canvas_iemguis(gl, gensym("vradio"));
}

void canvas_vumeter(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
{
    canvas_iemguis(gl, gensym("vu"));
}

void canvas_mycnv(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
{
    canvas_iemguis(gl, gensym("cnv"));
}

void canvas_numbox(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
{
    canvas_iemguis(gl, gensym("nbx"));
}

/* iemlib */

void canvas_objfor(t_glist *gl, t_text *x, int argc, t_atom *argv)
{
    x->te_width = 0;                            /* don't know it yet. */
    x->te_type = T_OBJECT;
    x->te_binbuf = binbuf_new();
    x->te_xpix = atom_getfloatarg(0, argc, argv);
    x->te_ypix = atom_getfloatarg(1, argc, argv);
    if (argc > 2) binbuf_restore(x->te_binbuf, argc-2, argv+2);
    glist_add(gl, &x->te_g);
}

/* ---------------------- the "message" text item ------------------------ */

typedef struct _messresponder
{
    t_pd mr_pd;
    t_outlet *mr_outlet;
} t_messresponder;

typedef struct _message
{
    t_text m_text;
    t_messresponder m_messresponder;
    t_glist *m_glist;
    t_clock *m_clock;
} t_message;

static t_class *messresponder_class;
Miller Puckette's avatar
Miller Puckette committed

static void messresponder_bang(t_messresponder *x)
{
    outlet_bang(x->mr_outlet);
}

static void messresponder_float(t_messresponder *x, t_float f)
{
    outlet_float(x->mr_outlet, f);
}

static void messresponder_symbol(t_messresponder *x, t_symbol *s)
{
    outlet_symbol(x->mr_outlet, s);
}

static void messresponder_blob(t_messresponder *x, t_blob *st)
{ /* MP 20070107 blob type */
    outlet_blob(x->mr_outlet, st);
}

Miller Puckette's avatar
Miller Puckette committed
static void messresponder_list(t_messresponder *x, 
    t_symbol *s, int argc, t_atom *argv)
{
    outlet_list(x->mr_outlet, s, argc, argv);
}

static void messresponder_anything(t_messresponder *x,
    t_symbol *s, int argc, t_atom *argv)
{
    outlet_anything(x->mr_outlet, s, argc, argv);
}

static void message_bang(t_message *x)
{
    binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, 0, 0);
}

static void message_float(t_message *x, t_float f)
{
    t_atom at;
    SETFLOAT(&at, f);
    binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, 1, &at);
}

static void message_symbol(t_message *x, t_symbol *s)
{
    t_atom at;
    SETSYMBOL(&at, s);
    binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, 1, &at);
}

static void message_blob(t_message *x, t_blob *st)
{
    t_atom at;
    SETBLOB(&at, st);
    binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, 1, &at);
}

Miller Puckette's avatar
Miller Puckette committed
static void message_list(t_message *x, t_symbol *s, int argc, t_atom *argv)
{
    // TODO: here and elsewhere in the message, do we want $0 to be parsed
    // into canvas instance? Makes sense since there is no such argument,
    // but will this break anything?
    //canvas_setcurrent(x->m_glist);
Miller Puckette's avatar
Miller Puckette committed
    binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, argc, argv);
Miller Puckette's avatar
Miller Puckette committed
}

static void message_set(t_message *x, t_symbol *s, int argc, t_atom *argv)
{
    binbuf_clear(x->m_text.te_binbuf);
    binbuf_add(x->m_text.te_binbuf, argc, argv);
    glist_retext(x->m_glist, &x->m_text);
    if (glist_isvisible(glist_getcanvas(x->m_glist)))
Miller Puckette's avatar
Miller Puckette committed
}

static void message_add2(t_message *x, t_symbol *s, int argc, t_atom *argv)
{
    binbuf_add(x->m_text.te_binbuf, argc, argv);
    glist_retext(x->m_glist, &x->m_text);
    if (glist_isvisible(glist_getcanvas(x->m_glist)))
Miller Puckette's avatar
Miller Puckette committed
}

static void message_add(t_message *x, t_symbol *s, int argc, t_atom *argv)
{
    binbuf_add(x->m_text.te_binbuf, argc, argv);
    binbuf_addsemi(x->m_text.te_binbuf);
    glist_retext(x->m_glist, &x->m_text);
    if (glist_isvisible(glist_getcanvas(x->m_glist)))
Miller Puckette's avatar
Miller Puckette committed
}

static void message_addcomma(t_message *x)
{
    t_atom a;
    SETCOMMA(&a);
    binbuf_add(x->m_text.te_binbuf, 1, &a);
    glist_retext(x->m_glist, &x->m_text);
    if (glist_isvisible(glist_getcanvas(x->m_glist)))
Miller Puckette's avatar
Miller Puckette committed
}

static void message_addsemi(t_message *x)
{
    message_add(x, 0, 0, 0);
    if (glist_isvisible(glist_getcanvas(x->m_glist)))
Miller Puckette's avatar
Miller Puckette committed
}

static void message_adddollar(t_message *x, t_floatarg f)
{
    t_atom a;
    int n = f;
    if (n < 0)
        n = 0;
    SETDOLLAR(&a, n);
    binbuf_add(x->m_text.te_binbuf, 1, &a);
    glist_retext(x->m_glist, &x->m_text);
    if (glist_isvisible(glist_getcanvas(x->m_glist)))
Miller Puckette's avatar
Miller Puckette committed
}

static void message_adddollsym(t_message *x, t_symbol *s)
{
    t_atom a;
    char buf[MAXPDSTRING];
    buf[0] = '$';
    strncpy(buf+1, s->s_name, MAXPDSTRING-2);
    buf[MAXPDSTRING-1] = 0;
    SETDOLLSYM(&a, gensym(buf));
    binbuf_add(x->m_text.te_binbuf, 1, &a);
    glist_retext(x->m_glist, &x->m_text);
    if (glist_isvisible(glist_getcanvas(x->m_glist)))
Miller Puckette's avatar
Miller Puckette committed
}

static void message_click(t_message *x,
    t_floatarg xpos, t_floatarg ypos, t_floatarg shift,
        t_floatarg ctrl, t_floatarg alt)
{
    message_float(x, 0);
    if (glist_isvisible(x->m_glist))
    {
        t_rtext *y = glist_findrtext(x->m_glist, &x->m_text);
user's avatar
user committed
        //sys_vgui(".x%lx.c itemconfigure %sR -strokewidth 5\n", 
        //    glist_getcanvas(x->m_glist), rtext_gettag(y));
        gui_vmess("gui_message_flash", "xsi",
            glist_getcanvas(x->m_glist),
            rtext_gettag(y), 1);
Miller Puckette's avatar
Miller Puckette committed
        clock_delay(x->m_clock, 120);
    }
}

static void message_tick(t_message *x)
{
    if (glist_isvisible(x->m_glist))
    {
        t_rtext *y = glist_findrtext(x->m_glist, &x->m_text);
user's avatar
user committed
        //sys_vgui(".x%lx.c itemconfigure %sR -strokewidth 1\n",
        //    glist_getcanvas(x->m_glist), rtext_gettag(y));
        gui_vmess("gui_message_flash", "xsi",
            glist_getcanvas(x->m_glist),
            rtext_gettag(y), 0);
Miller Puckette's avatar
Miller Puckette committed
    }
}

static void message_free(t_message *x)
{
    clock_free(x->m_clock);
}

t_pd *pd_mess_from_responder(t_pd *x)
{
    if (pd_class(x) == messresponder_class)
    {
        /* do pointer math to try to get to the container message struct */
        void *tmp = (void *)x - sizeof(t_text);
        /* if it looks like a message, it must be a message */
        if(((t_text *)tmp)->te_pd == message_class)
            return ((t_pd *)tmp);
    }
    return x;
}

Miller Puckette's avatar
Miller Puckette committed
void canvas_msg(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
{
    /*fprintf(stderr,"canvas_msg\n");
    int i = 0;
    while(i < argc)
    {
        if (argv[i].a_type == A_FLOAT)
            fprintf(stderr," %f", atom_getfloatarg(i, argc, argv));
        else
            fprintf(stderr," %s", atom_getsymbolarg(i, argc, argv)->s_name);
        i++;
    }
    fprintf(stderr,"\n");*/
Miller Puckette's avatar
Miller Puckette committed
    t_message *x = (t_message *)pd_new(message_class);
    x->m_messresponder.mr_pd = messresponder_class;
    x->m_messresponder.mr_outlet = outlet_new(&x->m_text, &s_float);
    x->m_text.te_width = 0;                             /* don't know it yet. */
    x->m_text.te_type = T_MESSAGE;
Miller Puckette's avatar
Miller Puckette committed
    x->m_text.te_binbuf = binbuf_new();
    x->m_glist = gl;
    x->m_clock = clock_new(x, (t_method)message_tick);
    if (argc > 1)
    {
        x->m_text.te_xpix = atom_getfloatarg(0, argc, argv);
        x->m_text.te_ypix = atom_getfloatarg(1, argc, argv);
        if (argc > 2) binbuf_restore(x->m_text.te_binbuf, argc-2, argv+2);
        glist_add(gl, &x->m_text.te_g);
    }
Miller Puckette's avatar
Miller Puckette committed
    else if (!glist_isvisible(gl))
        post("unable to create stub message in closed canvas!");
Miller Puckette's avatar
Miller Puckette committed
    else
    {
Miller Puckette's avatar
Miller Puckette committed
        int connectme, xpix, ypix, indx, nobj;
        canvas_howputnew(gl, &connectme, &xpix, &ypix, &indx, &nobj);
        
Miller Puckette's avatar
Miller Puckette committed
        pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1);
Miller Puckette's avatar
Miller Puckette committed
        x->m_text.te_xpix = xpix;
        x->m_text.te_ypix = ypix;
Miller Puckette's avatar
Miller Puckette committed
        glist_add(gl, &x->m_text.te_g);
        glist_noselect(gl);
        glist_select(gl, &x->m_text.te_g);
        gobj_activate(&x->m_text.te_g, gl,
            2); // <-- hack to signal we're a new message box
Miller Puckette's avatar
Miller Puckette committed
            canvas_connect(gl, indx, 0, nobj, 0);
            canvas_displaceselection(glist_getcanvas(gl), -8, -8);
            canvas_startmotion(glist_getcanvas(gl));
        }
        //canvas_setundo(glist_getcanvas(gl),
        //    canvas_undo_create, canvas_undo_set_create(gl), "create");
        canvas_undo_add(glist_getcanvas(gl), 9, "create",
            (void *)canvas_undo_set_create(glist_getcanvas(gl)));
Miller Puckette's avatar
Miller Puckette committed
    }
}

/* ---------------------- the "atom" text item ------------------------ */

#define ATOMBUFSIZE 40
#define ATOM_LABELLEFT 0
#define ATOM_LABELRIGHT 1
#define ATOM_LABELUP 2
#define ATOM_LABELDOWN 3

typedef struct _gatom
{
    t_text a_text;
    t_atom a_atom;          /* this holds the value and the type */
    t_glist *a_glist;       /* owning glist */
    t_float a_toggle;       /* value to toggle to */
    t_float a_draghi;       /* high end of drag range */
    t_float a_draglo;       /* low end of drag range */
    t_symbol *a_label;      /* symbol to show as label next to box */
    t_symbol *a_symfrom;    /* "receive" name -- bind ourselvs to this */
    t_symbol *a_symto;      /* "send" name -- send to this on output */
    char a_buf[ATOMBUFSIZE];/* string buffer for typing */
    char a_shift;           /* was shift key down when dragging started? */
    char a_wherelabel;      /* 0-3 for left, right, above, below */
    t_symbol *a_expanded_to; /* a_symto after $0, $1, ...  expansion */
} t_gatom;

    /* prepend "-" as necessary to avoid empty strings, so we can
    use them in Pd messages.  A more complete solution would be
    to introduce some quoting mechanism; but then we'd be much more
    complicated. */
static t_symbol *gatom_escapit(t_symbol *s)
{
    if (!*s->s_name)
        return (gensym("-"));
    else if (*s->s_name == '-')
    {
        char shmo[100];
        shmo[0] = '-';
        strncpy(shmo+1, s->s_name, 99);
        shmo[99] = 0;
        return (gensym(shmo));
    }
    else return (iemgui_dollar2raute(s));
}

    /* undo previous operation: strip leading "-" if found. */
static t_symbol *gatom_unescapit(t_symbol *s)
{
    if (*s->s_name == '-')
        return (gensym(s->s_name+1));
    else return (iemgui_raute2dollar(s));
}

static void gatom_redraw(t_gobj *client, t_glist *glist)
{
    t_gatom *x = (t_gatom *)client;
    glist_retext(x->a_glist, &x->a_text);
}

    /* recolor option offers     0 ignore recolor
                                1 recolor */
static void gatom_retext(t_gatom *x, int senditup, int recolor)
{
    t_canvas *canvas = glist_getcanvas(x->a_glist);
    t_rtext *y = glist_findrtext(x->a_glist, &x->a_text);
    if (recolor)
user's avatar
user committed
    {
        //sys_vgui(".x%lx.c itemconfigure %s -fill %s\n", canvas, 
        //    rtext_gettag(y), "$pd_colors(text)");
    }
Miller Puckette's avatar
Miller Puckette committed
    binbuf_clear(x->a_text.te_binbuf);
    binbuf_add(x->a_text.te_binbuf, 1, &x->a_atom);
    if (senditup && glist_isvisible(x->a_glist))
        sys_queuegui(x, x->a_glist, gatom_redraw);
}

static void gatom_set(t_gatom *x, t_symbol *s, int argc, t_atom *argv)
{
    t_atom oldatom = x->a_atom;
    int changed = 0;
    if (!argc) return;
    if (x->a_atom.a_type == A_FLOAT)
        x->a_atom.a_w.w_float = atom_getfloat(argv),
            changed = (x->a_atom.a_w.w_float != oldatom.a_w.w_float);
    else if (x->a_atom.a_type == A_SYMBOL)
        x->a_atom.a_w.w_symbol = atom_getsymbol(argv),
            changed = (x->a_atom.a_w.w_symbol != oldatom.a_w.w_symbol);
    if (changed)
    {
        if (x->a_atom.a_type == A_FLOAT && x->a_text.te_width == 1)
            gatom_retext(x, 1, 1);
        else
            gatom_retext(x, 1, 0);
    }
Miller Puckette's avatar
Miller Puckette committed
    x->a_buf[0] = 0;
}

static void gatom_bang(t_gatom *x)
{
    if (x->a_atom.a_type == A_FLOAT)
    {
        if (x->a_text.te_outlet)
            outlet_float(x->a_text.te_outlet, x->a_atom.a_w.w_float);
        if (*x->a_expanded_to->s_name && x->a_expanded_to->s_thing)
        {
            if (x->a_symto == x->a_symfrom)
                pd_error(x,
                    "%s: atom with same send/receive name (infinite loop)",
                        x->a_symto->s_name);
            else pd_float(x->a_expanded_to->s_thing, x->a_atom.a_w.w_float);
        }
    }
    else if (x->a_atom.a_type == A_SYMBOL)
    {
        if (x->a_text.te_outlet)
            outlet_symbol(x->a_text.te_outlet, x->a_atom.a_w.w_symbol);
        if (*x->a_symto->s_name && x->a_expanded_to->s_thing)
        {
            if (x->a_symto == x->a_symfrom)
                pd_error(x,
                    "%s: atom with same send/receive name (infinite loop)",
                        x->a_symto->s_name);
            else pd_symbol(x->a_expanded_to->s_thing, x->a_atom.a_w.w_symbol);
        }
    }
}

static void gatom_float(t_gatom *x, t_float f)
{
    t_atom at;
    SETFLOAT(&at, f);
    gatom_set(x, 0, 1, &at);
    gatom_bang(x);
}

static void gatom_clipfloat(t_gatom *x, t_float f)
{
    if (x->a_draglo != 0 || x->a_draghi != 0)
    {
        if (f < x->a_draglo)
            f = x->a_draglo;
        if (f > x->a_draghi)
            f = x->a_draghi;
    }
    gatom_float(x, f);
}

static void gatom_symbol(t_gatom *x, t_symbol *s)
{
    t_atom at;
    SETSYMBOL(&at, s);
    gatom_set(x, 0, 1, &at);
    gatom_bang(x);
}

    /* We need a list method because, since there's both an "inlet" and a
    "nofirstin" flag, the standard list behavior gets confused. */
static void gatom_list(t_gatom *x, t_symbol *s, int argc, t_atom *argv)
{
    if (!argc)
        gatom_bang(x);
    else if (argv->a_type == A_FLOAT)
        gatom_float(x, argv->a_w.w_float);
    else if (argv->a_type == A_SYMBOL)
        gatom_symbol(x, argv->a_w.w_symbol);
    else pd_error(x, "gatom_list: need float or symbol");
}

static void gatom_motion(void *z, t_floatarg dx, t_floatarg dy)
{
    t_gatom *x = (t_gatom *)z;
    if (dy == 0) return;
    if (x->a_atom.a_type == A_FLOAT)
    {
        if (x->a_shift)
        {
            double nval = x->a_atom.a_w.w_float - 0.01 * dy;
            double trunc = 0.01 * (floor(100. * nval + 0.5));
            if (trunc < nval + 0.0001 && trunc > nval - 0.0001) nval = trunc;
            gatom_clipfloat(x, nval);
        }
        else
        {
            double nval = x->a_atom.a_w.w_float - dy;
            double trunc = 0.01 * (floor(100. * nval + 0.5));
            if (trunc < nval + 0.0001 && trunc > nval - 0.0001) nval = trunc;
            trunc = floor(nval + 0.5);
            if (trunc < nval + 0.001 && trunc > nval - 0.001) nval = trunc;
            gatom_clipfloat(x, nval);