Add FS #10214. Initial commit of the original PDa code for the GSoC Pure Data plugin...
[kugel-rb.git] / apps / plugins / pdbox / PDa / src / g_editor.c
bloba1dea79df1a85e8ed88f158ca32cc6a0f6a85e5d
1 /* Copyright (c) 1997-2001 Miller Puckette and others.
2 * For information on usage and redistribution, and for a DISCLAIMER OF ALL
3 * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include "m_pd.h"
8 #include "m_imp.h"
9 #include "s_stuff.h"
10 #include "g_canvas.h"
11 #include <string.h>
13 void glist_readfrombinbuf(t_glist *x, t_binbuf *b, char *filename,
14 int selectem);
16 void open_via_helppath(const char *name, const char *dir);
17 char *class_gethelpdir(t_class *c);
19 /* ------------------ forward declarations --------------- */
20 static void canvas_doclear(t_canvas *x);
21 static void glist_setlastxy(t_glist *gl, int xval, int yval);
22 static void glist_donewloadbangs(t_glist *x);
23 static t_binbuf *canvas_docopy(t_canvas *x);
24 static void canvas_dopaste(t_canvas *x, t_binbuf *b);
25 static void canvas_paste(t_canvas *x);
26 static void canvas_clearline(t_canvas *x);
27 static t_binbuf *copy_binbuf;
29 /* ---------------- generic widget behavior ------------------------- */
31 void gobj_getrect(t_gobj *x, t_glist *glist, int *x1, int *y1,
32 int *x2, int *y2)
34 if (x->g_pd->c_wb && x->g_pd->c_wb->w_getrectfn)
35 (*x->g_pd->c_wb->w_getrectfn)(x, glist, x1, y1, x2, y2);
38 void gobj_displace(t_gobj *x, t_glist *glist, int dx, int dy)
40 if (x->g_pd->c_wb && x->g_pd->c_wb->w_displacefn)
41 (*x->g_pd->c_wb->w_displacefn)(x, glist, dx, dy);
44 void gobj_select(t_gobj *x, t_glist *glist, int state)
46 if (x->g_pd->c_wb && x->g_pd->c_wb->w_selectfn)
47 (*x->g_pd->c_wb->w_selectfn)(x, glist, state);
50 void gobj_activate(t_gobj *x, t_glist *glist, int state)
52 if (x->g_pd->c_wb && x->g_pd->c_wb->w_activatefn)
53 (*x->g_pd->c_wb->w_activatefn)(x, glist, state);
56 void gobj_delete(t_gobj *x, t_glist *glist)
58 if (x->g_pd->c_wb && x->g_pd->c_wb->w_deletefn)
59 (*x->g_pd->c_wb->w_deletefn)(x, glist);
62 void gobj_vis(t_gobj *x, struct _glist *glist, int flag)
64 if (x->g_pd->c_wb && x->g_pd->c_wb->w_visfn)
65 (*x->g_pd->c_wb->w_visfn)(x, glist, flag);
68 int gobj_click(t_gobj *x, struct _glist *glist,
69 int xpix, int ypix, int shift, int alt, int dbl, int doit)
71 if (x->g_pd->c_wb && x->g_pd->c_wb->w_clickfn)
72 return ((*x->g_pd->c_wb->w_clickfn)(x,
73 glist, xpix, ypix, shift, alt, dbl, doit));
74 else return (0);
77 /* ------------------------ managing the selection ----------------- */
79 void glist_selectline(t_glist *x, t_outconnect *oc, int index1,
80 int outno, int index2, int inno)
82 if (x->gl_editor)
84 glist_noselect(x);
85 x->gl_editor->e_selectedline = 1;
86 x->gl_editor->e_selectline_index1 = index1;
87 x->gl_editor->e_selectline_outno = outno;
88 x->gl_editor->e_selectline_index2 = index2;
89 x->gl_editor->e_selectline_inno = inno;
90 x->gl_editor->e_selectline_tag = oc;
91 sys_vgui(".x%x.c itemconfigure l%x -fill blue\n",
92 x, x->gl_editor->e_selectline_tag);
96 void glist_deselectline(t_glist *x)
98 if (x->gl_editor)
100 x->gl_editor->e_selectedline = 0;
101 sys_vgui(".x%x.c itemconfigure l%x -fill black\n",
102 x, x->gl_editor->e_selectline_tag);
106 int glist_isselected(t_glist *x, t_gobj *y)
108 if (x->gl_editor)
110 t_selection *sel;
111 for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
112 if (sel->sel_what == y) return (1);
114 return (0);
117 /* call this for unselected objects only */
118 void glist_select(t_glist *x, t_gobj *y)
120 if (x->gl_editor)
122 t_selection *sel = (t_selection *)getbytes(sizeof(*sel));
123 if (x->gl_editor->e_selectedline)
124 glist_deselectline(x);
125 /* LATER #ifdef out the following check */
126 if (glist_isselected(x, y)) bug("glist_select");
127 sel->sel_next = x->gl_editor->e_selection;
128 sel->sel_what = y;
129 x->gl_editor->e_selection = sel;
130 gobj_select(y, x, 1);
134 /* call this for selected objects only */
135 void glist_deselect(t_glist *x, t_gobj *y)
137 int fixdsp = 0;
138 static int reenter = 0;
139 if (reenter) return;
140 reenter = 1;
141 if (x->gl_editor)
143 t_selection *sel, *sel2;
144 t_rtext *z = 0;
145 if (!glist_isselected(x, y)) bug("glist_deselect");
146 if (x->gl_editor->e_textedfor)
148 t_rtext *fuddy = glist_findrtext(x, (t_text *)y);
149 if (x->gl_editor->e_textedfor == fuddy)
151 if (x->gl_editor->e_textdirty)
153 z = fuddy;
154 canvas_stowconnections(glist_getcanvas(x));
156 gobj_activate(y, x, 0);
158 if (zgetfn(&y->g_pd, gensym("dsp")))
159 fixdsp = 1;
161 if ((sel = x->gl_editor->e_selection)->sel_what == y)
163 x->gl_editor->e_selection = x->gl_editor->e_selection->sel_next;
164 gobj_select(sel->sel_what, x, 0);
165 freebytes(sel, sizeof(*sel));
167 else
169 for (sel = x->gl_editor->e_selection; sel2 = sel->sel_next;
170 sel = sel2)
172 if (sel2->sel_what == y)
174 sel->sel_next = sel2->sel_next;
175 gobj_select(sel2->sel_what, x, 0);
176 freebytes(sel2, sizeof(*sel2));
177 break;
181 if (z)
183 char *buf;
184 int bufsize;
186 rtext_gettext(z, &buf, &bufsize);
187 text_setto((t_text *)y, x, buf, bufsize);
188 canvas_fixlinesfor(glist_getcanvas(x), (t_text *)y);
189 x->gl_editor->e_textedfor = 0;
191 if (fixdsp)
192 canvas_update_dsp();
194 reenter = 0;
197 void glist_noselect(t_glist *x)
199 if (x->gl_editor)
201 while (x->gl_editor->e_selection)
202 glist_deselect(x, x->gl_editor->e_selection->sel_what);
203 if (x->gl_editor->e_selectedline)
204 glist_deselectline(x);
208 void glist_selectall(t_glist *x)
210 if (x->gl_editor)
212 glist_noselect(x);
213 if (x->gl_list)
215 t_selection *sel = (t_selection *)getbytes(sizeof(*sel));
216 t_gobj *y = x->gl_list;
217 x->gl_editor->e_selection = sel;
218 sel->sel_what = y;
219 gobj_select(y, x, 1);
220 while (y = y->g_next)
222 t_selection *sel2 = (t_selection *)getbytes(sizeof(*sel2));
223 sel->sel_next = sel2;
224 sel = sel2;
225 sel->sel_what = y;
226 gobj_select(y, x, 1);
228 sel->sel_next = 0;
233 /* get the index of a gobj in a glist. If y is zero, return the
234 total number of objects. */
235 int glist_getindex(t_glist *x, t_gobj *y)
237 t_gobj *y2;
238 int indx;
240 for (y2 = x->gl_list, indx = 0; y2 && y2 != y; y2 = y2->g_next)
241 indx++;
242 return (indx);
245 /* get the index of the object, among selected items, if "selected"
246 is set; otherwise, among unselected ones. If y is zero, just
247 counts the selected or unselected objects. */
248 int glist_selectionindex(t_glist *x, t_gobj *y, int selected)
250 t_gobj *y2;
251 int indx;
253 for (y2 = x->gl_list, indx = 0; y2 && y2 != y; y2 = y2->g_next)
254 if (selected == glist_isselected(x, y2))
255 indx++;
256 return (indx);
259 static t_gobj *glist_nth(t_glist *x, int n)
261 t_gobj *y;
262 int indx;
263 for (y = x->gl_list, indx = 0; y; y = y->g_next, indx++)
264 if (indx == n)
265 return (y);
266 return (0);
269 /* ------------------- support for undo/redo -------------------------- */
271 static t_undofn canvas_undo_fn; /* current undo function if any */
272 static int canvas_undo_whatnext; /* whether we can now UNDO or REDO */
273 static void *canvas_undo_buf; /* data private to the undo function */
274 static t_canvas *canvas_undo_canvas; /* which canvas we can undo on */
275 static const char *canvas_undo_name;
277 void canvas_setundo(t_canvas *x, t_undofn undofn, void *buf,
278 const char *name)
280 int hadone = 0;
281 /* blow away the old undo information. In one special case the
282 old undo info is re-used; if so we shouldn't free it here. */
283 if (canvas_undo_fn && canvas_undo_buf && (buf != canvas_undo_buf))
285 (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_FREE);
286 hadone = 1;
288 canvas_undo_canvas = x;
289 canvas_undo_fn = undofn;
290 canvas_undo_buf = buf;
291 canvas_undo_whatnext = UNDO_UNDO;
292 canvas_undo_name = name;
293 if (x && glist_isvisible(x) && glist_istoplevel(x))
294 /* enable undo in menu */
295 sys_vgui("pdtk_undomenu .x%x %s no\n", x, name);
296 else if (hadone)
297 sys_vgui("pdtk_undomenu nobody no no\n");
300 /* clear undo if it happens to be for the canvas x.
301 (but if x is 0, clear it regardless of who owns it.) */
302 void canvas_noundo(t_canvas *x)
304 if (!x || (x == canvas_undo_canvas))
305 canvas_setundo(0, 0, 0, "foo");
308 static void canvas_undo(t_canvas *x)
310 if (x != canvas_undo_canvas)
311 bug("canvas_undo 1");
312 else if (canvas_undo_whatnext != UNDO_UNDO)
313 bug("canvas_undo 2");
314 else
316 /* post("undo"); */
317 (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_UNDO);
318 /* enable redo in menu */
319 if (glist_isvisible(x) && glist_istoplevel(x))
320 sys_vgui("pdtk_undomenu .x%x no %s\n", x, canvas_undo_name);
321 canvas_undo_whatnext = UNDO_REDO;
325 static void canvas_redo(t_canvas *x)
327 if (x != canvas_undo_canvas)
328 bug("canvas_undo 1");
329 else if (canvas_undo_whatnext != UNDO_REDO)
330 bug("canvas_undo 2");
331 else
333 /* post("redo"); */
334 (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_REDO);
335 /* enable undo in menu */
336 if (glist_isvisible(x) && glist_istoplevel(x))
337 sys_vgui("pdtk_undomenu .x%x %s no\n", x, canvas_undo_name);
338 canvas_undo_whatnext = UNDO_UNDO;
342 /* ------- specific undo methods: 1. connect and disconnect -------- */
344 typedef struct _undo_connect
346 int u_index1;
347 int u_outletno;
348 int u_index2;
349 int u_inletno;
350 } t_undo_connect;
352 static void *canvas_undo_set_disconnect(t_canvas *x,
353 int index1, int outno, int index2, int inno)
355 t_undo_connect *buf = (t_undo_connect *)getbytes(sizeof(*buf));
356 buf->u_index1 = index1;
357 buf->u_outletno = outno;
358 buf->u_index2 = index2;
359 buf->u_inletno = inno;
360 return (buf);
363 void canvas_disconnect(t_canvas *x,
364 float index1, float outno, float index2, float inno)
366 t_linetraverser t;
367 t_outconnect *oc;
368 linetraverser_start(&t, x);
369 while (oc = linetraverser_next(&t))
371 int srcno = canvas_getindex(x, &t.tr_ob->ob_g);
372 int sinkno = canvas_getindex(x, &t.tr_ob2->ob_g);
373 if (srcno == index1 && t.tr_outno == outno &&
374 sinkno == index2 && t.tr_inno == inno)
376 sys_vgui(".x%x.c delete l%x\n", x, oc);
377 obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);
378 break;
383 static void canvas_undo_disconnect(t_canvas *x, void *z, int action)
385 t_undo_connect *buf = z;
386 if (action == UNDO_UNDO)
388 canvas_connect(x, buf->u_index1, buf->u_outletno,
389 buf->u_index2, buf->u_inletno);
391 else if (action == UNDO_REDO)
393 canvas_disconnect(x, buf->u_index1, buf->u_outletno,
394 buf->u_index2, buf->u_inletno);
396 else if (action == UNDO_FREE)
397 t_freebytes(buf, sizeof(*buf));
400 /* connect just calls disconnect actions backward... */
401 static void *canvas_undo_set_connect(t_canvas *x,
402 int index1, int outno, int index2, int inno)
404 return (canvas_undo_set_disconnect(x, index1, outno, index2, inno));
407 static void canvas_undo_connect(t_canvas *x, void *z, int action)
409 int myaction;
410 if (action == UNDO_UNDO)
411 myaction = UNDO_REDO;
412 else if (action == UNDO_REDO)
413 myaction = UNDO_UNDO;
414 else myaction = action;
415 canvas_undo_disconnect(x, z, myaction);
418 /* ---------- ... 2. cut, clear, and typing into objects: -------- */
420 #define UCUT_CUT 1 /* operation was a cut */
421 #define UCUT_CLEAR 2 /* .. a clear */
422 #define UCUT_TEXT 3 /* text typed into a box */
424 typedef struct _undo_cut
426 t_binbuf *u_objectbuf; /* the object cleared or typed into */
427 t_binbuf *u_reconnectbuf; /* connections into and out of object */
428 t_binbuf *u_redotextbuf; /* buffer to paste back for redo if TEXT */
429 int u_mode; /* from flags above */
430 } t_undo_cut;
432 static void *canvas_undo_set_cut(t_canvas *x, int mode)
434 t_undo_cut *buf;
435 t_gobj *y;
436 t_linetraverser t;
437 t_outconnect *oc;
438 int nnotsel= glist_selectionindex(x, 0, 0);
439 buf = (t_undo_cut *)getbytes(sizeof(*buf));
440 buf->u_mode = mode;
441 buf->u_redotextbuf = 0;
443 /* store connections into/out of the selection */
444 buf->u_reconnectbuf = binbuf_new();
445 linetraverser_start(&t, x);
446 while (oc = linetraverser_next(&t))
448 int issel1 = glist_isselected(x, &t.tr_ob->ob_g);
449 int issel2 = glist_isselected(x, &t.tr_ob2->ob_g);
450 if (issel1 != issel2)
452 binbuf_addv(buf->u_reconnectbuf, "ssiiii;",
453 gensym("#X"), gensym("connect"),
454 (issel1 ? nnotsel : 0)
455 + glist_selectionindex(x, &t.tr_ob->ob_g, issel1),
456 t.tr_outno,
457 (issel2 ? nnotsel : 0) +
458 glist_selectionindex(x, &t.tr_ob2->ob_g, issel2),
459 t.tr_inno);
462 if (mode == UCUT_TEXT)
464 buf->u_objectbuf = canvas_docopy(x);
466 else if (mode == UCUT_CUT)
468 buf->u_objectbuf = 0;
470 else if (mode == UCUT_CLEAR)
472 buf->u_objectbuf = canvas_docopy(x);
474 return (buf);
477 static void canvas_undo_cut(t_canvas *x, void *z, int action)
479 t_undo_cut *buf = z;
480 int mode = buf->u_mode;
481 if (action == UNDO_UNDO)
483 if (mode == UCUT_CUT)
484 canvas_dopaste(x, copy_binbuf);
485 else if (mode == UCUT_CLEAR)
486 canvas_dopaste(x, buf->u_objectbuf);
487 else if (mode == UCUT_TEXT)
489 t_gobj *y1, *y2;
490 glist_noselect(x);
491 for (y1 = x->gl_list; y2 = y1->g_next; y1 = y2)
493 if (y1)
495 if (!buf->u_redotextbuf)
497 glist_noselect(x);
498 glist_select(x, y1);
499 buf->u_redotextbuf = canvas_docopy(x);
500 glist_noselect(x);
502 glist_delete(x, y1);
504 canvas_dopaste(x, buf->u_objectbuf);
506 pd_bind(&x->gl_pd, gensym("#X"));
507 binbuf_eval(buf->u_reconnectbuf, 0, 0, 0);
508 pd_unbind(&x->gl_pd, gensym("#X"));
510 else if (action == UNDO_REDO)
512 if (mode == UCUT_CUT || mode == UCUT_CLEAR)
513 canvas_doclear(x);
514 else if (mode == UCUT_TEXT)
516 t_gobj *y1, *y2;
517 for (y1 = x->gl_list; y2 = y1->g_next; y1 = y2)
519 if (y1)
520 glist_delete(x, y1);
521 canvas_dopaste(x, buf->u_redotextbuf);
522 pd_bind(&x->gl_pd, gensym("#X"));
523 binbuf_eval(buf->u_reconnectbuf, 0, 0, 0);
524 pd_unbind(&x->gl_pd, gensym("#X"));
527 else if (action == UNDO_FREE)
529 if (buf->u_objectbuf)
530 binbuf_free(buf->u_objectbuf);
531 if (buf->u_reconnectbuf)
532 binbuf_free(buf->u_reconnectbuf);
533 if (buf->u_redotextbuf)
534 binbuf_free(buf->u_redotextbuf);
535 t_freebytes(buf, sizeof(*buf));
539 /* --------- 3. motion, including "tidy up" and stretching ----------- */
541 typedef struct _undo_move_elem
543 int e_index;
544 int e_xpix;
545 int e_ypix;
546 } t_undo_move_elem;
548 typedef struct _undo_move
550 t_undo_move_elem *u_vec;
551 int u_n;
552 } t_undo_move;
554 static int canvas_undo_already_set_move;
556 static void *canvas_undo_set_move(t_canvas *x, int selected)
558 int x1, y1, x2, y2, i, indx;
559 t_gobj *y;
560 t_undo_move *buf = (t_undo_move *)getbytes(sizeof(*buf));
561 buf->u_n = selected ? glist_selectionindex(x, 0, 1) : glist_getindex(x, 0);
562 buf->u_vec = (t_undo_move_elem *)getbytes(sizeof(*buf->u_vec) *
563 (selected ? glist_selectionindex(x, 0, 1) : glist_getindex(x, 0)));
564 if (selected)
566 for (y = x->gl_list, i = indx = 0; y; y = y->g_next, indx++)
567 if (glist_isselected(x, y))
569 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
570 buf->u_vec[i].e_index = indx;
571 buf->u_vec[i].e_xpix = x1;
572 buf->u_vec[i].e_ypix = y1;
573 i++;
576 else
578 for (y = x->gl_list, indx = 0; y; y = y->g_next, indx++)
580 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
581 buf->u_vec[indx].e_index = indx;
582 buf->u_vec[indx].e_xpix = x1;
583 buf->u_vec[indx].e_ypix = y1;
586 canvas_undo_already_set_move = 1;
587 return (buf);
590 static void canvas_undo_move(t_canvas *x, void *z, int action)
592 t_undo_move *buf = z;
593 if (action == UNDO_UNDO || action == UNDO_REDO)
595 int i;
596 for (i = 0; i < buf->u_n; i++)
598 int x1, y1, x2, y2, newx, newy;
599 t_gobj *y;
600 newx = buf->u_vec[i].e_xpix;
601 newy = buf->u_vec[i].e_ypix;
602 y = glist_nth(x, buf->u_vec[i].e_index);
603 if (y)
605 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
606 gobj_displace(y, x, newx-x1, newy - y1);
607 buf->u_vec[i].e_xpix = x1;
608 buf->u_vec[i].e_ypix = y1;
612 else if (action == UNDO_FREE)
614 t_freebytes(buf->u_vec, buf->u_n * sizeof(*buf->u_vec));
615 t_freebytes(buf, sizeof(*buf));
619 /* --------- 4. paste (also duplicate) ----------- */
621 typedef struct _undo_paste
623 int u_index; /* index of first object pasted */
624 } t_undo_paste;
626 static void *canvas_undo_set_paste(t_canvas *x)
628 t_undo_paste *buf = (t_undo_paste *)getbytes(sizeof(*buf));
629 buf->u_index = glist_getindex(x, 0);
630 return (buf);
633 static void canvas_undo_paste(t_canvas *x, void *z, int action)
635 t_undo_paste *buf = z;
636 if (action == UNDO_UNDO)
638 t_gobj *y;
639 glist_noselect(x);
640 for (y = glist_nth(x, buf->u_index); y; y = y->g_next)
641 glist_select(x, y);
642 canvas_doclear(x);
644 else if (action == UNDO_REDO)
646 t_selection *sel;
647 canvas_dopaste(x, copy_binbuf);
648 /* if it was "duplicate" have to re-enact the displacement. */
649 if (canvas_undo_name && canvas_undo_name[0] == 'd')
650 for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
651 gobj_displace(sel->sel_what, x, 10, 10);
653 else if (action == UNDO_FREE)
654 t_freebytes(buf, sizeof(*buf));
657 /* recursively check for abstractions to reload as result of a save.
658 Don't reload the one we just saved ("except") though. */
659 /* LATER try to do the same trick for externs. */
660 static void glist_doreload(t_glist *gl, t_symbol *name, t_symbol *dir,
661 t_gobj *except)
663 t_gobj *g;
664 int i, nobj = glist_getindex(gl, 0); /* number of objects */
665 for (g = gl->gl_list, i = 0; g && i < nobj; i++)
667 if (g != except && pd_class(&g->g_pd) == canvas_class &&
668 canvas_isabstraction((t_canvas *)g) &&
669 ((t_canvas *)g)->gl_name == name &&
670 canvas_getdir((t_canvas *)g) == dir)
672 /* we're going to remake the object, so "g" will go stale.
673 Get its index here, and afterward restore g. Also, the
674 replacement will be at teh end of the list, so we don't
675 do g = g->g_next in this case. */
676 int j = glist_getindex(gl, g);
677 if (!gl->gl_havewindow)
678 canvas_vis(glist_getcanvas(gl), 1);
679 glist_noselect(gl);
680 glist_select(gl, g);
681 canvas_setundo(gl, canvas_undo_cut,
682 canvas_undo_set_cut(gl, UCUT_CLEAR), "clear");
683 canvas_doclear(gl);
684 canvas_undo(gl);
685 glist_noselect(gl);
686 g = glist_nth(gl, j);
688 else
690 if (g != except && pd_class(&g->g_pd) == canvas_class)
691 glist_doreload((t_canvas *)g, name, dir, except);
692 g = g->g_next;
697 /* call canvas_doreload on everyone */
698 void canvas_reload(t_symbol *name, t_symbol *dir, t_gobj *except)
700 t_canvas *x;
701 /* find all root canvases */
702 for (x = canvas_list; x; x = x->gl_next)
703 glist_doreload(x, name, dir, except);
706 /* ------------------------ event handling ------------------------ */
708 #define CURSOR_RUNMODE_NOTHING 0
709 #define CURSOR_RUNMODE_CLICKME 1
710 #define CURSOR_RUNMODE_THICKEN 2
711 #define CURSOR_RUNMODE_ADDPOINT 3
712 #define CURSOR_EDITMODE_NOTHING 4
713 #define CURSOR_EDITMODE_CONNECT 5
714 #define CURSOR_EDITMODE_DISCONNECT 6
716 static char *cursorlist[] = {
717 #ifdef MSW
718 "right_ptr", /* CURSOR_RUNMODE_NOTHING */
719 #else
720 "left_ptr", /* CURSOR_RUNMODE_NOTHING */
721 #endif
722 "arrow", /* CURSOR_RUNMODE_CLICKME */
723 "sb_v_double_arrow", /* CURSOR_RUNMODE_THICKEN */
724 "plus", /* CURSOR_RUNMODE_ADDPOINT */
725 "hand2", /* CURSOR_EDITMODE_NOTHING */
726 "circle", /* CURSOR_EDITMODE_CONNECT */
727 "X_cursor" /* CURSOR_EDITMODE_DISCONNECT */
730 void canvas_setcursor(t_canvas *x, unsigned int cursornum)
732 static t_canvas *xwas;
733 static unsigned int cursorwas;
734 if (cursornum >= sizeof(cursorlist)/sizeof *cursorlist)
736 bug("canvas_setcursor");
737 return;
739 if (xwas != x || cursorwas != cursornum)
741 sys_vgui(".x%x configure -cursor %s\n", x, cursorlist[cursornum]);
742 xwas = x;
743 cursorwas = cursornum;
747 /* check if a point lies in a gobj. */
748 int canvas_hitbox(t_canvas *x, t_gobj *y, int xpos, int ypos,
749 int *x1p, int *y1p, int *x2p, int *y2p)
751 int x1, y1, x2, y2;
752 t_text *ob;
753 if ((ob = pd_checkobject(&y->g_pd)) &&
754 !text_shouldvis(ob, x))
755 return (0);
756 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
757 if (xpos >= x1 && xpos <= x2 && ypos >= y1 && ypos <= y2)
759 *x1p = x1;
760 *y1p = y1;
761 *x2p = x2;
762 *y2p = y2;
763 return (1);
765 else return (0);
768 /* find the last gobj, if any, containing the point. */
769 static t_gobj *canvas_findhitbox(t_canvas *x, int xpos, int ypos,
770 int *x1p, int *y1p, int *x2p, int *y2p)
772 t_gobj *y, *rval = 0;
773 for (y = x->gl_list; y; y = y->g_next)
775 if (canvas_hitbox(x, y, xpos, ypos, x1p, y1p, x2p, y2p))
776 rval = y;
778 return (rval);
781 /* right-clicking on a canvas object pops up a menu. */
782 static void canvas_rightclick(t_canvas *x, int xpos, int ypos, t_gobj *y)
784 int canprop, canopen;
785 canprop = (!y || (y && class_getpropertiesfn(pd_class(&y->g_pd))));
786 canopen = (y && zgetfn(&y->g_pd, gensym("menu-open")));
787 sys_vgui("pdtk_canvas_popup .x%x %d %d %d %d\n",
788 x, xpos, ypos, canprop, canopen);
791 /* tell GUI to create a properties dialog on the canvas. We tell
792 the user the negative of the "pixel" y scale to make it appear to grow
793 naturally upward, whereas pixels grow downward. */
794 static void canvas_properties(t_glist *x)
796 char graphbuf[200];
797 sprintf(graphbuf, "pdtk_canvas_dialog %%s %g %g %g %g \n",
798 glist_dpixtodx(x, 1), -glist_dpixtody(x, 1),
799 (float)glist_isgraph(x), (float)x->gl_stretch);
800 gfxstub_new(&x->gl_pd, x, graphbuf);
804 void canvas_setgraph(t_glist *x, int flag)
806 if (!flag && glist_isgraph(x))
808 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
809 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
810 x->gl_isgraph = 0;
811 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
813 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
814 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
817 else if (flag && !glist_isgraph(x))
819 if (x->gl_pixwidth <= 0)
820 x->gl_pixwidth = GLIST_DEFGRAPHWIDTH;
822 if (x->gl_pixheight <= 0)
823 x->gl_pixheight = GLIST_DEFGRAPHHEIGHT;
825 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
826 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
827 x->gl_isgraph = 1;
828 /* if (x->gl_owner && glist_isvisible(x->gl_owner))
829 canvas_vis(x, 1); */
830 if (x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner))
831 canvas_create_editor(x, 1);
832 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
834 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
835 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
840 /* called from the gui when "OK" is selected on the canvas properties
841 dialog. Again we negate "y" scale. */
842 static void canvas_donecanvasdialog(t_glist *x, t_floatarg xperpix,
843 t_floatarg yperpix, t_floatarg fgraphme)
845 int graphme = (fgraphme != 0), redraw = 0;
846 yperpix = -yperpix;
847 if (xperpix == 0)
848 xperpix = 1;
849 if (yperpix == 0)
850 yperpix = 1;
851 canvas_setgraph(x, graphme);
852 if (!x->gl_isgraph && (xperpix != glist_dpixtodx(x, 1)))
854 if (xperpix > 0)
856 x->gl_x1 = 0;
857 x->gl_x2 = xperpix;
859 else
861 x->gl_x1 = -xperpix * (x->gl_screenx2 - x->gl_screenx1);
862 x->gl_x2 = x->gl_x1 + xperpix;
864 redraw = 1;
866 if (!x->gl_isgraph && (yperpix != glist_dpixtody(x, 1)))
868 if (yperpix > 0)
870 x->gl_y1 = 0;
871 x->gl_y2 = yperpix;
873 else
875 x->gl_y1 = -yperpix * (x->gl_screeny2 - x->gl_screeny1);
876 x->gl_y2 = x->gl_y1 + yperpix;
878 redraw = 1;
880 if (redraw)
881 canvas_redraw(x);
884 /* called from the gui when a popup menu comes back with "properties,"
885 "open," or "help." */
886 static void canvas_done_popup(t_canvas *x, float which, float xpos, float ypos)
888 char pathbuf[MAXPDSTRING], namebuf[MAXPDSTRING];
889 t_gobj *y;
890 for (y = x->gl_list; y; y = y->g_next)
892 int x1, y1, x2, y2;
893 if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2))
895 if (which == 0) /* properties */
897 if (!class_getpropertiesfn(pd_class(&y->g_pd)))
898 continue;
899 (*class_getpropertiesfn(pd_class(&y->g_pd)))(y, x);
900 return;
902 else if (which == 1) /* open */
904 if (!zgetfn(&y->g_pd, gensym("menu-open")))
905 continue;
906 vmess(&y->g_pd, gensym("menu-open"), "");
907 return;
909 else /* help */
911 char *dir;
912 if (pd_class(&y->g_pd) == canvas_class &&
913 canvas_isabstraction((t_canvas *)y))
915 t_object *ob = (t_object *)y;
916 int ac = binbuf_getnatom(ob->te_binbuf);
917 t_atom *av = binbuf_getvec(ob->te_binbuf);
918 if (ac < 1)
919 return;
920 atom_string(av, namebuf, MAXPDSTRING);
921 dir = canvas_getdir((t_canvas *)y)->s_name;
923 else
925 strcpy(namebuf, class_gethelpname(pd_class(&y->g_pd)));
926 dir = class_gethelpdir(pd_class(&y->g_pd));
928 if (strcmp(namebuf + strlen(namebuf) - 3, ".pd"))
929 strcat(namebuf, ".pd");
930 open_via_helppath(namebuf, dir);
931 return;
935 if (which == 0)
936 canvas_properties(x);
937 else if (which == 2)
939 strcpy(pathbuf, sys_libdir->s_name);
940 strcat(pathbuf, "/doc/5.reference/0.INTRO.txt");
941 sys_vgui("menu_opentext %s\n", pathbuf);
945 #define NOMOD 0
946 #define SHIFTMOD 1
947 #define CTRLMOD 2
948 #define ALTMOD 4
949 #define RIGHTCLICK 8
951 /* on one-button-mouse machines, you can use double click to
952 mean right click (which gets the popup menu.) Do this for Mac. */
953 #ifdef MACOSX
954 #define SIMULATERIGHTCLICK
955 #endif
957 #ifdef SIMULATERIGHTCLICK
958 static double canvas_upclicktime;
959 static int canvas_upx, canvas_upy;
960 #define DCLICKINTERVAL 0.25
961 #endif
963 /* mouse click */
964 void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
965 int mod, int doit)
967 t_gobj *y;
968 int shiftmod, runmode, altmod, rightclick;
969 int x1, y1, x2, y2, clickreturned = 0;
971 if (!x->gl_editor)
973 bug("editor");
974 return;
977 shiftmod = (mod & SHIFTMOD);
978 runmode = ((mod & CTRLMOD) || (!x->gl_edit));
979 altmod = (mod & ALTMOD);
980 rightclick = (mod & RIGHTCLICK);
982 canvas_undo_already_set_move = 0;
984 /* if keyboard was grabbed, notify grabber and cancel the grab */
985 if (doit && x->gl_editor->e_grab && x->gl_editor->e_keyfn)
987 (* x->gl_editor->e_keyfn) (x->gl_editor->e_grab, 0);
988 glist_grab(x, 0, 0, 0, 0, 0);
991 #ifdef SIMULATERIGHTCLICK
992 if (doit && !runmode && xpos == canvas_upx && ypos == canvas_upy &&
993 sys_getrealtime() - canvas_upclicktime < DCLICKINTERVAL)
994 rightclick = 1;
995 #endif
997 x->gl_editor->e_lastmoved = 0;
998 if (doit)
1000 x->gl_editor->e_grab = 0;
1001 x->gl_editor->e_onmotion = MA_NONE;
1003 /* post("click %d %d %d %d", xpos, ypos, which, mod); */
1005 if (x->gl_editor->e_onmotion != MA_NONE)
1006 return;
1008 x->gl_editor->e_xwas = xpos;
1009 x->gl_editor->e_ywas = ypos;
1011 if (runmode && !rightclick)
1013 for (y = x->gl_list; y; y = y->g_next)
1015 /* check if the object wants to be clicked */
1016 if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2)
1017 && (clickreturned = gobj_click(y, x, xpos, ypos,
1018 shiftmod, altmod, 0, doit)))
1019 break;
1021 if (!doit)
1023 if (y)
1024 canvas_setcursor(x, clickreturned);
1025 else canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
1027 return;
1029 /* if not a runmode left click, fall here. */
1030 if (y = canvas_findhitbox(x, xpos, ypos, &x1, &y1, &x2, &y2))
1032 t_object *ob = pd_checkobject(&y->g_pd);
1033 /* check you're in the rectangle */
1034 ob = pd_checkobject(&y->g_pd);
1035 if (rightclick)
1036 canvas_rightclick(x, xpos, ypos, y);
1037 else if (shiftmod)
1039 if (doit)
1041 t_rtext *rt;
1042 if (ob && (rt = x->gl_editor->e_textedfor) &&
1043 rt == glist_findrtext(x, ob))
1045 rtext_mouse(rt, xpos - x1, ypos - y1, RTEXT_SHIFT);
1046 x->gl_editor->e_onmotion = MA_DRAGTEXT;
1047 x->gl_editor->e_xwas = x1;
1048 x->gl_editor->e_ywas = y1;
1050 else
1052 if (glist_isselected(x, y))
1053 glist_deselect(x, y);
1054 else glist_select(x, y);
1058 else
1060 /* look for an outlet */
1061 int noutlet;
1062 if (ob && (noutlet = obj_noutlets(ob)) && ypos >= y2-4)
1064 int width = x2 - x1;
1065 int nout1 = (noutlet > 1 ? noutlet - 1 : 1);
1066 int closest = ((xpos-x1) * (nout1) + width/2)/width;
1067 int hotspot = x1 +
1068 (width - IOWIDTH) * closest / (nout1);
1069 if (closest < noutlet &&
1070 xpos >= (hotspot-1) && xpos <= hotspot + (IOWIDTH+1))
1072 if (doit)
1074 int issignal = obj_issignaloutlet(ob, closest);
1075 x->gl_editor->e_onmotion = MA_CONNECT;
1076 x->gl_editor->e_xwas = xpos;
1077 x->gl_editor->e_ywas = ypos;
1078 sys_vgui(
1079 ".x%x.c create line %d %d %d %d -width %d -tags x\n",
1080 x, xpos, ypos, xpos, ypos,
1081 (issignal ? 2 : 1));
1083 else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT);
1085 else if (doit)
1086 goto nooutletafterall;
1088 /* not in an outlet; select and move */
1089 else if (doit)
1091 t_rtext *rt;
1092 /* check if the box is being text edited */
1093 nooutletafterall:
1094 if (ob && (rt = x->gl_editor->e_textedfor) &&
1095 rt == glist_findrtext(x, ob))
1097 rtext_mouse(rt, xpos - x1, ypos - y1, RTEXT_DOWN);
1098 x->gl_editor->e_onmotion = MA_DRAGTEXT;
1099 x->gl_editor->e_xwas = x1;
1100 x->gl_editor->e_ywas = y1;
1102 else
1104 /* otherwise select and drag to displace */
1105 if (!glist_isselected(x, y))
1107 glist_noselect(x);
1108 glist_select(x, y);
1110 x->gl_editor->e_onmotion = MA_MOVE;
1113 else canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
1115 return;
1117 /* if right click doesn't hit any boxes, call rightclick
1118 routine anyway */
1119 if (rightclick)
1120 canvas_rightclick(x, xpos, ypos, 0);
1122 /* if not an editing action, and if we didn't hit a
1123 box, set cursor and return */
1124 if (runmode || rightclick)
1126 canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
1127 return;
1129 /* having failed to find a box, we try lines now. */
1130 if (!runmode && !altmod && !shiftmod)
1132 t_linetraverser t;
1133 t_outconnect *oc;
1134 float fx = xpos, fy = ypos;
1135 t_glist *glist2 = glist_getcanvas(x);
1136 linetraverser_start(&t, glist2);
1137 while (oc = linetraverser_next(&t))
1139 float lx1 = t.tr_lx1, ly1 = t.tr_ly1,
1140 lx2 = t.tr_lx2, ly2 = t.tr_ly2;
1141 float area = (lx2 - lx1) * (fy - ly1) -
1142 (ly2 - ly1) * (fx - lx1);
1143 float dsquare = (lx2-lx1) * (lx2-lx1) + (ly2-ly1) * (ly2-ly1);
1144 if (area * area >= 50 * dsquare) continue;
1145 if ((lx2-lx1) * (fx-lx1) + (ly2-ly1) * (fy-ly1) < 0) continue;
1146 if ((lx2-lx1) * (lx2-fx) + (ly2-ly1) * (ly2-fy) < 0) continue;
1147 if (doit)
1149 glist_selectline(glist2, oc,
1150 canvas_getindex(glist2, &t.tr_ob->ob_g), t.tr_outno,
1151 canvas_getindex(glist2, &t.tr_ob2->ob_g), t.tr_inno);
1153 canvas_setcursor(x, CURSOR_EDITMODE_DISCONNECT);
1154 return;
1157 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
1158 if (doit)
1160 if (!shiftmod) glist_noselect(x);
1161 sys_vgui(".x%x.c create rectangle %d %d %d %d -tags x\n",
1162 x, xpos, ypos, xpos, ypos);
1163 x->gl_editor->e_xwas = xpos;
1164 x->gl_editor->e_ywas = ypos;
1165 x->gl_editor->e_onmotion = MA_REGION;
1169 void canvas_mousedown(t_canvas *x, t_floatarg xpos, t_floatarg ypos,
1170 t_floatarg which, t_floatarg mod)
1172 canvas_doclick(x, xpos, ypos, which, mod, 1);
1175 int canvas_isconnected (t_canvas *x, t_text *ob1, int n1,
1176 t_text *ob2, int n2)
1178 t_linetraverser t;
1179 t_outconnect *oc;
1180 linetraverser_start(&t, x);
1181 while (oc = linetraverser_next(&t))
1182 if (t.tr_ob == ob1 && t.tr_outno == n1 &&
1183 t.tr_ob2 == ob2 && t.tr_inno == n2)
1184 return (1);
1185 return (0);
1188 void canvas_doconnect(t_canvas *x, int xpos, int ypos, int which, int doit)
1190 int x11, y11, x12, y12;
1191 t_gobj *y1;
1192 int x21, y21, x22, y22;
1193 t_gobj *y2;
1194 int xwas = x->gl_editor->e_xwas,
1195 ywas = x->gl_editor->e_ywas;
1196 if (doit) sys_vgui(".x%x.c delete x\n", x);
1197 else sys_vgui(".x%x.c coords x %d %d %d %d\n",
1198 x, x->gl_editor->e_xwas,
1199 x->gl_editor->e_ywas, xpos, ypos);
1201 if ((y1 = canvas_findhitbox(x, xwas, ywas, &x11, &y11, &x12, &y12))
1202 && (y2 = canvas_findhitbox(x, xpos, ypos, &x21, &y21, &x22, &y22)))
1204 t_object *ob1 = pd_checkobject(&y1->g_pd);
1205 t_object *ob2 = pd_checkobject(&y2->g_pd);
1206 int noutlet1, ninlet2;
1207 if (ob1 && ob2 && ob1 != ob2 &&
1208 (noutlet1 = obj_noutlets(ob1))
1209 && (ninlet2 = obj_ninlets(ob2)))
1211 int width1 = x12 - x11, closest1, hotspot1;
1212 int width2 = x22 - x21, closest2, hotspot2;
1213 int lx1, lx2, ly1, ly2;
1214 t_outconnect *oc;
1216 if (noutlet1 > 1)
1218 closest1 = ((xwas-x11) * (noutlet1-1) + width1/2)/width1;
1219 hotspot1 = x11 +
1220 (width1 - IOWIDTH) * closest1 / (noutlet1-1);
1222 else closest1 = 0, hotspot1 = x11;
1224 if (ninlet2 > 1)
1226 closest2 = ((xpos-x21) * (ninlet2-1) + width2/2)/width2;
1227 hotspot2 = x21 +
1228 (width2 - IOWIDTH) * closest2 / (ninlet2-1);
1230 else closest2 = 0, hotspot2 = x21;
1232 if (closest1 >= noutlet1)
1233 closest1 = noutlet1 - 1;
1234 if (closest2 >= ninlet2)
1235 closest2 = ninlet2 - 1;
1237 if (canvas_isconnected (x, ob1, closest1, ob2, closest2))
1239 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
1240 return;
1242 if (obj_issignaloutlet(ob1, closest1) &&
1243 !obj_issignalinlet(ob2, closest2))
1245 if (doit)
1246 error("can't connect signal outlet to control inlet");
1247 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
1248 return;
1250 if (doit)
1252 oc = obj_connect(ob1, closest1, ob2, closest2);
1253 lx1 = x11 + (noutlet1 > 1 ?
1254 ((x12-x11-IOWIDTH) * closest1)/(noutlet1-1) : 0)
1255 + IOMIDDLE;
1256 ly1 = y12;
1257 lx2 = x21 + (ninlet2 > 1 ?
1258 ((x22-x21-IOWIDTH) * closest2)/(ninlet2-1) : 0)
1259 + IOMIDDLE;
1260 ly2 = y21;
1261 sys_vgui(".x%x.c create line %d %d %d %d -width %d -tags l%x\n",
1262 glist_getcanvas(x),
1263 lx1, ly1, lx2, ly2,
1264 (obj_issignaloutlet(ob1, closest1) ? 2 : 1), oc);
1265 canvas_setundo(x, canvas_undo_connect,
1266 canvas_undo_set_connect(x,
1267 canvas_getindex(x, &ob1->ob_g), closest1,
1268 canvas_getindex(x, &ob2->ob_g), closest2),
1269 "connect");
1271 else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT);
1272 return;
1275 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
1278 void canvas_selectinrect(t_canvas *x, int lox, int loy, int hix, int hiy)
1280 t_gobj *y;
1281 for (y = x->gl_list; y; y = y->g_next)
1283 int x1, y1, x2, y2;
1284 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
1285 if (hix >= x1 && lox <= x2 && hiy >= y1 && loy <= y2
1286 && !glist_isselected(x, y))
1287 glist_select(x, y);
1291 static void canvas_doregion(t_canvas *x, int xpos, int ypos, int doit)
1293 if (doit)
1295 int lox, loy, hix, hiy;
1296 if (x->gl_editor->e_xwas < xpos)
1297 lox = x->gl_editor->e_xwas, hix = xpos;
1298 else hix = x->gl_editor->e_xwas, lox = xpos;
1299 if (x->gl_editor->e_ywas < ypos)
1300 loy = x->gl_editor->e_ywas, hiy = ypos;
1301 else hiy = x->gl_editor->e_ywas, loy = ypos;
1302 canvas_selectinrect(x, lox, loy, hix, hiy);
1303 sys_vgui(".x%x.c delete x\n", x);
1304 x->gl_editor->e_onmotion = 0;
1306 else sys_vgui(".x%x.c coords x %d %d %d %d\n",
1307 x, x->gl_editor->e_xwas,
1308 x->gl_editor->e_ywas, xpos, ypos);
1311 void canvas_mouseup(t_canvas *x,
1312 t_floatarg fxpos, t_floatarg fypos, t_floatarg fwhich)
1314 t_gobj *y;
1316 int xpos = fxpos, ypos = fypos, which = fwhich;
1318 if (!x->gl_editor)
1320 bug("editor");
1321 return;
1324 #ifdef SIMULATERIGHTCLICK
1325 canvas_upclicktime = sys_getrealtime();
1326 canvas_upx = xpos;
1327 canvas_upy = ypos;
1328 #endif
1330 if (x->gl_editor->e_onmotion == MA_CONNECT)
1331 canvas_doconnect(x, xpos, ypos, which, 1);
1332 else if (x->gl_editor->e_onmotion == MA_REGION)
1333 canvas_doregion(x, xpos, ypos, 1);
1334 else if (x->gl_editor->e_onmotion == MA_MOVE)
1336 /* after motion, if there's only one item selected, activate it */
1337 if (x->gl_editor->e_selection &&
1338 !(x->gl_editor->e_selection->sel_next))
1339 gobj_activate(x->gl_editor->e_selection->sel_what,
1340 x, 1);
1342 else if (x->gl_editor->e_onmotion == MA_PASSOUT)
1343 x->gl_editor->e_onmotion = 0;
1344 x->gl_editor->e_onmotion = MA_NONE;
1347 #if 1
1348 /* GG misused the (unused) dbl parameter to tell if mouseup */
1350 for (y = x->gl_list; y; y = y->g_next) {
1351 int x1, y1, x2, y2, clickreturned = 0;
1353 /* check if the object wants to be clicked */
1354 if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2)
1355 && (clickreturned = gobj_click(y, x, xpos, ypos,
1356 0, 0, 1, 0)))
1357 break;
1359 #endif
1364 /* displace the selection by (dx, dy) pixels */
1365 static void canvas_displaceselection(t_canvas *x, int dx, int dy)
1367 t_selection *y;
1368 int resortin = 0, resortout = 0;
1369 if (!canvas_undo_already_set_move)
1371 canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, 1),
1372 "motion");
1373 canvas_undo_already_set_move = 1;
1375 for (y = x->gl_editor->e_selection; y; y = y->sel_next)
1377 t_class *cl = pd_class(&y->sel_what->g_pd);
1378 gobj_displace(y->sel_what, x, dx, dy);
1379 if (cl == vinlet_class) resortin = 1;
1380 else if (cl == voutlet_class) resortout = 1;
1382 if (resortin) canvas_resortinlets(x);
1383 if (resortout) canvas_resortoutlets(x);
1384 canvas_dirty(x, 1);
1387 /* this routine is called whenever a key is pressed or released. "x"
1388 may be zero if there's no current canvas. The first argument is true or
1389 fals for down/up; the second one is either a symbolic key name (e.g.,
1390 "Right" or an Ascii key number. */
1391 void canvas_key(t_canvas *x, t_symbol *s, int ac, t_atom *av)
1393 static t_symbol *keynumsym, *keyupsym, *keynamesym;
1394 int keynum, fflag;
1395 t_symbol *gotkeysym;
1397 int down, shift;
1399 if (ac < 3)
1400 return;
1401 if (!x->gl_editor)
1403 bug("editor");
1404 return;
1406 canvas_undo_already_set_move = 0;
1407 down = (atom_getfloat(av) != 0); /* nonzero if it's a key down */
1408 shift = (atom_getfloat(av+2) != 0); /* nonzero if shift-ed */
1409 if (av[1].a_type == A_SYMBOL)
1410 gotkeysym = av[1].a_w.w_symbol;
1411 else if (av[1].a_type == A_FLOAT)
1413 char buf[3];
1414 sprintf(buf, "%c", (int)(av[1].a_w.w_float));
1415 gotkeysym = gensym(buf);
1417 else gotkeysym = gensym("?");
1418 fflag = (av[0].a_type == A_FLOAT ? av[0].a_w.w_float : 0);
1419 keynum = (av[1].a_type == A_FLOAT ? av[1].a_w.w_float : 0);
1420 if (keynum == '\\' || keynum == '{' || keynum == '}')
1422 post("%c: dropped", (int)keynum);
1423 return;
1425 if (keynum == '\r') keynum = '\n';
1426 if (av[1].a_type == A_SYMBOL &&
1427 !strcmp(av[1].a_w.w_symbol->s_name, "Return"))
1428 keynum = '\n';
1429 if (!keynumsym)
1431 keynumsym = gensym("#key");
1432 keyupsym = gensym("#keyup");
1433 keynamesym = gensym("#keyname");
1435 #ifdef MACOSX
1436 if (keynum == 30)
1437 keynum = 0, gotkeysym = gensym("Up");
1438 else if (keynum == 31)
1439 keynum = 0, gotkeysym = gensym("Down");
1440 else if (keynum == 28)
1441 keynum = 0, gotkeysym = gensym("Left");
1442 else if (keynum == 29)
1443 keynum = 0, gotkeysym = gensym("Right");
1444 #endif
1445 if (keynumsym->s_thing && down)
1446 pd_float(keynumsym->s_thing, (float)keynum);
1447 if (keyupsym->s_thing && !down)
1448 pd_float(keyupsym->s_thing, (float)keynum);
1449 if (keynamesym->s_thing)
1451 t_atom at[2];
1452 at[0] = av[0];
1453 SETFLOAT(at, down);
1454 SETSYMBOL(at+1, gotkeysym);
1455 pd_list(keynamesym->s_thing, 0, 2, at);
1457 if (!x->gl_editor) /* if that 'invis'ed the window, we'd better stop. */
1458 return;
1459 if (x && down)
1461 /* if an object has "grabbed" keys just send them on */
1462 if (x->gl_editor->e_grab
1463 && x->gl_editor->e_keyfn && keynum)
1464 (* x->gl_editor->e_keyfn)
1465 (x->gl_editor->e_grab, (float)keynum);
1466 /* if a text editor is open send it on */
1467 else if (x->gl_editor->e_textedfor)
1469 if (!x->gl_editor->e_textdirty)
1471 canvas_setundo(x, canvas_undo_cut,
1472 canvas_undo_set_cut(x, UCUT_TEXT), "typing");
1474 rtext_key(x->gl_editor->e_textedfor,
1475 (int)keynum, gotkeysym);
1476 if (x->gl_editor->e_textdirty)
1477 canvas_dirty(x, 1);
1479 /* check for backspace or clear */
1480 else if (keynum == 8 || keynum == 127)
1482 if (x->gl_editor->e_selectedline)
1483 canvas_clearline(x);
1484 else if (x->gl_editor->e_selection)
1486 canvas_setundo(x, canvas_undo_cut,
1487 canvas_undo_set_cut(x, UCUT_CLEAR), "clear");
1488 canvas_doclear(x);
1491 /* check for arrow keys */
1492 else if (!strcmp(gotkeysym->s_name, "Up"))
1493 canvas_displaceselection(x, 0, shift ? -10 : -1);
1494 else if (!strcmp(gotkeysym->s_name, "Down"))
1495 canvas_displaceselection(x, 0, shift ? 10 : 1);
1496 else if (!strcmp(gotkeysym->s_name, "Left"))
1497 canvas_displaceselection(x, shift ? -10 : -1, 0);
1498 else if (!strcmp(gotkeysym->s_name, "Right"))
1499 canvas_displaceselection(x, shift ? 10 : 1, 0);
1503 void canvas_motion(t_canvas *x, t_floatarg xpos, t_floatarg ypos,
1504 t_floatarg fmod)
1506 /* post("motion %d %d", xpos, ypos); */
1507 int mod = fmod;
1508 if (!x->gl_editor)
1510 bug("editor");
1511 return;
1513 glist_setlastxy(x, xpos, ypos);
1514 if (x->gl_editor->e_onmotion == MA_MOVE)
1516 canvas_displaceselection(x,
1517 xpos - x->gl_editor->e_xwas, ypos - x->gl_editor->e_ywas);
1518 x->gl_editor->e_xwas = xpos;
1519 x->gl_editor->e_ywas = ypos;
1521 else if (x->gl_editor->e_onmotion == MA_REGION)
1522 canvas_doregion(x, xpos, ypos, 0);
1523 else if (x->gl_editor->e_onmotion == MA_CONNECT)
1524 canvas_doconnect(x, xpos, ypos, 0, 0);
1525 else if (x->gl_editor->e_onmotion == MA_PASSOUT)
1527 if (!x->gl_editor->e_motionfn)
1528 bug("e_motionfn");
1529 (*x->gl_editor->e_motionfn)(&x->gl_editor->e_grab->g_pd,
1530 xpos - x->gl_editor->e_xwas,
1531 ypos - x->gl_editor->e_ywas);
1532 x->gl_editor->e_xwas = xpos;
1533 x->gl_editor->e_ywas = ypos;
1535 else if (x->gl_editor->e_onmotion == MA_DRAGTEXT)
1537 t_rtext *rt = x->gl_editor->e_textedfor;
1538 if (rt)
1539 rtext_mouse(rt, xpos - x->gl_editor->e_xwas,
1540 ypos - x->gl_editor->e_ywas, RTEXT_DRAG);
1542 else canvas_doclick(x, xpos, ypos, 0, mod, 0);
1544 x->gl_editor->e_lastmoved = 1;
1547 void canvas_startmotion(t_canvas *x)
1549 int xval, yval;
1550 if (!x->gl_editor) return;
1551 glist_getnextxy(x, &xval, &yval);
1552 if (xval == 0 && yval == 0) return;
1553 x->gl_editor->e_onmotion = MA_MOVE;
1554 x->gl_editor->e_xwas = xval;
1555 x->gl_editor->e_ywas = yval;
1558 /* ----------------------------- window stuff ----------------------- */
1560 void canvas_print(t_canvas *x, t_symbol *s)
1562 if (*s->s_name) sys_vgui(".x%x.c postscript -file %s\n", x, s->s_name);
1563 else sys_vgui(".x%x.c postscript -file x.ps\n", x);
1566 void canvas_menuclose(t_canvas *x, t_floatarg force)
1568 if (x->gl_owner)
1569 canvas_vis(x, 0);
1570 else if ((force != 0) || (!x->gl_dirty))
1571 pd_free(&x->gl_pd);
1572 else sys_vgui("pdtk_check {This window has been modified. Close anyway?}\
1573 {.x%x menuclose 1;\n}\n", x);
1576 /* put up a dialog which may call canvas_font back to do the work */
1577 static void canvas_menufont(t_canvas *x)
1579 char buf[80];
1580 t_canvas *x2 = canvas_getrootfor(x);
1581 gfxstub_deleteforkey(x2);
1582 sprintf(buf, "pdtk_canvas_dofont %%s %d\n", x2->gl_font);
1583 gfxstub_new(&x2->gl_pd, &x2->gl_pd, buf);
1586 static int canvas_find_index1, canvas_find_index2;
1587 static t_binbuf *canvas_findbuf;
1588 int binbuf_match(t_binbuf *inbuf, t_binbuf *searchbuf);
1590 /* find an atom or string of atoms */
1591 static int canvas_dofind(t_canvas *x, int *myindex1p)
1593 t_gobj *y;
1594 int myindex1 = *myindex1p, myindex2;
1595 if (myindex1 >= canvas_find_index1)
1597 for (y = x->gl_list, myindex2 = 0; y;
1598 y = y->g_next, myindex2++)
1600 t_object *ob = 0;
1601 if (ob = pd_checkobject(&y->g_pd))
1603 if (binbuf_match(ob->ob_binbuf, canvas_findbuf))
1605 if (myindex1 > canvas_find_index1 ||
1606 myindex1 == canvas_find_index1 &&
1607 myindex2 > canvas_find_index2)
1609 canvas_find_index1 = myindex1;
1610 canvas_find_index2 = myindex2;
1611 glist_noselect(x);
1612 canvas_vis(x, 1);
1613 canvas_editmode(x, 1.);
1614 glist_select(x, y);
1615 return (1);
1621 for (y = x->gl_list, myindex2 = 0; y; y = y->g_next, myindex2++)
1623 if (pd_class(&y->g_pd) == canvas_class)
1625 (*myindex1p)++;
1626 if (canvas_dofind((t_canvas *)y, myindex1p))
1627 return (1);
1630 return (0);
1633 static void canvas_find(t_canvas *x, t_symbol *s, int ac, t_atom *av)
1635 int myindex1 = 0, i;
1636 for (i = 0; i < ac; i++)
1638 if (av[i].a_type == A_SYMBOL)
1640 if (!strcmp(av[i].a_w.w_symbol->s_name, "_semi_"))
1641 SETSEMI(&av[i]);
1642 else if (!strcmp(av[i].a_w.w_symbol->s_name, "_comma_"))
1643 SETCOMMA(&av[i]);
1646 if (!canvas_findbuf)
1647 canvas_findbuf = binbuf_new();
1648 binbuf_clear(canvas_findbuf);
1649 binbuf_add(canvas_findbuf, ac, av);
1650 canvas_find_index1 = 0;
1651 canvas_find_index2 = -1;
1652 canvas_whichfind = x;
1653 if (!canvas_dofind(x, &myindex1))
1655 binbuf_print(canvas_findbuf);
1656 post("... couldn't find");
1660 static void canvas_find_again(t_canvas *x)
1662 int myindex1 = 0;
1663 if (!canvas_findbuf || !canvas_whichfind)
1664 return;
1665 if (!canvas_dofind(canvas_whichfind, &myindex1))
1667 binbuf_print(canvas_findbuf);
1668 post("... couldn't find");
1672 static void canvas_find_parent(t_canvas *x)
1674 if (x->gl_owner)
1675 canvas_vis(glist_getcanvas(x->gl_owner), 1);
1678 static int glist_dofinderror(t_glist *gl, void *error_object)
1680 t_gobj *g;
1681 for (g = gl->gl_list; g; g = g->g_next)
1683 if ((void *)g == error_object)
1685 /* got it... now show it. */
1686 glist_noselect(gl);
1687 canvas_vis(glist_getcanvas(gl), 1);
1688 canvas_editmode(glist_getcanvas(gl), 1.);
1689 glist_select(gl, g);
1690 return (1);
1692 else if (g->g_pd == canvas_class)
1694 if (glist_dofinderror((t_canvas *)g, error_object))
1695 return (1);
1698 return (0);
1701 void canvas_finderror(void *error_object)
1703 t_canvas *x;
1704 /* find all root canvases */
1705 for (x = canvas_list; x; x = x->gl_next)
1707 if (glist_dofinderror(x, error_object))
1708 return;
1710 post("... sorry, I couldn't find the source of that error.");
1713 void canvas_stowconnections(t_canvas *x)
1715 t_gobj *selhead = 0, *seltail = 0, *nonhead = 0, *nontail = 0, *y, *y2;
1716 t_linetraverser t;
1717 t_outconnect *oc;
1718 if (!x->gl_editor) return;
1719 /* split list to "selected" and "unselected" parts */
1720 for (y = x->gl_list; y; y = y2)
1722 y2 = y->g_next;
1723 if (glist_isselected(x, y))
1725 if (seltail)
1727 seltail->g_next = y;
1728 seltail = y;
1729 y->g_next = 0;
1731 else
1733 selhead = seltail = y;
1734 seltail->g_next = 0;
1737 else
1739 if (nontail)
1741 nontail->g_next = y;
1742 nontail = y;
1743 y->g_next = 0;
1745 else
1747 nonhead = nontail = y;
1748 nontail->g_next = 0;
1752 /* move the selected part to the end */
1753 if (!nonhead) x->gl_list = selhead;
1754 else x->gl_list = nonhead, nontail->g_next = selhead;
1756 /* add connections to binbuf */
1757 binbuf_clear(x->gl_editor->e_connectbuf);
1758 linetraverser_start(&t, x);
1759 while (oc = linetraverser_next(&t))
1761 int s1 = glist_isselected(x, &t.tr_ob->ob_g);
1762 int s2 = glist_isselected(x, &t.tr_ob2->ob_g);
1763 if (s1 != s2)
1764 binbuf_addv(x->gl_editor->e_connectbuf, "ssiiii;",
1765 gensym("#X"), gensym("connect"),
1766 glist_getindex(x, &t.tr_ob->ob_g), t.tr_outno,
1767 glist_getindex(x, &t.tr_ob2->ob_g), t.tr_inno);
1771 void canvas_restoreconnections(t_canvas *x)
1773 pd_bind(&x->gl_pd, gensym("#X"));
1774 binbuf_eval(x->gl_editor->e_connectbuf, 0, 0, 0);
1775 pd_unbind(&x->gl_pd, gensym("#X"));
1778 static t_binbuf *canvas_docopy(t_canvas *x)
1780 t_gobj *y;
1781 t_linetraverser t;
1782 t_outconnect *oc;
1783 t_binbuf *b = binbuf_new();
1784 for (y = x->gl_list; y; y = y->g_next)
1786 if (glist_isselected(x, y))
1787 gobj_save(y, b);
1789 linetraverser_start(&t, x);
1790 while (oc = linetraverser_next(&t))
1792 if (glist_isselected(x, &t.tr_ob->ob_g)
1793 && glist_isselected(x, &t.tr_ob2->ob_g))
1795 binbuf_addv(b, "ssiiii;", gensym("#X"), gensym("connect"),
1796 glist_selectionindex(x, &t.tr_ob->ob_g, 1), t.tr_outno,
1797 glist_selectionindex(x, &t.tr_ob2->ob_g, 1), t.tr_inno);
1800 return (b);
1803 static void canvas_copy(t_canvas *x)
1805 if (!x->gl_editor || !x->gl_editor->e_selection)
1806 return;
1807 binbuf_free(copy_binbuf);
1808 copy_binbuf = canvas_docopy(x);
1811 static void canvas_clearline(t_canvas *x)
1813 if (x->gl_editor->e_selectedline)
1815 canvas_disconnect(x, x->gl_editor->e_selectline_index1,
1816 x->gl_editor->e_selectline_outno,
1817 x->gl_editor->e_selectline_index2,
1818 x->gl_editor->e_selectline_inno);
1819 canvas_setundo(x, canvas_undo_disconnect,
1820 canvas_undo_set_disconnect(x,
1821 x->gl_editor->e_selectline_index1,
1822 x->gl_editor->e_selectline_outno,
1823 x->gl_editor->e_selectline_index2,
1824 x->gl_editor->e_selectline_inno),
1825 "disconnect");
1829 extern t_pd *newest;
1830 static void canvas_doclear(t_canvas *x)
1832 t_gobj *y, *y2;
1833 int dspstate;
1835 dspstate = canvas_suspend_dsp();
1836 if (x->gl_editor->e_selectedline)
1838 canvas_disconnect(x, x->gl_editor->e_selectline_index1,
1839 x->gl_editor->e_selectline_outno,
1840 x->gl_editor->e_selectline_index2,
1841 x->gl_editor->e_selectline_inno);
1842 canvas_setundo(x, canvas_undo_disconnect,
1843 canvas_undo_set_disconnect(x,
1844 x->gl_editor->e_selectline_index1,
1845 x->gl_editor->e_selectline_outno,
1846 x->gl_editor->e_selectline_index2,
1847 x->gl_editor->e_selectline_inno),
1848 "disconnect");
1850 /* if text is selected, deselecting it might remake the
1851 object. So we deselect it and hunt for a "new" object on
1852 the glist to reselect. */
1853 if (x->gl_editor->e_textedfor)
1855 newest = 0;
1856 glist_noselect(x);
1857 if (newest)
1859 for (y = x->gl_list; y; y = y->g_next)
1860 if (&y->g_pd == newest) glist_select(x, y);
1863 while (1) /* this is pretty wierd... should rewrite it */
1865 for (y = x->gl_list; y; y = y2)
1867 y2 = y->g_next;
1868 if (glist_isselected(x, y))
1870 glist_delete(x, y);
1871 #if 0
1872 if (y2) post("cut 5 %x %x", y2, y2->g_next);
1873 else post("cut 6");
1874 #endif
1875 goto next;
1878 goto restore;
1879 next: ;
1881 restore:
1882 canvas_resume_dsp(dspstate);
1883 canvas_dirty(x, 1);
1886 static void canvas_cut(t_canvas *x)
1888 if (x->gl_editor && x->gl_editor->e_selectedline)
1889 canvas_clearline(x);
1890 else if (x->gl_editor && x->gl_editor->e_selection)
1892 canvas_setundo(x, canvas_undo_cut,
1893 canvas_undo_set_cut(x, UCUT_CUT), "cut");
1894 canvas_copy(x);
1895 canvas_doclear(x);
1899 static int paste_onset;
1900 static t_canvas *paste_canvas;
1902 static void glist_donewloadbangs(t_glist *x)
1904 if (x->gl_editor)
1906 t_selection *sel;
1907 for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
1908 if (pd_class(&sel->sel_what->g_pd) == canvas_class)
1909 canvas_loadbang((t_canvas *)(&sel->sel_what->g_pd));
1913 static void canvas_dopaste(t_canvas *x, t_binbuf *b)
1915 t_gobj *newgobj, *last, *g2;
1916 int dspstate = canvas_suspend_dsp(), nbox, count;
1918 canvas_editmode(x, 1.);
1919 glist_noselect(x);
1920 for (g2 = x->gl_list, nbox = 0; g2; g2 = g2->g_next) nbox++;
1922 paste_onset = nbox;
1923 paste_canvas = x;
1925 pd_bind(&x->gl_pd, gensym("#X"));
1926 binbuf_eval(b, 0, 0, 0);
1927 pd_unbind(&x->gl_pd, gensym("#X"));
1928 for (g2 = x->gl_list, count = 0; g2; g2 = g2->g_next, count++)
1929 if (count >= nbox)
1930 glist_select(x, g2);
1931 paste_canvas = 0;
1932 canvas_resume_dsp(dspstate);
1933 canvas_dirty(x, 1);
1934 glist_donewloadbangs(x);
1937 static void canvas_paste(t_canvas *x)
1939 canvas_setundo(x, canvas_undo_paste, canvas_undo_set_paste(x), "paste");
1940 canvas_dopaste(x, copy_binbuf);
1943 static void canvas_duplicate(t_canvas *x)
1945 if (x->gl_editor->e_onmotion == MA_NONE)
1947 t_selection *y;
1948 canvas_copy(x);
1949 canvas_setundo(x, canvas_undo_paste, canvas_undo_set_paste(x),
1950 "duplicate");
1951 canvas_dopaste(x, copy_binbuf);
1952 for (y = x->gl_editor->e_selection; y; y = y->sel_next)
1953 gobj_displace(y->sel_what, x,
1954 10, 10);
1955 canvas_dirty(x, 1);
1959 static void canvas_selectall(t_canvas *x)
1961 t_gobj *y;
1962 if (!x->gl_edit)
1963 canvas_editmode(x, 1);
1964 for (y = x->gl_list; y; y = y->g_next)
1966 if (!glist_isselected(x, y))
1967 glist_select(x, y);
1971 void canvas_connect(t_canvas *x, t_floatarg fwhoout, t_floatarg foutno,
1972 t_floatarg fwhoin, t_floatarg finno)
1974 int whoout = fwhoout, outno = foutno, whoin = fwhoin, inno = finno;
1975 t_gobj *src = 0, *sink = 0;
1976 t_object *objsrc, *objsink;
1977 t_outconnect *oc;
1978 int nin = whoin, nout = whoout;
1979 if (paste_canvas == x) whoout += paste_onset, whoin += paste_onset;
1980 for (src = x->gl_list; whoout; src = src->g_next, whoout--)
1981 if (!src->g_next) goto bad; /* bug fix thanks to Hannes */
1982 for (sink = x->gl_list; whoin; sink = sink->g_next, whoin--)
1983 if (!sink->g_next) goto bad;
1984 if (!(objsrc = pd_checkobject(&src->g_pd)) ||
1985 !(objsink = pd_checkobject(&sink->g_pd)))
1986 goto bad;
1987 if (!(oc = obj_connect(objsrc, outno, objsink, inno))) goto bad;
1988 if (glist_isvisible(x))
1990 sys_vgui(".x%x.c create line %d %d %d %d -width %d -tags l%x\n",
1991 glist_getcanvas(x), 0, 0, 0, 0,
1992 (obj_issignaloutlet(objsrc, outno) ? 2 : 1),oc);
1993 canvas_fixlinesfor(x, objsrc);
1995 return;
1997 bad:
1998 post("%s %d %d %d %d (%s->%s) connection failed",
1999 x->gl_name->s_name, nout, outno, nin, inno,
2000 (src? class_getname(pd_class(&src->g_pd)) : "???"),
2001 (sink? class_getname(pd_class(&sink->g_pd)) : "???"));
2004 #define XTOLERANCE 4
2005 #define YTOLERANCE 3
2006 #define NHIST 15
2008 /* LATER might have to speed this up */
2009 static void canvas_tidy(t_canvas *x)
2011 t_gobj *y, *y2, *y3;
2012 int ax1, ay1, ax2, ay2, bx1, by1, bx2, by2;
2013 int histogram[NHIST], *ip, i, besthist, bestdist;
2014 /* if nobody is selected, this means do it to all boxes;
2015 othewise just the selection */
2016 int all = (x->gl_editor ? (x->gl_editor->e_selection == 0) : 1);
2018 canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, !all),
2019 "motion");
2021 /* tidy horizontally */
2022 for (y = x->gl_list; y; y = y->g_next)
2023 if (all || glist_isselected(x, y))
2025 gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);
2027 for (y2 = x->gl_list; y2; y2 = y2->g_next)
2028 if (all || glist_isselected(x, y2))
2030 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
2031 if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE &&
2032 bx1 < ax1)
2033 goto nothorizhead;
2036 for (y2 = x->gl_list; y2; y2 = y2->g_next)
2037 if (all || glist_isselected(x, y2))
2039 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
2040 if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE
2041 && by1 != ay1)
2042 gobj_displace(y2, x, 0, ay1-by1);
2044 nothorizhead: ;
2046 /* tidy vertically. First guess the user's favorite vertical spacing */
2047 for (i = NHIST, ip = histogram; i--; ip++) *ip = 0;
2048 for (y = x->gl_list; y; y = y->g_next)
2049 if (all || glist_isselected(x, y))
2051 gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);
2052 for (y2 = x->gl_list; y2; y2 = y2->g_next)
2053 if (all || glist_isselected(x, y2))
2055 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
2056 if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE)
2058 int distance = by1-ay2;
2059 if (distance >= 0 && distance < NHIST)
2060 histogram[distance]++;
2064 for (i = 1, besthist = 0, bestdist = 4, ip = histogram + 1;
2065 i < (NHIST-1); i++, ip++)
2067 int hit = ip[-1] + 2 * ip[0] + ip[1];
2068 if (hit > besthist)
2070 besthist = hit;
2071 bestdist = i;
2074 post("best vertical distance %d", bestdist);
2075 for (y = x->gl_list; y; y = y->g_next)
2076 if (all || glist_isselected(x, y))
2078 int keep = 1;
2079 gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);
2080 for (y2 = x->gl_list; y2; y2 = y2->g_next)
2081 if (all || glist_isselected(x, y2))
2083 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
2084 if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE &&
2085 ay1 >= by2 - 10 && ay1 < by2 + NHIST)
2086 goto nothead;
2088 while (keep)
2090 keep = 0;
2091 for (y2 = x->gl_list; y2; y2 = y2->g_next)
2092 if (all || glist_isselected(x, y2))
2094 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
2095 if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE &&
2096 by1 > ay1 && by1 < ay2 + NHIST)
2098 int vmove = ay2 + bestdist - by1;
2099 gobj_displace(y2, x, ax1-bx1, vmove);
2100 ay1 = by1 + vmove;
2101 ay2 = by2 + vmove;
2102 keep = 1;
2103 break;
2107 nothead: ;
2109 canvas_dirty(x, 1);
2112 static void canvas_texteditor(t_canvas *x)
2114 t_rtext *foo;
2115 char *buf;
2116 int bufsize;
2117 if (foo = x->gl_editor->e_textedfor)
2118 rtext_gettext(foo, &buf, &bufsize);
2119 else buf = "", bufsize = 0;
2120 sys_vgui("pdtk_pd_texteditor {%.*s}\n", bufsize, buf);
2124 void glob_key(void *dummy, t_symbol *s, int ac, t_atom *av)
2126 /* canvas_editing can be zero; canvas_key checks for that */
2127 canvas_key(canvas_editing, s, ac, av);
2130 void canvas_editmode(t_canvas *x, t_floatarg fyesplease)
2132 int yesplease = fyesplease;
2133 if (yesplease && x->gl_edit)
2134 return;
2135 x->gl_edit = !x->gl_edit;
2136 if (x->gl_edit && glist_isvisible(x) && glist_istoplevel(x))
2137 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
2138 else
2140 glist_noselect(x);
2141 if (glist_isvisible(x) && glist_istoplevel(x))
2142 canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
2144 sys_vgui("pdtk_canvas_editval .x%x %d\n",
2145 glist_getcanvas(x), x->gl_edit);
2148 /* called by canvas_font below */
2149 static void canvas_dofont(t_canvas *x, t_floatarg font, t_floatarg xresize,
2150 t_floatarg yresize)
2152 t_gobj *y;
2153 x->gl_font = font;
2154 if (xresize != 1 || yresize != 1)
2156 canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, 0),
2157 "motion");
2158 for (y = x->gl_list; y; y = y->g_next)
2160 int x1, x2, y1, y2, nx1, ny1;
2161 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
2162 nx1 = x1 * xresize + 0.5;
2163 ny1 = y1 * yresize + 0.5;
2164 gobj_displace(y, x, nx1-x1, ny1-y1);
2167 if (glist_isvisible(x))
2168 glist_redraw(x);
2169 for (y = x->gl_list; y; y = y->g_next)
2170 if (pd_class(&y->g_pd) == canvas_class
2171 && !canvas_isabstraction((t_canvas *)y))
2172 canvas_dofont((t_canvas *)y, font, xresize, yresize);
2175 /* canvas_menufont calls up a TK dialog which calls this back */
2176 static void canvas_font(t_canvas *x, t_floatarg font, t_floatarg resize,
2177 t_floatarg whichresize)
2179 float realresize, realresx = 1, realresy = 1;
2180 t_canvas *x2 = canvas_getrootfor(x);
2181 if (!resize) realresize = 1;
2182 else
2184 if (resize < 20) resize = 20;
2185 if (resize > 500) resize = 500;
2186 realresize = resize * 0.01;
2188 if (whichresize != 3) realresx = realresize;
2189 if (whichresize != 2) realresy = realresize;
2190 canvas_dofont(x2, font, realresx, realresy);
2191 sys_defaultfont = font;
2194 static t_glist *canvas_last_glist;
2195 static int canvas_last_glist_x, canvas_last_glist_y;
2197 void glist_getnextxy(t_glist *gl, int *xpix, int *ypix)
2199 if (canvas_last_glist == gl)
2200 *xpix = canvas_last_glist_x, *ypix = canvas_last_glist_y;
2201 else *xpix = *ypix = 40;
2204 static void glist_setlastxy(t_glist *gl, int xval, int yval)
2206 canvas_last_glist = gl;
2207 canvas_last_glist_x = xval;
2208 canvas_last_glist_y = yval;
2212 void g_editor_setup(void)
2214 /* ------------------------ events ---------------------------------- */
2215 class_addmethod(canvas_class, (t_method)canvas_mousedown, gensym("mouse"),
2216 A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2217 class_addmethod(canvas_class, (t_method)canvas_mouseup, gensym("mouseup"),
2218 A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2219 class_addmethod(canvas_class, (t_method)canvas_key, gensym("key"),
2220 A_GIMME, A_NULL);
2221 class_addmethod(canvas_class, (t_method)canvas_motion, gensym("motion"),
2222 A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2224 /* ------------------------ menu actions ---------------------------- */
2225 class_addmethod(canvas_class, (t_method)canvas_menuclose,
2226 gensym("menuclose"), A_DEFFLOAT, 0);
2227 class_addmethod(canvas_class, (t_method)canvas_cut,
2228 gensym("cut"), A_NULL);
2229 class_addmethod(canvas_class, (t_method)canvas_copy,
2230 gensym("copy"), A_NULL);
2231 class_addmethod(canvas_class, (t_method)canvas_paste,
2232 gensym("paste"), A_NULL);
2233 class_addmethod(canvas_class, (t_method)canvas_duplicate,
2234 gensym("duplicate"), A_NULL);
2235 class_addmethod(canvas_class, (t_method)canvas_selectall,
2236 gensym("selectall"), A_NULL);
2237 class_addmethod(canvas_class, (t_method)canvas_undo,
2238 gensym("undo"), A_NULL);
2239 class_addmethod(canvas_class, (t_method)canvas_redo,
2240 gensym("redo"), A_NULL);
2241 class_addmethod(canvas_class, (t_method)canvas_tidy,
2242 gensym("tidy"), A_NULL);
2243 class_addmethod(canvas_class, (t_method)canvas_texteditor,
2244 gensym("texteditor"), A_NULL);
2245 class_addmethod(canvas_class, (t_method)canvas_editmode,
2246 gensym("editmode"), A_DEFFLOAT, A_NULL);
2247 class_addmethod(canvas_class, (t_method)canvas_print,
2248 gensym("print"), A_SYMBOL, A_NULL);
2249 class_addmethod(canvas_class, (t_method)canvas_menufont,
2250 gensym("menufont"), A_NULL);
2251 class_addmethod(canvas_class, (t_method)canvas_font,
2252 gensym("font"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2253 class_addmethod(canvas_class, (t_method)canvas_find,
2254 gensym("find"), A_GIMME, A_NULL);
2255 class_addmethod(canvas_class, (t_method)canvas_find_again,
2256 gensym("findagain"), A_NULL);
2257 class_addmethod(canvas_class, (t_method)canvas_find_parent,
2258 gensym("findparent"), A_NULL);
2259 class_addmethod(canvas_class, (t_method)canvas_done_popup,
2260 gensym("done-popup"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2261 class_addmethod(canvas_class, (t_method)canvas_donecanvasdialog,
2262 gensym("donecanvasdialog"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2263 class_addmethod(canvas_class, (t_method)glist_arraydialog,
2264 gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2266 /* -------------- connect method used in reading files ------------------ */
2267 class_addmethod(canvas_class, (t_method)canvas_connect,
2268 gensym("connect"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2270 class_addmethod(canvas_class, (t_method)canvas_disconnect,
2271 gensym("disconnect"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2272 /* -------------- copy buffer ------------------ */
2273 copy_binbuf = binbuf_new();
2275 /* Copyright (c) 1997-2001 Miller Puckette and others.
2276 * For information on usage and redistribution, and for a DISCLAIMER OF ALL
2277 * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
2279 #include <stdlib.h>
2280 #include <stdio.h>
2281 #include "m_pd.h"
2282 #include "m_imp.h"
2283 #include "s_stuff.h"
2284 #include "g_canvas.h"
2285 #include <string.h>
2287 void glist_readfrombinbuf(t_glist *x, t_binbuf *b, char *filename,
2288 int selectem);
2290 void open_via_helppath(const char *name, const char *dir);
2291 char *class_gethelpdir(t_class *c);
2293 /* ------------------ forward declarations --------------- */
2294 static void canvas_doclear(t_canvas *x);
2295 static void glist_setlastxy(t_glist *gl, int xval, int yval);
2296 static void glist_donewloadbangs(t_glist *x);
2297 static t_binbuf *canvas_docopy(t_canvas *x);
2298 static void canvas_dopaste(t_canvas *x, t_binbuf *b);
2299 static void canvas_paste(t_canvas *x);
2300 static void canvas_clearline(t_canvas *x);
2301 static t_binbuf *copy_binbuf;
2303 /* ---------------- generic widget behavior ------------------------- */
2305 void gobj_getrect(t_gobj *x, t_glist *glist, int *x1, int *y1,
2306 int *x2, int *y2)
2308 if (x->g_pd->c_wb && x->g_pd->c_wb->w_getrectfn)
2309 (*x->g_pd->c_wb->w_getrectfn)(x, glist, x1, y1, x2, y2);
2312 void gobj_displace(t_gobj *x, t_glist *glist, int dx, int dy)
2314 if (x->g_pd->c_wb && x->g_pd->c_wb->w_displacefn)
2315 (*x->g_pd->c_wb->w_displacefn)(x, glist, dx, dy);
2318 void gobj_select(t_gobj *x, t_glist *glist, int state)
2320 if (x->g_pd->c_wb && x->g_pd->c_wb->w_selectfn)
2321 (*x->g_pd->c_wb->w_selectfn)(x, glist, state);
2324 void gobj_activate(t_gobj *x, t_glist *glist, int state)
2326 if (x->g_pd->c_wb && x->g_pd->c_wb->w_activatefn)
2327 (*x->g_pd->c_wb->w_activatefn)(x, glist, state);
2330 void gobj_delete(t_gobj *x, t_glist *glist)
2332 if (x->g_pd->c_wb && x->g_pd->c_wb->w_deletefn)
2333 (*x->g_pd->c_wb->w_deletefn)(x, glist);
2336 void gobj_vis(t_gobj *x, struct _glist *glist, int flag)
2338 if (x->g_pd->c_wb && x->g_pd->c_wb->w_visfn)
2339 (*x->g_pd->c_wb->w_visfn)(x, glist, flag);
2342 int gobj_click(t_gobj *x, struct _glist *glist,
2343 int xpix, int ypix, int shift, int alt, int dbl, int doit)
2345 if (x->g_pd->c_wb && x->g_pd->c_wb->w_clickfn)
2346 return ((*x->g_pd->c_wb->w_clickfn)(x,
2347 glist, xpix, ypix, shift, alt, dbl, doit));
2348 else return (0);
2351 /* ------------------------ managing the selection ----------------- */
2353 void glist_selectline(t_glist *x, t_outconnect *oc, int index1,
2354 int outno, int index2, int inno)
2356 if (x->gl_editor)
2358 glist_noselect(x);
2359 x->gl_editor->e_selectedline = 1;
2360 x->gl_editor->e_selectline_index1 = index1;
2361 x->gl_editor->e_selectline_outno = outno;
2362 x->gl_editor->e_selectline_index2 = index2;
2363 x->gl_editor->e_selectline_inno = inno;
2364 x->gl_editor->e_selectline_tag = oc;
2365 sys_vgui(".x%x.c itemconfigure l%x -fill blue\n",
2366 x, x->gl_editor->e_selectline_tag);
2370 void glist_deselectline(t_glist *x)
2372 if (x->gl_editor)
2374 x->gl_editor->e_selectedline = 0;
2375 sys_vgui(".x%x.c itemconfigure l%x -fill black\n",
2376 x, x->gl_editor->e_selectline_tag);
2380 int glist_isselected(t_glist *x, t_gobj *y)
2382 if (x->gl_editor)
2384 t_selection *sel;
2385 for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
2386 if (sel->sel_what == y) return (1);
2388 return (0);
2391 /* call this for unselected objects only */
2392 void glist_select(t_glist *x, t_gobj *y)
2394 if (x->gl_editor)
2396 t_selection *sel = (t_selection *)getbytes(sizeof(*sel));
2397 if (x->gl_editor->e_selectedline)
2398 glist_deselectline(x);
2399 /* LATER #ifdef out the following check */
2400 if (glist_isselected(x, y)) bug("glist_select");
2401 sel->sel_next = x->gl_editor->e_selection;
2402 sel->sel_what = y;
2403 x->gl_editor->e_selection = sel;
2404 gobj_select(y, x, 1);
2408 /* call this for selected objects only */
2409 void glist_deselect(t_glist *x, t_gobj *y)
2411 int fixdsp = 0;
2412 static int reenter = 0;
2413 if (reenter) return;
2414 reenter = 1;
2415 if (x->gl_editor)
2417 t_selection *sel, *sel2;
2418 t_rtext *z = 0;
2419 if (!glist_isselected(x, y)) bug("glist_deselect");
2420 if (x->gl_editor->e_textedfor)
2422 t_rtext *fuddy = glist_findrtext(x, (t_text *)y);
2423 if (x->gl_editor->e_textedfor == fuddy)
2425 if (x->gl_editor->e_textdirty)
2427 z = fuddy;
2428 canvas_stowconnections(glist_getcanvas(x));
2430 gobj_activate(y, x, 0);
2432 if (zgetfn(&y->g_pd, gensym("dsp")))
2433 fixdsp = 1;
2435 if ((sel = x->gl_editor->e_selection)->sel_what == y)
2437 x->gl_editor->e_selection = x->gl_editor->e_selection->sel_next;
2438 gobj_select(sel->sel_what, x, 0);
2439 freebytes(sel, sizeof(*sel));
2441 else
2443 for (sel = x->gl_editor->e_selection; sel2 = sel->sel_next;
2444 sel = sel2)
2446 if (sel2->sel_what == y)
2448 sel->sel_next = sel2->sel_next;
2449 gobj_select(sel2->sel_what, x, 0);
2450 freebytes(sel2, sizeof(*sel2));
2451 break;
2455 if (z)
2457 char *buf;
2458 int bufsize;
2460 rtext_gettext(z, &buf, &bufsize);
2461 text_setto((t_text *)y, x, buf, bufsize);
2462 canvas_fixlinesfor(glist_getcanvas(x), (t_text *)y);
2463 x->gl_editor->e_textedfor = 0;
2465 if (fixdsp)
2466 canvas_update_dsp();
2468 reenter = 0;
2471 void glist_noselect(t_glist *x)
2473 if (x->gl_editor)
2475 while (x->gl_editor->e_selection)
2476 glist_deselect(x, x->gl_editor->e_selection->sel_what);
2477 if (x->gl_editor->e_selectedline)
2478 glist_deselectline(x);
2482 void glist_selectall(t_glist *x)
2484 if (x->gl_editor)
2486 glist_noselect(x);
2487 if (x->gl_list)
2489 t_selection *sel = (t_selection *)getbytes(sizeof(*sel));
2490 t_gobj *y = x->gl_list;
2491 x->gl_editor->e_selection = sel;
2492 sel->sel_what = y;
2493 gobj_select(y, x, 1);
2494 while (y = y->g_next)
2496 t_selection *sel2 = (t_selection *)getbytes(sizeof(*sel2));
2497 sel->sel_next = sel2;
2498 sel = sel2;
2499 sel->sel_what = y;
2500 gobj_select(y, x, 1);
2502 sel->sel_next = 0;
2507 /* get the index of a gobj in a glist. If y is zero, return the
2508 total number of objects. */
2509 int glist_getindex(t_glist *x, t_gobj *y)
2511 t_gobj *y2;
2512 int indx;
2514 for (y2 = x->gl_list, indx = 0; y2 && y2 != y; y2 = y2->g_next)
2515 indx++;
2516 return (indx);
2519 /* get the index of the object, among selected items, if "selected"
2520 is set; otherwise, among unselected ones. If y is zero, just
2521 counts the selected or unselected objects. */
2522 int glist_selectionindex(t_glist *x, t_gobj *y, int selected)
2524 t_gobj *y2;
2525 int indx;
2527 for (y2 = x->gl_list, indx = 0; y2 && y2 != y; y2 = y2->g_next)
2528 if (selected == glist_isselected(x, y2))
2529 indx++;
2530 return (indx);
2533 static t_gobj *glist_nth(t_glist *x, int n)
2535 t_gobj *y;
2536 int indx;
2537 for (y = x->gl_list, indx = 0; y; y = y->g_next, indx++)
2538 if (indx == n)
2539 return (y);
2540 return (0);
2543 /* ------------------- support for undo/redo -------------------------- */
2545 static t_undofn canvas_undo_fn; /* current undo function if any */
2546 static int canvas_undo_whatnext; /* whether we can now UNDO or REDO */
2547 static void *canvas_undo_buf; /* data private to the undo function */
2548 static t_canvas *canvas_undo_canvas; /* which canvas we can undo on */
2549 static const char *canvas_undo_name;
2551 void canvas_setundo(t_canvas *x, t_undofn undofn, void *buf,
2552 const char *name)
2554 int hadone = 0;
2555 /* blow away the old undo information. In one special case the
2556 old undo info is re-used; if so we shouldn't free it here. */
2557 if (canvas_undo_fn && canvas_undo_buf && (buf != canvas_undo_buf))
2559 (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_FREE);
2560 hadone = 1;
2562 canvas_undo_canvas = x;
2563 canvas_undo_fn = undofn;
2564 canvas_undo_buf = buf;
2565 canvas_undo_whatnext = UNDO_UNDO;
2566 canvas_undo_name = name;
2567 if (x && glist_isvisible(x) && glist_istoplevel(x))
2568 /* enable undo in menu */
2569 sys_vgui("pdtk_undomenu .x%x %s no\n", x, name);
2570 else if (hadone)
2571 sys_vgui("pdtk_undomenu nobody no no\n");
2574 /* clear undo if it happens to be for the canvas x.
2575 (but if x is 0, clear it regardless of who owns it.) */
2576 void canvas_noundo(t_canvas *x)
2578 if (!x || (x == canvas_undo_canvas))
2579 canvas_setundo(0, 0, 0, "foo");
2582 static void canvas_undo(t_canvas *x)
2584 if (x != canvas_undo_canvas)
2585 bug("canvas_undo 1");
2586 else if (canvas_undo_whatnext != UNDO_UNDO)
2587 bug("canvas_undo 2");
2588 else
2590 /* post("undo"); */
2591 (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_UNDO);
2592 /* enable redo in menu */
2593 if (glist_isvisible(x) && glist_istoplevel(x))
2594 sys_vgui("pdtk_undomenu .x%x no %s\n", x, canvas_undo_name);
2595 canvas_undo_whatnext = UNDO_REDO;
2599 static void canvas_redo(t_canvas *x)
2601 if (x != canvas_undo_canvas)
2602 bug("canvas_undo 1");
2603 else if (canvas_undo_whatnext != UNDO_REDO)
2604 bug("canvas_undo 2");
2605 else
2607 /* post("redo"); */
2608 (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_REDO);
2609 /* enable undo in menu */
2610 if (glist_isvisible(x) && glist_istoplevel(x))
2611 sys_vgui("pdtk_undomenu .x%x %s no\n", x, canvas_undo_name);
2612 canvas_undo_whatnext = UNDO_UNDO;
2616 /* ------- specific undo methods: 1. connect and disconnect -------- */
2618 typedef struct _undo_connect
2620 int u_index1;
2621 int u_outletno;
2622 int u_index2;
2623 int u_inletno;
2624 } t_undo_connect;
2626 static void *canvas_undo_set_disconnect(t_canvas *x,
2627 int index1, int outno, int index2, int inno)
2629 t_undo_connect *buf = (t_undo_connect *)getbytes(sizeof(*buf));
2630 buf->u_index1 = index1;
2631 buf->u_outletno = outno;
2632 buf->u_index2 = index2;
2633 buf->u_inletno = inno;
2634 return (buf);
2637 void canvas_disconnect(t_canvas *x,
2638 float index1, float outno, float index2, float inno)
2640 t_linetraverser t;
2641 t_outconnect *oc;
2642 linetraverser_start(&t, x);
2643 while (oc = linetraverser_next(&t))
2645 int srcno = canvas_getindex(x, &t.tr_ob->ob_g);
2646 int sinkno = canvas_getindex(x, &t.tr_ob2->ob_g);
2647 if (srcno == index1 && t.tr_outno == outno &&
2648 sinkno == index2 && t.tr_inno == inno)
2650 sys_vgui(".x%x.c delete l%x\n", x, oc);
2651 obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);
2652 break;
2657 static void canvas_undo_disconnect(t_canvas *x, void *z, int action)
2659 t_undo_connect *buf = z;
2660 if (action == UNDO_UNDO)
2662 canvas_connect(x, buf->u_index1, buf->u_outletno,
2663 buf->u_index2, buf->u_inletno);
2665 else if (action == UNDO_REDO)
2667 canvas_disconnect(x, buf->u_index1, buf->u_outletno,
2668 buf->u_index2, buf->u_inletno);
2670 else if (action == UNDO_FREE)
2671 t_freebytes(buf, sizeof(*buf));
2674 /* connect just calls disconnect actions backward... */
2675 static void *canvas_undo_set_connect(t_canvas *x,
2676 int index1, int outno, int index2, int inno)
2678 return (canvas_undo_set_disconnect(x, index1, outno, index2, inno));
2681 static void canvas_undo_connect(t_canvas *x, void *z, int action)
2683 int myaction;
2684 if (action == UNDO_UNDO)
2685 myaction = UNDO_REDO;
2686 else if (action == UNDO_REDO)
2687 myaction = UNDO_UNDO;
2688 else myaction = action;
2689 canvas_undo_disconnect(x, z, myaction);
2692 /* ---------- ... 2. cut, clear, and typing into objects: -------- */
2694 #define UCUT_CUT 1 /* operation was a cut */
2695 #define UCUT_CLEAR 2 /* .. a clear */
2696 #define UCUT_TEXT 3 /* text typed into a box */
2698 typedef struct _undo_cut
2700 t_binbuf *u_objectbuf; /* the object cleared or typed into */
2701 t_binbuf *u_reconnectbuf; /* connections into and out of object */
2702 t_binbuf *u_redotextbuf; /* buffer to paste back for redo if TEXT */
2703 int u_mode; /* from flags above */
2704 } t_undo_cut;
2706 static void *canvas_undo_set_cut(t_canvas *x, int mode)
2708 t_undo_cut *buf;
2709 t_gobj *y;
2710 t_linetraverser t;
2711 t_outconnect *oc;
2712 int nnotsel= glist_selectionindex(x, 0, 0);
2713 buf = (t_undo_cut *)getbytes(sizeof(*buf));
2714 buf->u_mode = mode;
2715 buf->u_redotextbuf = 0;
2717 /* store connections into/out of the selection */
2718 buf->u_reconnectbuf = binbuf_new();
2719 linetraverser_start(&t, x);
2720 while (oc = linetraverser_next(&t))
2722 int issel1 = glist_isselected(x, &t.tr_ob->ob_g);
2723 int issel2 = glist_isselected(x, &t.tr_ob2->ob_g);
2724 if (issel1 != issel2)
2726 binbuf_addv(buf->u_reconnectbuf, "ssiiii;",
2727 gensym("#X"), gensym("connect"),
2728 (issel1 ? nnotsel : 0)
2729 + glist_selectionindex(x, &t.tr_ob->ob_g, issel1),
2730 t.tr_outno,
2731 (issel2 ? nnotsel : 0) +
2732 glist_selectionindex(x, &t.tr_ob2->ob_g, issel2),
2733 t.tr_inno);
2736 if (mode == UCUT_TEXT)
2738 buf->u_objectbuf = canvas_docopy(x);
2740 else if (mode == UCUT_CUT)
2742 buf->u_objectbuf = 0;
2744 else if (mode == UCUT_CLEAR)
2746 buf->u_objectbuf = canvas_docopy(x);
2748 return (buf);
2751 static void canvas_undo_cut(t_canvas *x, void *z, int action)
2753 t_undo_cut *buf = z;
2754 int mode = buf->u_mode;
2755 if (action == UNDO_UNDO)
2757 if (mode == UCUT_CUT)
2758 canvas_dopaste(x, copy_binbuf);
2759 else if (mode == UCUT_CLEAR)
2760 canvas_dopaste(x, buf->u_objectbuf);
2761 else if (mode == UCUT_TEXT)
2763 t_gobj *y1, *y2;
2764 glist_noselect(x);
2765 for (y1 = x->gl_list; y2 = y1->g_next; y1 = y2)
2767 if (y1)
2769 if (!buf->u_redotextbuf)
2771 glist_noselect(x);
2772 glist_select(x, y1);
2773 buf->u_redotextbuf = canvas_docopy(x);
2774 glist_noselect(x);
2776 glist_delete(x, y1);
2778 canvas_dopaste(x, buf->u_objectbuf);
2780 pd_bind(&x->gl_pd, gensym("#X"));
2781 binbuf_eval(buf->u_reconnectbuf, 0, 0, 0);
2782 pd_unbind(&x->gl_pd, gensym("#X"));
2784 else if (action == UNDO_REDO)
2786 if (mode == UCUT_CUT || mode == UCUT_CLEAR)
2787 canvas_doclear(x);
2788 else if (mode == UCUT_TEXT)
2790 t_gobj *y1, *y2;
2791 for (y1 = x->gl_list; y2 = y1->g_next; y1 = y2)
2793 if (y1)
2794 glist_delete(x, y1);
2795 canvas_dopaste(x, buf->u_redotextbuf);
2796 pd_bind(&x->gl_pd, gensym("#X"));
2797 binbuf_eval(buf->u_reconnectbuf, 0, 0, 0);
2798 pd_unbind(&x->gl_pd, gensym("#X"));
2801 else if (action == UNDO_FREE)
2803 if (buf->u_objectbuf)
2804 binbuf_free(buf->u_objectbuf);
2805 if (buf->u_reconnectbuf)
2806 binbuf_free(buf->u_reconnectbuf);
2807 if (buf->u_redotextbuf)
2808 binbuf_free(buf->u_redotextbuf);
2809 t_freebytes(buf, sizeof(*buf));
2813 /* --------- 3. motion, including "tidy up" and stretching ----------- */
2815 typedef struct _undo_move_elem
2817 int e_index;
2818 int e_xpix;
2819 int e_ypix;
2820 } t_undo_move_elem;
2822 typedef struct _undo_move
2824 t_undo_move_elem *u_vec;
2825 int u_n;
2826 } t_undo_move;
2828 static int canvas_undo_already_set_move;
2830 static void *canvas_undo_set_move(t_canvas *x, int selected)
2832 int x1, y1, x2, y2, i, indx;
2833 t_gobj *y;
2834 t_undo_move *buf = (t_undo_move *)getbytes(sizeof(*buf));
2835 buf->u_n = selected ? glist_selectionindex(x, 0, 1) : glist_getindex(x, 0);
2836 buf->u_vec = (t_undo_move_elem *)getbytes(sizeof(*buf->u_vec) *
2837 (selected ? glist_selectionindex(x, 0, 1) : glist_getindex(x, 0)));
2838 if (selected)
2840 for (y = x->gl_list, i = indx = 0; y; y = y->g_next, indx++)
2841 if (glist_isselected(x, y))
2843 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
2844 buf->u_vec[i].e_index = indx;
2845 buf->u_vec[i].e_xpix = x1;
2846 buf->u_vec[i].e_ypix = y1;
2847 i++;
2850 else
2852 for (y = x->gl_list, indx = 0; y; y = y->g_next, indx++)
2854 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
2855 buf->u_vec[indx].e_index = indx;
2856 buf->u_vec[indx].e_xpix = x1;
2857 buf->u_vec[indx].e_ypix = y1;
2860 canvas_undo_already_set_move = 1;
2861 return (buf);
2864 static void canvas_undo_move(t_canvas *x, void *z, int action)
2866 t_undo_move *buf = z;
2867 if (action == UNDO_UNDO || action == UNDO_REDO)
2869 int i;
2870 for (i = 0; i < buf->u_n; i++)
2872 int x1, y1, x2, y2, newx, newy;
2873 t_gobj *y;
2874 newx = buf->u_vec[i].e_xpix;
2875 newy = buf->u_vec[i].e_ypix;
2876 y = glist_nth(x, buf->u_vec[i].e_index);
2877 if (y)
2879 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
2880 gobj_displace(y, x, newx-x1, newy - y1);
2881 buf->u_vec[i].e_xpix = x1;
2882 buf->u_vec[i].e_ypix = y1;
2886 else if (action == UNDO_FREE)
2888 t_freebytes(buf->u_vec, buf->u_n * sizeof(*buf->u_vec));
2889 t_freebytes(buf, sizeof(*buf));
2893 /* --------- 4. paste (also duplicate) ----------- */
2895 typedef struct _undo_paste
2897 int u_index; /* index of first object pasted */
2898 } t_undo_paste;
2900 static void *canvas_undo_set_paste(t_canvas *x)
2902 t_undo_paste *buf = (t_undo_paste *)getbytes(sizeof(*buf));
2903 buf->u_index = glist_getindex(x, 0);
2904 return (buf);
2907 static void canvas_undo_paste(t_canvas *x, void *z, int action)
2909 t_undo_paste *buf = z;
2910 if (action == UNDO_UNDO)
2912 t_gobj *y;
2913 glist_noselect(x);
2914 for (y = glist_nth(x, buf->u_index); y; y = y->g_next)
2915 glist_select(x, y);
2916 canvas_doclear(x);
2918 else if (action == UNDO_REDO)
2920 t_selection *sel;
2921 canvas_dopaste(x, copy_binbuf);
2922 /* if it was "duplicate" have to re-enact the displacement. */
2923 if (canvas_undo_name && canvas_undo_name[0] == 'd')
2924 for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
2925 gobj_displace(sel->sel_what, x, 10, 10);
2927 else if (action == UNDO_FREE)
2928 t_freebytes(buf, sizeof(*buf));
2931 /* recursively check for abstractions to reload as result of a save.
2932 Don't reload the one we just saved ("except") though. */
2933 /* LATER try to do the same trick for externs. */
2934 static void glist_doreload(t_glist *gl, t_symbol *name, t_symbol *dir,
2935 t_gobj *except)
2937 t_gobj *g;
2938 int i, nobj = glist_getindex(gl, 0); /* number of objects */
2939 for (g = gl->gl_list, i = 0; g && i < nobj; i++)
2941 if (g != except && pd_class(&g->g_pd) == canvas_class &&
2942 canvas_isabstraction((t_canvas *)g) &&
2943 ((t_canvas *)g)->gl_name == name &&
2944 canvas_getdir((t_canvas *)g) == dir)
2946 /* we're going to remake the object, so "g" will go stale.
2947 Get its index here, and afterward restore g. Also, the
2948 replacement will be at teh end of the list, so we don't
2949 do g = g->g_next in this case. */
2950 int j = glist_getindex(gl, g);
2951 if (!gl->gl_havewindow)
2952 canvas_vis(glist_getcanvas(gl), 1);
2953 glist_noselect(gl);
2954 glist_select(gl, g);
2955 canvas_setundo(gl, canvas_undo_cut,
2956 canvas_undo_set_cut(gl, UCUT_CLEAR), "clear");
2957 canvas_doclear(gl);
2958 canvas_undo(gl);
2959 glist_noselect(gl);
2960 g = glist_nth(gl, j);
2962 else
2964 if (g != except && pd_class(&g->g_pd) == canvas_class)
2965 glist_doreload((t_canvas *)g, name, dir, except);
2966 g = g->g_next;
2971 /* call canvas_doreload on everyone */
2972 void canvas_reload(t_symbol *name, t_symbol *dir, t_gobj *except)
2974 t_canvas *x;
2975 /* find all root canvases */
2976 for (x = canvas_list; x; x = x->gl_next)
2977 glist_doreload(x, name, dir, except);
2980 /* ------------------------ event handling ------------------------ */
2982 #define CURSOR_RUNMODE_NOTHING 0
2983 #define CURSOR_RUNMODE_CLICKME 1
2984 #define CURSOR_RUNMODE_THICKEN 2
2985 #define CURSOR_RUNMODE_ADDPOINT 3
2986 #define CURSOR_EDITMODE_NOTHING 4
2987 #define CURSOR_EDITMODE_CONNECT 5
2988 #define CURSOR_EDITMODE_DISCONNECT 6
2990 static char *cursorlist[] = {
2991 #ifdef MSW
2992 "right_ptr", /* CURSOR_RUNMODE_NOTHING */
2993 #else
2994 "left_ptr", /* CURSOR_RUNMODE_NOTHING */
2995 #endif
2996 "arrow", /* CURSOR_RUNMODE_CLICKME */
2997 "sb_v_double_arrow", /* CURSOR_RUNMODE_THICKEN */
2998 "plus", /* CURSOR_RUNMODE_ADDPOINT */
2999 "hand2", /* CURSOR_EDITMODE_NOTHING */
3000 "circle", /* CURSOR_EDITMODE_CONNECT */
3001 "X_cursor" /* CURSOR_EDITMODE_DISCONNECT */
3004 void canvas_setcursor(t_canvas *x, unsigned int cursornum)
3006 static t_canvas *xwas;
3007 static unsigned int cursorwas;
3008 if (cursornum >= sizeof(cursorlist)/sizeof *cursorlist)
3010 bug("canvas_setcursor");
3011 return;
3013 if (xwas != x || cursorwas != cursornum)
3015 sys_vgui(".x%x configure -cursor %s\n", x, cursorlist[cursornum]);
3016 xwas = x;
3017 cursorwas = cursornum;
3021 /* check if a point lies in a gobj. */
3022 int canvas_hitbox(t_canvas *x, t_gobj *y, int xpos, int ypos,
3023 int *x1p, int *y1p, int *x2p, int *y2p)
3025 int x1, y1, x2, y2;
3026 t_text *ob;
3027 if ((ob = pd_checkobject(&y->g_pd)) &&
3028 !text_shouldvis(ob, x))
3029 return (0);
3030 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
3031 if (xpos >= x1 && xpos <= x2 && ypos >= y1 && ypos <= y2)
3033 *x1p = x1;
3034 *y1p = y1;
3035 *x2p = x2;
3036 *y2p = y2;
3037 return (1);
3039 else return (0);
3042 /* find the last gobj, if any, containing the point. */
3043 static t_gobj *canvas_findhitbox(t_canvas *x, int xpos, int ypos,
3044 int *x1p, int *y1p, int *x2p, int *y2p)
3046 t_gobj *y, *rval = 0;
3047 for (y = x->gl_list; y; y = y->g_next)
3049 if (canvas_hitbox(x, y, xpos, ypos, x1p, y1p, x2p, y2p))
3050 rval = y;
3052 return (rval);
3055 /* right-clicking on a canvas object pops up a menu. */
3056 static void canvas_rightclick(t_canvas *x, int xpos, int ypos, t_gobj *y)
3058 int canprop, canopen;
3059 canprop = (!y || (y && class_getpropertiesfn(pd_class(&y->g_pd))));
3060 canopen = (y && zgetfn(&y->g_pd, gensym("menu-open")));
3061 sys_vgui("pdtk_canvas_popup .x%x %d %d %d %d\n",
3062 x, xpos, ypos, canprop, canopen);
3065 /* tell GUI to create a properties dialog on the canvas. We tell
3066 the user the negative of the "pixel" y scale to make it appear to grow
3067 naturally upward, whereas pixels grow downward. */
3068 static void canvas_properties(t_glist *x)
3070 char graphbuf[200];
3071 sprintf(graphbuf, "pdtk_canvas_dialog %%s %g %g %g %g \n",
3072 glist_dpixtodx(x, 1), -glist_dpixtody(x, 1),
3073 (float)glist_isgraph(x), (float)x->gl_stretch);
3074 gfxstub_new(&x->gl_pd, x, graphbuf);
3078 void canvas_setgraph(t_glist *x, int flag)
3080 if (!flag && glist_isgraph(x))
3082 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
3083 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
3084 x->gl_isgraph = 0;
3085 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
3087 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
3088 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
3091 else if (flag && !glist_isgraph(x))
3093 if (x->gl_pixwidth <= 0)
3094 x->gl_pixwidth = GLIST_DEFGRAPHWIDTH;
3096 if (x->gl_pixheight <= 0)
3097 x->gl_pixheight = GLIST_DEFGRAPHHEIGHT;
3099 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
3100 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
3101 x->gl_isgraph = 1;
3102 /* if (x->gl_owner && glist_isvisible(x->gl_owner))
3103 canvas_vis(x, 1); */
3104 if (x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner))
3105 canvas_create_editor(x, 1);
3106 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
3108 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
3109 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
3114 /* called from the gui when "OK" is selected on the canvas properties
3115 dialog. Again we negate "y" scale. */
3116 static void canvas_donecanvasdialog(t_glist *x, t_floatarg xperpix,
3117 t_floatarg yperpix, t_floatarg fgraphme)
3119 int graphme = (fgraphme != 0), redraw = 0;
3120 yperpix = -yperpix;
3121 if (xperpix == 0)
3122 xperpix = 1;
3123 if (yperpix == 0)
3124 yperpix = 1;
3125 canvas_setgraph(x, graphme);
3126 if (!x->gl_isgraph && (xperpix != glist_dpixtodx(x, 1)))
3128 if (xperpix > 0)
3130 x->gl_x1 = 0;
3131 x->gl_x2 = xperpix;
3133 else
3135 x->gl_x1 = -xperpix * (x->gl_screenx2 - x->gl_screenx1);
3136 x->gl_x2 = x->gl_x1 + xperpix;
3138 redraw = 1;
3140 if (!x->gl_isgraph && (yperpix != glist_dpixtody(x, 1)))
3142 if (yperpix > 0)
3144 x->gl_y1 = 0;
3145 x->gl_y2 = yperpix;
3147 else
3149 x->gl_y1 = -yperpix * (x->gl_screeny2 - x->gl_screeny1);
3150 x->gl_y2 = x->gl_y1 + yperpix;
3152 redraw = 1;
3154 if (redraw)
3155 canvas_redraw(x);
3158 /* called from the gui when a popup menu comes back with "properties,"
3159 "open," or "help." */
3160 static void canvas_done_popup(t_canvas *x, float which, float xpos, float ypos)
3162 char pathbuf[MAXPDSTRING], namebuf[MAXPDSTRING];
3163 t_gobj *y;
3164 for (y = x->gl_list; y; y = y->g_next)
3166 int x1, y1, x2, y2;
3167 if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2))
3169 if (which == 0) /* properties */
3171 if (!class_getpropertiesfn(pd_class(&y->g_pd)))
3172 continue;
3173 (*class_getpropertiesfn(pd_class(&y->g_pd)))(y, x);
3174 return;
3176 else if (which == 1) /* open */
3178 if (!zgetfn(&y->g_pd, gensym("menu-open")))
3179 continue;
3180 vmess(&y->g_pd, gensym("menu-open"), "");
3181 return;
3183 else /* help */
3185 char *dir;
3186 if (pd_class(&y->g_pd) == canvas_class &&
3187 canvas_isabstraction((t_canvas *)y))
3189 t_object *ob = (t_object *)y;
3190 int ac = binbuf_getnatom(ob->te_binbuf);
3191 t_atom *av = binbuf_getvec(ob->te_binbuf);
3192 if (ac < 1)
3193 return;
3194 atom_string(av, namebuf, MAXPDSTRING);
3195 dir = canvas_getdir((t_canvas *)y)->s_name;
3197 else
3199 strcpy(namebuf, class_gethelpname(pd_class(&y->g_pd)));
3200 dir = class_gethelpdir(pd_class(&y->g_pd));
3202 if (strcmp(namebuf + strlen(namebuf) - 3, ".pd"))
3203 strcat(namebuf, ".pd");
3204 open_via_helppath(namebuf, dir);
3205 return;
3209 if (which == 0)
3210 canvas_properties(x);
3211 else if (which == 2)
3213 strcpy(pathbuf, sys_libdir->s_name);
3214 strcat(pathbuf, "/doc/5.reference/0.INTRO.txt");
3215 sys_vgui("menu_opentext %s\n", pathbuf);
3219 #define NOMOD 0
3220 #define SHIFTMOD 1
3221 #define CTRLMOD 2
3222 #define ALTMOD 4
3223 #define RIGHTCLICK 8
3225 /* on one-button-mouse machines, you can use double click to
3226 mean right click (which gets the popup menu.) Do this for Mac. */
3227 #ifdef MACOSX
3228 #define SIMULATERIGHTCLICK
3229 #endif
3231 #ifdef SIMULATERIGHTCLICK
3232 static double canvas_upclicktime;
3233 static int canvas_upx, canvas_upy;
3234 #define DCLICKINTERVAL 0.25
3235 #endif
3237 /* mouse click */
3238 void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
3239 int mod, int doit)
3241 t_gobj *y;
3242 int shiftmod, runmode, altmod, rightclick;
3243 int x1, y1, x2, y2, clickreturned = 0;
3245 if (!x->gl_editor)
3247 bug("editor");
3248 return;
3251 shiftmod = (mod & SHIFTMOD);
3252 runmode = ((mod & CTRLMOD) || (!x->gl_edit));
3253 altmod = (mod & ALTMOD);
3254 rightclick = (mod & RIGHTCLICK);
3256 canvas_undo_already_set_move = 0;
3258 /* if keyboard was grabbed, notify grabber and cancel the grab */
3259 if (doit && x->gl_editor->e_grab && x->gl_editor->e_keyfn)
3261 (* x->gl_editor->e_keyfn) (x->gl_editor->e_grab, 0);
3262 glist_grab(x, 0, 0, 0, 0, 0);
3265 #ifdef SIMULATERIGHTCLICK
3266 if (doit && !runmode && xpos == canvas_upx && ypos == canvas_upy &&
3267 sys_getrealtime() - canvas_upclicktime < DCLICKINTERVAL)
3268 rightclick = 1;
3269 #endif
3271 x->gl_editor->e_lastmoved = 0;
3272 if (doit)
3274 x->gl_editor->e_grab = 0;
3275 x->gl_editor->e_onmotion = MA_NONE;
3277 /* post("click %d %d %d %d", xpos, ypos, which, mod); */
3279 if (x->gl_editor->e_onmotion != MA_NONE)
3280 return;
3282 x->gl_editor->e_xwas = xpos;
3283 x->gl_editor->e_ywas = ypos;
3285 if (runmode && !rightclick)
3287 for (y = x->gl_list; y; y = y->g_next)
3289 /* check if the object wants to be clicked */
3290 if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2)
3291 && (clickreturned = gobj_click(y, x, xpos, ypos,
3292 shiftmod, altmod, 0, doit)))
3293 break;
3295 if (!doit)
3297 if (y)
3298 canvas_setcursor(x, clickreturned);
3299 else canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
3301 return;
3303 /* if not a runmode left click, fall here. */
3304 if (y = canvas_findhitbox(x, xpos, ypos, &x1, &y1, &x2, &y2))
3306 t_object *ob = pd_checkobject(&y->g_pd);
3307 /* check you're in the rectangle */
3308 ob = pd_checkobject(&y->g_pd);
3309 if (rightclick)
3310 canvas_rightclick(x, xpos, ypos, y);
3311 else if (shiftmod)
3313 if (doit)
3315 t_rtext *rt;
3316 if (ob && (rt = x->gl_editor->e_textedfor) &&
3317 rt == glist_findrtext(x, ob))
3319 rtext_mouse(rt, xpos - x1, ypos - y1, RTEXT_SHIFT);
3320 x->gl_editor->e_onmotion = MA_DRAGTEXT;
3321 x->gl_editor->e_xwas = x1;
3322 x->gl_editor->e_ywas = y1;
3324 else
3326 if (glist_isselected(x, y))
3327 glist_deselect(x, y);
3328 else glist_select(x, y);
3332 else
3334 /* look for an outlet */
3335 int noutlet;
3336 if (ob && (noutlet = obj_noutlets(ob)) && ypos >= y2-4)
3338 int width = x2 - x1;
3339 int nout1 = (noutlet > 1 ? noutlet - 1 : 1);
3340 int closest = ((xpos-x1) * (nout1) + width/2)/width;
3341 int hotspot = x1 +
3342 (width - IOWIDTH) * closest / (nout1);
3343 if (closest < noutlet &&
3344 xpos >= (hotspot-1) && xpos <= hotspot + (IOWIDTH+1))
3346 if (doit)
3348 int issignal = obj_issignaloutlet(ob, closest);
3349 x->gl_editor->e_onmotion = MA_CONNECT;
3350 x->gl_editor->e_xwas = xpos;
3351 x->gl_editor->e_ywas = ypos;
3352 sys_vgui(
3353 ".x%x.c create line %d %d %d %d -width %d -tags x\n",
3354 x, xpos, ypos, xpos, ypos,
3355 (issignal ? 2 : 1));
3357 else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT);
3359 else if (doit)
3360 goto nooutletafterall;
3362 /* not in an outlet; select and move */
3363 else if (doit)
3365 t_rtext *rt;
3366 /* check if the box is being text edited */
3367 nooutletafterall:
3368 if (ob && (rt = x->gl_editor->e_textedfor) &&
3369 rt == glist_findrtext(x, ob))
3371 rtext_mouse(rt, xpos - x1, ypos - y1, RTEXT_DOWN);
3372 x->gl_editor->e_onmotion = MA_DRAGTEXT;
3373 x->gl_editor->e_xwas = x1;
3374 x->gl_editor->e_ywas = y1;
3376 else
3378 /* otherwise select and drag to displace */
3379 if (!glist_isselected(x, y))
3381 glist_noselect(x);
3382 glist_select(x, y);
3384 x->gl_editor->e_onmotion = MA_MOVE;
3387 else canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
3389 return;
3391 /* if right click doesn't hit any boxes, call rightclick
3392 routine anyway */
3393 if (rightclick)
3394 canvas_rightclick(x, xpos, ypos, 0);
3396 /* if not an editing action, and if we didn't hit a
3397 box, set cursor and return */
3398 if (runmode || rightclick)
3400 canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
3401 return;
3403 /* having failed to find a box, we try lines now. */
3404 if (!runmode && !altmod && !shiftmod)
3406 t_linetraverser t;
3407 t_outconnect *oc;
3408 float fx = xpos, fy = ypos;
3409 t_glist *glist2 = glist_getcanvas(x);
3410 linetraverser_start(&t, glist2);
3411 while (oc = linetraverser_next(&t))
3413 float lx1 = t.tr_lx1, ly1 = t.tr_ly1,
3414 lx2 = t.tr_lx2, ly2 = t.tr_ly2;
3415 float area = (lx2 - lx1) * (fy - ly1) -
3416 (ly2 - ly1) * (fx - lx1);
3417 float dsquare = (lx2-lx1) * (lx2-lx1) + (ly2-ly1) * (ly2-ly1);
3418 if (area * area >= 50 * dsquare) continue;
3419 if ((lx2-lx1) * (fx-lx1) + (ly2-ly1) * (fy-ly1) < 0) continue;
3420 if ((lx2-lx1) * (lx2-fx) + (ly2-ly1) * (ly2-fy) < 0) continue;
3421 if (doit)
3423 glist_selectline(glist2, oc,
3424 canvas_getindex(glist2, &t.tr_ob->ob_g), t.tr_outno,
3425 canvas_getindex(glist2, &t.tr_ob2->ob_g), t.tr_inno);
3427 canvas_setcursor(x, CURSOR_EDITMODE_DISCONNECT);
3428 return;
3431 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
3432 if (doit)
3434 if (!shiftmod) glist_noselect(x);
3435 sys_vgui(".x%x.c create rectangle %d %d %d %d -tags x\n",
3436 x, xpos, ypos, xpos, ypos);
3437 x->gl_editor->e_xwas = xpos;
3438 x->gl_editor->e_ywas = ypos;
3439 x->gl_editor->e_onmotion = MA_REGION;
3443 void canvas_mousedown(t_canvas *x, t_floatarg xpos, t_floatarg ypos,
3444 t_floatarg which, t_floatarg mod)
3446 canvas_doclick(x, xpos, ypos, which, mod, 1);
3449 int canvas_isconnected (t_canvas *x, t_text *ob1, int n1,
3450 t_text *ob2, int n2)
3452 t_linetraverser t;
3453 t_outconnect *oc;
3454 linetraverser_start(&t, x);
3455 while (oc = linetraverser_next(&t))
3456 if (t.tr_ob == ob1 && t.tr_outno == n1 &&
3457 t.tr_ob2 == ob2 && t.tr_inno == n2)
3458 return (1);
3459 return (0);
3462 void canvas_doconnect(t_canvas *x, int xpos, int ypos, int which, int doit)
3464 int x11, y11, x12, y12;
3465 t_gobj *y1;
3466 int x21, y21, x22, y22;
3467 t_gobj *y2;
3468 int xwas = x->gl_editor->e_xwas,
3469 ywas = x->gl_editor->e_ywas;
3470 if (doit) sys_vgui(".x%x.c delete x\n", x);
3471 else sys_vgui(".x%x.c coords x %d %d %d %d\n",
3472 x, x->gl_editor->e_xwas,
3473 x->gl_editor->e_ywas, xpos, ypos);
3475 if ((y1 = canvas_findhitbox(x, xwas, ywas, &x11, &y11, &x12, &y12))
3476 && (y2 = canvas_findhitbox(x, xpos, ypos, &x21, &y21, &x22, &y22)))
3478 t_object *ob1 = pd_checkobject(&y1->g_pd);
3479 t_object *ob2 = pd_checkobject(&y2->g_pd);
3480 int noutlet1, ninlet2;
3481 if (ob1 && ob2 && ob1 != ob2 &&
3482 (noutlet1 = obj_noutlets(ob1))
3483 && (ninlet2 = obj_ninlets(ob2)))
3485 int width1 = x12 - x11, closest1, hotspot1;
3486 int width2 = x22 - x21, closest2, hotspot2;
3487 int lx1, lx2, ly1, ly2;
3488 t_outconnect *oc;
3490 if (noutlet1 > 1)
3492 closest1 = ((xwas-x11) * (noutlet1-1) + width1/2)/width1;
3493 hotspot1 = x11 +
3494 (width1 - IOWIDTH) * closest1 / (noutlet1-1);
3496 else closest1 = 0, hotspot1 = x11;
3498 if (ninlet2 > 1)
3500 closest2 = ((xpos-x21) * (ninlet2-1) + width2/2)/width2;
3501 hotspot2 = x21 +
3502 (width2 - IOWIDTH) * closest2 / (ninlet2-1);
3504 else closest2 = 0, hotspot2 = x21;
3506 if (closest1 >= noutlet1)
3507 closest1 = noutlet1 - 1;
3508 if (closest2 >= ninlet2)
3509 closest2 = ninlet2 - 1;
3511 if (canvas_isconnected (x, ob1, closest1, ob2, closest2))
3513 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
3514 return;
3516 if (obj_issignaloutlet(ob1, closest1) &&
3517 !obj_issignalinlet(ob2, closest2))
3519 if (doit)
3520 error("can't connect signal outlet to control inlet");
3521 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
3522 return;
3524 if (doit)
3526 oc = obj_connect(ob1, closest1, ob2, closest2);
3527 lx1 = x11 + (noutlet1 > 1 ?
3528 ((x12-x11-IOWIDTH) * closest1)/(noutlet1-1) : 0)
3529 + IOMIDDLE;
3530 ly1 = y12;
3531 lx2 = x21 + (ninlet2 > 1 ?
3532 ((x22-x21-IOWIDTH) * closest2)/(ninlet2-1) : 0)
3533 + IOMIDDLE;
3534 ly2 = y21;
3535 sys_vgui(".x%x.c create line %d %d %d %d -width %d -tags l%x\n",
3536 glist_getcanvas(x),
3537 lx1, ly1, lx2, ly2,
3538 (obj_issignaloutlet(ob1, closest1) ? 2 : 1), oc);
3539 canvas_setundo(x, canvas_undo_connect,
3540 canvas_undo_set_connect(x,
3541 canvas_getindex(x, &ob1->ob_g), closest1,
3542 canvas_getindex(x, &ob2->ob_g), closest2),
3543 "connect");
3545 else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT);
3546 return;
3549 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
3552 void canvas_selectinrect(t_canvas *x, int lox, int loy, int hix, int hiy)
3554 t_gobj *y;
3555 for (y = x->gl_list; y; y = y->g_next)
3557 int x1, y1, x2, y2;
3558 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
3559 if (hix >= x1 && lox <= x2 && hiy >= y1 && loy <= y2
3560 && !glist_isselected(x, y))
3561 glist_select(x, y);
3565 static void canvas_doregion(t_canvas *x, int xpos, int ypos, int doit)
3567 if (doit)
3569 int lox, loy, hix, hiy;
3570 if (x->gl_editor->e_xwas < xpos)
3571 lox = x->gl_editor->e_xwas, hix = xpos;
3572 else hix = x->gl_editor->e_xwas, lox = xpos;
3573 if (x->gl_editor->e_ywas < ypos)
3574 loy = x->gl_editor->e_ywas, hiy = ypos;
3575 else hiy = x->gl_editor->e_ywas, loy = ypos;
3576 canvas_selectinrect(x, lox, loy, hix, hiy);
3577 sys_vgui(".x%x.c delete x\n", x);
3578 x->gl_editor->e_onmotion = 0;
3580 else sys_vgui(".x%x.c coords x %d %d %d %d\n",
3581 x, x->gl_editor->e_xwas,
3582 x->gl_editor->e_ywas, xpos, ypos);
3585 void canvas_mouseup(t_canvas *x,
3586 t_floatarg fxpos, t_floatarg fypos, t_floatarg fwhich)
3588 t_gobj *y;
3590 int xpos = fxpos, ypos = fypos, which = fwhich;
3592 if (!x->gl_editor)
3594 bug("editor");
3595 return;
3598 #ifdef SIMULATERIGHTCLICK
3599 canvas_upclicktime = sys_getrealtime();
3600 canvas_upx = xpos;
3601 canvas_upy = ypos;
3602 #endif
3604 if (x->gl_editor->e_onmotion == MA_CONNECT)
3605 canvas_doconnect(x, xpos, ypos, which, 1);
3606 else if (x->gl_editor->e_onmotion == MA_REGION)
3607 canvas_doregion(x, xpos, ypos, 1);
3608 else if (x->gl_editor->e_onmotion == MA_MOVE)
3610 /* after motion, if there's only one item selected, activate it */
3611 if (x->gl_editor->e_selection &&
3612 !(x->gl_editor->e_selection->sel_next))
3613 gobj_activate(x->gl_editor->e_selection->sel_what,
3614 x, 1);
3616 else if (x->gl_editor->e_onmotion == MA_PASSOUT)
3617 x->gl_editor->e_onmotion = 0;
3618 x->gl_editor->e_onmotion = MA_NONE;
3621 #if 1
3622 /* GG misused the (unused) dbl parameter to tell if mouseup */
3624 for (y = x->gl_list; y; y = y->g_next) {
3625 int x1, y1, x2, y2, clickreturned = 0;
3627 /* check if the object wants to be clicked */
3628 if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2)
3629 && (clickreturned = gobj_click(y, x, xpos, ypos,
3630 0, 0, 1, 0)))
3631 break;
3633 #endif
3638 /* displace the selection by (dx, dy) pixels */
3639 static void canvas_displaceselection(t_canvas *x, int dx, int dy)
3641 t_selection *y;
3642 int resortin = 0, resortout = 0;
3643 if (!canvas_undo_already_set_move)
3645 canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, 1),
3646 "motion");
3647 canvas_undo_already_set_move = 1;
3649 for (y = x->gl_editor->e_selection; y; y = y->sel_next)
3651 t_class *cl = pd_class(&y->sel_what->g_pd);
3652 gobj_displace(y->sel_what, x, dx, dy);
3653 if (cl == vinlet_class) resortin = 1;
3654 else if (cl == voutlet_class) resortout = 1;
3656 if (resortin) canvas_resortinlets(x);
3657 if (resortout) canvas_resortoutlets(x);
3658 canvas_dirty(x, 1);
3661 /* this routine is called whenever a key is pressed or released. "x"
3662 may be zero if there's no current canvas. The first argument is true or
3663 fals for down/up; the second one is either a symbolic key name (e.g.,
3664 "Right" or an Ascii key number. */
3665 void canvas_key(t_canvas *x, t_symbol *s, int ac, t_atom *av)
3667 static t_symbol *keynumsym, *keyupsym, *keynamesym;
3668 int keynum, fflag;
3669 t_symbol *gotkeysym;
3671 int down, shift;
3673 if (ac < 3)
3674 return;
3675 if (!x->gl_editor)
3677 bug("editor");
3678 return;
3680 canvas_undo_already_set_move = 0;
3681 down = (atom_getfloat(av) != 0); /* nonzero if it's a key down */
3682 shift = (atom_getfloat(av+2) != 0); /* nonzero if shift-ed */
3683 if (av[1].a_type == A_SYMBOL)
3684 gotkeysym = av[1].a_w.w_symbol;
3685 else if (av[1].a_type == A_FLOAT)
3687 char buf[3];
3688 sprintf(buf, "%c", (int)(av[1].a_w.w_float));
3689 gotkeysym = gensym(buf);
3691 else gotkeysym = gensym("?");
3692 fflag = (av[0].a_type == A_FLOAT ? av[0].a_w.w_float : 0);
3693 keynum = (av[1].a_type == A_FLOAT ? av[1].a_w.w_float : 0);
3694 if (keynum == '\\' || keynum == '{' || keynum == '}')
3696 post("%c: dropped", (int)keynum);
3697 return;
3699 if (keynum == '\r') keynum = '\n';
3700 if (av[1].a_type == A_SYMBOL &&
3701 !strcmp(av[1].a_w.w_symbol->s_name, "Return"))
3702 keynum = '\n';
3703 if (!keynumsym)
3705 keynumsym = gensym("#key");
3706 keyupsym = gensym("#keyup");
3707 keynamesym = gensym("#keyname");
3709 #ifdef MACOSX
3710 if (keynum == 30)
3711 keynum = 0, gotkeysym = gensym("Up");
3712 else if (keynum == 31)
3713 keynum = 0, gotkeysym = gensym("Down");
3714 else if (keynum == 28)
3715 keynum = 0, gotkeysym = gensym("Left");
3716 else if (keynum == 29)
3717 keynum = 0, gotkeysym = gensym("Right");
3718 #endif
3719 if (keynumsym->s_thing && down)
3720 pd_float(keynumsym->s_thing, (float)keynum);
3721 if (keyupsym->s_thing && !down)
3722 pd_float(keyupsym->s_thing, (float)keynum);
3723 if (keynamesym->s_thing)
3725 t_atom at[2];
3726 at[0] = av[0];
3727 SETFLOAT(at, down);
3728 SETSYMBOL(at+1, gotkeysym);
3729 pd_list(keynamesym->s_thing, 0, 2, at);
3731 if (!x->gl_editor) /* if that 'invis'ed the window, we'd better stop. */
3732 return;
3733 if (x && down)
3735 /* if an object has "grabbed" keys just send them on */
3736 if (x->gl_editor->e_grab
3737 && x->gl_editor->e_keyfn && keynum)
3738 (* x->gl_editor->e_keyfn)
3739 (x->gl_editor->e_grab, (float)keynum);
3740 /* if a text editor is open send it on */
3741 else if (x->gl_editor->e_textedfor)
3743 if (!x->gl_editor->e_textdirty)
3745 canvas_setundo(x, canvas_undo_cut,
3746 canvas_undo_set_cut(x, UCUT_TEXT), "typing");
3748 rtext_key(x->gl_editor->e_textedfor,
3749 (int)keynum, gotkeysym);
3750 if (x->gl_editor->e_textdirty)
3751 canvas_dirty(x, 1);
3753 /* check for backspace or clear */
3754 else if (keynum == 8 || keynum == 127)
3756 if (x->gl_editor->e_selectedline)
3757 canvas_clearline(x);
3758 else if (x->gl_editor->e_selection)
3760 canvas_setundo(x, canvas_undo_cut,
3761 canvas_undo_set_cut(x, UCUT_CLEAR), "clear");
3762 canvas_doclear(x);
3765 /* check for arrow keys */
3766 else if (!strcmp(gotkeysym->s_name, "Up"))
3767 canvas_displaceselection(x, 0, shift ? -10 : -1);
3768 else if (!strcmp(gotkeysym->s_name, "Down"))
3769 canvas_displaceselection(x, 0, shift ? 10 : 1);
3770 else if (!strcmp(gotkeysym->s_name, "Left"))
3771 canvas_displaceselection(x, shift ? -10 : -1, 0);
3772 else if (!strcmp(gotkeysym->s_name, "Right"))
3773 canvas_displaceselection(x, shift ? 10 : 1, 0);
3777 void canvas_motion(t_canvas *x, t_floatarg xpos, t_floatarg ypos,
3778 t_floatarg fmod)
3780 /* post("motion %d %d", xpos, ypos); */
3781 int mod = fmod;
3782 if (!x->gl_editor)
3784 bug("editor");
3785 return;
3787 glist_setlastxy(x, xpos, ypos);
3788 if (x->gl_editor->e_onmotion == MA_MOVE)
3790 canvas_displaceselection(x,
3791 xpos - x->gl_editor->e_xwas, ypos - x->gl_editor->e_ywas);
3792 x->gl_editor->e_xwas = xpos;
3793 x->gl_editor->e_ywas = ypos;
3795 else if (x->gl_editor->e_onmotion == MA_REGION)
3796 canvas_doregion(x, xpos, ypos, 0);
3797 else if (x->gl_editor->e_onmotion == MA_CONNECT)
3798 canvas_doconnect(x, xpos, ypos, 0, 0);
3799 else if (x->gl_editor->e_onmotion == MA_PASSOUT)
3801 if (!x->gl_editor->e_motionfn)
3802 bug("e_motionfn");
3803 (*x->gl_editor->e_motionfn)(&x->gl_editor->e_grab->g_pd,
3804 xpos - x->gl_editor->e_xwas,
3805 ypos - x->gl_editor->e_ywas);
3806 x->gl_editor->e_xwas = xpos;
3807 x->gl_editor->e_ywas = ypos;
3809 else if (x->gl_editor->e_onmotion == MA_DRAGTEXT)
3811 t_rtext *rt = x->gl_editor->e_textedfor;
3812 if (rt)
3813 rtext_mouse(rt, xpos - x->gl_editor->e_xwas,
3814 ypos - x->gl_editor->e_ywas, RTEXT_DRAG);
3816 else canvas_doclick(x, xpos, ypos, 0, mod, 0);
3818 x->gl_editor->e_lastmoved = 1;
3821 void canvas_startmotion(t_canvas *x)
3823 int xval, yval;
3824 if (!x->gl_editor) return;
3825 glist_getnextxy(x, &xval, &yval);
3826 if (xval == 0 && yval == 0) return;
3827 x->gl_editor->e_onmotion = MA_MOVE;
3828 x->gl_editor->e_xwas = xval;
3829 x->gl_editor->e_ywas = yval;
3832 /* ----------------------------- window stuff ----------------------- */
3834 void canvas_print(t_canvas *x, t_symbol *s)
3836 if (*s->s_name) sys_vgui(".x%x.c postscript -file %s\n", x, s->s_name);
3837 else sys_vgui(".x%x.c postscript -file x.ps\n", x);
3840 void canvas_menuclose(t_canvas *x, t_floatarg force)
3842 if (x->gl_owner)
3843 canvas_vis(x, 0);
3844 else if ((force != 0) || (!x->gl_dirty))
3845 pd_free(&x->gl_pd);
3846 else sys_vgui("pdtk_check {This window has been modified. Close anyway?}\
3847 {.x%x menuclose 1;\n}\n", x);
3850 /* put up a dialog which may call canvas_font back to do the work */
3851 static void canvas_menufont(t_canvas *x)
3853 char buf[80];
3854 t_canvas *x2 = canvas_getrootfor(x);
3855 gfxstub_deleteforkey(x2);
3856 sprintf(buf, "pdtk_canvas_dofont %%s %d\n", x2->gl_font);
3857 gfxstub_new(&x2->gl_pd, &x2->gl_pd, buf);
3860 static int canvas_find_index1, canvas_find_index2;
3861 static t_binbuf *canvas_findbuf;
3862 int binbuf_match(t_binbuf *inbuf, t_binbuf *searchbuf);
3864 /* find an atom or string of atoms */
3865 static int canvas_dofind(t_canvas *x, int *myindex1p)
3867 t_gobj *y;
3868 int myindex1 = *myindex1p, myindex2;
3869 if (myindex1 >= canvas_find_index1)
3871 for (y = x->gl_list, myindex2 = 0; y;
3872 y = y->g_next, myindex2++)
3874 t_object *ob = 0;
3875 if (ob = pd_checkobject(&y->g_pd))
3877 if (binbuf_match(ob->ob_binbuf, canvas_findbuf))
3879 if (myindex1 > canvas_find_index1 ||
3880 myindex1 == canvas_find_index1 &&
3881 myindex2 > canvas_find_index2)
3883 canvas_find_index1 = myindex1;
3884 canvas_find_index2 = myindex2;
3885 glist_noselect(x);
3886 canvas_vis(x, 1);
3887 canvas_editmode(x, 1.);
3888 glist_select(x, y);
3889 return (1);
3895 for (y = x->gl_list, myindex2 = 0; y; y = y->g_next, myindex2++)
3897 if (pd_class(&y->g_pd) == canvas_class)
3899 (*myindex1p)++;
3900 if (canvas_dofind((t_canvas *)y, myindex1p))
3901 return (1);
3904 return (0);
3907 static void canvas_find(t_canvas *x, t_symbol *s, int ac, t_atom *av)
3909 int myindex1 = 0, i;
3910 for (i = 0; i < ac; i++)
3912 if (av[i].a_type == A_SYMBOL)
3914 if (!strcmp(av[i].a_w.w_symbol->s_name, "_semi_"))
3915 SETSEMI(&av[i]);
3916 else if (!strcmp(av[i].a_w.w_symbol->s_name, "_comma_"))
3917 SETCOMMA(&av[i]);
3920 if (!canvas_findbuf)
3921 canvas_findbuf = binbuf_new();
3922 binbuf_clear(canvas_findbuf);
3923 binbuf_add(canvas_findbuf, ac, av);
3924 canvas_find_index1 = 0;
3925 canvas_find_index2 = -1;
3926 canvas_whichfind = x;
3927 if (!canvas_dofind(x, &myindex1))
3929 binbuf_print(canvas_findbuf);
3930 post("... couldn't find");
3934 static void canvas_find_again(t_canvas *x)
3936 int myindex1 = 0;
3937 if (!canvas_findbuf || !canvas_whichfind)
3938 return;
3939 if (!canvas_dofind(canvas_whichfind, &myindex1))
3941 binbuf_print(canvas_findbuf);
3942 post("... couldn't find");
3946 static void canvas_find_parent(t_canvas *x)
3948 if (x->gl_owner)
3949 canvas_vis(glist_getcanvas(x->gl_owner), 1);
3952 static int glist_dofinderror(t_glist *gl, void *error_object)
3954 t_gobj *g;
3955 for (g = gl->gl_list; g; g = g->g_next)
3957 if ((void *)g == error_object)
3959 /* got it... now show it. */
3960 glist_noselect(gl);
3961 canvas_vis(glist_getcanvas(gl), 1);
3962 canvas_editmode(glist_getcanvas(gl), 1.);
3963 glist_select(gl, g);
3964 return (1);
3966 else if (g->g_pd == canvas_class)
3968 if (glist_dofinderror((t_canvas *)g, error_object))
3969 return (1);
3972 return (0);
3975 void canvas_finderror(void *error_object)
3977 t_canvas *x;
3978 /* find all root canvases */
3979 for (x = canvas_list; x; x = x->gl_next)
3981 if (glist_dofinderror(x, error_object))
3982 return;
3984 post("... sorry, I couldn't find the source of that error.");
3987 void canvas_stowconnections(t_canvas *x)
3989 t_gobj *selhead = 0, *seltail = 0, *nonhead = 0, *nontail = 0, *y, *y2;
3990 t_linetraverser t;
3991 t_outconnect *oc;
3992 if (!x->gl_editor) return;
3993 /* split list to "selected" and "unselected" parts */
3994 for (y = x->gl_list; y; y = y2)
3996 y2 = y->g_next;
3997 if (glist_isselected(x, y))
3999 if (seltail)
4001 seltail->g_next = y;
4002 seltail = y;
4003 y->g_next = 0;
4005 else
4007 selhead = seltail = y;
4008 seltail->g_next = 0;
4011 else
4013 if (nontail)
4015 nontail->g_next = y;
4016 nontail = y;
4017 y->g_next = 0;
4019 else
4021 nonhead = nontail = y;
4022 nontail->g_next = 0;
4026 /* move the selected part to the end */
4027 if (!nonhead) x->gl_list = selhead;
4028 else x->gl_list = nonhead, nontail->g_next = selhead;
4030 /* add connections to binbuf */
4031 binbuf_clear(x->gl_editor->e_connectbuf);
4032 linetraverser_start(&t, x);
4033 while (oc = linetraverser_next(&t))
4035 int s1 = glist_isselected(x, &t.tr_ob->ob_g);
4036 int s2 = glist_isselected(x, &t.tr_ob2->ob_g);
4037 if (s1 != s2)
4038 binbuf_addv(x->gl_editor->e_connectbuf, "ssiiii;",
4039 gensym("#X"), gensym("connect"),
4040 glist_getindex(x, &t.tr_ob->ob_g), t.tr_outno,
4041 glist_getindex(x, &t.tr_ob2->ob_g), t.tr_inno);
4045 void canvas_restoreconnections(t_canvas *x)
4047 pd_bind(&x->gl_pd, gensym("#X"));
4048 binbuf_eval(x->gl_editor->e_connectbuf, 0, 0, 0);
4049 pd_unbind(&x->gl_pd, gensym("#X"));
4052 static t_binbuf *canvas_docopy(t_canvas *x)
4054 t_gobj *y;
4055 t_linetraverser t;
4056 t_outconnect *oc;
4057 t_binbuf *b = binbuf_new();
4058 for (y = x->gl_list; y; y = y->g_next)
4060 if (glist_isselected(x, y))
4061 gobj_save(y, b);
4063 linetraverser_start(&t, x);
4064 while (oc = linetraverser_next(&t))
4066 if (glist_isselected(x, &t.tr_ob->ob_g)
4067 && glist_isselected(x, &t.tr_ob2->ob_g))
4069 binbuf_addv(b, "ssiiii;", gensym("#X"), gensym("connect"),
4070 glist_selectionindex(x, &t.tr_ob->ob_g, 1), t.tr_outno,
4071 glist_selectionindex(x, &t.tr_ob2->ob_g, 1), t.tr_inno);
4074 return (b);
4077 static void canvas_copy(t_canvas *x)
4079 if (!x->gl_editor || !x->gl_editor->e_selection)
4080 return;
4081 binbuf_free(copy_binbuf);
4082 copy_binbuf = canvas_docopy(x);
4085 static void canvas_clearline(t_canvas *x)
4087 if (x->gl_editor->e_selectedline)
4089 canvas_disconnect(x, x->gl_editor->e_selectline_index1,
4090 x->gl_editor->e_selectline_outno,
4091 x->gl_editor->e_selectline_index2,
4092 x->gl_editor->e_selectline_inno);
4093 canvas_setundo(x, canvas_undo_disconnect,
4094 canvas_undo_set_disconnect(x,
4095 x->gl_editor->e_selectline_index1,
4096 x->gl_editor->e_selectline_outno,
4097 x->gl_editor->e_selectline_index2,
4098 x->gl_editor->e_selectline_inno),
4099 "disconnect");
4103 extern t_pd *newest;
4104 static void canvas_doclear(t_canvas *x)
4106 t_gobj *y, *y2;
4107 int dspstate;
4109 dspstate = canvas_suspend_dsp();
4110 if (x->gl_editor->e_selectedline)
4112 canvas_disconnect(x, x->gl_editor->e_selectline_index1,
4113 x->gl_editor->e_selectline_outno,
4114 x->gl_editor->e_selectline_index2,
4115 x->gl_editor->e_selectline_inno);
4116 canvas_setundo(x, canvas_undo_disconnect,
4117 canvas_undo_set_disconnect(x,
4118 x->gl_editor->e_selectline_index1,
4119 x->gl_editor->e_selectline_outno,
4120 x->gl_editor->e_selectline_index2,
4121 x->gl_editor->e_selectline_inno),
4122 "disconnect");
4124 /* if text is selected, deselecting it might remake the
4125 object. So we deselect it and hunt for a "new" object on
4126 the glist to reselect. */
4127 if (x->gl_editor->e_textedfor)
4129 newest = 0;
4130 glist_noselect(x);
4131 if (newest)
4133 for (y = x->gl_list; y; y = y->g_next)
4134 if (&y->g_pd == newest) glist_select(x, y);
4137 while (1) /* this is pretty wierd... should rewrite it */
4139 for (y = x->gl_list; y; y = y2)
4141 y2 = y->g_next;
4142 if (glist_isselected(x, y))
4144 glist_delete(x, y);
4145 #if 0
4146 if (y2) post("cut 5 %x %x", y2, y2->g_next);
4147 else post("cut 6");
4148 #endif
4149 goto next;
4152 goto restore;
4153 next: ;
4155 restore:
4156 canvas_resume_dsp(dspstate);
4157 canvas_dirty(x, 1);
4160 static void canvas_cut(t_canvas *x)
4162 if (x->gl_editor && x->gl_editor->e_selectedline)
4163 canvas_clearline(x);
4164 else if (x->gl_editor && x->gl_editor->e_selection)
4166 canvas_setundo(x, canvas_undo_cut,
4167 canvas_undo_set_cut(x, UCUT_CUT), "cut");
4168 canvas_copy(x);
4169 canvas_doclear(x);
4173 static int paste_onset;
4174 static t_canvas *paste_canvas;
4176 static void glist_donewloadbangs(t_glist *x)
4178 if (x->gl_editor)
4180 t_selection *sel;
4181 for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
4182 if (pd_class(&sel->sel_what->g_pd) == canvas_class)
4183 canvas_loadbang((t_canvas *)(&sel->sel_what->g_pd));
4187 static void canvas_dopaste(t_canvas *x, t_binbuf *b)
4189 t_gobj *newgobj, *last, *g2;
4190 int dspstate = canvas_suspend_dsp(), nbox, count;
4192 canvas_editmode(x, 1.);
4193 glist_noselect(x);
4194 for (g2 = x->gl_list, nbox = 0; g2; g2 = g2->g_next) nbox++;
4196 paste_onset = nbox;
4197 paste_canvas = x;
4199 pd_bind(&x->gl_pd, gensym("#X"));
4200 binbuf_eval(b, 0, 0, 0);
4201 pd_unbind(&x->gl_pd, gensym("#X"));
4202 for (g2 = x->gl_list, count = 0; g2; g2 = g2->g_next, count++)
4203 if (count >= nbox)
4204 glist_select(x, g2);
4205 paste_canvas = 0;
4206 canvas_resume_dsp(dspstate);
4207 canvas_dirty(x, 1);
4208 glist_donewloadbangs(x);
4211 static void canvas_paste(t_canvas *x)
4213 canvas_setundo(x, canvas_undo_paste, canvas_undo_set_paste(x), "paste");
4214 canvas_dopaste(x, copy_binbuf);
4217 static void canvas_duplicate(t_canvas *x)
4219 if (x->gl_editor->e_onmotion == MA_NONE)
4221 t_selection *y;
4222 canvas_copy(x);
4223 canvas_setundo(x, canvas_undo_paste, canvas_undo_set_paste(x),
4224 "duplicate");
4225 canvas_dopaste(x, copy_binbuf);
4226 for (y = x->gl_editor->e_selection; y; y = y->sel_next)
4227 gobj_displace(y->sel_what, x,
4228 10, 10);
4229 canvas_dirty(x, 1);
4233 static void canvas_selectall(t_canvas *x)
4235 t_gobj *y;
4236 if (!x->gl_edit)
4237 canvas_editmode(x, 1);
4238 for (y = x->gl_list; y; y = y->g_next)
4240 if (!glist_isselected(x, y))
4241 glist_select(x, y);
4245 void canvas_connect(t_canvas *x, t_floatarg fwhoout, t_floatarg foutno,
4246 t_floatarg fwhoin, t_floatarg finno)
4248 int whoout = fwhoout, outno = foutno, whoin = fwhoin, inno = finno;
4249 t_gobj *src = 0, *sink = 0;
4250 t_object *objsrc, *objsink;
4251 t_outconnect *oc;
4252 int nin = whoin, nout = whoout;
4253 if (paste_canvas == x) whoout += paste_onset, whoin += paste_onset;
4254 for (src = x->gl_list; whoout; src = src->g_next, whoout--)
4255 if (!src->g_next) goto bad; /* bug fix thanks to Hannes */
4256 for (sink = x->gl_list; whoin; sink = sink->g_next, whoin--)
4257 if (!sink->g_next) goto bad;
4258 if (!(objsrc = pd_checkobject(&src->g_pd)) ||
4259 !(objsink = pd_checkobject(&sink->g_pd)))
4260 goto bad;
4261 if (!(oc = obj_connect(objsrc, outno, objsink, inno))) goto bad;
4262 if (glist_isvisible(x))
4264 sys_vgui(".x%x.c create line %d %d %d %d -width %d -tags l%x\n",
4265 glist_getcanvas(x), 0, 0, 0, 0,
4266 (obj_issignaloutlet(objsrc, outno) ? 2 : 1),oc);
4267 canvas_fixlinesfor(x, objsrc);
4269 return;
4271 bad:
4272 post("%s %d %d %d %d (%s->%s) connection failed",
4273 x->gl_name->s_name, nout, outno, nin, inno,
4274 (src? class_getname(pd_class(&src->g_pd)) : "???"),
4275 (sink? class_getname(pd_class(&sink->g_pd)) : "???"));
4278 #define XTOLERANCE 4
4279 #define YTOLERANCE 3
4280 #define NHIST 15
4282 /* LATER might have to speed this up */
4283 static void canvas_tidy(t_canvas *x)
4285 t_gobj *y, *y2, *y3;
4286 int ax1, ay1, ax2, ay2, bx1, by1, bx2, by2;
4287 int histogram[NHIST], *ip, i, besthist, bestdist;
4288 /* if nobody is selected, this means do it to all boxes;
4289 othewise just the selection */
4290 int all = (x->gl_editor ? (x->gl_editor->e_selection == 0) : 1);
4292 canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, !all),
4293 "motion");
4295 /* tidy horizontally */
4296 for (y = x->gl_list; y; y = y->g_next)
4297 if (all || glist_isselected(x, y))
4299 gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);
4301 for (y2 = x->gl_list; y2; y2 = y2->g_next)
4302 if (all || glist_isselected(x, y2))
4304 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
4305 if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE &&
4306 bx1 < ax1)
4307 goto nothorizhead;
4310 for (y2 = x->gl_list; y2; y2 = y2->g_next)
4311 if (all || glist_isselected(x, y2))
4313 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
4314 if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE
4315 && by1 != ay1)
4316 gobj_displace(y2, x, 0, ay1-by1);
4318 nothorizhead: ;
4320 /* tidy vertically. First guess the user's favorite vertical spacing */
4321 for (i = NHIST, ip = histogram; i--; ip++) *ip = 0;
4322 for (y = x->gl_list; y; y = y->g_next)
4323 if (all || glist_isselected(x, y))
4325 gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);
4326 for (y2 = x->gl_list; y2; y2 = y2->g_next)
4327 if (all || glist_isselected(x, y2))
4329 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
4330 if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE)
4332 int distance = by1-ay2;
4333 if (distance >= 0 && distance < NHIST)
4334 histogram[distance]++;
4338 for (i = 1, besthist = 0, bestdist = 4, ip = histogram + 1;
4339 i < (NHIST-1); i++, ip++)
4341 int hit = ip[-1] + 2 * ip[0] + ip[1];
4342 if (hit > besthist)
4344 besthist = hit;
4345 bestdist = i;
4348 post("best vertical distance %d", bestdist);
4349 for (y = x->gl_list; y; y = y->g_next)
4350 if (all || glist_isselected(x, y))
4352 int keep = 1;
4353 gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);
4354 for (y2 = x->gl_list; y2; y2 = y2->g_next)
4355 if (all || glist_isselected(x, y2))
4357 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
4358 if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE &&
4359 ay1 >= by2 - 10 && ay1 < by2 + NHIST)
4360 goto nothead;
4362 while (keep)
4364 keep = 0;
4365 for (y2 = x->gl_list; y2; y2 = y2->g_next)
4366 if (all || glist_isselected(x, y2))
4368 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
4369 if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE &&
4370 by1 > ay1 && by1 < ay2 + NHIST)
4372 int vmove = ay2 + bestdist - by1;
4373 gobj_displace(y2, x, ax1-bx1, vmove);
4374 ay1 = by1 + vmove;
4375 ay2 = by2 + vmove;
4376 keep = 1;
4377 break;
4381 nothead: ;
4383 canvas_dirty(x, 1);
4386 static void canvas_texteditor(t_canvas *x)
4388 t_rtext *foo;
4389 char *buf;
4390 int bufsize;
4391 if (foo = x->gl_editor->e_textedfor)
4392 rtext_gettext(foo, &buf, &bufsize);
4393 else buf = "", bufsize = 0;
4394 sys_vgui("pdtk_pd_texteditor {%.*s}\n", bufsize, buf);
4398 void glob_key(void *dummy, t_symbol *s, int ac, t_atom *av)
4400 /* canvas_editing can be zero; canvas_key checks for that */
4401 canvas_key(canvas_editing, s, ac, av);
4404 void canvas_editmode(t_canvas *x, t_floatarg fyesplease)
4406 int yesplease = fyesplease;
4407 if (yesplease && x->gl_edit)
4408 return;
4409 x->gl_edit = !x->gl_edit;
4410 if (x->gl_edit && glist_isvisible(x) && glist_istoplevel(x))
4411 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
4412 else
4414 glist_noselect(x);
4415 if (glist_isvisible(x) && glist_istoplevel(x))
4416 canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
4418 sys_vgui("pdtk_canvas_editval .x%x %d\n",
4419 glist_getcanvas(x), x->gl_edit);
4422 /* called by canvas_font below */
4423 static void canvas_dofont(t_canvas *x, t_floatarg font, t_floatarg xresize,
4424 t_floatarg yresize)
4426 t_gobj *y;
4427 x->gl_font = font;
4428 if (xresize != 1 || yresize != 1)
4430 canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, 0),
4431 "motion");
4432 for (y = x->gl_list; y; y = y->g_next)
4434 int x1, x2, y1, y2, nx1, ny1;
4435 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
4436 nx1 = x1 * xresize + 0.5;
4437 ny1 = y1 * yresize + 0.5;
4438 gobj_displace(y, x, nx1-x1, ny1-y1);
4441 if (glist_isvisible(x))
4442 glist_redraw(x);
4443 for (y = x->gl_list; y; y = y->g_next)
4444 if (pd_class(&y->g_pd) == canvas_class
4445 && !canvas_isabstraction((t_canvas *)y))
4446 canvas_dofont((t_canvas *)y, font, xresize, yresize);
4449 /* canvas_menufont calls up a TK dialog which calls this back */
4450 static void canvas_font(t_canvas *x, t_floatarg font, t_floatarg resize,
4451 t_floatarg whichresize)
4453 float realresize, realresx = 1, realresy = 1;
4454 t_canvas *x2 = canvas_getrootfor(x);
4455 if (!resize) realresize = 1;
4456 else
4458 if (resize < 20) resize = 20;
4459 if (resize > 500) resize = 500;
4460 realresize = resize * 0.01;
4462 if (whichresize != 3) realresx = realresize;
4463 if (whichresize != 2) realresy = realresize;
4464 canvas_dofont(x2, font, realresx, realresy);
4465 sys_defaultfont = font;
4468 static t_glist *canvas_last_glist;
4469 static int canvas_last_glist_x, canvas_last_glist_y;
4471 void glist_getnextxy(t_glist *gl, int *xpix, int *ypix)
4473 if (canvas_last_glist == gl)
4474 *xpix = canvas_last_glist_x, *ypix = canvas_last_glist_y;
4475 else *xpix = *ypix = 40;
4478 static void glist_setlastxy(t_glist *gl, int xval, int yval)
4480 canvas_last_glist = gl;
4481 canvas_last_glist_x = xval;
4482 canvas_last_glist_y = yval;
4486 void g_editor_setup(void)
4488 /* ------------------------ events ---------------------------------- */
4489 class_addmethod(canvas_class, (t_method)canvas_mousedown, gensym("mouse"),
4490 A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
4491 class_addmethod(canvas_class, (t_method)canvas_mouseup, gensym("mouseup"),
4492 A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
4493 class_addmethod(canvas_class, (t_method)canvas_key, gensym("key"),
4494 A_GIMME, A_NULL);
4495 class_addmethod(canvas_class, (t_method)canvas_motion, gensym("motion"),
4496 A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
4498 /* ------------------------ menu actions ---------------------------- */
4499 class_addmethod(canvas_class, (t_method)canvas_menuclose,
4500 gensym("menuclose"), A_DEFFLOAT, 0);
4501 class_addmethod(canvas_class, (t_method)canvas_cut,
4502 gensym("cut"), A_NULL);
4503 class_addmethod(canvas_class, (t_method)canvas_copy,
4504 gensym("copy"), A_NULL);
4505 class_addmethod(canvas_class, (t_method)canvas_paste,
4506 gensym("paste"), A_NULL);
4507 class_addmethod(canvas_class, (t_method)canvas_duplicate,
4508 gensym("duplicate"), A_NULL);
4509 class_addmethod(canvas_class, (t_method)canvas_selectall,
4510 gensym("selectall"), A_NULL);
4511 class_addmethod(canvas_class, (t_method)canvas_undo,
4512 gensym("undo"), A_NULL);
4513 class_addmethod(canvas_class, (t_method)canvas_redo,
4514 gensym("redo"), A_NULL);
4515 class_addmethod(canvas_class, (t_method)canvas_tidy,
4516 gensym("tidy"), A_NULL);
4517 class_addmethod(canvas_class, (t_method)canvas_texteditor,
4518 gensym("texteditor"), A_NULL);
4519 class_addmethod(canvas_class, (t_method)canvas_editmode,
4520 gensym("editmode"), A_DEFFLOAT, A_NULL);
4521 class_addmethod(canvas_class, (t_method)canvas_print,
4522 gensym("print"), A_SYMBOL, A_NULL);
4523 class_addmethod(canvas_class, (t_method)canvas_menufont,
4524 gensym("menufont"), A_NULL);
4525 class_addmethod(canvas_class, (t_method)canvas_font,
4526 gensym("font"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
4527 class_addmethod(canvas_class, (t_method)canvas_find,
4528 gensym("find"), A_GIMME, A_NULL);
4529 class_addmethod(canvas_class, (t_method)canvas_find_again,
4530 gensym("findagain"), A_NULL);
4531 class_addmethod(canvas_class, (t_method)canvas_find_parent,
4532 gensym("findparent"), A_NULL);
4533 class_addmethod(canvas_class, (t_method)canvas_done_popup,
4534 gensym("done-popup"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
4535 class_addmethod(canvas_class, (t_method)canvas_donecanvasdialog,
4536 gensym("donecanvasdialog"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
4537 class_addmethod(canvas_class, (t_method)glist_arraydialog,
4538 gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
4540 /* -------------- connect method used in reading files ------------------ */
4541 class_addmethod(canvas_class, (t_method)canvas_connect,
4542 gensym("connect"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
4544 class_addmethod(canvas_class, (t_method)canvas_disconnect,
4545 gensym("disconnect"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
4546 /* -------------- copy buffer ------------------ */
4547 copy_binbuf = binbuf_new();