Commit b2a27d76 authored by Miller Puckette's avatar Miller Puckette
Browse files

first try for test8.

Protection against closing dirty abstractions
closing and quitting doesn't query if everyone is clean
(but "pd perf" message turns that protection back on for performances)
ctrl-1, etc, automatically place new object under a selected one and
connect to it
return when editing an object (not a message or comment) deactivates text
parent e04a0760
#N canvas 304 143 967 599 10;
#X obj 370 524 spigot;
#N canvas 304 143 967 599 12;
#X obj 358 544 spigot;
#X msg 442 397 bang;
#X obj 429 488 bonk~;
#X obj 428 508 bonk~;
#X msg 442 214 learn 1;
#X msg 442 274 learn 0;
#X msg 437 456 print;
#X msg 446 451 print;
#X obj 390 437 adc~;
#X msg 614 538 \; pd dsp 1;
#X obj 277 524 spigot;
#X msg 613 558 \; pd dsp 1;
#X obj 276 544 spigot;
#N canvas 366 126 604 404 synth 0;
#X obj 112 24 r bonk-cooked;
#X obj 112 49 unpack;
......@@ -59,14 +59,14 @@
#X connect 21 0 1 0;
#X connect 22 0 6 0;
#X connect 23 0 9 0;
#X restore 846 555 pd synth;
#X floatatom 846 532 0 0 0 0 - - -;
#X msg 846 502 0;
#X restore 845 575 pd synth;
#X floatatom 845 552 0 0 0 0 - - -;
#X msg 845 522 0;
#X msg 442 244 learn 10;
#X msg 442 304 forget;
#X msg 442 334 write templates.txt;
#X msg 442 364 read templates.txt;
#X msg 877 502 90;
#X msg 876 522 90;
#X msg 442 81 thresh 6 50;
#X text 8 56 The Bonk object takes an audio signal input and looks
for "attacks" defined as sharp changes in the spectral envelope of
......@@ -75,13 +75,13 @@ Bonk check the attack against a collection of stored templates to try
to guess which of two or more instruments was hit. Bonk is described
theoretically in the 1998 ICMC proceedings \, reprinted on http://man104nfs.ucsd.edu/~mpuckett.
;
#X text 602 504 click here;
#X text 603 517 to start DSP;
#X text 601 524 click here;
#X text 602 537 to start DSP;
#X text 8 377 In this patch \, after starting DSP \, you can print
out the raw or cooked output using the two "spigots" or listen to a
synthesizer output by raising its volume.;
#X text 747 501 output volume;
#X text 761 519 (0-100);
#X text 746 521 output volume;
#X text 760 539 (0-100);
#X msg 442 150 mask 4 0.7;
#X msg 442 184 debounce 0;
#X text 8 309 Bonk's analysis is carried out on a 256-point window
......@@ -89,7 +89,7 @@ synthesizer output by raising its volume.;
The analysis period can be specified as Bonk's creation argument but
must be a multiple of 64;
#X text 532 185 Minimum time (msec) between attacks;
#X text 532 140 Describes how energy in each frequency band masks later
#X text 526 136 Describes how energy in each frequency band masks later
energy in the band. Here the masking is total for 4 analysis periods
and then drops by 0.7 each period.;
#X text 530 214 Forget all templates and start learning new ones. The
......@@ -101,22 +101,22 @@ to erase and record over a template.;
#X text 595 334 Write templates to a file in text-editable format.
;
#X text 596 364 Read templates from a file.;
#X text 538 453 Print out all settings and templates.;
#X text 514 450 Print out all settings and templates.;
#X msg 442 120 minvel 10;
#X text 538 392 Poll the current spectrum via "raw" outlet \, You can
#X text 514 392 Poll the current spectrum via "raw" outlet \, You can
set a very high threshold if you don't want attacks mixed in.;
#X msg 437 426 debug 0;
#X text 538 426 turn debugging on or off.;
#X obj 326 525 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
#X text 514 426 turn debugging on or off.;
#X obj 325 545 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
1;
#X obj 419 525 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
#X obj 407 545 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
1;
#X obj 370 554 print cooked;
#X obj 277 554 print raw;
#X text 253 490 enable printout:;
#X text 533 121 Minimum "velocity" to output (quieter notes are ignored.)
#X obj 358 574 print cooked;
#X obj 276 574 print raw;
#X text 158 545 enable printout:;
#X text 530 121 Minimum "velocity" to output (quieter notes are ignored.)
;
#X obj 462 513 s bonk-cooked;
#X obj 461 533 s bonk-cooked;
#X text 218 12 BONK~ - an attack detector for small percussion instruments
;
#X text 8 174 Bonk's two outputs are the raw spectrum of the attack
......@@ -127,12 +127,14 @@ an instrument number (counting up from zero) and a "velocity". This
bands \, normalized so that 100 is an attack of amplitude of about
1 The instrument number is significant only if Bonk has a "template
set" in memory.;
#X text 532 52 Set low and high thresholds. Signal growth must exceed
#X text 531 47 Set low and high thresholds. Signal growth must exceed
the high one and then fall to the low one to make an attack. The unit
is the sum of the proportional growth in the 11 filter bands. Proportional
growth is essentially the logarithmic time derivative.;
#X text 238 27 (NOTE: this documentation does not yet describe new
features for Pd 0.42).;
#X msg 446 479 print 1;
#X text 514 481 print out filterbank settings;
#X connect 0 0 40 0;
#X connect 1 0 2 0;
#X connect 2 0 8 0;
......@@ -157,3 +159,4 @@ features for Pd 0.42).;
#X connect 36 0 2 0;
#X connect 38 0 8 1;
#X connect 39 0 0 1;
#X connect 49 0 2 0;
......@@ -95,6 +95,7 @@ static t_class *bonk_class;
#define DEFHALFTONES 6
#define DEFOVERLAP 1
#define DEFFIRSTBIN 1
#define DEFMINBANDWIDTH 1.5
#define DEFHITHRESH 5
#define DEFLOTHRESH 2.5
#define DEFMASKTIME 4
......@@ -122,6 +123,7 @@ typedef struct _filterbank
float b_halftones; /* filter bandwidth in halftones */
float b_overlap; /* overlap; default 1 for 1/2-power pts */
float b_firstbin; /* freq of first filter in bins, default 1 */
float b_minbandwidth; /* minimum bandwidth, default 1.5 */
t_filterkernel *b_vec; /* filter kernels */
int b_refcount; /* number of bonk~ objects using this */
struct _filterbank *b_next; /* next in linked list */
......@@ -206,7 +208,7 @@ typedef struct _bonk
float x_halftones; /* nominal halftones between filters */
float x_overlap;
float x_firstbin;
float x_minbandwidth;
float x_hithresh; /* threshold for total growth to trigger */
float x_lothresh; /* threshold for total growth to re-arm */
float x_minvel; /* minimum velocity we output */
......@@ -275,10 +277,11 @@ char *strcpy(char *s1, const char *s2);
static void bonk_tick(t_bonk *x);
#define HALFWIDTH 0.75 /* half peak bandwidth at half power point in bins */
#define HALFWIDTH 0.75 /* half peak bandwidth at half power point in bins */
#define SLIDE 0.25 /* relative slide between filter subwindows */
static t_filterbank *bonk_newfilterbank(int npoints, int nfilters,
float halftones, float overlap, float firstbin)
float halftones, float overlap, float firstbin, float minbandwidth)
{
int i, j;
float cf, bw, h, relspace;
......@@ -288,6 +291,7 @@ static t_filterbank *bonk_newfilterbank(int npoints, int nfilters,
b->b_halftones = halftones;
b->b_overlap = overlap;
b->b_firstbin = firstbin;
b->b_minbandwidth = minbandwidth;
b->b_refcount = 0;
b->b_next = bonk_filterbanklist;
bonk_filterbanklist = b;
......@@ -296,17 +300,21 @@ static t_filterbank *bonk_newfilterbank(int npoints, int nfilters,
h = exp((log(2.)/12.)*halftones); /* specced interval between filters */
relspace = (h - 1)/(h + 1); /* nominal spacing-per-f for fbank */
if (minbandwidth < 2*HALFWIDTH)
minbandwidth = 2*HALFWIDTH;
if (firstbin < minbandwidth/(2*HALFWIDTH))
firstbin = minbandwidth/(2*HALFWIDTH);
cf = firstbin;
bw = cf * relspace * overlap;
if (bw < HALFWIDTH)
bw = HALFWIDTH;
if (bw < (0.5*minbandwidth))
bw = (0.5*minbandwidth);
for (i = 0; i < nfilters; i++)
{
float *fp, newcf, newbw;
float normalizer = 0;
int filterpoints, skippoints, hoppoints, nhops;
filterpoints = 0.5 + npoints * HALFWIDTH/bw;
filterpoints = npoints * HALFWIDTH/bw;
if (cf > npoints/2)
{
post("bonk~: only using %d filters (ran past Nyquist)", i+1);
......@@ -320,7 +328,7 @@ static t_filterbank *bonk_newfilterbank(int npoints, int nfilters,
else if (filterpoints > npoints)
filterpoints = npoints;
hoppoints = 0.5 + 0.5 * npoints * HALFWIDTH/bw;
hoppoints = SLIDE * npoints * HALFWIDTH/bw;
nhops = 1. + (npoints-filterpoints)/(float)hoppoints;
skippoints = 0.5 * (npoints-filterpoints - (nhops-1) * hoppoints);
......@@ -343,7 +351,7 @@ static t_filterbank *bonk_newfilterbank(int npoints, int nfilters,
fp[1] = window * sin(phase);
normalizer += window;
}
normalizer = 1/(normalizer * nhops);
normalizer = 1/(normalizer * sqrt(nhops));
for (fp = b->b_vec[i].k_stuff, j = 0;
j < filterpoints; j++, fp+= 2)
fp[0] *= normalizer, fp[1] *= normalizer;
......@@ -353,10 +361,10 @@ static t_filterbank *bonk_newfilterbank(int npoints, int nfilters,
#endif
newcf = (cf + bw/overlap)/(1 - relspace);
newbw = newcf * overlap * relspace;
if (newbw < HALFWIDTH)
if (newbw < 0.5*minbandwidth)
{
newbw = HALFWIDTH;
newcf = cf + 2 * HALFWIDTH / overlap;
newbw = 0.5*minbandwidth;
newcf = cf + minbandwidth / overlap;
}
cf = newcf;
bw = newbw;
......@@ -387,7 +395,7 @@ static void bonk_freefilterbank(t_filterbank *b)
static void bonk_donew(t_bonk *x, int npoints, int period, int nsig,
int nfilters, float halftones, float overlap, float firstbin,
float samplerate)
float minbandwidth, float samplerate)
{
int i, j;
t_hist *h;
......@@ -440,7 +448,8 @@ static void bonk_donew(t_bonk *x, int npoints, int period, int nsig,
fb->b_halftones == x->x_halftones &&
fb->b_firstbin == firstbin &&
fb->b_overlap == overlap &&
fb->b_npoints == x->x_npoints)
fb->b_npoints == x->x_npoints &&
fb->b_minbandwidth == minbandwidth)
{
fb->b_refcount++;
x->x_filterbank = fb;
......@@ -448,7 +457,7 @@ static void bonk_donew(t_bonk *x, int npoints, int period, int nsig,
}
if (!x->x_filterbank)
x->x_filterbank = bonk_newfilterbank(npoints, nfilters,
halftones, overlap, firstbin),
halftones, overlap, firstbin, minbandwidth),
x->x_filterbank->b_refcount++;
}
......@@ -1035,7 +1044,7 @@ static void *bonk_new(t_symbol *s, int argc, t_atom *argv)
int nsig = 1, period = DEFPERIOD, npts = DEFNPOINTS,
nfilters = DEFNFILTERS, j;
float halftones = DEFHALFTONES, overlap = DEFOVERLAP,
firstbin = DEFFIRSTBIN;
firstbin = DEFFIRSTBIN, minbandwidth = DEFMINBANDWIDTH;
t_insig *g;
if (argc > 0 && argv[0].a_type == A_FLOAT)
......@@ -1082,6 +1091,11 @@ static void *bonk_new(t_symbol *s, int argc, t_atom *argv)
firstbin = atom_getfloatarg(1, argc, argv);
argc -= 2; argv += 2;
}
else if (!strcmp(firstarg->s_name, "-minbandwidth") && argc > 1)
{
minbandwidth = atom_getfloatarg(1, argc, argv);
argc -= 2; argv += 2;
}
else if (!strcmp(firstarg->s_name, "-spew") && argc > 1)
{
x->x_spew = (atom_getfloatarg(1, argc, argv) != 0);
......@@ -1123,7 +1137,7 @@ static void *bonk_new(t_symbol *s, int argc, t_atom *argv)
}
x->x_cookedout = outlet_new(&x->x_obj, gensym("list"));
bonk_donew(x, npts, period, nsig, nfilters, halftones, overlap,
firstbin, sys_getsr());
firstbin, minbandwidth, sys_getsr());
return (x);
}
......@@ -1182,6 +1196,9 @@ int main()
attr = attr_offset_new("firstbin", sym_float32, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_firstbin));
class_addattr(c, attr);
attr = attr_offset_new("minbandwidth", sym_float32, attrflags, (method)0L, (method)0L, calcoffset(t_bonk, x_minbandwidth));
class_addattr(c, attr);
attr = attr_offset_new("minvel", sym_float32, attrflags, (method)0L, (method)bonk_minvel_set, calcoffset(t_bonk, x_minvel));
class_addattr(c, attr);
......@@ -1250,6 +1267,7 @@ static void *bonk_new(t_symbol *s, long ac, t_atom *av)
x->x_nfilters = DEFNFILTERS;
x->x_halftones = DEFHALFTONES;
x->x_firstbin = DEFFIRSTBIN;
x->x_minbandwidth = DEFMINBANDWIDTH;
x->x_overlap = DEFOVERLAP;
x->x_ninsig = 1;
......@@ -1305,7 +1323,8 @@ static void *bonk_new(t_symbol *s, long ac, t_atom *av)
x->x_clock = clock_new(x, (method)bonk_tick);
bonk_donew(x, x->x_npoints, x->x_period, x->x_ninsig, x->x_nfilters,
x->x_halftones, x->x_overlap, x->x_firstbin, sys_getsr());
x->x_halftones, x->x_overlap, x->x_firstbin, x->x_minbandwidth,
sys_getsr());
}
return (x);
}
......
......@@ -602,7 +602,8 @@ void canvas_reflecttitle(t_canvas *x)
canvas_getdir(x)->s_name);
}
void canvas_dirty(t_canvas *x, t_int n)
/* mark a glist dirty or clean */
void canvas_dirty(t_canvas *x, t_floatarg n)
{
t_canvas *x2 = canvas_getrootfor(x);
if (glist_amreloadingabstractions)
......@@ -686,7 +687,7 @@ void glist_menu_open(t_glist *x)
{
t_glist *gl2 = x->gl_owner;
if (!gl2)
bug("canvas_vis"); /* shouldn't happen but don't get too upset. */
bug("glist_menu_open"); /* shouldn't happen but not dangerous */
else
{
/* erase ourself in parent window */
......@@ -735,7 +736,8 @@ void canvas_free(t_canvas *x)
glist_noselect(x);
while (y = x->gl_list)
glist_delete(x, y);
canvas_vis(x, 0);
if (x == glist_getcanvas(x))
canvas_vis(x, 0);
if (strcmp(x->gl_name->s_name, "Pd"))
pd_unbind(&x->gl_pd, canvas_makebindsym(x->gl_name));
......@@ -1549,6 +1551,8 @@ void g_canvas_setup(void)
gensym("menu-open"), A_NULL);
class_addmethod(canvas_class, (t_method)canvas_map,
gensym("map"), A_FLOAT, A_NULL);
class_addmethod(canvas_class, (t_method)canvas_dirty,
gensym("dirty"), A_FLOAT, A_NULL);
class_setpropertiesfn(canvas_class, (t_propertiesfn)canvas_properties);
/* ---------------------- list handling ------------------------ */
......
......@@ -467,7 +467,7 @@ EXTERN void canvas_setcurrent(t_canvas *x);
EXTERN void canvas_unsetcurrent(t_canvas *x);
EXTERN t_symbol *canvas_realizedollar(t_canvas *x, t_symbol *s);
EXTERN t_canvas *canvas_getrootfor(t_canvas *x);
EXTERN void canvas_dirty(t_canvas *x, t_int n);
EXTERN void canvas_dirty(t_canvas *x, t_floatarg n);
EXTERN int canvas_getfont(t_canvas *x);
typedef int (*t_canvasapply)(t_canvas *x, t_int x1, t_int x2, t_int x3);
......
......@@ -27,6 +27,7 @@ static void canvas_clearline(t_canvas *x);
static t_binbuf *copy_binbuf;
static char *canvas_textcopybuf;
static int canvas_textcopybufsize;
static t_glist *glist_finddirty(t_glist *x);
/* ---------------- generic widget behavior ------------------------- */
......@@ -166,12 +167,26 @@ void glist_select(t_glist *x, t_gobj *y)
}
}
/* recursively deselect everything in a gobj "g", if it happens to be
a glist, in preparation for deselecting g itself in glist_dselect() */
static void glist_checkanddeselectall(t_glist *gl, t_gobj *g)
{
t_glist *gl2;
t_gobj *g2;
if (pd_class(&g->g_pd) != canvas_class)
return;
gl2 = (t_glist *)g;
for (g2 = gl2->gl_list; g2; g2 = g2->g_next)
glist_checkanddeselectall(gl2, g2);
glist_noselect(gl2);
}
/* call this for selected objects only */
void glist_deselect(t_glist *x, t_gobj *y)
{
int fixdsp = 0;
static int reenter = 0;
if (reenter) return;
/* if (reenter) return; */
reenter = 1;
if (x->gl_editor)
{
......@@ -187,6 +202,7 @@ void glist_deselect(t_glist *x, t_gobj *y)
{
z = fuddy;
canvas_stowconnections(glist_getcanvas(x));
glist_checkanddeselectall(x, y);
}
gobj_activate(y, x, 0);
}
......@@ -1537,7 +1553,7 @@ static void canvas_doregion(t_canvas *x, int xpos, int ypos, int doit)
else hiy = x->gl_editor->e_ywas, loy = ypos;
canvas_selectinrect(x, lox, loy, hix, hiy);
sys_vgui(".x%lx.c delete x\n", x);
x->gl_editor->e_onmotion = 0;
x->gl_editor->e_onmotion = MA_NONE;
}
else sys_vgui(".x%lx.c coords x %d %d %d %d\n",
x, x->gl_editor->e_xwas,
......@@ -1568,8 +1584,25 @@ void canvas_mouseup(t_canvas *x,
/* after motion, if there's only one item selected, activate it */
if (x->gl_editor->e_selection &&
!(x->gl_editor->e_selection->sel_next))
gobj_activate(x->gl_editor->e_selection->sel_what,
x, 1);
{
t_gobj *g = x->gl_editor->e_selection->sel_what;
t_glist *gl2;
/* first though, check we aren't an abstraction with a
dirty sub-patch that would be discarded if we edit this. */
if (pd_class(&g->g_pd) == canvas_class &&
canvas_isabstraction((t_glist *)g) &&
(gl2 = glist_finddirty((t_glist *)g)))
{
vmess(&gl2->gl_pd, gensym("menu-open"), "");
x->gl_editor->e_onmotion = MA_NONE;
sys_vgui(
"pdtk_check {Discard changes to '%s'?} {.x%lx dirty 0;\n} no\n",
canvas_getrootfor(gl2)->gl_name->s_name, gl2);
return;
}
/* OK, activate it */
gobj_activate(x->gl_editor->e_selection->sel_what, x, 1);
}
}
if (x->gl_editor->e_onmotion != MA_NONE)
sys_vgui("pdtk_canvas_getscroll .x%lx.c\n", x);
......@@ -1597,7 +1630,8 @@ static void canvas_displaceselection(t_canvas *x, int dx, int dy)
}
if (resortin) canvas_resortinlets(x);
if (resortout) canvas_resortoutlets(x);
canvas_dirty(x, 1);
if (x->gl_editor->e_selection)
canvas_dirty(x, 1);
}
/* this routine is called whenever a key is pressed or released. "x"
......@@ -1674,6 +1708,10 @@ void canvas_key(t_canvas *x, t_symbol *s, int ac, t_atom *av)
return;
if (x && down)
{
t_object *ob;
/* cancel any dragging action */
if (x->gl_editor->e_onmotion == MA_MOVE)
x->gl_editor->e_onmotion = MA_NONE;
/* if an object has "grabbed" keys just send them on */
if (x->gl_editor->e_grab
&& x->gl_editor->e_keyfn && keynum)
......@@ -1687,15 +1725,34 @@ void canvas_key(t_canvas *x, t_symbol *s, int ac, t_atom *av)
|| !strcmp(gotkeysym->s_name, "Left")
|| !strcmp(gotkeysym->s_name, "Right")))
{
if (!x->gl_editor->e_textdirty)
/* special case - carriage return to object "makes" it */
if (keynum == '\n' && (ob =
pd_checkobject(&x->gl_editor->e_selection->sel_what->g_pd)) &&
ob->te_type == T_OBJECT)
{
canvas_setundo(x, canvas_undo_cut,
canvas_undo_set_cut(x, UCUT_TEXT), "typing");
t_gobj *g;
int nobj, indx =
canvas_getindex(x, x->gl_editor->e_selection->sel_what);
glist_noselect(x);
/* "ob" may have disappeared; just search to the last
object and select it */
for (g = x->gl_list, nobj = 0; g; g = g->g_next, nobj++)
if (nobj == indx)
glist_select(x, g);
}
else
{
/* otherwise send the key to the box's editor */
if (!x->gl_editor->e_textdirty)
{
canvas_setundo(x, canvas_undo_cut,
canvas_undo_set_cut(x, UCUT_TEXT), "typing");
}
rtext_key(x->gl_editor->e_textedfor,
(int)keynum, gotkeysym);
if (x->gl_editor->e_textdirty)
canvas_dirty(x, 1);
}
rtext_key(x->gl_editor->e_textedfor,
(int)keynum, gotkeysym);
if (x->gl_editor->e_textdirty)
canvas_dirty(x, 1);
}
/* check for backspace or clear */
else if (keynum == 8 || keynum == 127)
......@@ -1783,6 +1840,7 @@ void canvas_startmotion(t_canvas *x)
}
/* ----------------------------- window stuff ----------------------- */
extern int sys_perf;
void canvas_print(t_canvas *x, t_symbol *s)
{
......@@ -1815,11 +1873,11 @@ void glob_verifyquit(void *dummy, t_floatarg f)
{
canvas_vis(g2, 1);
sys_vgui(
"pdtk_check {Discard changes to this window??} {.x%lx menuclose 3;\n} no\n",
g2);
"pdtk_check {Discard changes to '%s'?} {.x%lx menuclose 3;\n} no\n",
canvas_getrootfor(g2)->gl_name->s_name, g2);
return;
}
if (f == 0)
if (f == 0 && sys_perf)
sys_vgui("pdtk_check {really quit?} {pd quit;\n} yes\n");
else glob_quit(0);
}
......@@ -1842,18 +1900,19 @@ void canvas_menuclose(t_canvas *x, t_floatarg fforce)
g = glist_finddirty(x);
if (g)
{
canvas_vis(g, 1);
vmess(&g->gl_pd, gensym("menu-open"), "");
sys_vgui(
"pdtk_check {Discard changes to this window??} {.x%lx menuclose 2;\n} no\n",
g);
"pdtk_check {Discard changes to '%s'?} {.x%lx menuclose 2;\n} no\n",
canvas_getrootfor(g)->gl_name->s_name, g);
return;
}
else
else if (sys_perf)
{
sys_vgui(
"pdtk_check {Close this window??} {.x%lx menuclose 1;\n} yes\n",
x);
"pdtk_check {Close '%s'?} {.x%lx menuclose 1;\n} yes\n",
canvas_getrootfor(x)->gl_name->s_name, x);
}
else pd_free(&x->gl_pd);
}
else if (force == 1)
pd_free(&x->gl_pd);
......@@ -1865,10 +1924,10 @@ void canvas_menuclose(t_canvas *x, t_floatarg fforce)
g = glist_finddirty(x);
if (g)
{
canvas_vis(g, 1);
vmess(&g->gl_pd, gensym("menu-open"), "");
sys_vgui(
"pdtk_check {Discard changes to this window??} {.x%lx menuclose 2;\n} no\n",
g);
"pdtk_check {Discard changes to '%s'?} {.x%lx menuclose 2;\n} no\n",
canvas_getrootfor(x)->gl_name->s_name, g);
return;
}
else pd_free(&x->gl_pd);
......@@ -2349,10 +2408,10 @@ void canvas_connect(t_canvas *x, t_floatarg fwhoout, t_floatarg foutno,
as needed */
if (pd_class(&src->g_pd) == text_class && objsrc->te_type == T_OBJECT)
while (outno >= obj_noutlets(objsrc))
outlet_new(objsrc, &s_);
outlet_new(objsrc, 0);
if (pd_class(&sink->g_pd) == text_class && objsink->te_type == T_OBJECT)
while (inno >= obj_ninlets(objsink))
inlet_new(objsink, &objsink->ob_pd, &s_, &s_);
inlet_new(objsink, &objsink->ob_pd, 0, 0);
if (!(oc = obj_connect(objsrc, outno, objsink, inno))) goto bad;
if (glist_isvisible(x))
......
......@@ -131,9 +131,36 @@ static void canvas_objtext(t_glist *gl, int xpix, int ypix, int selected,
canvas_unsetcurrent((t_canvas *)gl);
}
/* utility routine to figure out where to put a new text box from menu
and whether to connect to it automatically */
static void canvas_howputnew(t_canvas *x, int *connectp, int *xpixp, int *ypixp,
int *indexp, int *totalp)
{
int xpix, ypix, indx = 0, nobj = 0, x1, x2, y1, y2;
int connectme = (x->gl_editor->e_selection &&
!x->gl_editor->e_selection->sel_next);
if (connectme)
{
t_gobj *g;
for (g = x->gl_list, nobj = 0; g; g = g->g_next, nobj++)
if (g == x->gl_editor->e_selection->sel_what)
{
gobj_getrect(g, x, &x1, &y1, &x2, &y2);
indx = nobj;
*xpixp = x1;
*ypixp = y2 + 5;
}
}
else glist_getnextxy(x, xpixp, ypixp);
glist_noselect(x);
*connectp = connectme;
*indexp = indx;
*totalp = nobj;
}
/* 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. */
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)
{
......@@ -145,18 +172,20 @@ void canvas_obj(t_glist *gl, t_symbol *s, int argc, t_atom *argv)
canvas_objtext(gl, atom_getintarg(0, argc, argv),
atom_getintarg(1, argc, argv), 0, b);
}
else if(!glist_isvisible(gl)){
/* JMZ: not a good idea to go into interactive mode in a closed canvas... */
post("unable to create stub object in closed canvas!");
return;
} else {
/* 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 */
t_binbuf *b = binbuf_new();
int xpix, ypix;
int connectme, xpix, ypix, indx, nobj;
canvas_howputnew(gl, &connectme, &xpix, &ypix, &indx, &nobj);
pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1);