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. */
13 void glist_readfrombinbuf(t_glist
*x
, t_binbuf
*b
, char *filename
,
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
,
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
));
77 /* ------------------------ managing the selection ----------------- */
79 void glist_selectline(t_glist
*x
, t_outconnect
*oc
, int index1
,
80 int outno
, int index2
, int inno
)
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
)
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
)
111 for (sel
= x
->gl_editor
->e_selection
; sel
; sel
= sel
->sel_next
)
112 if (sel
->sel_what
== y
) return (1);
117 /* call this for unselected objects only */
118 void glist_select(t_glist
*x
, t_gobj
*y
)
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
;
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
)
138 static int reenter
= 0;
143 t_selection
*sel
, *sel2
;
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
)
154 canvas_stowconnections(glist_getcanvas(x
));
156 gobj_activate(y
, x
, 0);
158 if (zgetfn(&y
->g_pd
, gensym("dsp")))
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
));
169 for (sel
= x
->gl_editor
->e_selection
; sel2
= sel
->sel_next
;
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
));
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;
197 void glist_noselect(t_glist
*x
)
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
)
215 t_selection
*sel
= (t_selection
*)getbytes(sizeof(*sel
));
216 t_gobj
*y
= x
->gl_list
;
217 x
->gl_editor
->e_selection
= sel
;
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
;
226 gobj_select(y
, x
, 1);
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
)
240 for (y2
= x
->gl_list
, indx
= 0; y2
&& y2
!= y
; y2
= y2
->g_next
)
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
)
253 for (y2
= x
->gl_list
, indx
= 0; y2
&& y2
!= y
; y2
= y2
->g_next
)
254 if (selected
== glist_isselected(x
, y2
))
259 static t_gobj
*glist_nth(t_glist
*x
, int n
)
263 for (y
= x
->gl_list
, indx
= 0; y
; y
= y
->g_next
, indx
++)
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
,
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
);
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
);
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");
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");
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
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
;
363 void canvas_disconnect(t_canvas
*x
,
364 float index1
, float outno
, float index2
, float inno
)
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
);
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
)
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 */
432 static void *canvas_undo_set_cut(t_canvas
*x
, int mode
)
438 int nnotsel
= glist_selectionindex(x
, 0, 0);
439 buf
= (t_undo_cut
*)getbytes(sizeof(*buf
));
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
),
457 (issel2
? nnotsel
: 0) +
458 glist_selectionindex(x
, &t
.tr_ob2
->ob_g
, issel2
),
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
);
477 static void canvas_undo_cut(t_canvas
*x
, void *z
, int action
)
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
)
491 for (y1
= x
->gl_list
; y2
= y1
->g_next
; y1
= y2
)
495 if (!buf
->u_redotextbuf
)
499 buf
->u_redotextbuf
= canvas_docopy(x
);
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
)
514 else if (mode
== UCUT_TEXT
)
517 for (y1
= x
->gl_list
; y2
= y1
->g_next
; y1
= y2
)
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
548 typedef struct _undo_move
550 t_undo_move_elem
*u_vec
;
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
;
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)));
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
;
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;
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
)
596 for (i
= 0; i
< buf
->u_n
; i
++)
598 int x1
, y1
, x2
, y2
, newx
, newy
;
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
);
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 */
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);
633 static void canvas_undo_paste(t_canvas
*x
, void *z
, int action
)
635 t_undo_paste
*buf
= z
;
636 if (action
== UNDO_UNDO
)
640 for (y
= glist_nth(x
, buf
->u_index
); y
; y
= y
->g_next
)
644 else if (action
== UNDO_REDO
)
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
,
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);
681 canvas_setundo(gl
, canvas_undo_cut
,
682 canvas_undo_set_cut(gl
, UCUT_CLEAR
), "clear");
686 g
= glist_nth(gl
, j
);
690 if (g
!= except
&& pd_class(&g
->g_pd
) == canvas_class
)
691 glist_doreload((t_canvas
*)g
, name
, dir
, except
);
697 /* call canvas_doreload on everyone */
698 void canvas_reload(t_symbol
*name
, t_symbol
*dir
, t_gobj
*except
)
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
[] = {
718 "right_ptr", /* CURSOR_RUNMODE_NOTHING */
720 "left_ptr", /* CURSOR_RUNMODE_NOTHING */
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");
739 if (xwas
!= x
|| cursorwas
!= cursornum
)
741 sys_vgui(".x%x configure -cursor %s\n", x
, cursorlist
[cursornum
]);
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
)
753 if ((ob
= pd_checkobject(&y
->g_pd
)) &&
754 !text_shouldvis(ob
, x
))
756 gobj_getrect(y
, x
, &x1
, &y1
, &x2
, &y2
);
757 if (xpos
>= x1
&& xpos
<= x2
&& ypos
>= y1
&& ypos
<= y2
)
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
))
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
)
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);
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);
828 /* if (x->gl_owner && glist_isvisible(x->gl_owner))
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;
851 canvas_setgraph(x
, graphme
);
852 if (!x
->gl_isgraph
&& (xperpix
!= glist_dpixtodx(x
, 1)))
861 x
->gl_x1
= -xperpix
* (x
->gl_screenx2
- x
->gl_screenx1
);
862 x
->gl_x2
= x
->gl_x1
+ xperpix
;
866 if (!x
->gl_isgraph
&& (yperpix
!= glist_dpixtody(x
, 1)))
875 x
->gl_y1
= -yperpix
* (x
->gl_screeny2
- x
->gl_screeny1
);
876 x
->gl_y2
= x
->gl_y1
+ yperpix
;
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
];
890 for (y
= x
->gl_list
; y
; y
= y
->g_next
)
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
)))
899 (*class_getpropertiesfn(pd_class(&y
->g_pd
)))(y
, x
);
902 else if (which
== 1) /* open */
904 if (!zgetfn(&y
->g_pd
, gensym("menu-open")))
906 vmess(&y
->g_pd
, gensym("menu-open"), "");
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
);
920 atom_string(av
, namebuf
, MAXPDSTRING
);
921 dir
= canvas_getdir((t_canvas
*)y
)->s_name
;
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
);
936 canvas_properties(x
);
939 strcpy(pathbuf
, sys_libdir
->s_name
);
940 strcat(pathbuf
, "/doc/5.reference/0.INTRO.txt");
941 sys_vgui("menu_opentext %s\n", pathbuf
);
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. */
954 #define SIMULATERIGHTCLICK
957 #ifdef SIMULATERIGHTCLICK
958 static double canvas_upclicktime
;
959 static int canvas_upx
, canvas_upy
;
960 #define DCLICKINTERVAL 0.25
964 void canvas_doclick(t_canvas
*x
, int xpos
, int ypos
, int which
,
968 int shiftmod
, runmode
, altmod
, rightclick
;
969 int x1
, y1
, x2
, y2
, clickreturned
= 0;
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
)
997 x
->gl_editor
->e_lastmoved
= 0;
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
)
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
)))
1024 canvas_setcursor(x
, clickreturned
);
1025 else canvas_setcursor(x
, CURSOR_RUNMODE_NOTHING
);
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
);
1036 canvas_rightclick(x
, xpos
, ypos
, y
);
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
;
1052 if (glist_isselected(x
, y
))
1053 glist_deselect(x
, y
);
1054 else glist_select(x
, y
);
1060 /* look for an outlet */
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
;
1068 (width
- IOWIDTH
) * closest
/ (nout1
);
1069 if (closest
< noutlet
&&
1070 xpos
>= (hotspot
-1) && xpos
<= hotspot
+ (IOWIDTH
+1))
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
;
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
);
1086 goto nooutletafterall
;
1088 /* not in an outlet; select and move */
1092 /* check if the box is being text edited */
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
;
1104 /* otherwise select and drag to displace */
1105 if (!glist_isselected(x
, y
))
1110 x
->gl_editor
->e_onmotion
= MA_MOVE
;
1113 else canvas_setcursor(x
, CURSOR_EDITMODE_NOTHING
);
1117 /* if right click doesn't hit any boxes, call 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
);
1129 /* having failed to find a box, we try lines now. */
1130 if (!runmode
&& !altmod
&& !shiftmod
)
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;
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
);
1157 canvas_setcursor(x
, CURSOR_EDITMODE_NOTHING
);
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
)
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
)
1188 void canvas_doconnect(t_canvas
*x
, int xpos
, int ypos
, int which
, int doit
)
1190 int x11
, y11
, x12
, y12
;
1192 int x21
, y21
, x22
, y22
;
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
;
1218 closest1
= ((xwas
-x11
) * (noutlet1
-1) + width1
/2)/width1
;
1220 (width1
- IOWIDTH
) * closest1
/ (noutlet1
-1);
1222 else closest1
= 0, hotspot1
= x11
;
1226 closest2
= ((xpos
-x21
) * (ninlet2
-1) + width2
/2)/width2
;
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
);
1242 if (obj_issignaloutlet(ob1
, closest1
) &&
1243 !obj_issignalinlet(ob2
, closest2
))
1246 error("can't connect signal outlet to control inlet");
1247 canvas_setcursor(x
, CURSOR_EDITMODE_NOTHING
);
1252 oc
= obj_connect(ob1
, closest1
, ob2
, closest2
);
1253 lx1
= x11
+ (noutlet1
> 1 ?
1254 ((x12
-x11
-IOWIDTH
) * closest1
)/(noutlet1
-1) : 0)
1257 lx2
= x21
+ (ninlet2
> 1 ?
1258 ((x22
-x21
-IOWIDTH
) * closest2
)/(ninlet2
-1) : 0)
1261 sys_vgui(".x%x.c create line %d %d %d %d -width %d -tags l%x\n",
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
),
1271 else canvas_setcursor(x
, CURSOR_EDITMODE_CONNECT
);
1275 canvas_setcursor(x
, CURSOR_EDITMODE_NOTHING
);
1278 void canvas_selectinrect(t_canvas
*x
, int lox
, int loy
, int hix
, int hiy
)
1281 for (y
= x
->gl_list
; y
; y
= y
->g_next
)
1284 gobj_getrect(y
, x
, &x1
, &y1
, &x2
, &y2
);
1285 if (hix
>= x1
&& lox
<= x2
&& hiy
>= y1
&& loy
<= y2
1286 && !glist_isselected(x
, y
))
1291 static void canvas_doregion(t_canvas
*x
, int xpos
, int ypos
, int 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
)
1316 int xpos
= fxpos
, ypos
= fypos
, which
= fwhich
;
1324 #ifdef SIMULATERIGHTCLICK
1325 canvas_upclicktime
= sys_getrealtime();
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
,
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
;
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
,
1364 /* displace the selection by (dx, dy) pixels */
1365 static void canvas_displaceselection(t_canvas
*x
, int dx
, int dy
)
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),
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
);
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
;
1395 t_symbol
*gotkeysym
;
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
)
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
);
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"))
1431 keynumsym
= gensym("#key");
1432 keyupsym
= gensym("#keyup");
1433 keynamesym
= gensym("#keyname");
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");
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
)
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. */
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
)
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");
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
,
1506 /* post("motion %d %d", xpos, ypos); */
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
)
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
;
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
)
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
)
1570 else if ((force
!= 0) || (!x
->gl_dirty
))
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
)
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
)
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
++)
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
;
1613 canvas_editmode(x
, 1.);
1621 for (y
= x
->gl_list
, myindex2
= 0; y
; y
= y
->g_next
, myindex2
++)
1623 if (pd_class(&y
->g_pd
) == canvas_class
)
1626 if (canvas_dofind((t_canvas
*)y
, myindex1p
))
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_"))
1642 else if (!strcmp(av
[i
].a_w
.w_symbol
->s_name
, "_comma_"))
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
)
1663 if (!canvas_findbuf
|| !canvas_whichfind
)
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
)
1675 canvas_vis(glist_getcanvas(x
->gl_owner
), 1);
1678 static int glist_dofinderror(t_glist
*gl
, void *error_object
)
1681 for (g
= gl
->gl_list
; g
; g
= g
->g_next
)
1683 if ((void *)g
== error_object
)
1685 /* got it... now show it. */
1687 canvas_vis(glist_getcanvas(gl
), 1);
1688 canvas_editmode(glist_getcanvas(gl
), 1.);
1689 glist_select(gl
, g
);
1692 else if (g
->g_pd
== canvas_class
)
1694 if (glist_dofinderror((t_canvas
*)g
, error_object
))
1701 void canvas_finderror(void *error_object
)
1704 /* find all root canvases */
1705 for (x
= canvas_list
; x
; x
= x
->gl_next
)
1707 if (glist_dofinderror(x
, error_object
))
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
;
1718 if (!x
->gl_editor
) return;
1719 /* split list to "selected" and "unselected" parts */
1720 for (y
= x
->gl_list
; y
; y
= y2
)
1723 if (glist_isselected(x
, y
))
1727 seltail
->g_next
= y
;
1733 selhead
= seltail
= y
;
1734 seltail
->g_next
= 0;
1741 nontail
->g_next
= y
;
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
);
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
)
1783 t_binbuf
*b
= binbuf_new();
1784 for (y
= x
->gl_list
; y
; y
= y
->g_next
)
1786 if (glist_isselected(x
, y
))
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
);
1803 static void canvas_copy(t_canvas
*x
)
1805 if (!x
->gl_editor
|| !x
->gl_editor
->e_selection
)
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
),
1829 extern t_pd
*newest
;
1830 static void canvas_doclear(t_canvas
*x
)
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
),
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
)
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
)
1868 if (glist_isselected(x
, y
))
1872 if (y2
) post("cut 5 %x %x", y2
, y2
->g_next
);
1882 canvas_resume_dsp(dspstate
);
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");
1899 static int paste_onset
;
1900 static t_canvas
*paste_canvas
;
1902 static void glist_donewloadbangs(t_glist
*x
)
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.);
1920 for (g2
= x
->gl_list
, nbox
= 0; g2
; g2
= g2
->g_next
) nbox
++;
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
++)
1930 glist_select(x
, g2
);
1932 canvas_resume_dsp(dspstate
);
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
)
1949 canvas_setundo(x
, canvas_undo_paste
, canvas_undo_set_paste(x
),
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
,
1959 static void canvas_selectall(t_canvas
*x
)
1963 canvas_editmode(x
, 1);
1964 for (y
= x
->gl_list
; y
; y
= y
->g_next
)
1966 if (!glist_isselected(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
;
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
)))
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
);
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
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
),
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
&&
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
2042 gobj_displace(y2
, x
, 0, ay1
-by1
);
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];
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
))
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
)
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
);
2112 static void canvas_texteditor(t_canvas
*x
)
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
)
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
);
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
,
2154 if (xresize
!= 1 || yresize
!= 1)
2156 canvas_setundo(x
, canvas_undo_move
, canvas_undo_set_move(x
, 0),
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
))
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;
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"),
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. */
2283 #include "s_stuff.h"
2284 #include "g_canvas.h"
2287 void glist_readfrombinbuf(t_glist
*x
, t_binbuf
*b
, char *filename
,
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
,
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
));
2351 /* ------------------------ managing the selection ----------------- */
2353 void glist_selectline(t_glist
*x
, t_outconnect
*oc
, int index1
,
2354 int outno
, int index2
, int inno
)
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
)
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
)
2385 for (sel
= x
->gl_editor
->e_selection
; sel
; sel
= sel
->sel_next
)
2386 if (sel
->sel_what
== y
) return (1);
2391 /* call this for unselected objects only */
2392 void glist_select(t_glist
*x
, t_gobj
*y
)
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
;
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
)
2412 static int reenter
= 0;
2413 if (reenter
) return;
2417 t_selection
*sel
, *sel2
;
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
)
2428 canvas_stowconnections(glist_getcanvas(x
));
2430 gobj_activate(y
, x
, 0);
2432 if (zgetfn(&y
->g_pd
, gensym("dsp")))
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
));
2443 for (sel
= x
->gl_editor
->e_selection
; sel2
= sel
->sel_next
;
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
));
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;
2466 canvas_update_dsp();
2471 void glist_noselect(t_glist
*x
)
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
)
2489 t_selection
*sel
= (t_selection
*)getbytes(sizeof(*sel
));
2490 t_gobj
*y
= x
->gl_list
;
2491 x
->gl_editor
->e_selection
= sel
;
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
;
2500 gobj_select(y
, x
, 1);
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
)
2514 for (y2
= x
->gl_list
, indx
= 0; y2
&& y2
!= y
; y2
= y2
->g_next
)
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
)
2527 for (y2
= x
->gl_list
, indx
= 0; y2
&& y2
!= y
; y2
= y2
->g_next
)
2528 if (selected
== glist_isselected(x
, y2
))
2533 static t_gobj
*glist_nth(t_glist
*x
, int n
)
2537 for (y
= x
->gl_list
, indx
= 0; y
; y
= y
->g_next
, indx
++)
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
,
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
);
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
);
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");
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");
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
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
;
2637 void canvas_disconnect(t_canvas
*x
,
2638 float index1
, float outno
, float index2
, float inno
)
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
);
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
)
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 */
2706 static void *canvas_undo_set_cut(t_canvas
*x
, int mode
)
2712 int nnotsel
= glist_selectionindex(x
, 0, 0);
2713 buf
= (t_undo_cut
*)getbytes(sizeof(*buf
));
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
),
2731 (issel2
? nnotsel
: 0) +
2732 glist_selectionindex(x
, &t
.tr_ob2
->ob_g
, issel2
),
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
);
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
)
2765 for (y1
= x
->gl_list
; y2
= y1
->g_next
; y1
= y2
)
2769 if (!buf
->u_redotextbuf
)
2772 glist_select(x
, y1
);
2773 buf
->u_redotextbuf
= canvas_docopy(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
)
2788 else if (mode
== UCUT_TEXT
)
2791 for (y1
= x
->gl_list
; y2
= y1
->g_next
; y1
= y2
)
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
2822 typedef struct _undo_move
2824 t_undo_move_elem
*u_vec
;
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
;
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)));
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
;
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;
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
)
2870 for (i
= 0; i
< buf
->u_n
; i
++)
2872 int x1
, y1
, x2
, y2
, newx
, newy
;
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
);
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 */
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);
2907 static void canvas_undo_paste(t_canvas
*x
, void *z
, int action
)
2909 t_undo_paste
*buf
= z
;
2910 if (action
== UNDO_UNDO
)
2914 for (y
= glist_nth(x
, buf
->u_index
); y
; y
= y
->g_next
)
2918 else if (action
== UNDO_REDO
)
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
,
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);
2954 glist_select(gl
, g
);
2955 canvas_setundo(gl
, canvas_undo_cut
,
2956 canvas_undo_set_cut(gl
, UCUT_CLEAR
), "clear");
2960 g
= glist_nth(gl
, j
);
2964 if (g
!= except
&& pd_class(&g
->g_pd
) == canvas_class
)
2965 glist_doreload((t_canvas
*)g
, name
, dir
, except
);
2971 /* call canvas_doreload on everyone */
2972 void canvas_reload(t_symbol
*name
, t_symbol
*dir
, t_gobj
*except
)
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
[] = {
2992 "right_ptr", /* CURSOR_RUNMODE_NOTHING */
2994 "left_ptr", /* CURSOR_RUNMODE_NOTHING */
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");
3013 if (xwas
!= x
|| cursorwas
!= cursornum
)
3015 sys_vgui(".x%x configure -cursor %s\n", x
, cursorlist
[cursornum
]);
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
)
3027 if ((ob
= pd_checkobject(&y
->g_pd
)) &&
3028 !text_shouldvis(ob
, x
))
3030 gobj_getrect(y
, x
, &x1
, &y1
, &x2
, &y2
);
3031 if (xpos
>= x1
&& xpos
<= x2
&& ypos
>= y1
&& ypos
<= y2
)
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
))
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
)
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);
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);
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;
3125 canvas_setgraph(x
, graphme
);
3126 if (!x
->gl_isgraph
&& (xperpix
!= glist_dpixtodx(x
, 1)))
3135 x
->gl_x1
= -xperpix
* (x
->gl_screenx2
- x
->gl_screenx1
);
3136 x
->gl_x2
= x
->gl_x1
+ xperpix
;
3140 if (!x
->gl_isgraph
&& (yperpix
!= glist_dpixtody(x
, 1)))
3149 x
->gl_y1
= -yperpix
* (x
->gl_screeny2
- x
->gl_screeny1
);
3150 x
->gl_y2
= x
->gl_y1
+ yperpix
;
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
];
3164 for (y
= x
->gl_list
; y
; y
= y
->g_next
)
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
)))
3173 (*class_getpropertiesfn(pd_class(&y
->g_pd
)))(y
, x
);
3176 else if (which
== 1) /* open */
3178 if (!zgetfn(&y
->g_pd
, gensym("menu-open")))
3180 vmess(&y
->g_pd
, gensym("menu-open"), "");
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
);
3194 atom_string(av
, namebuf
, MAXPDSTRING
);
3195 dir
= canvas_getdir((t_canvas
*)y
)->s_name
;
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
);
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
);
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. */
3228 #define SIMULATERIGHTCLICK
3231 #ifdef SIMULATERIGHTCLICK
3232 static double canvas_upclicktime
;
3233 static int canvas_upx
, canvas_upy
;
3234 #define DCLICKINTERVAL 0.25
3238 void canvas_doclick(t_canvas
*x
, int xpos
, int ypos
, int which
,
3242 int shiftmod
, runmode
, altmod
, rightclick
;
3243 int x1
, y1
, x2
, y2
, clickreturned
= 0;
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
)
3271 x
->gl_editor
->e_lastmoved
= 0;
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
)
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
)))
3298 canvas_setcursor(x
, clickreturned
);
3299 else canvas_setcursor(x
, CURSOR_RUNMODE_NOTHING
);
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
);
3310 canvas_rightclick(x
, xpos
, ypos
, y
);
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
;
3326 if (glist_isselected(x
, y
))
3327 glist_deselect(x
, y
);
3328 else glist_select(x
, y
);
3334 /* look for an outlet */
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
;
3342 (width
- IOWIDTH
) * closest
/ (nout1
);
3343 if (closest
< noutlet
&&
3344 xpos
>= (hotspot
-1) && xpos
<= hotspot
+ (IOWIDTH
+1))
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
;
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
);
3360 goto nooutletafterall
;
3362 /* not in an outlet; select and move */
3366 /* check if the box is being text edited */
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
;
3378 /* otherwise select and drag to displace */
3379 if (!glist_isselected(x
, y
))
3384 x
->gl_editor
->e_onmotion
= MA_MOVE
;
3387 else canvas_setcursor(x
, CURSOR_EDITMODE_NOTHING
);
3391 /* if right click doesn't hit any boxes, call 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
);
3403 /* having failed to find a box, we try lines now. */
3404 if (!runmode
&& !altmod
&& !shiftmod
)
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;
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
);
3431 canvas_setcursor(x
, CURSOR_EDITMODE_NOTHING
);
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
)
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
)
3462 void canvas_doconnect(t_canvas
*x
, int xpos
, int ypos
, int which
, int doit
)
3464 int x11
, y11
, x12
, y12
;
3466 int x21
, y21
, x22
, y22
;
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
;
3492 closest1
= ((xwas
-x11
) * (noutlet1
-1) + width1
/2)/width1
;
3494 (width1
- IOWIDTH
) * closest1
/ (noutlet1
-1);
3496 else closest1
= 0, hotspot1
= x11
;
3500 closest2
= ((xpos
-x21
) * (ninlet2
-1) + width2
/2)/width2
;
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
);
3516 if (obj_issignaloutlet(ob1
, closest1
) &&
3517 !obj_issignalinlet(ob2
, closest2
))
3520 error("can't connect signal outlet to control inlet");
3521 canvas_setcursor(x
, CURSOR_EDITMODE_NOTHING
);
3526 oc
= obj_connect(ob1
, closest1
, ob2
, closest2
);
3527 lx1
= x11
+ (noutlet1
> 1 ?
3528 ((x12
-x11
-IOWIDTH
) * closest1
)/(noutlet1
-1) : 0)
3531 lx2
= x21
+ (ninlet2
> 1 ?
3532 ((x22
-x21
-IOWIDTH
) * closest2
)/(ninlet2
-1) : 0)
3535 sys_vgui(".x%x.c create line %d %d %d %d -width %d -tags l%x\n",
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
),
3545 else canvas_setcursor(x
, CURSOR_EDITMODE_CONNECT
);
3549 canvas_setcursor(x
, CURSOR_EDITMODE_NOTHING
);
3552 void canvas_selectinrect(t_canvas
*x
, int lox
, int loy
, int hix
, int hiy
)
3555 for (y
= x
->gl_list
; y
; y
= y
->g_next
)
3558 gobj_getrect(y
, x
, &x1
, &y1
, &x2
, &y2
);
3559 if (hix
>= x1
&& lox
<= x2
&& hiy
>= y1
&& loy
<= y2
3560 && !glist_isselected(x
, y
))
3565 static void canvas_doregion(t_canvas
*x
, int xpos
, int ypos
, int 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
)
3590 int xpos
= fxpos
, ypos
= fypos
, which
= fwhich
;
3598 #ifdef SIMULATERIGHTCLICK
3599 canvas_upclicktime
= sys_getrealtime();
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
,
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
;
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
,
3638 /* displace the selection by (dx, dy) pixels */
3639 static void canvas_displaceselection(t_canvas
*x
, int dx
, int dy
)
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),
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
);
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
;
3669 t_symbol
*gotkeysym
;
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
)
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
);
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"))
3705 keynumsym
= gensym("#key");
3706 keyupsym
= gensym("#keyup");
3707 keynamesym
= gensym("#keyname");
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");
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
)
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. */
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
)
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");
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
,
3780 /* post("motion %d %d", xpos, ypos); */
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
)
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
;
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
)
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
)
3844 else if ((force
!= 0) || (!x
->gl_dirty
))
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
)
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
)
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
++)
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
;
3887 canvas_editmode(x
, 1.);
3895 for (y
= x
->gl_list
, myindex2
= 0; y
; y
= y
->g_next
, myindex2
++)
3897 if (pd_class(&y
->g_pd
) == canvas_class
)
3900 if (canvas_dofind((t_canvas
*)y
, myindex1p
))
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_"))
3916 else if (!strcmp(av
[i
].a_w
.w_symbol
->s_name
, "_comma_"))
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
)
3937 if (!canvas_findbuf
|| !canvas_whichfind
)
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
)
3949 canvas_vis(glist_getcanvas(x
->gl_owner
), 1);
3952 static int glist_dofinderror(t_glist
*gl
, void *error_object
)
3955 for (g
= gl
->gl_list
; g
; g
= g
->g_next
)
3957 if ((void *)g
== error_object
)
3959 /* got it... now show it. */
3961 canvas_vis(glist_getcanvas(gl
), 1);
3962 canvas_editmode(glist_getcanvas(gl
), 1.);
3963 glist_select(gl
, g
);
3966 else if (g
->g_pd
== canvas_class
)
3968 if (glist_dofinderror((t_canvas
*)g
, error_object
))
3975 void canvas_finderror(void *error_object
)
3978 /* find all root canvases */
3979 for (x
= canvas_list
; x
; x
= x
->gl_next
)
3981 if (glist_dofinderror(x
, error_object
))
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
;
3992 if (!x
->gl_editor
) return;
3993 /* split list to "selected" and "unselected" parts */
3994 for (y
= x
->gl_list
; y
; y
= y2
)
3997 if (glist_isselected(x
, y
))
4001 seltail
->g_next
= y
;
4007 selhead
= seltail
= y
;
4008 seltail
->g_next
= 0;
4015 nontail
->g_next
= y
;
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
);
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
)
4057 t_binbuf
*b
= binbuf_new();
4058 for (y
= x
->gl_list
; y
; y
= y
->g_next
)
4060 if (glist_isselected(x
, y
))
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
);
4077 static void canvas_copy(t_canvas
*x
)
4079 if (!x
->gl_editor
|| !x
->gl_editor
->e_selection
)
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
),
4103 extern t_pd
*newest
;
4104 static void canvas_doclear(t_canvas
*x
)
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
),
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
)
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
)
4142 if (glist_isselected(x
, y
))
4146 if (y2
) post("cut 5 %x %x", y2
, y2
->g_next
);
4156 canvas_resume_dsp(dspstate
);
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");
4173 static int paste_onset
;
4174 static t_canvas
*paste_canvas
;
4176 static void glist_donewloadbangs(t_glist
*x
)
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.);
4194 for (g2
= x
->gl_list
, nbox
= 0; g2
; g2
= g2
->g_next
) nbox
++;
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
++)
4204 glist_select(x
, g2
);
4206 canvas_resume_dsp(dspstate
);
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
)
4223 canvas_setundo(x
, canvas_undo_paste
, canvas_undo_set_paste(x
),
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
,
4233 static void canvas_selectall(t_canvas
*x
)
4237 canvas_editmode(x
, 1);
4238 for (y
= x
->gl_list
; y
; y
= y
->g_next
)
4240 if (!glist_isselected(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
;
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
)))
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
);
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
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
),
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
&&
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
4316 gobj_displace(y2
, x
, 0, ay1
-by1
);
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];
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
))
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
)
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
);
4386 static void canvas_texteditor(t_canvas
*x
)
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
)
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
);
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
,
4428 if (xresize
!= 1 || yresize
!= 1)
4430 canvas_setundo(x
, canvas_undo_move
, canvas_undo_set_move(x
, 0),
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
))
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;
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"),
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();