/* Copyright (c) 1997 The Regents of the University of California.
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution.  */

#include <stdlib.h>
#include "m_imp.h"
#include "t_tk.h"
#include "g_canvas.h"
#include <stdio.h>
#include <string.h>
#include <math.h>

static t_class *text_class;
static t_class *message_class;
static t_class *gatom_class;

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


    /* add a "text" object 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;
    x->te_binbuf = binbuf_new();
    if (argc)
    {
    	x->te_xpos = atom_getintarg(0, argc, argv);
    	x->te_ypos = atom_getintarg(1, argc, argv);
    	if (argc > 2) binbuf_restore(x->te_binbuf, argc-2, argv+2);
    	else
    	{
    	    SETSYMBOL(&at, gensym("comment"));
    	    binbuf_restore(x->te_binbuf, 1, &at);
    	}
    	glist_add(gl, &x->te_g);
    }
    else
    {
	vmess((t_pd *)glist_getcanvas(gl), gensym("editmode"), "i", 1);
    	SETSYMBOL(&at, gensym("comment"));
    	x->te_xpos = x->te_ypos = 50;
    	binbuf_restore(x->te_binbuf, 1, &at);
    	glist_add(gl, &x->te_g);
    	glist_noselect(gl);
    	glist_select(gl, &x->te_g);
    }
}

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

extern t_pd *newest;
void canvas_getargs(int *argcp, t_atom **argvp);

static void canvas_objtext(t_glist *gl, int xpos, int ypos, int selected,
    t_binbuf *b)
{
    t_text *x;
    int argc;
    t_atom *argv;
    newest = 0;
    canvas_setcurrent((t_canvas *)gl);
    canvas_getargs(&argc, &argv);
    binbuf_eval(b, s__N.s_thing, 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 */
    	x = (t_text *)pd_new(text_class);
    }
    x->te_binbuf = b;
    x->te_xpos = xpos;
    x->te_ypos = ypos;
    x->te_width = 0;
    x->te_type = T_OBJECT;
    glist_add(gl, &x->te_g);
    if (selected)
    {
    	glist_select(gl, &x->te_g);
    	gobj_activate(&x->te_g, gl, 1);
    }
    canvas_unsetcurrent((t_canvas *)gl);
}

    /* object creation routine.  These are called without any arguments if
    they're invoked from the
    gui; when pasting or restoring from a file, we get at least x and y. */

void canvas_obj(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
{
    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, b);
    }
    else
    {
    	t_binbuf *b = binbuf_new();
	vmess(&gl->gl_pd, gensym("editmode"), "i", 1);
    	canvas_objtext(gl, 50, 50, 1, b);
    }
}

    /* make an object box for an object that's already there */

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_xpos = atom_getintarg(0, argc, argv);
    x->te_ypos = atom_getintarg(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_message;

static t_class *message_class, *messresponder_class;

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_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_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);
}

static void message_click(t_message *x,
    t_floatarg xpos, t_floatarg ypos, t_floatarg down)
{
    message_float(x, 0);
}

void canvas_msg(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
{
    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;
    x->m_text.te_binbuf = binbuf_new();
    x->m_glist = gl;
    if (argc)
    {
    	x->m_text.te_xpos = atom_getintarg(0, argc, argv);
    	x->m_text.te_ypos = atom_getintarg(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);
    }
    else
    {
	vmess(&gl->gl_pd, gensym("editmode"), "i", 1);
    	x->m_text.te_xpos = x->m_text.te_ypos = 50;
    	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);
    }
}

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

#define ATOMBUFSIZE 40

typedef struct _gatom
{
    t_text a_text;
    t_atom a_atom;
    t_glist *a_glist;
    char a_buf[ATOMBUFSIZE];
    char a_shift;
} t_gatom;

static void gatom_set(t_gatom *x, t_symbol *s, int argc, t_atom *argv)
{
    if (!argc) return;
    else if (x->a_atom.a_type == A_FLOAT)
    	x->a_atom.a_w.w_float = atom_getfloat(argv);
    else if (x->a_atom.a_type == A_SYMBOL)
    	x->a_atom.a_w.w_symbol = atom_getsymbol(argv);
    binbuf_clear(x->a_text.te_binbuf);
    binbuf_add(x->a_text.te_binbuf, 1, &x->a_atom);
    glist_retext(x->a_glist, &x->a_text);
    x->a_buf[0] = 0;
}

static void gatom_bang(t_gatom *x)
{
    if (x->a_atom.a_type == A_FLOAT)
    	outlet_float(x->a_text.te_outlet, x->a_atom.a_w.w_float);
    else if (x->a_atom.a_type == A_SYMBOL)
    	outlet_symbol(x->a_text.te_outlet, 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_symbol(t_gatom *x, t_symbol *s)
{
    t_atom at;
    SETSYMBOL(&at, s);
    gatom_set(x, 0, 1, &at);
    gatom_bang(x);
}

static void gatom_motion(t_gatom *x, t_floatarg dx, t_floatarg dy)
{
    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_float(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_float(x, nval);
    	}
    }
}

static void gatom_key(t_gatom *x, t_floatarg f)
{
    int c = f;
    int l = strlen(x->a_buf);
    t_atom at;
    char sbuf[ATOMBUFSIZE + 4];
    if (c == ' ') return;
    else if (c == '\b')
    {
    	if (l > 0)
    	{
    	    x->a_buf[l-1] = 0;
    	    goto redraw;
    	}
    }
    else if (c == '\n')
    {
    	if (x->a_atom.a_type == A_FLOAT)
    	    gatom_float(x, atof(x->a_buf));
    	else if (x->a_atom.a_type == A_SYMBOL)
    	    gatom_symbol(x, gensym(x->a_buf));
    	else bug("gatom_key");
    }
    else if (l < (ATOMBUFSIZE-1))
    {
    	x->a_buf[l] = c;
    	x->a_buf[l+1] = 0;
    	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 shifted)
{
    x->a_shift = shifted;
    x->a_buf[0] = 0;
    glist_grab(x->a_glist, &x->a_text.te_g, xpos, ypos);
}

void canvas_atom(t_glist *gl, t_atomtype type,
    t_symbol *s, int argc, t_atom *argv)
{
    t_gatom *x = (t_gatom *)pd_new(gatom_class);
    t_atom at;
    x->a_text.te_width = 0;	    	    	   /* don't know it yet. */
    x->a_text.te_type = T_ATOM;
    x->a_text.te_binbuf = binbuf_new();
    x->a_glist = gl;
    x->a_atom.a_type = type;
    if (type == A_FLOAT)
    {
    	x->a_atom.a_w.w_float = 0;
    	outlet_new(&x->a_text, &s_float);
    	SETFLOAT(&at, 0);
    }
    else
    {
    	x->a_atom.a_w.w_symbol = &s_;
    	outlet_new(&x->a_text, &s_symbol);
    	SETSYMBOL(&at, &s_);
    }
    binbuf_add(x->a_text.te_binbuf, 1, &at);
    if (argc)
    {
    	x->a_text.te_xpos = atom_getintarg(0, argc, argv);
    	x->a_text.te_ypos = atom_getintarg(1, argc, argv);
    	glist_add(gl, &x->a_text.te_g);
    }
    else
    {
	vmess(&gl->gl_pd, gensym("editmode"), "i", 1);
    	x->a_text.te_xpos = x->a_text.te_ypos = 50;
    	glist_add(gl, &x->a_text.te_g);
    	glist_noselect(gl);
    	glist_select(gl, &x->a_text.te_g);
    	gobj_activate(&x->a_text.te_g, gl, 1);
    }
}

void canvas_floatatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
{
    canvas_atom(gl, A_FLOAT, s, argc, argv);
}

void canvas_symbolatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
{
    canvas_atom(gl, A_SYMBOL, s, argc, argv);
}

/* -------------------- widget behavior for text objects ------------ */

    /* if we're invisible we don't know our size so we just lie about
    it.  This is called on invisible boxes to establish order of inlets
    and possibly other reasons. */

static void text_getrect(t_gobj *z, t_glist *owner,
    int *x1, int *y1, int *x2, int *y2)
{
    t_text *x = (t_text *)z;
    int width, height;
    if (glist_isvisible(owner))
    {
    	t_rtext *y = glist_findrtext(owner, x);
    	width = rtext_width(y);
    	height = rtext_height(y);
    }
    else width = height = 10;
    *x1 = x->te_xpos;
    *y1 = x->te_ypos;
    *x2 = x->te_xpos + width;
    *y2 = x->te_ypos + height;
}

static void text_displace(t_gobj *z, t_glist *glist,
    int dx, int dy)
{
    t_text *x = (t_text *)z;
    t_rtext *y = glist_findrtext(glist, x);
    x->te_xpos += dx;
    x->te_ypos += dy;
    rtext_displace(y, dx, dy);
    text_drawborder(x, glist, rtext_gettag(y),
	rtext_width(y), rtext_height(y), 0);
    canvas_fixlinesfor(glist_getcanvas(glist), x);
}

static void text_select(t_gobj *z, t_glist *glist, int state)
{
    t_text *x = (t_text *)z;
    t_rtext *y = glist_findrtext(glist, x);
    rtext_select(y, state);
}

static void text_activate(t_gobj *z, t_glist *glist, int state)
{
    t_text *x = (t_text *)z;
    t_rtext *y = glist_findrtext(glist, x);
    if (z->g_pd != gatom_class) rtext_activate(y, state);
}

static void text_delete(t_gobj *z, t_glist *glist)
{
    t_text *x = (t_text *)z;
    canvas_deletelinesfor(glist_getcanvas(glist), x);
}

static void text_vis(t_gobj *z, t_glist *glist, int vis)
{
    t_text *x = (t_text *)z;
    if (vis)
    {
    	t_rtext *y = rtext_new(glist, x, glist->gl_editor->e_rtext);
    	text_drawborder(x, glist, rtext_gettag(y),
	    rtext_width(y), rtext_height(y), 1);
    }
    else
    {
    	t_rtext *y = glist_findrtext(glist, x);
    	text_eraseborder(x, glist, rtext_gettag(y));
    	rtext_free(y);
    }
}

int text_isabstraction(t_text *x);

static void text_save(t_gobj *z, t_binbuf *b)
{
    t_text *x = (t_text *)z;
    if (x->te_type == T_OBJECT)
    {
    	if (zgetfn(&x->te_pd, gensym("saveto")) &&
    	    !text_isabstraction(x))
    	{  
    	    mess1(&x->te_pd, gensym("saveto"), b);
    	    binbuf_addv(b, "ssii", gensym("#X"), gensym("restore"),
    	    	(t_int)x->te_xpos, (t_int)x->te_ypos);
    	}
    	else
    	{
    	    binbuf_addv(b, "ssii", gensym("#X"), gensym("obj"),
    	    	(t_int)x->te_xpos, (t_int)x->te_ypos);
        }
        binbuf_addbinbuf(b, x->te_binbuf);
        binbuf_addv(b, ";");
    }
    else if (x->te_type == T_MESSAGE)
    {
    	binbuf_addv(b, "ssii", gensym("#X"), gensym("msg"),
    	    (t_int)x->te_xpos, (t_int)x->te_ypos);
        binbuf_addbinbuf(b, x->te_binbuf);
        binbuf_addv(b, ";");
    }
    else if (x->te_type == T_ATOM)
    {
    	t_atomtype t = ((t_gatom *)x)->a_atom.a_type;
    	t_symbol *sel = (t == A_SYMBOL ? gensym("symbolatom") :
    	    (t == A_FLOAT ? gensym("floatatom") : gensym("intatom")));
    	binbuf_addv(b, "ssii", gensym("#X"), sel,
    	    (t_int)x->te_xpos, (t_int)x->te_ypos);
        binbuf_addv(b, ";");
    }    	
    else 	
    {
    	binbuf_addv(b, "ssii", gensym("#X"), gensym("text"),
    	    (t_int)x->te_xpos, (t_int)x->te_ypos);
        binbuf_addbinbuf(b, x->te_binbuf);
        binbuf_addv(b, ";");
    }    	
}


t_widgetbehavior text_widgetbehavior =
{
    text_getrect,
    text_displace,
    text_select,
    text_activate,
    text_delete,
    text_vis,
    text_save
};

/* -------------------- the "text" class  ------------ */

void text_drawborder(t_text *x, t_glist *glist,
    char *tag, int width, int height, int firsttime)
{
    t_object *ob;
    if (x->te_type == T_OBJECT)
    {
	if (firsttime)
    	    sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %sR\n",
		glist_getcanvas(glist), x->te_xpos, x->te_ypos,
	    	    x->te_xpos + width, x->te_ypos + height, tag);
	else
    	    sys_vgui(".x%x.c coords %sR %d %d %d %d\n",
		glist_getcanvas(glist), tag, x->te_xpos, x->te_ypos,
	    	    x->te_xpos + width, x->te_ypos + height);
    }
    else if (x->te_type == T_MESSAGE)
    {
	if (firsttime)
    	    sys_vgui(".x%x.c create line\
 %d %d %d %d %d %d %d %d %d %d %d %d %d %d -tags %sR\n",
		glist_getcanvas(glist),
		x->te_xpos, x->te_ypos,
	    	x->te_xpos + width + 4, x->te_ypos,
	    	x->te_xpos + width, x->te_ypos + 4,
	    	x->te_xpos + width, x->te_ypos + height - 4,
	    	x->te_xpos + width + 4, x->te_ypos + height,
	    	x->te_xpos, x->te_ypos + height,
	    	x->te_xpos, x->te_ypos,
	    	    tag);
	else
    	    sys_vgui(".x%x.c coords %sR\
 %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
		glist_getcanvas(glist), tag,
		x->te_xpos, x->te_ypos,
	    	x->te_xpos + width + 4, x->te_ypos,
	    	x->te_xpos + width, x->te_ypos + 4,
	    	x->te_xpos + width, x->te_ypos + height - 4,
	    	x->te_xpos + width + 4, x->te_ypos + height,
	    	x->te_xpos, x->te_ypos + height,
	    	x->te_xpos, x->te_ypos);
    }
    else if (x->te_type == T_ATOM)
    {
	if (firsttime)
    	    sys_vgui(".x%x.c create line\
 %d %d %d %d %d %d %d %d %d %d %d %d -tags %sR\n",
		glist_getcanvas(glist),
		x->te_xpos, x->te_ypos,
	    	x->te_xpos + width, x->te_ypos,
	    	x->te_xpos + width + 4, x->te_ypos + 4,
	    	x->te_xpos + width + 4, x->te_ypos + height,
	    	x->te_xpos, x->te_ypos + height,
	    	x->te_xpos, x->te_ypos,
	    	    tag);
	else
    	    sys_vgui(".x%x.c coords %sR\
 %d %d %d %d %d %d %d %d %d %d %d %d\n",
		glist_getcanvas(glist), tag,
		x->te_xpos, x->te_ypos,
	    	x->te_xpos + width, x->te_ypos,
	    	x->te_xpos + width + 4, x->te_ypos + 4,
	    	x->te_xpos + width + 4, x->te_ypos + height,
	    	x->te_xpos, x->te_ypos + height,
	    	x->te_xpos, x->te_ypos);
    }
    	/* draw inlets/outlets */
    
    if (ob = pd_checkobject(&x->te_pd))
    {
    	int n = obj_noutlets(ob), nplus, i;
    	nplus = (n == 1 ? 1 : n-1);
    	for (i = 0; i < n; i++)
    	{
    	    int onset = x->te_xpos + (width - IOWIDTH) * i / nplus;
    	    if (firsttime)
    	    	sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %so%d\n",
    	    	    glist_getcanvas(glist),
    	    	    onset, x->te_ypos + height - 1,
    	    	    onset + IOWIDTH, x->te_ypos + height,
    	    	    tag, i);
    	    else
    	    	sys_vgui(".x%x.c coords %so%d %d %d %d %d\n",
    	    	    glist_getcanvas(glist), tag, i,
    	    	    onset, x->te_ypos + height - 1,
    	    	    onset + IOWIDTH, x->te_ypos + height);
   	}
    	n = obj_ninlets(ob);
    	nplus = (n == 1 ? 1 : n-1);
    	for (i = 0; i < n; i++)
    	{
    	    int onset = x->te_xpos + (width - IOWIDTH) * i / nplus;
    	    if (firsttime)
    	    	sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %si%d\n",
    	    	    glist_getcanvas(glist),
    	    	    onset, x->te_ypos,
    	    	    onset + IOWIDTH, x->te_ypos + 1,
    	    	    tag, i);
    	    else
    	    	sys_vgui(".x%x.c coords %si%d %d %d %d %d\n",
    	    	    glist_getcanvas(glist), tag, i,
    	    	    onset, x->te_ypos,
    	    	    onset + IOWIDTH, x->te_ypos + 1);
    	     
   	}
    }
    
    x->te_width = width;
}

void text_eraseborder(t_text *x, t_glist *glist, char *tag)
{
    int i, n;
    if (x->te_type == T_TEXT) return;
    sys_vgui(".x%x.c delete %sR\n",
    	glist_getcanvas(glist), tag);
    n = obj_noutlets(x);
    for (i = 0; i < n; i++)
    	sys_vgui(".x%x.c delete %so%d\n",
    	    glist_getcanvas(glist), tag, i);
    n = obj_ninlets(x);
    for (i = 0; i < n; i++)
    	sys_vgui(".x%x.c delete %si%d\n",
    	    glist_getcanvas(glist), tag, i);
}

    /* change text; if T_OBJECT, remake it.  LATER we'll have an undo buffer
    which should be filled in here before making the change. */

void text_setto(t_text *x, t_glist *glist, char *buf, int bufsize)
{
    if (x->te_type == T_OBJECT)
    {
    	t_binbuf *b = binbuf_new();
    	binbuf_text(b, buf, bufsize);
    	canvas_objtext(glist, x->te_xpos, x->te_ypos, 0, b);
    	glist_delete(glist, &x->te_g);
    	canvas_restoreconnections(glist_getcanvas(glist));
    }
    else binbuf_text(x->te_binbuf, buf, bufsize);
}

void g_text_setup()
{
    text_class = class_new(gensym("text"), 0, 0, sizeof(t_text),
    	CLASS_NOINLET | CLASS_PATCHABLE, 0);

    message_class = class_new(gensym("message"), 0, 0, sizeof(t_message),
    	CLASS_PATCHABLE, 0);
    class_addbang(message_class, message_bang);
    class_addfloat(message_class, message_float);
    class_addsymbol(message_class, message_symbol);
    class_addlist(message_class, message_list);
    class_addanything(message_class, message_list);

    class_addmethod(message_class, (t_method)message_click, gensym("click"),
    	A_FLOAT, A_FLOAT, A_FLOAT, 0);
    class_addmethod(message_class, message_set, gensym("set"), A_GIMME, 0);

    messresponder_class = class_new(gensym("messresponder"), 0, 0,
    	sizeof(t_text), CLASS_PD, 0);
    class_addbang(messresponder_class, messresponder_bang);
    class_addfloat(messresponder_class, (t_method) messresponder_float);
    class_addsymbol(messresponder_class, messresponder_symbol);
    class_addlist(messresponder_class, messresponder_list);
    class_addanything(messresponder_class, messresponder_anything);

    gatom_class = class_new(gensym("gatom"), 0, 0, sizeof(t_gatom),
    	CLASS_PATCHABLE, 0);
    class_addbang(gatom_class, gatom_bang);
    class_addfloat(gatom_class, gatom_float);
    class_addmethod(gatom_class, gatom_set, gensym("set"), A_GIMME, 0);
    class_addmethod(gatom_class, (t_method)gatom_click, gensym("click"),
    	A_FLOAT, A_FLOAT, A_FLOAT, 0);
    class_addmethod(gatom_class, (t_method)gatom_motion, gensym("motion"),
    	A_FLOAT, A_FLOAT, 0);
    class_addmethod(gatom_class, (t_method)gatom_key, gensym("key"),
    	A_FLOAT, 0);

}

