pddplink.c 12.2 KB
Newer Older
Ivica Ico Bukvic's avatar
Ivica Ico Bukvic committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/* Copyright (c) 2005 krzYszcz and others.
 * For information on usage and redistribution, and for a DISCLAIMER OF ALL
 * WARRANTIES, see the file, "LICENSE.txt," in this distribution.  */

/* This is a prototype of an active comment.  It might be replaced with
   a new core object type, T_LINK (te_type bitfield would have to be
   extended then). */

#include <stdio.h>
#include <string.h>
#include "m_pd.h"
#include "m_imp.h"  /* FIXME need access to c_externdir... */
#include "g_canvas.h"

typedef struct _pddplink
{
    t_object   x_ob;
    t_glist   *x_glist;
    int        x_isboxed;
    int        x_isgopvisible;
    char      *x_vistext;
    int        x_vissize;
    int        x_vislength;
    int        x_rtextactive;
    t_symbol  *x_dirsym;
    t_symbol  *x_ulink;
    t_atom     x_openargs[2];
    int        x_linktype;
    int        x_ishit;
} t_pddplink;

static t_class *pddplink_class;
static t_class *pddplinkbox_class;

/* Code that might be merged back to g_text.c starts here: */

static void pddplink_getrect(t_gobj *z, t_glist *glist,
			     int *xp1, int *yp1, int *xp2, int *yp2)
{
    t_pddplink *x = (t_pddplink *)z;
    int width, height;
    float x1, y1, x2, y2;
    if (glist->gl_editor && glist->gl_editor->e_rtext)
    {
	if (x->x_rtextactive)
	{
	    t_rtext *y = glist_findrtext(glist, (t_text *)x);
	    width = rtext_width(y);
	    height = rtext_height(y) - 2;
	}
	else
	{
	    int font = glist_getfont(glist);
	    width = x->x_vislength * sys_fontwidth(font) + 2;
	    height = sys_fontheight(font) + 2;
	}
    }
    else width = height = 10;
    x1 = text_xpix((t_text *)x, glist);
    y1 = text_ypix((t_text *)x, glist);
    x2 = x1 + width;
    y2 = y1 + height;
    y1 += 1;
    *xp1 = x1;
    *yp1 = y1;
    *xp2 = x2;
    *yp2 = y2;
}

static void pddplink_displace(t_gobj *z, t_glist *glist, int dx, int dy)
{
    t_text *t = (t_text *)z;
    t->te_xpix += dx;
    t->te_ypix += dy;
    if (glist_isvisible(glist))
    {
        t_rtext *y = glist_findrtext(glist, t);
        rtext_displace(y, dx, dy);
    }
}

82
83
84
85
86
87
88
89
90
91
92
93
static void pddplink_displace_withtag(t_gobj *z, t_glist *glist, int dx, int dy)
{
    t_text *t = (t_text *)z;
    t->te_xpix += dx;
    t->te_ypix += dy;
    /*if (glist_isvisible(glist))
    {
        t_rtext *y = glist_findrtext(glist, t);
        rtext_displace(y, dx, dy);
    }*/
}

Ivica Ico Bukvic's avatar
Ivica Ico Bukvic committed
94
95
96
97
98
99
100
static void pddplink_select(t_gobj *z, t_glist *glist, int state)
{
    t_pddplink *x = (t_pddplink *)z;
    t_rtext *y = glist_findrtext(glist, (t_text *)x);
    rtext_select(y, state);
    if (glist_isvisible(glist) && glist->gl_havewindow)
    {
101
102
103
104
105
106
107
108
109
        if (state) {
            gui_vmess("gui_gobj_select", "xs",
                glist, rtext_gettag(y));
        }
        else
        {
            gui_vmess("gui_gobj_deselect", "xs",
                glist, rtext_gettag(y));
        }
Ivica Ico Bukvic's avatar
Ivica Ico Bukvic committed
110
111
112
113
114
115
116
117
118
119
120
121
    }
}

static void pddplink_vis(t_gobj *z, t_glist *glist, int vis)
{
    t_pddplink *x = (t_pddplink *)z;
    t_rtext *y;
    if (vis)
    {
        if ((glist->gl_havewindow || x->x_isgopvisible)
            && (y = glist_findrtext(glist, (t_text *)x)))
        {
122
            gui_vmess("gui_gobj_new", "xssiii",
123
124
                glist_getcanvas(glist),
                rtext_gettag(y),
125
                "pd_link",
126
127
128
                text_xpix(&x->x_ob, glist_getcanvas(glist)),
                text_ypix(&x->x_ob, glist_getcanvas(glist)),
                glist_istoplevel(glist));
129
130
131
132
133
134
135
136
137
            /* This is a bit screwy... first we do rtext_draw
               which sends the wrong box text (at least when we're
               not in "-box" mode). Then we call the GUI with the
               correct text to overwrite that.

               Probably there's a way to simplify this, but I'm so
               afraid of side-effects in Pd that I'm just keeping this
               code essentially as it was before the port to the new
               GUI. */
138
139
140
141
142
            rtext_draw(y);
            gui_vmess("gui_text_set", "xss",
                glist_getcanvas(glist),
                rtext_gettag(y),
                x->x_vistext);
Ivica Ico Bukvic's avatar
Ivica Ico Bukvic committed
143
144
145
146
147
148
        }
    }
    else
    {
        if ((glist->gl_havewindow || x->x_isgopvisible)
	    && (y = glist_findrtext(glist, (t_text *)x)))
149
150
151
152
153
154
        {
            //rtext_erase(y);
            gui_vmess("gui_gobj_erase", "xs",
                glist_getcanvas(glist),
                rtext_gettag(y));
        }
Ivica Ico Bukvic's avatar
Ivica Ico Bukvic committed
155
156
157
    }
}

158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
static void pddplink_activate(t_gobj *z, t_glist *glist, int state)
{
    t_pddplink *x = (t_pddplink *)z;
    t_rtext *y = glist_findrtext(glist, (t_text *)x);
    rtext_activate(y, state);
    x->x_rtextactive = state;
    if (!state) {
        /* Big workaround for pddplink without the -box option...

           After this call, Pd calls text_setto to see if it needs to
           instantiate the object. For nearly all t_text objects, the
           object doesn't get rebuilt if the text inside the box remains
           the same. Instead, it just calls rtext_senditup which (eventually)
           sends a message to the GUI to simply supply new text for the
           current object. (It shouldn't do this if the text hasn't actually
           changed, but that's another story...).

           Anyway, since pddplink has this weird widget behavior, the text
           displayed is different than the text we typed in the box-- hence
           the x->x_vistext member here. So if we happened to edit the box
           without changing the text, rtext_senditup would update the link 
           to be "pddplink foo" instead of just "foo".

           To prevent this, we just zero out the object's te_binbuf to ensure
           that text_setto re-instantiates. That ensures the correct text is
           printed for the link */
        t_binbuf *b = binbuf_new();
        t_binbuf *old = x->x_ob.te_binbuf;
        x->x_ob.te_binbuf = b;
        binbuf_free(old);
        /* ico@vt.edu 20200906: this is ugly but necessary because otherwise 
           the object only activates correctly (reverts to its active link)
           every other time when being deselected
        */
        pddplink_vis(z, glist, 0);
        pddplink_vis(z, glist, 1);
    }
}

Ivica Ico Bukvic's avatar
Ivica Ico Bukvic committed
197
198
199
200
201
202
203
204
205
206
207
208
static int pddplink_wbclick(t_gobj *z, t_glist *glist, int xpix, int ypix,
			    int shift, int alt, int dbl, int doit);

static t_widgetbehavior pddplink_widgetbehavior =
{
    pddplink_getrect,
    pddplink_displace,
    pddplink_select,
    pddplink_activate,
    0,
    pddplink_vis,
    pddplink_wbclick,
209
	pddplink_displace_withtag,
Ivica Ico Bukvic's avatar
Ivica Ico Bukvic committed
210
211
212
213
214
215
216
217
218
219
220
221
222
223
};

/* Code that might be merged back to g_text.c ends here. */

/* FIXME need access to glob_pdobject... */
static t_pd *pddplink_pdtarget(t_pddplink *x)
{
    t_pd *pdtarget = gensym("pd")->s_thing;
    if (pdtarget && !strcmp(class_getname(*pdtarget), "pd"))
	return (pdtarget);
    else
	return ((t_pd *)x);  /* internal error */
}

224
static void pddplink_symbol(t_pddplink *x, t_symbol *s)
Ivica Ico Bukvic's avatar
Ivica Ico Bukvic committed
225
226
227
{
    if (x->x_ishit)
    {
228
229
230
231
232
233
234
235
236
237
238
239
        post("pddplink: internal error (%s)", (s ? s->s_name : ""));
    }
    else
    {
        if (s == &s_) return; // nothing to see here, move along...
        x->x_ishit = 1;
        char final_name[FILENAME_MAX];
        sys_expandpathelems(s->s_name, final_name);
        gui_vmess("gui_pddplink_open", "ss",
                  final_name,
                  x->x_dirsym->s_name);
        x->x_ishit = 0;
Ivica Ico Bukvic's avatar
Ivica Ico Bukvic committed
240
241
242
243
244
245
246
    }
}

static void pddplink_click(t_pddplink *x, t_floatarg xpos, t_floatarg ypos,
			   t_floatarg shift, t_floatarg ctrl, t_floatarg alt)
{
    x->x_ishit = 1;
247
248
    char final_name[FILENAME_MAX];
    sys_expandpathelems(x->x_ulink->s_name, final_name);
Jonathan Wilkes's avatar
Jonathan Wilkes committed
249
250
251
    gui_vmess("gui_pddplink_open", "ss",
        final_name,
        x->x_dirsym->s_name);
Ivica Ico Bukvic's avatar
Ivica Ico Bukvic committed
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
    x->x_ishit = 0;
}

static void pddplink_bang(t_pddplink *x)
{
  pddplink_click(x, 0, 0, 0, 0, 0);
}

static int pddplink_wbclick(t_gobj *z, t_glist *glist, int xpix, int ypix,
			    int shift, int alt, int dbl, int doit)
{
    t_pddplink *x = (t_pddplink *)z;
    if (glist->gl_havewindow || x->x_isgopvisible)
    {
	if (doit)
	    pddplink_click(x, (t_floatarg)xpix, (t_floatarg)ypix,
			   (t_floatarg)shift, 0, (t_floatarg)alt);
	return (1);
    }
    else return (0);
}

static int pddplink_isoption(char *name)
{
    if (*name == '-')
    {
	char c = name[1];
	return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
    }
    else return (0);
}

static t_symbol *pddplink_nextsymbol(int ac, t_atom *av, int opt, int *skipp)
{
    int ndx;
    for (ndx = 0; ndx < ac; ndx++, av++)
    {
	if (av->a_type == A_SYMBOL &&
	    (!opt || pddplink_isoption(av->a_w.w_symbol->s_name)))
	{
	    *skipp = ++ndx;
	    return (av->a_w.w_symbol);
	}
    }
    return (0);
}

static int pddplink_dooptext(char *dst, int maxsize, int ac, t_atom *av)
{
    int i, sz, sep, len;
    char buf[32], *src;
    for (i = 0, sz = 0, sep = 0; i < ac; i++, av++)
    {
	if (sep)
	{
	    sz++;
	    if (sz >= maxsize)
		break;
	    else if (dst)
	    {
		*dst++ = ' ';
		*dst = 0;
	    }
	}
	else sep = 1;
	if (av->a_type == A_SYMBOL)
	    src = av->a_w.w_symbol->s_name;
	else if (av->a_type == A_FLOAT)
	{
	    src = buf;
	    sprintf(src, "%g", av->a_w.w_float);
	}
	else
	{
	    sep = 0;
	    continue;
	}
	len = strlen(src);
	sz += len;
	if (sz >= maxsize)
	    break;
	else if (dst)
	{
	    strcpy(dst, src);
	    dst += len;
	}
    }
    return (sz);
}

static char *pddplink_optext(int *sizep, int ac, t_atom *av)
{
    char *result;
    int sz = pddplink_dooptext(0, MAXPDSTRING, ac, av);
    *sizep = sz + (sz >= MAXPDSTRING ? 4 : 1);
    result = getbytes(*sizep);
    pddplink_dooptext(result, sz + 1, ac, av);
    if (sz >= MAXPDSTRING)
    {
	sz = strlen(result);
	strcpy(result + sz, "...");
    }
    return (result);
}

static void pddplink_free(t_pddplink *x)
{
    if (x->x_vistext)
	freebytes(x->x_vistext, x->x_vissize);
}

static void *pddplink_new(t_symbol *s, int ac, t_atom *av)
{
    t_pddplink xgen, *x;
    int skip;
    xgen.x_isboxed = 0;
    xgen.x_isgopvisible = 0;
    xgen.x_vistext = 0;
    xgen.x_vissize = 0;
    if ((xgen.x_ulink = pddplink_nextsymbol(ac, av, 0, &skip)))
    {
	t_symbol *opt;
	ac -= skip;
	av += skip;
	while ((opt = pddplink_nextsymbol(ac, av, 1, &skip)))
	{
	    ac -= skip;
	    av += skip;
	    if (opt == gensym("-box"))
		xgen.x_isboxed = 1;
	    else if (opt == gensym("-gop"))
		xgen.x_isgopvisible = 1;
	    else if (opt == gensym("-text"))
	    {
		t_symbol *nextsym = pddplink_nextsymbol(ac, av, 1, &skip);
		int natoms = (nextsym ? skip - 1 : ac);
		if (natoms)
		    xgen.x_vistext =
			pddplink_optext(&xgen.x_vissize, natoms, av);
	    }
	}
    }
    x = (t_pddplink *)
	pd_new(xgen.x_isboxed ? pddplinkbox_class : pddplink_class);
    x->x_glist = canvas_getcurrent();
    x->x_dirsym = canvas_getdir(x->x_glist);  /* FIXME */

    x->x_isboxed = xgen.x_isboxed;
    x->x_isgopvisible = xgen.x_isgopvisible;
    x->x_vistext = xgen.x_vistext;
    x->x_vissize = xgen.x_vissize;
    x->x_vislength = (x->x_vistext ? strlen(x->x_vistext) : 0);
    x->x_rtextactive = 0;
    if (xgen.x_ulink)
        x->x_ulink = xgen.x_ulink;
    else
        x->x_ulink = gensym("Untitled");
    SETSYMBOL(&x->x_openargs[0], x->x_ulink);
    SETSYMBOL(&x->x_openargs[1], x->x_dirsym);
    x->x_ishit = 0;
412
    if (!x->x_isboxed)
Ivica Ico Bukvic's avatar
Ivica Ico Bukvic committed
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
    {
	/* do we need to set ((t_text *)x)->te_type = T_TEXT; ? */
	if (!x->x_vistext)
	{
	    x->x_vislength = strlen(x->x_ulink->s_name);
	    x->x_vissize = x->x_vislength + 1;
	    x->x_vistext = getbytes(x->x_vissize);
	    strcpy(x->x_vistext, x->x_ulink->s_name);
	}
    }
    return (x);
}

void pddplink_setup(void)
{
    t_symbol *dirsym;

    pddplink_class = class_new(gensym("pddplink"),
			       (t_newmethod)pddplink_new,
			       (t_method)pddplink_free,
			       sizeof(t_pddplink),
			       CLASS_NOINLET | CLASS_PATCHABLE,
			       A_GIMME, 0);
436
    class_addsymbol(pddplink_class, pddplink_symbol);
Ivica Ico Bukvic's avatar
Ivica Ico Bukvic committed
437
438
439
440
441
442
    class_setwidget(pddplink_class, &pddplink_widgetbehavior);

    pddplinkbox_class = class_new(gensym("pddplink"), 0,
				  (t_method)pddplink_free,
				  sizeof(t_pddplink), 0, A_GIMME, 0);
    class_addbang(pddplinkbox_class, pddplink_bang);
443
    class_addsymbol(pddplinkbox_class, pddplink_symbol);
Ivica Ico Bukvic's avatar
Ivica Ico Bukvic committed
444
445
446
447
448
    class_addmethod(pddplinkbox_class, (t_method)pddplink_click,
		    gensym("click"),
		    A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);

    dirsym = pddplink_class->c_externdir;  /* FIXME */
449
450
    /* The pddplink.tcl file is no longer needed */
    //sys_vgui("source {%s/pddplink.tcl}\n", dirsym->s_name);
Ivica Ico Bukvic's avatar
Ivica Ico Bukvic committed
451
}