Skip to content
Snippets Groups Projects
g_text.c 71.7 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 "t_tk.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_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_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)
{
    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);
        if (argc > 2) {
            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)));
        if (connectme == 0) canvas_startmotion(glist_getcanvas(gl));
Miller Puckette's avatar
Miller Puckette committed
    }
}

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

extern t_pd *newest;
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;
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)
        {
            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 */
		//fprintf(stderr,"creating blank object\n");
		x = (t_text *)pd_new(text_class);
	/* 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, 1);
    }
    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);
	// 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) ) ) {
		sys_vgui("pdtk_canvas_getscroll .x%lx.c\n", glist_getcanvas(gl));
	}
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)
{
Miller Puckette's avatar
Miller Puckette committed
    int xpix, ypix, 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;
Miller Puckette's avatar
Miller Puckette committed
        for (g = x->gl_list, nobj = 0; g; g = g->g_next, nobj++)
Miller Puckette's avatar
Miller Puckette committed
            if (g == selected)
Miller Puckette's avatar
Miller Puckette committed
        {
            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;
		sys_vgui("pdtk_canvas_getscroll .x%lx.c\n", x);
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)
{
Miller Puckette's avatar
Miller Puckette committed
    t_text *x;
    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, xpix, ypix, 0, 1, b);
			//fprintf(stderr,"canvas_obj calls canvas_connect\n");
Miller Puckette's avatar
Miller Puckette committed
            canvas_connect(gl, indx, 0, nobj, 0);
        else if (connectme == 0) {
			//fprintf(stderr,"canvas_obj calls canvas_startmotion\n");
			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");

    t_binbuf *b = binbuf_new();
	binbuf_restore(b, 2, argv);
    int connectme, xpix, ypix, indx, nobj;
    canvas_howputnew(gl, &connectme, &xpix, &ypix, &indx, &nobj);
    pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1);
#ifdef PDL2ORK
	if (sys_k12_mode)
		pd_vmess (&gl->gl_pd, gensym("tooltips"), "i", 1);
#endif
    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);
	}
    else if (connectme == 0) {
		canvas_startmotion(glist_getcanvas(gl));
	}
	canvas_undo_add(glist_getcanvas(gl), 9, "create",
		(void *)canvas_undo_set_create(glist_getcanvas(gl)));
	glist_setlastxy(glist_getcanvas(gl), xpix, ypix);
}

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();
    //int xpix, ypix;

	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);
    else if (connectme == 0 ) 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;

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)
{
    binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, argc, argv);
}

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);
	sys_vgui("pdtk_canvas_getscroll .x%lx.c\n", (t_int)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);
	sys_vgui("pdtk_canvas_getscroll .x%lx.c\n", (t_int)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);
	sys_vgui("pdtk_canvas_getscroll .x%lx.c\n", (t_int)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);
	sys_vgui("pdtk_canvas_getscroll .x%lx.c\n", (t_int)x->m_glist);
Miller Puckette's avatar
Miller Puckette committed
}

static void message_addsemi(t_message *x)
{
    message_add(x, 0, 0, 0);
	sys_vgui("pdtk_canvas_getscroll .x%lx.c\n", (t_int)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);
	sys_vgui("pdtk_canvas_getscroll .x%lx.c\n", (t_int)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);
	sys_vgui("pdtk_canvas_getscroll .x%lx.c\n", (t_int)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);
        sys_vgui(".x%lx.c itemconfigure %sR -strokewidth 5\n", 
Miller Puckette's avatar
Miller Puckette committed
            glist_getcanvas(x->m_glist), rtext_gettag(y));
        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);
        sys_vgui(".x%lx.c itemconfigure %sR -strokewidth 1\n",
Miller Puckette's avatar
Miller Puckette committed
            glist_getcanvas(x->m_glist), rtext_gettag(y));
    }
}

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

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, 1);
Miller Puckette's avatar
Miller Puckette committed
            canvas_connect(gl, indx, 0, nobj, 0);
        else if (connectme == 0) 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)
		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)
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);
        }
    }
}

static void gatom_key(void *z, t_floatarg f)
{
	//fprintf(stderr,"gatom_key %f\n", f);
Miller Puckette's avatar
Miller Puckette committed
    t_gatom *x = (t_gatom *)z;
    int c = f;
    int len = strlen(x->a_buf);
    t_atom at;
    char sbuf[ATOMBUFSIZE + 4];
    if (c == 0)
    {
        /* we're being notified that no more keys will come for this grab */
        if (x->a_buf[0])
            gatom_retext(x, 1, 1);
		else
			gatom_retext(x, 0, 1);
Miller Puckette's avatar
Miller Puckette committed
        return;
    }
    else if (c == '\b')
    {
        if (len > 0)
        x->a_buf[len-1] = 0;
        goto redraw;
    }
    else if (c == '\n')
    {
        if (x->a_atom.a_type == A_FLOAT)
            x->a_atom.a_w.w_float = atof(x->a_buf);
        else if (x->a_atom.a_type == A_SYMBOL)
            x->a_atom.a_w.w_symbol = gensym(x->a_buf);
        else bug("gatom_key");
        gatom_bang(x);
Miller Puckette's avatar
Miller Puckette committed
        x->a_buf[0] = 0;
    }
    else if (len < (ATOMBUFSIZE-1))
    {
            /* for numbers, only let reasonable characters through */
        if ((x->a_atom.a_type == A_SYMBOL) ||
            (c >= '0' && c <= '9' || c == '.' || c == '-'
                || c == 'e' || c == 'E'))
        {
            /* the wchar could expand to up to 4 bytes, which
             * which might overrun our a_buf;
             * therefore we first expand into a temporary buffer, 
             * and only if the resulting utf8 string fits into a_buf
             * we apply it
             */
            char utf8[UTF8_MAXBYTES];
            int utf8len = u8_wc_toutf8(utf8, c);
            if((len+utf8len) < (ATOMBUFSIZE-1))
            {
                int j=0;
                for(j=0; j<utf8len; j++)
                    x->a_buf[len+j] = utf8[j];
                 
                x->a_buf[len+utf8len] = 0;
            }
Miller Puckette's avatar
Miller Puckette committed
            goto redraw;
        }
    }
    return;
redraw:
        /* LATER figure out how to avoid creating all these symbols! */
    sprintf(sbuf, "%s...", x->a_buf);
    SETSYMBOL(&at, gensym(sbuf));
    binbuf_clear(x->a_text.te_binbuf);
    binbuf_add(x->a_text.te_binbuf, 1, &at);
    glist_retext(x->a_glist, &x->a_text);
}

static void gatom_click(t_gatom *x,
    t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl,
    t_floatarg alt)
{
    if (x->a_text.te_width == 1)
    {
        if (x->a_atom.a_type == A_FLOAT)
            gatom_float(x, (x->a_atom.a_w.w_float == 0));
    }
    else
    {
        if (alt)
        {
            if (x->a_atom.a_type != A_FLOAT) return;
            if (x->a_atom.a_w.w_float != 0)
            {
                x->a_toggle = x->a_atom.a_w.w_float;
                gatom_float(x, 0);
                return;
            }
            else gatom_float(x, x->a_toggle);
        }
        x->a_shift = shift;
        x->a_buf[0] = 0;
        glist_grab(x->a_glist, &x->a_text.te_g, gatom_motion, gatom_key,
            xpos, ypos);
    }
}

EXTERN int glist_getindex(t_glist *x, t_gobj *y);
EXTERN int canvas_apply_restore_original_position(t_canvas *x, int pos);

Miller Puckette's avatar
Miller Puckette committed
    /* message back from dialog window */
static void gatom_param(t_gatom *x, t_symbol *sel, int argc, t_atom *argv)
{
	canvas_apply_setundo(x->a_glist, (t_gobj *)x);

Miller Puckette's avatar
Miller Puckette committed
    t_float width = atom_getfloatarg(0, argc, argv);
    t_float draglo = atom_getfloatarg(1, argc, argv);
    t_float draghi = atom_getfloatarg(2, argc, argv);
    t_symbol *label = gatom_unescapit(atom_getsymbolarg(3, argc, argv));
    t_float wherelabel = atom_getfloatarg(4, argc, argv);
    t_symbol *symfrom = gatom_unescapit(atom_getsymbolarg(5, argc, argv));
    t_symbol *symto = gatom_unescapit(atom_getsymbolarg(6, argc, argv));

    gobj_vis(&x->a_text.te_g, x->a_glist, 0);
    if (!*symfrom->s_name && *x->a_symfrom->s_name)
        inlet_new(&x->a_text, &x->a_text.te_pd, 0, 0);
    else if (*symfrom->s_name && !*x->a_symfrom->s_name && x->a_text.te_inlet)
    {
        canvas_deletelinesforio(x->a_glist, &x->a_text,
            x->a_text.te_inlet, 0);
        inlet_free(x->a_text.te_inlet);
    }
    if (!*symto->s_name && *x->a_symto->s_name)
        outlet_new(&x->a_text, 0);
    else if (*symto->s_name && !*x->a_symto->s_name && x->a_text.te_outlet)
    {
        canvas_deletelinesforio(x->a_glist, &x->a_text,
            0, x->a_text.te_outlet);
        outlet_free(x->a_text.te_outlet);
    }
    if (draglo >= draghi)
        draglo = draghi = 0;
    x->a_draglo = draglo;