summaryrefslogtreecommitdiff
path: root/apps/plugins/pdbox/PDa/src/g_editor.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/pdbox/PDa/src/g_editor.c')
-rw-r--r--apps/plugins/pdbox/PDa/src/g_editor.c4548
1 files changed, 4548 insertions, 0 deletions
diff --git a/apps/plugins/pdbox/PDa/src/g_editor.c b/apps/plugins/pdbox/PDa/src/g_editor.c
new file mode 100644
index 0000000000..a1dea79df1
--- /dev/null
+++ b/apps/plugins/pdbox/PDa/src/g_editor.c
@@ -0,0 +1,4548 @@
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. */
4
5#include <stdlib.h>
6#include <stdio.h>
7#include "m_pd.h"
8#include "m_imp.h"
9#include "s_stuff.h"
10#include "g_canvas.h"
11#include <string.h>
12
13void glist_readfrombinbuf(t_glist *x, t_binbuf *b, char *filename,
14 int selectem);
15
16void open_via_helppath(const char *name, const char *dir);
17char *class_gethelpdir(t_class *c);
18
19/* ------------------ forward declarations --------------- */
20static void canvas_doclear(t_canvas *x);
21static void glist_setlastxy(t_glist *gl, int xval, int yval);
22static void glist_donewloadbangs(t_glist *x);
23static t_binbuf *canvas_docopy(t_canvas *x);
24static void canvas_dopaste(t_canvas *x, t_binbuf *b);
25static void canvas_paste(t_canvas *x);
26static void canvas_clearline(t_canvas *x);
27static t_binbuf *copy_binbuf;
28
29/* ---------------- generic widget behavior ------------------------- */
30
31void gobj_getrect(t_gobj *x, t_glist *glist, int *x1, int *y1,
32 int *x2, int *y2)
33{
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);
36}
37
38void gobj_displace(t_gobj *x, t_glist *glist, int dx, int dy)
39{
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);
42}
43
44void gobj_select(t_gobj *x, t_glist *glist, int state)
45{
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);
48}
49
50void gobj_activate(t_gobj *x, t_glist *glist, int state)
51{
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);
54}
55
56void gobj_delete(t_gobj *x, t_glist *glist)
57{
58 if (x->g_pd->c_wb && x->g_pd->c_wb->w_deletefn)
59 (*x->g_pd->c_wb->w_deletefn)(x, glist);
60}
61
62void gobj_vis(t_gobj *x, struct _glist *glist, int flag)
63{
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);
66}
67
68int gobj_click(t_gobj *x, struct _glist *glist,
69 int xpix, int ypix, int shift, int alt, int dbl, int doit)
70{
71 if (x->g_pd->c_wb && x->g_pd->c_wb->w_clickfn)
72 return ((*x->g_pd->c_wb->w_clickfn)(x,
73 glist, xpix, ypix, shift, alt, dbl, doit));
74 else return (0);
75}
76
77/* ------------------------ managing the selection ----------------- */
78
79void glist_selectline(t_glist *x, t_outconnect *oc, int index1,
80 int outno, int index2, int inno)
81{
82 if (x->gl_editor)
83 {
84 glist_noselect(x);
85 x->gl_editor->e_selectedline = 1;
86 x->gl_editor->e_selectline_index1 = index1;
87 x->gl_editor->e_selectline_outno = outno;
88 x->gl_editor->e_selectline_index2 = index2;
89 x->gl_editor->e_selectline_inno = inno;
90 x->gl_editor->e_selectline_tag = oc;
91 sys_vgui(".x%x.c itemconfigure l%x -fill blue\n",
92 x, x->gl_editor->e_selectline_tag);
93 }
94}
95
96void glist_deselectline(t_glist *x)
97{
98 if (x->gl_editor)
99 {
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);
103 }
104}
105
106int glist_isselected(t_glist *x, t_gobj *y)
107{
108 if (x->gl_editor)
109 {
110 t_selection *sel;
111 for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
112 if (sel->sel_what == y) return (1);
113 }
114 return (0);
115}
116
117 /* call this for unselected objects only */
118void glist_select(t_glist *x, t_gobj *y)
119{
120 if (x->gl_editor)
121 {
122 t_selection *sel = (t_selection *)getbytes(sizeof(*sel));
123 if (x->gl_editor->e_selectedline)
124 glist_deselectline(x);
125 /* LATER #ifdef out the following check */
126 if (glist_isselected(x, y)) bug("glist_select");
127 sel->sel_next = x->gl_editor->e_selection;
128 sel->sel_what = y;
129 x->gl_editor->e_selection = sel;
130 gobj_select(y, x, 1);
131 }
132}
133
134 /* call this for selected objects only */
135void glist_deselect(t_glist *x, t_gobj *y)
136{
137 int fixdsp = 0;
138 static int reenter = 0;
139 if (reenter) return;
140 reenter = 1;
141 if (x->gl_editor)
142 {
143 t_selection *sel, *sel2;
144 t_rtext *z = 0;
145 if (!glist_isselected(x, y)) bug("glist_deselect");
146 if (x->gl_editor->e_textedfor)
147 {
148 t_rtext *fuddy = glist_findrtext(x, (t_text *)y);
149 if (x->gl_editor->e_textedfor == fuddy)
150 {
151 if (x->gl_editor->e_textdirty)
152 {
153 z = fuddy;
154 canvas_stowconnections(glist_getcanvas(x));
155 }
156 gobj_activate(y, x, 0);
157 }
158 if (zgetfn(&y->g_pd, gensym("dsp")))
159 fixdsp = 1;
160 }
161 if ((sel = x->gl_editor->e_selection)->sel_what == y)
162 {
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));
166 }
167 else
168 {
169 for (sel = x->gl_editor->e_selection; sel2 = sel->sel_next;
170 sel = sel2)
171 {
172 if (sel2->sel_what == y)
173 {
174 sel->sel_next = sel2->sel_next;
175 gobj_select(sel2->sel_what, x, 0);
176 freebytes(sel2, sizeof(*sel2));
177 break;
178 }
179 }
180 }
181 if (z)
182 {
183 char *buf;
184 int bufsize;
185
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;
190 }
191 if (fixdsp)
192 canvas_update_dsp();
193 }
194 reenter = 0;
195}
196
197void glist_noselect(t_glist *x)
198{
199 if (x->gl_editor)
200 {
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);
205 }
206}
207
208void glist_selectall(t_glist *x)
209{
210 if (x->gl_editor)
211 {
212 glist_noselect(x);
213 if (x->gl_list)
214 {
215 t_selection *sel = (t_selection *)getbytes(sizeof(*sel));
216 t_gobj *y = x->gl_list;
217 x->gl_editor->e_selection = sel;
218 sel->sel_what = y;
219 gobj_select(y, x, 1);
220 while (y = y->g_next)
221 {
222 t_selection *sel2 = (t_selection *)getbytes(sizeof(*sel2));
223 sel->sel_next = sel2;
224 sel = sel2;
225 sel->sel_what = y;
226 gobj_select(y, x, 1);
227 }
228 sel->sel_next = 0;
229 }
230 }
231}
232
233 /* get the index of a gobj in a glist. If y is zero, return the
234 total number of objects. */
235int glist_getindex(t_glist *x, t_gobj *y)
236{
237 t_gobj *y2;
238 int indx;
239
240 for (y2 = x->gl_list, indx = 0; y2 && y2 != y; y2 = y2->g_next)
241 indx++;
242 return (indx);
243}
244
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. */
248int glist_selectionindex(t_glist *x, t_gobj *y, int selected)
249{
250 t_gobj *y2;
251 int indx;
252
253 for (y2 = x->gl_list, indx = 0; y2 && y2 != y; y2 = y2->g_next)
254 if (selected == glist_isselected(x, y2))
255 indx++;
256 return (indx);
257}
258
259static t_gobj *glist_nth(t_glist *x, int n)
260{
261 t_gobj *y;
262 int indx;
263 for (y = x->gl_list, indx = 0; y; y = y->g_next, indx++)
264 if (indx == n)
265 return (y);
266 return (0);
267}
268
269/* ------------------- support for undo/redo -------------------------- */
270
271static t_undofn canvas_undo_fn; /* current undo function if any */
272static int canvas_undo_whatnext; /* whether we can now UNDO or REDO */
273static void *canvas_undo_buf; /* data private to the undo function */
274static t_canvas *canvas_undo_canvas; /* which canvas we can undo on */
275static const char *canvas_undo_name;
276
277void canvas_setundo(t_canvas *x, t_undofn undofn, void *buf,
278 const char *name)
279{
280 int hadone = 0;
281 /* blow away the old undo information. In one special case the
282 old undo info is re-used; if so we shouldn't free it here. */
283 if (canvas_undo_fn && canvas_undo_buf && (buf != canvas_undo_buf))
284 {
285 (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_FREE);
286 hadone = 1;
287 }
288 canvas_undo_canvas = x;
289 canvas_undo_fn = undofn;
290 canvas_undo_buf = buf;
291 canvas_undo_whatnext = UNDO_UNDO;
292 canvas_undo_name = name;
293 if (x && glist_isvisible(x) && glist_istoplevel(x))
294 /* enable undo in menu */
295 sys_vgui("pdtk_undomenu .x%x %s no\n", x, name);
296 else if (hadone)
297 sys_vgui("pdtk_undomenu nobody no no\n");
298}
299
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.) */
302void canvas_noundo(t_canvas *x)
303{
304 if (!x || (x == canvas_undo_canvas))
305 canvas_setundo(0, 0, 0, "foo");
306}
307
308static void canvas_undo(t_canvas *x)
309{
310 if (x != canvas_undo_canvas)
311 bug("canvas_undo 1");
312 else if (canvas_undo_whatnext != UNDO_UNDO)
313 bug("canvas_undo 2");
314 else
315 {
316 /* post("undo"); */
317 (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_UNDO);
318 /* enable redo in menu */
319 if (glist_isvisible(x) && glist_istoplevel(x))
320 sys_vgui("pdtk_undomenu .x%x no %s\n", x, canvas_undo_name);
321 canvas_undo_whatnext = UNDO_REDO;
322 }
323}
324
325static void canvas_redo(t_canvas *x)
326{
327 if (x != canvas_undo_canvas)
328 bug("canvas_undo 1");
329 else if (canvas_undo_whatnext != UNDO_REDO)
330 bug("canvas_undo 2");
331 else
332 {
333 /* post("redo"); */
334 (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_REDO);
335 /* enable undo in menu */
336 if (glist_isvisible(x) && glist_istoplevel(x))
337 sys_vgui("pdtk_undomenu .x%x %s no\n", x, canvas_undo_name);
338 canvas_undo_whatnext = UNDO_UNDO;
339 }
340}
341
342/* ------- specific undo methods: 1. connect and disconnect -------- */
343
344typedef struct _undo_connect
345{
346 int u_index1;
347 int u_outletno;
348 int u_index2;
349 int u_inletno;
350} t_undo_connect;
351
352static void *canvas_undo_set_disconnect(t_canvas *x,
353 int index1, int outno, int index2, int inno)
354{
355 t_undo_connect *buf = (t_undo_connect *)getbytes(sizeof(*buf));
356 buf->u_index1 = index1;
357 buf->u_outletno = outno;
358 buf->u_index2 = index2;
359 buf->u_inletno = inno;
360 return (buf);
361}
362
363void canvas_disconnect(t_canvas *x,
364 float index1, float outno, float index2, float inno)
365{
366 t_linetraverser t;
367 t_outconnect *oc;
368 linetraverser_start(&t, x);
369 while (oc = linetraverser_next(&t))
370 {
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)
375 {
376 sys_vgui(".x%x.c delete l%x\n", x, oc);
377 obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);
378 break;
379 }
380 }
381}
382
383static void canvas_undo_disconnect(t_canvas *x, void *z, int action)
384{
385 t_undo_connect *buf = z;
386 if (action == UNDO_UNDO)
387 {
388 canvas_connect(x, buf->u_index1, buf->u_outletno,
389 buf->u_index2, buf->u_inletno);
390 }
391 else if (action == UNDO_REDO)
392 {
393 canvas_disconnect(x, buf->u_index1, buf->u_outletno,
394 buf->u_index2, buf->u_inletno);
395 }
396 else if (action == UNDO_FREE)
397 t_freebytes(buf, sizeof(*buf));
398}
399
400 /* connect just calls disconnect actions backward... */
401static void *canvas_undo_set_connect(t_canvas *x,
402 int index1, int outno, int index2, int inno)
403{
404 return (canvas_undo_set_disconnect(x, index1, outno, index2, inno));
405}
406
407static void canvas_undo_connect(t_canvas *x, void *z, int action)
408{
409 int myaction;
410 if (action == UNDO_UNDO)
411 myaction = UNDO_REDO;
412 else if (action == UNDO_REDO)
413 myaction = UNDO_UNDO;
414 else myaction = action;
415 canvas_undo_disconnect(x, z, myaction);
416}
417
418/* ---------- ... 2. cut, clear, and typing into objects: -------- */
419
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 */
423
424typedef struct _undo_cut
425{
426 t_binbuf *u_objectbuf; /* the object cleared or typed into */
427 t_binbuf *u_reconnectbuf; /* connections into and out of object */
428 t_binbuf *u_redotextbuf; /* buffer to paste back for redo if TEXT */
429 int u_mode; /* from flags above */
430} t_undo_cut;
431
432static void *canvas_undo_set_cut(t_canvas *x, int mode)
433{
434 t_undo_cut *buf;
435 t_gobj *y;
436 t_linetraverser t;
437 t_outconnect *oc;
438 int nnotsel= glist_selectionindex(x, 0, 0);
439 buf = (t_undo_cut *)getbytes(sizeof(*buf));
440 buf->u_mode = mode;
441 buf->u_redotextbuf = 0;
442
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))
447 {
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)
451 {
452 binbuf_addv(buf->u_reconnectbuf, "ssiiii;",
453 gensym("#X"), gensym("connect"),
454 (issel1 ? nnotsel : 0)
455 + glist_selectionindex(x, &t.tr_ob->ob_g, issel1),
456 t.tr_outno,
457 (issel2 ? nnotsel : 0) +
458 glist_selectionindex(x, &t.tr_ob2->ob_g, issel2),
459 t.tr_inno);
460 }
461 }
462 if (mode == UCUT_TEXT)
463 {
464 buf->u_objectbuf = canvas_docopy(x);
465 }
466 else if (mode == UCUT_CUT)
467 {
468 buf->u_objectbuf = 0;
469 }
470 else if (mode == UCUT_CLEAR)
471 {
472 buf->u_objectbuf = canvas_docopy(x);
473 }
474 return (buf);
475}
476
477static void canvas_undo_cut(t_canvas *x, void *z, int action)
478{
479 t_undo_cut *buf = z;
480 int mode = buf->u_mode;
481 if (action == UNDO_UNDO)
482 {
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)
488 {
489 t_gobj *y1, *y2;
490 glist_noselect(x);
491 for (y1 = x->gl_list; y2 = y1->g_next; y1 = y2)
492 ;
493 if (y1)
494 {
495 if (!buf->u_redotextbuf)
496 {
497 glist_noselect(x);
498 glist_select(x, y1);
499 buf->u_redotextbuf = canvas_docopy(x);
500 glist_noselect(x);
501 }
502 glist_delete(x, y1);
503 }
504 canvas_dopaste(x, buf->u_objectbuf);
505 }
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"));
509 }
510 else if (action == UNDO_REDO)
511 {
512 if (mode == UCUT_CUT || mode == UCUT_CLEAR)
513 canvas_doclear(x);
514 else if (mode == UCUT_TEXT)
515 {
516 t_gobj *y1, *y2;
517 for (y1 = x->gl_list; y2 = y1->g_next; y1 = y2)
518 ;
519 if (y1)
520 glist_delete(x, y1);
521 canvas_dopaste(x, buf->u_redotextbuf);
522 pd_bind(&x->gl_pd, gensym("#X"));
523 binbuf_eval(buf->u_reconnectbuf, 0, 0, 0);
524 pd_unbind(&x->gl_pd, gensym("#X"));
525 }
526 }
527 else if (action == UNDO_FREE)
528 {
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));
536 }
537}
538
539/* --------- 3. motion, including "tidy up" and stretching ----------- */
540
541typedef struct _undo_move_elem
542{
543 int e_index;
544 int e_xpix;
545 int e_ypix;
546} t_undo_move_elem;
547
548typedef struct _undo_move
549{
550 t_undo_move_elem *u_vec;
551 int u_n;
552} t_undo_move;
553
554static int canvas_undo_already_set_move;
555
556static void *canvas_undo_set_move(t_canvas *x, int selected)
557{
558 int x1, y1, x2, y2, i, indx;
559 t_gobj *y;
560 t_undo_move *buf = (t_undo_move *)getbytes(sizeof(*buf));
561 buf->u_n = selected ? glist_selectionindex(x, 0, 1) : glist_getindex(x, 0);
562 buf->u_vec = (t_undo_move_elem *)getbytes(sizeof(*buf->u_vec) *
563 (selected ? glist_selectionindex(x, 0, 1) : glist_getindex(x, 0)));
564 if (selected)
565 {
566 for (y = x->gl_list, i = indx = 0; y; y = y->g_next, indx++)
567 if (glist_isselected(x, y))
568 {
569 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
570 buf->u_vec[i].e_index = indx;
571 buf->u_vec[i].e_xpix = x1;
572 buf->u_vec[i].e_ypix = y1;
573 i++;
574 }
575 }
576 else
577 {
578 for (y = x->gl_list, indx = 0; y; y = y->g_next, indx++)
579 {
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;
584 }
585 }
586 canvas_undo_already_set_move = 1;
587 return (buf);
588}
589
590static void canvas_undo_move(t_canvas *x, void *z, int action)
591{
592 t_undo_move *buf = z;
593 if (action == UNDO_UNDO || action == UNDO_REDO)
594 {
595 int i;
596 for (i = 0; i < buf->u_n; i++)
597 {
598 int x1, y1, x2, y2, newx, newy;
599 t_gobj *y;
600 newx = buf->u_vec[i].e_xpix;
601 newy = buf->u_vec[i].e_ypix;
602 y = glist_nth(x, buf->u_vec[i].e_index);
603 if (y)
604 {
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;
609 }
610 }
611 }
612 else if (action == UNDO_FREE)
613 {
614 t_freebytes(buf->u_vec, buf->u_n * sizeof(*buf->u_vec));
615 t_freebytes(buf, sizeof(*buf));
616 }
617}
618
619/* --------- 4. paste (also duplicate) ----------- */
620
621typedef struct _undo_paste
622{
623 int u_index; /* index of first object pasted */
624} t_undo_paste;
625
626static void *canvas_undo_set_paste(t_canvas *x)
627{
628 t_undo_paste *buf = (t_undo_paste *)getbytes(sizeof(*buf));
629 buf->u_index = glist_getindex(x, 0);
630 return (buf);
631}
632
633static void canvas_undo_paste(t_canvas *x, void *z, int action)
634{
635 t_undo_paste *buf = z;
636 if (action == UNDO_UNDO)
637 {
638 t_gobj *y;
639 glist_noselect(x);
640 for (y = glist_nth(x, buf->u_index); y; y = y->g_next)
641 glist_select(x, y);
642 canvas_doclear(x);
643 }
644 else if (action == UNDO_REDO)
645 {
646 t_selection *sel;
647 canvas_dopaste(x, copy_binbuf);
648 /* if it was "duplicate" have to re-enact the displacement. */
649 if (canvas_undo_name && canvas_undo_name[0] == 'd')
650 for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
651 gobj_displace(sel->sel_what, x, 10, 10);
652 }
653else if (action == UNDO_FREE)
654 t_freebytes(buf, sizeof(*buf));
655}
656
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. */
660static void glist_doreload(t_glist *gl, t_symbol *name, t_symbol *dir,
661 t_gobj *except)
662{
663 t_gobj *g;
664 int i, nobj = glist_getindex(gl, 0); /* number of objects */
665 for (g = gl->gl_list, i = 0; g && i < nobj; i++)
666 {
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)
671 {
672 /* we're going to remake the object, so "g" will go stale.
673 Get its index here, and afterward restore g. Also, the
674 replacement will be at teh end of the list, so we don't
675 do g = g->g_next in this case. */
676 int j = glist_getindex(gl, g);
677 if (!gl->gl_havewindow)
678 canvas_vis(glist_getcanvas(gl), 1);
679 glist_noselect(gl);
680 glist_select(gl, g);
681 canvas_setundo(gl, canvas_undo_cut,
682 canvas_undo_set_cut(gl, UCUT_CLEAR), "clear");
683 canvas_doclear(gl);
684 canvas_undo(gl);
685 glist_noselect(gl);
686 g = glist_nth(gl, j);
687 }
688 else
689 {
690 if (g != except && pd_class(&g->g_pd) == canvas_class)
691 glist_doreload((t_canvas *)g, name, dir, except);
692 g = g->g_next;
693 }
694 }
695}
696
697 /* call canvas_doreload on everyone */
698void canvas_reload(t_symbol *name, t_symbol *dir, t_gobj *except)
699{
700 t_canvas *x;
701 /* find all root canvases */
702 for (x = canvas_list; x; x = x->gl_next)
703 glist_doreload(x, name, dir, except);
704}
705
706/* ------------------------ event handling ------------------------ */
707
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
715
716static char *cursorlist[] = {
717#ifdef MSW
718 "right_ptr", /* CURSOR_RUNMODE_NOTHING */
719#else
720 "left_ptr", /* CURSOR_RUNMODE_NOTHING */
721#endif
722 "arrow", /* CURSOR_RUNMODE_CLICKME */
723 "sb_v_double_arrow", /* CURSOR_RUNMODE_THICKEN */
724 "plus", /* CURSOR_RUNMODE_ADDPOINT */
725 "hand2", /* CURSOR_EDITMODE_NOTHING */
726 "circle", /* CURSOR_EDITMODE_CONNECT */
727 "X_cursor" /* CURSOR_EDITMODE_DISCONNECT */
728};
729
730void canvas_setcursor(t_canvas *x, unsigned int cursornum)
731{
732 static t_canvas *xwas;
733 static unsigned int cursorwas;
734 if (cursornum >= sizeof(cursorlist)/sizeof *cursorlist)
735 {
736 bug("canvas_setcursor");
737 return;
738 }
739 if (xwas != x || cursorwas != cursornum)
740 {
741 sys_vgui(".x%x configure -cursor %s\n", x, cursorlist[cursornum]);
742 xwas = x;
743 cursorwas = cursornum;
744 }
745}
746
747 /* check if a point lies in a gobj. */
748int canvas_hitbox(t_canvas *x, t_gobj *y, int xpos, int ypos,
749 int *x1p, int *y1p, int *x2p, int *y2p)
750{
751 int x1, y1, x2, y2;
752 t_text *ob;
753 if ((ob = pd_checkobject(&y->g_pd)) &&
754 !text_shouldvis(ob, x))
755 return (0);
756 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
757 if (xpos >= x1 && xpos <= x2 && ypos >= y1 && ypos <= y2)
758 {
759 *x1p = x1;
760 *y1p = y1;
761 *x2p = x2;
762 *y2p = y2;
763 return (1);
764 }
765 else return (0);
766}
767
768 /* find the last gobj, if any, containing the point. */
769static t_gobj *canvas_findhitbox(t_canvas *x, int xpos, int ypos,
770 int *x1p, int *y1p, int *x2p, int *y2p)
771{
772 t_gobj *y, *rval = 0;
773 for (y = x->gl_list; y; y = y->g_next)
774 {
775 if (canvas_hitbox(x, y, xpos, ypos, x1p, y1p, x2p, y2p))
776 rval = y;
777 }
778 return (rval);
779}
780
781 /* right-clicking on a canvas object pops up a menu. */
782static void canvas_rightclick(t_canvas *x, int xpos, int ypos, t_gobj *y)
783{
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);
789}
790
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. */
794static void canvas_properties(t_glist *x)
795{
796 char graphbuf[200];
797 sprintf(graphbuf, "pdtk_canvas_dialog %%s %g %g %g %g \n",
798 glist_dpixtodx(x, 1), -glist_dpixtody(x, 1),
799 (float)glist_isgraph(x), (float)x->gl_stretch);
800 gfxstub_new(&x->gl_pd, x, graphbuf);
801}
802
803
804void canvas_setgraph(t_glist *x, int flag)
805{
806 if (!flag && glist_isgraph(x))
807 {
808 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
809 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
810 x->gl_isgraph = 0;
811 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
812 {
813 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
814 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
815 }
816 }
817 else if (flag && !glist_isgraph(x))
818 {
819 if (x->gl_pixwidth <= 0)
820 x->gl_pixwidth = GLIST_DEFGRAPHWIDTH;
821
822 if (x->gl_pixheight <= 0)
823 x->gl_pixheight = GLIST_DEFGRAPHHEIGHT;
824
825 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
826 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
827 x->gl_isgraph = 1;
828 /* if (x->gl_owner && glist_isvisible(x->gl_owner))
829 canvas_vis(x, 1); */
830 if (x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner))
831 canvas_create_editor(x, 1);
832 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
833 {
834 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
835 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
836 }
837 }
838}
839
840 /* called from the gui when "OK" is selected on the canvas properties
841 dialog. Again we negate "y" scale. */
842static void canvas_donecanvasdialog(t_glist *x, t_floatarg xperpix,
843 t_floatarg yperpix, t_floatarg fgraphme)
844{
845 int graphme = (fgraphme != 0), redraw = 0;
846 yperpix = -yperpix;
847 if (xperpix == 0)
848 xperpix = 1;
849 if (yperpix == 0)
850 yperpix = 1;
851 canvas_setgraph(x, graphme);
852 if (!x->gl_isgraph && (xperpix != glist_dpixtodx(x, 1)))
853 {
854 if (xperpix > 0)
855 {
856 x->gl_x1 = 0;
857 x->gl_x2 = xperpix;
858 }
859 else
860 {
861 x->gl_x1 = -xperpix * (x->gl_screenx2 - x->gl_screenx1);
862 x->gl_x2 = x->gl_x1 + xperpix;
863 }
864 redraw = 1;
865 }
866 if (!x->gl_isgraph && (yperpix != glist_dpixtody(x, 1)))
867 {
868 if (yperpix > 0)
869 {
870 x->gl_y1 = 0;
871 x->gl_y2 = yperpix;
872 }
873 else
874 {
875 x->gl_y1 = -yperpix * (x->gl_screeny2 - x->gl_screeny1);
876 x->gl_y2 = x->gl_y1 + yperpix;
877 }
878 redraw = 1;
879 }
880 if (redraw)
881 canvas_redraw(x);
882}
883
884 /* called from the gui when a popup menu comes back with "properties,"
885 "open," or "help." */
886static void canvas_done_popup(t_canvas *x, float which, float xpos, float ypos)
887{
888 char pathbuf[MAXPDSTRING], namebuf[MAXPDSTRING];
889 t_gobj *y;
890 for (y = x->gl_list; y; y = y->g_next)
891 {
892 int x1, y1, x2, y2;
893 if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2))
894 {
895 if (which == 0) /* properties */
896 {
897 if (!class_getpropertiesfn(pd_class(&y->g_pd)))
898 continue;
899 (*class_getpropertiesfn(pd_class(&y->g_pd)))(y, x);
900 return;
901 }
902 else if (which == 1) /* open */
903 {
904 if (!zgetfn(&y->g_pd, gensym("menu-open")))
905 continue;
906 vmess(&y->g_pd, gensym("menu-open"), "");
907 return;
908 }
909 else /* help */
910 {
911 char *dir;
912 if (pd_class(&y->g_pd) == canvas_class &&
913 canvas_isabstraction((t_canvas *)y))
914 {
915 t_object *ob = (t_object *)y;
916 int ac = binbuf_getnatom(ob->te_binbuf);
917 t_atom *av = binbuf_getvec(ob->te_binbuf);
918 if (ac < 1)
919 return;
920 atom_string(av, namebuf, MAXPDSTRING);
921 dir = canvas_getdir((t_canvas *)y)->s_name;
922 }
923 else
924 {
925 strcpy(namebuf, class_gethelpname(pd_class(&y->g_pd)));
926 dir = class_gethelpdir(pd_class(&y->g_pd));
927 }
928 if (strcmp(namebuf + strlen(namebuf) - 3, ".pd"))
929 strcat(namebuf, ".pd");
930 open_via_helppath(namebuf, dir);
931 return;
932 }
933 }
934 }
935 if (which == 0)
936 canvas_properties(x);
937 else if (which == 2)
938 {
939 strcpy(pathbuf, sys_libdir->s_name);
940 strcat(pathbuf, "/doc/5.reference/0.INTRO.txt");
941 sys_vgui("menu_opentext %s\n", pathbuf);
942 }
943}
944
945#define NOMOD 0
946#define SHIFTMOD 1
947#define CTRLMOD 2
948#define ALTMOD 4
949#define RIGHTCLICK 8
950
951/* on one-button-mouse machines, you can use double click to
952 mean right click (which gets the popup menu.) Do this for Mac. */
953#ifdef MACOSX
954#define SIMULATERIGHTCLICK
955#endif
956
957#ifdef SIMULATERIGHTCLICK
958static double canvas_upclicktime;
959static int canvas_upx, canvas_upy;
960#define DCLICKINTERVAL 0.25
961#endif
962
963 /* mouse click */
964void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
965 int mod, int doit)
966{
967 t_gobj *y;
968 int shiftmod, runmode, altmod, rightclick;
969 int x1, y1, x2, y2, clickreturned = 0;
970
971 if (!x->gl_editor)
972 {
973 bug("editor");
974 return;
975 }
976
977 shiftmod = (mod & SHIFTMOD);
978 runmode = ((mod & CTRLMOD) || (!x->gl_edit));
979 altmod = (mod & ALTMOD);
980 rightclick = (mod & RIGHTCLICK);
981
982 canvas_undo_already_set_move = 0;
983
984 /* if keyboard was grabbed, notify grabber and cancel the grab */
985 if (doit && x->gl_editor->e_grab && x->gl_editor->e_keyfn)
986 {
987 (* x->gl_editor->e_keyfn) (x->gl_editor->e_grab, 0);
988 glist_grab(x, 0, 0, 0, 0, 0);
989 }
990
991#ifdef SIMULATERIGHTCLICK
992 if (doit && !runmode && xpos == canvas_upx && ypos == canvas_upy &&
993 sys_getrealtime() - canvas_upclicktime < DCLICKINTERVAL)
994 rightclick = 1;
995#endif
996
997 x->gl_editor->e_lastmoved = 0;
998 if (doit)
999 {
1000 x->gl_editor->e_grab = 0;
1001 x->gl_editor->e_onmotion = MA_NONE;
1002 }
1003 /* post("click %d %d %d %d", xpos, ypos, which, mod); */
1004
1005 if (x->gl_editor->e_onmotion != MA_NONE)
1006 return;
1007
1008 x->gl_editor->e_xwas = xpos;
1009 x->gl_editor->e_ywas = ypos;
1010
1011 if (runmode && !rightclick)
1012 {
1013 for (y = x->gl_list; y; y = y->g_next)
1014 {
1015 /* check if the object wants to be clicked */
1016 if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2)
1017 && (clickreturned = gobj_click(y, x, xpos, ypos,
1018 shiftmod, altmod, 0, doit)))
1019 break;
1020 }
1021 if (!doit)
1022 {
1023 if (y)
1024 canvas_setcursor(x, clickreturned);
1025 else canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
1026 }
1027 return;
1028 }
1029 /* if not a runmode left click, fall here. */
1030 if (y = canvas_findhitbox(x, xpos, ypos, &x1, &y1, &x2, &y2))
1031 {
1032 t_object *ob = pd_checkobject(&y->g_pd);
1033 /* check you're in the rectangle */
1034 ob = pd_checkobject(&y->g_pd);
1035 if (rightclick)
1036 canvas_rightclick(x, xpos, ypos, y);
1037 else if (shiftmod)
1038 {
1039 if (doit)
1040 {
1041 t_rtext *rt;
1042 if (ob && (rt = x->gl_editor->e_textedfor) &&
1043 rt == glist_findrtext(x, ob))
1044 {
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;
1049 }
1050 else
1051 {
1052 if (glist_isselected(x, y))
1053 glist_deselect(x, y);
1054 else glist_select(x, y);
1055 }
1056 }
1057 }
1058 else
1059 {
1060 /* look for an outlet */
1061 int noutlet;
1062 if (ob && (noutlet = obj_noutlets(ob)) && ypos >= y2-4)
1063 {
1064 int width = x2 - x1;
1065 int nout1 = (noutlet > 1 ? noutlet - 1 : 1);
1066 int closest = ((xpos-x1) * (nout1) + width/2)/width;
1067 int hotspot = x1 +
1068 (width - IOWIDTH) * closest / (nout1);
1069 if (closest < noutlet &&
1070 xpos >= (hotspot-1) && xpos <= hotspot + (IOWIDTH+1))
1071 {
1072 if (doit)
1073 {
1074 int issignal = obj_issignaloutlet(ob, closest);
1075 x->gl_editor->e_onmotion = MA_CONNECT;
1076 x->gl_editor->e_xwas = xpos;
1077 x->gl_editor->e_ywas = ypos;
1078 sys_vgui(
1079 ".x%x.c create line %d %d %d %d -width %d -tags x\n",
1080 x, xpos, ypos, xpos, ypos,
1081 (issignal ? 2 : 1));
1082 }
1083 else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT);
1084 }
1085 else if (doit)
1086 goto nooutletafterall;
1087 }
1088 /* not in an outlet; select and move */
1089 else if (doit)
1090 {
1091 t_rtext *rt;
1092 /* check if the box is being text edited */
1093 nooutletafterall:
1094 if (ob && (rt = x->gl_editor->e_textedfor) &&
1095 rt == glist_findrtext(x, ob))
1096 {
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;
1101 }
1102 else
1103 {
1104 /* otherwise select and drag to displace */
1105 if (!glist_isselected(x, y))
1106 {
1107 glist_noselect(x);
1108 glist_select(x, y);
1109 }
1110 x->gl_editor->e_onmotion = MA_MOVE;
1111 }
1112 }
1113 else canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
1114 }
1115 return;
1116 }
1117 /* if right click doesn't hit any boxes, call rightclick
1118 routine anyway */
1119 if (rightclick)
1120 canvas_rightclick(x, xpos, ypos, 0);
1121
1122 /* if not an editing action, and if we didn't hit a
1123 box, set cursor and return */
1124 if (runmode || rightclick)
1125 {
1126 canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
1127 return;
1128 }
1129 /* having failed to find a box, we try lines now. */
1130 if (!runmode && !altmod && !shiftmod)
1131 {
1132 t_linetraverser t;
1133 t_outconnect *oc;
1134 float fx = xpos, fy = ypos;
1135 t_glist *glist2 = glist_getcanvas(x);
1136 linetraverser_start(&t, glist2);
1137 while (oc = linetraverser_next(&t))
1138 {
1139 float lx1 = t.tr_lx1, ly1 = t.tr_ly1,
1140 lx2 = t.tr_lx2, ly2 = t.tr_ly2;
1141 float area = (lx2 - lx1) * (fy - ly1) -
1142 (ly2 - ly1) * (fx - lx1);
1143 float dsquare = (lx2-lx1) * (lx2-lx1) + (ly2-ly1) * (ly2-ly1);
1144 if (area * area >= 50 * dsquare) continue;
1145 if ((lx2-lx1) * (fx-lx1) + (ly2-ly1) * (fy-ly1) < 0) continue;
1146 if ((lx2-lx1) * (lx2-fx) + (ly2-ly1) * (ly2-fy) < 0) continue;
1147 if (doit)
1148 {
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);
1152 }
1153 canvas_setcursor(x, CURSOR_EDITMODE_DISCONNECT);
1154 return;
1155 }
1156 }
1157 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
1158 if (doit)
1159 {
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;
1166 }
1167}
1168
1169void canvas_mousedown(t_canvas *x, t_floatarg xpos, t_floatarg ypos,
1170 t_floatarg which, t_floatarg mod)
1171{
1172 canvas_doclick(x, xpos, ypos, which, mod, 1);
1173}
1174
1175int canvas_isconnected (t_canvas *x, t_text *ob1, int n1,
1176 t_text *ob2, int n2)
1177{
1178 t_linetraverser t;
1179 t_outconnect *oc;
1180 linetraverser_start(&t, x);
1181 while (oc = linetraverser_next(&t))
1182 if (t.tr_ob == ob1 && t.tr_outno == n1 &&
1183 t.tr_ob2 == ob2 && t.tr_inno == n2)
1184 return (1);
1185 return (0);
1186}
1187
1188void canvas_doconnect(t_canvas *x, int xpos, int ypos, int which, int doit)
1189{
1190 int x11, y11, x12, y12;
1191 t_gobj *y1;
1192 int x21, y21, x22, y22;
1193 t_gobj *y2;
1194 int xwas = x->gl_editor->e_xwas,
1195 ywas = x->gl_editor->e_ywas;
1196 if (doit) sys_vgui(".x%x.c delete x\n", x);
1197 else sys_vgui(".x%x.c coords x %d %d %d %d\n",
1198 x, x->gl_editor->e_xwas,
1199 x->gl_editor->e_ywas, xpos, ypos);
1200
1201 if ((y1 = canvas_findhitbox(x, xwas, ywas, &x11, &y11, &x12, &y12))
1202 && (y2 = canvas_findhitbox(x, xpos, ypos, &x21, &y21, &x22, &y22)))
1203 {
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)))
1210 {
1211 int width1 = x12 - x11, closest1, hotspot1;
1212 int width2 = x22 - x21, closest2, hotspot2;
1213 int lx1, lx2, ly1, ly2;
1214 t_outconnect *oc;
1215
1216 if (noutlet1 > 1)
1217 {
1218 closest1 = ((xwas-x11) * (noutlet1-1) + width1/2)/width1;
1219 hotspot1 = x11 +
1220 (width1 - IOWIDTH) * closest1 / (noutlet1-1);
1221 }
1222 else closest1 = 0, hotspot1 = x11;
1223
1224 if (ninlet2 > 1)
1225 {
1226 closest2 = ((xpos-x21) * (ninlet2-1) + width2/2)/width2;
1227 hotspot2 = x21 +
1228 (width2 - IOWIDTH) * closest2 / (ninlet2-1);
1229 }
1230 else closest2 = 0, hotspot2 = x21;
1231
1232 if (closest1 >= noutlet1)
1233 closest1 = noutlet1 - 1;
1234 if (closest2 >= ninlet2)
1235 closest2 = ninlet2 - 1;
1236
1237 if (canvas_isconnected (x, ob1, closest1, ob2, closest2))
1238 {
1239 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
1240 return;
1241 }
1242 if (obj_issignaloutlet(ob1, closest1) &&
1243 !obj_issignalinlet(ob2, closest2))
1244 {
1245 if (doit)
1246 error("can't connect signal outlet to control inlet");
1247 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
1248 return;
1249 }
1250 if (doit)
1251 {
1252 oc = obj_connect(ob1, closest1, ob2, closest2);
1253 lx1 = x11 + (noutlet1 > 1 ?
1254 ((x12-x11-IOWIDTH) * closest1)/(noutlet1-1) : 0)
1255 + IOMIDDLE;
1256 ly1 = y12;
1257 lx2 = x21 + (ninlet2 > 1 ?
1258 ((x22-x21-IOWIDTH) * closest2)/(ninlet2-1) : 0)
1259 + IOMIDDLE;
1260 ly2 = y21;
1261 sys_vgui(".x%x.c create line %d %d %d %d -width %d -tags l%x\n",
1262 glist_getcanvas(x),
1263 lx1, ly1, lx2, ly2,
1264 (obj_issignaloutlet(ob1, closest1) ? 2 : 1), oc);
1265 canvas_setundo(x, canvas_undo_connect,
1266 canvas_undo_set_connect(x,
1267 canvas_getindex(x, &ob1->ob_g), closest1,
1268 canvas_getindex(x, &ob2->ob_g), closest2),
1269 "connect");
1270 }
1271 else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT);
1272 return;
1273 }
1274 }
1275 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
1276}
1277
1278void canvas_selectinrect(t_canvas *x, int lox, int loy, int hix, int hiy)
1279{
1280 t_gobj *y;
1281 for (y = x->gl_list; y; y = y->g_next)
1282 {
1283 int x1, y1, x2, y2;
1284 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
1285 if (hix >= x1 && lox <= x2 && hiy >= y1 && loy <= y2
1286 && !glist_isselected(x, y))
1287 glist_select(x, y);
1288 }
1289}
1290
1291static void canvas_doregion(t_canvas *x, int xpos, int ypos, int doit)
1292{
1293 if (doit)
1294 {
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;
1305 }
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);
1309}
1310
1311void canvas_mouseup(t_canvas *x,
1312 t_floatarg fxpos, t_floatarg fypos, t_floatarg fwhich)
1313{
1314 t_gobj *y;
1315
1316 int xpos = fxpos, ypos = fypos, which = fwhich;
1317
1318 if (!x->gl_editor)
1319 {
1320 bug("editor");
1321 return;
1322 }
1323
1324#ifdef SIMULATERIGHTCLICK
1325 canvas_upclicktime = sys_getrealtime();
1326 canvas_upx = xpos;
1327 canvas_upy = ypos;
1328#endif
1329
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)
1335 {
1336 /* after motion, if there's only one item selected, activate it */
1337 if (x->gl_editor->e_selection &&
1338 !(x->gl_editor->e_selection->sel_next))
1339 gobj_activate(x->gl_editor->e_selection->sel_what,
1340 x, 1);
1341 }
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;
1345
1346
1347#if 1
1348 /* GG misused the (unused) dbl parameter to tell if mouseup */
1349
1350 for (y = x->gl_list; y; y = y->g_next) {
1351 int x1, y1, x2, y2, clickreturned = 0;
1352
1353 /* check if the object wants to be clicked */
1354 if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2)
1355 && (clickreturned = gobj_click(y, x, xpos, ypos,
1356 0, 0, 1, 0)))
1357 break;
1358 }
1359#endif
1360
1361
1362}
1363
1364 /* displace the selection by (dx, dy) pixels */
1365static void canvas_displaceselection(t_canvas *x, int dx, int dy)
1366{
1367 t_selection *y;
1368 int resortin = 0, resortout = 0;
1369 if (!canvas_undo_already_set_move)
1370 {
1371 canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, 1),
1372 "motion");
1373 canvas_undo_already_set_move = 1;
1374 }
1375 for (y = x->gl_editor->e_selection; y; y = y->sel_next)
1376 {
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;
1381 }
1382 if (resortin) canvas_resortinlets(x);
1383 if (resortout) canvas_resortoutlets(x);
1384 canvas_dirty(x, 1);
1385}
1386
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. */
1391void canvas_key(t_canvas *x, t_symbol *s, int ac, t_atom *av)
1392{
1393 static t_symbol *keynumsym, *keyupsym, *keynamesym;
1394 int keynum, fflag;
1395 t_symbol *gotkeysym;
1396
1397 int down, shift;
1398
1399 if (ac < 3)
1400 return;
1401 if (!x->gl_editor)
1402 {
1403 bug("editor");
1404 return;
1405 }
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)
1412 {
1413 char buf[3];
1414 sprintf(buf, "%c", (int)(av[1].a_w.w_float));
1415 gotkeysym = gensym(buf);
1416 }
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 == '}')
1421 {
1422 post("%c: dropped", (int)keynum);
1423 return;
1424 }
1425 if (keynum == '\r') keynum = '\n';
1426 if (av[1].a_type == A_SYMBOL &&
1427 !strcmp(av[1].a_w.w_symbol->s_name, "Return"))
1428 keynum = '\n';
1429 if (!keynumsym)
1430 {
1431 keynumsym = gensym("#key");
1432 keyupsym = gensym("#keyup");
1433 keynamesym = gensym("#keyname");
1434 }
1435#ifdef MACOSX
1436 if (keynum == 30)
1437 keynum = 0, gotkeysym = gensym("Up");
1438 else if (keynum == 31)
1439 keynum = 0, gotkeysym = gensym("Down");
1440 else if (keynum == 28)
1441 keynum = 0, gotkeysym = gensym("Left");
1442 else if (keynum == 29)
1443 keynum = 0, gotkeysym = gensym("Right");
1444#endif
1445 if (keynumsym->s_thing && down)
1446 pd_float(keynumsym->s_thing, (float)keynum);
1447 if (keyupsym->s_thing && !down)
1448 pd_float(keyupsym->s_thing, (float)keynum);
1449 if (keynamesym->s_thing)
1450 {
1451 t_atom at[2];
1452 at[0] = av[0];
1453 SETFLOAT(at, down);
1454 SETSYMBOL(at+1, gotkeysym);
1455 pd_list(keynamesym->s_thing, 0, 2, at);
1456 }
1457 if (!x->gl_editor) /* if that 'invis'ed the window, we'd better stop. */
1458 return;
1459 if (x && down)
1460 {
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)
1468 {
1469 if (!x->gl_editor->e_textdirty)
1470 {
1471 canvas_setundo(x, canvas_undo_cut,
1472 canvas_undo_set_cut(x, UCUT_TEXT), "typing");
1473 }
1474 rtext_key(x->gl_editor->e_textedfor,
1475 (int)keynum, gotkeysym);
1476 if (x->gl_editor->e_textdirty)
1477 canvas_dirty(x, 1);
1478 }
1479 /* check for backspace or clear */
1480 else if (keynum == 8 || keynum == 127)
1481 {
1482 if (x->gl_editor->e_selectedline)
1483 canvas_clearline(x);
1484 else if (x->gl_editor->e_selection)
1485 {
1486 canvas_setundo(x, canvas_undo_cut,
1487 canvas_undo_set_cut(x, UCUT_CLEAR), "clear");
1488 canvas_doclear(x);
1489 }
1490 }
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);
1500 }
1501}
1502
1503void canvas_motion(t_canvas *x, t_floatarg xpos, t_floatarg ypos,
1504 t_floatarg fmod)
1505{
1506 /* post("motion %d %d", xpos, ypos); */
1507 int mod = fmod;
1508 if (!x->gl_editor)
1509 {
1510 bug("editor");
1511 return;
1512 }
1513 glist_setlastxy(x, xpos, ypos);
1514 if (x->gl_editor->e_onmotion == MA_MOVE)
1515 {
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;
1520 }
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)
1526 {
1527 if (!x->gl_editor->e_motionfn)
1528 bug("e_motionfn");
1529 (*x->gl_editor->e_motionfn)(&x->gl_editor->e_grab->g_pd,
1530 xpos - x->gl_editor->e_xwas,
1531 ypos - x->gl_editor->e_ywas);
1532 x->gl_editor->e_xwas = xpos;
1533 x->gl_editor->e_ywas = ypos;
1534 }
1535 else if (x->gl_editor->e_onmotion == MA_DRAGTEXT)
1536 {
1537 t_rtext *rt = x->gl_editor->e_textedfor;
1538 if (rt)
1539 rtext_mouse(rt, xpos - x->gl_editor->e_xwas,
1540 ypos - x->gl_editor->e_ywas, RTEXT_DRAG);
1541 }
1542 else canvas_doclick(x, xpos, ypos, 0, mod, 0);
1543
1544 x->gl_editor->e_lastmoved = 1;
1545}
1546
1547void canvas_startmotion(t_canvas *x)
1548{
1549 int xval, yval;
1550 if (!x->gl_editor) return;
1551 glist_getnextxy(x, &xval, &yval);
1552 if (xval == 0 && yval == 0) return;
1553 x->gl_editor->e_onmotion = MA_MOVE;
1554 x->gl_editor->e_xwas = xval;
1555 x->gl_editor->e_ywas = yval;
1556}
1557
1558/* ----------------------------- window stuff ----------------------- */
1559
1560void canvas_print(t_canvas *x, t_symbol *s)
1561{
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);
1564}
1565
1566void canvas_menuclose(t_canvas *x, t_floatarg force)
1567{
1568 if (x->gl_owner)
1569 canvas_vis(x, 0);
1570 else if ((force != 0) || (!x->gl_dirty))
1571 pd_free(&x->gl_pd);
1572 else sys_vgui("pdtk_check {This window has been modified. Close anyway?}\
1573 {.x%x menuclose 1;\n}\n", x);
1574}
1575
1576 /* put up a dialog which may call canvas_font back to do the work */
1577static void canvas_menufont(t_canvas *x)
1578{
1579 char buf[80];
1580 t_canvas *x2 = canvas_getrootfor(x);
1581 gfxstub_deleteforkey(x2);
1582 sprintf(buf, "pdtk_canvas_dofont %%s %d\n", x2->gl_font);
1583 gfxstub_new(&x2->gl_pd, &x2->gl_pd, buf);
1584}
1585
1586static int canvas_find_index1, canvas_find_index2;
1587static t_binbuf *canvas_findbuf;
1588int binbuf_match(t_binbuf *inbuf, t_binbuf *searchbuf);
1589
1590 /* find an atom or string of atoms */
1591static int canvas_dofind(t_canvas *x, int *myindex1p)
1592{
1593 t_gobj *y;
1594 int myindex1 = *myindex1p, myindex2;
1595 if (myindex1 >= canvas_find_index1)
1596 {
1597 for (y = x->gl_list, myindex2 = 0; y;
1598 y = y->g_next, myindex2++)
1599 {
1600 t_object *ob = 0;
1601 if (ob = pd_checkobject(&y->g_pd))
1602 {
1603 if (binbuf_match(ob->ob_binbuf, canvas_findbuf))
1604 {
1605 if (myindex1 > canvas_find_index1 ||
1606 myindex1 == canvas_find_index1 &&
1607 myindex2 > canvas_find_index2)
1608 {
1609 canvas_find_index1 = myindex1;
1610 canvas_find_index2 = myindex2;
1611 glist_noselect(x);
1612 canvas_vis(x, 1);
1613 canvas_editmode(x, 1.);
1614 glist_select(x, y);
1615 return (1);
1616 }
1617 }
1618 }
1619 }
1620 }
1621 for (y = x->gl_list, myindex2 = 0; y; y = y->g_next, myindex2++)
1622 {
1623 if (pd_class(&y->g_pd) == canvas_class)
1624 {
1625 (*myindex1p)++;
1626 if (canvas_dofind((t_canvas *)y, myindex1p))
1627 return (1);
1628 }
1629 }
1630 return (0);
1631}
1632
1633static void canvas_find(t_canvas *x, t_symbol *s, int ac, t_atom *av)
1634{
1635 int myindex1 = 0, i;
1636 for (i = 0; i < ac; i++)
1637 {
1638 if (av[i].a_type == A_SYMBOL)
1639 {
1640 if (!strcmp(av[i].a_w.w_symbol->s_name, "_semi_"))
1641 SETSEMI(&av[i]);
1642 else if (!strcmp(av[i].a_w.w_symbol->s_name, "_comma_"))
1643 SETCOMMA(&av[i]);
1644 }
1645 }
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))
1654 {
1655 binbuf_print(canvas_findbuf);
1656 post("... couldn't find");
1657 }
1658}
1659
1660static void canvas_find_again(t_canvas *x)
1661{
1662 int myindex1 = 0;
1663 if (!canvas_findbuf || !canvas_whichfind)
1664 return;
1665 if (!canvas_dofind(canvas_whichfind, &myindex1))
1666 {
1667 binbuf_print(canvas_findbuf);
1668 post("... couldn't find");
1669 }
1670}
1671
1672static void canvas_find_parent(t_canvas *x)
1673{
1674 if (x->gl_owner)
1675 canvas_vis(glist_getcanvas(x->gl_owner), 1);
1676}
1677
1678static int glist_dofinderror(t_glist *gl, void *error_object)
1679{
1680 t_gobj *g;
1681 for (g = gl->gl_list; g; g = g->g_next)
1682 {
1683 if ((void *)g == error_object)
1684 {
1685 /* got it... now show it. */
1686 glist_noselect(gl);
1687 canvas_vis(glist_getcanvas(gl), 1);
1688 canvas_editmode(glist_getcanvas(gl), 1.);
1689 glist_select(gl, g);
1690 return (1);
1691 }
1692 else if (g->g_pd == canvas_class)
1693 {
1694 if (glist_dofinderror((t_canvas *)g, error_object))
1695 return (1);
1696 }
1697 }
1698 return (0);
1699}
1700
1701void canvas_finderror(void *error_object)
1702{
1703 t_canvas *x;
1704 /* find all root canvases */
1705 for (x = canvas_list; x; x = x->gl_next)
1706 {
1707 if (glist_dofinderror(x, error_object))
1708 return;
1709 }
1710 post("... sorry, I couldn't find the source of that error.");
1711}
1712
1713void canvas_stowconnections(t_canvas *x)
1714{
1715 t_gobj *selhead = 0, *seltail = 0, *nonhead = 0, *nontail = 0, *y, *y2;
1716 t_linetraverser t;
1717 t_outconnect *oc;
1718 if (!x->gl_editor) return;
1719 /* split list to "selected" and "unselected" parts */
1720 for (y = x->gl_list; y; y = y2)
1721 {
1722 y2 = y->g_next;
1723 if (glist_isselected(x, y))
1724 {
1725 if (seltail)
1726 {
1727 seltail->g_next = y;
1728 seltail = y;
1729 y->g_next = 0;
1730 }
1731 else
1732 {
1733 selhead = seltail = y;
1734 seltail->g_next = 0;
1735 }
1736 }
1737 else
1738 {
1739 if (nontail)
1740 {
1741 nontail->g_next = y;
1742 nontail = y;
1743 y->g_next = 0;
1744 }
1745 else
1746 {
1747 nonhead = nontail = y;
1748 nontail->g_next = 0;
1749 }
1750 }
1751 }
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;
1755
1756 /* add connections to binbuf */
1757 binbuf_clear(x->gl_editor->e_connectbuf);
1758 linetraverser_start(&t, x);
1759 while (oc = linetraverser_next(&t))
1760 {
1761 int s1 = glist_isselected(x, &t.tr_ob->ob_g);
1762 int s2 = glist_isselected(x, &t.tr_ob2->ob_g);
1763 if (s1 != s2)
1764 binbuf_addv(x->gl_editor->e_connectbuf, "ssiiii;",
1765 gensym("#X"), gensym("connect"),
1766 glist_getindex(x, &t.tr_ob->ob_g), t.tr_outno,
1767 glist_getindex(x, &t.tr_ob2->ob_g), t.tr_inno);
1768 }
1769}
1770
1771void canvas_restoreconnections(t_canvas *x)
1772{
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"));
1776}
1777
1778static t_binbuf *canvas_docopy(t_canvas *x)
1779{
1780 t_gobj *y;
1781 t_linetraverser t;
1782 t_outconnect *oc;
1783 t_binbuf *b = binbuf_new();
1784 for (y = x->gl_list; y; y = y->g_next)
1785 {
1786 if (glist_isselected(x, y))
1787 gobj_save(y, b);
1788 }
1789 linetraverser_start(&t, x);
1790 while (oc = linetraverser_next(&t))
1791 {
1792 if (glist_isselected(x, &t.tr_ob->ob_g)
1793 && glist_isselected(x, &t.tr_ob2->ob_g))
1794 {
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);
1798 }
1799 }
1800 return (b);
1801}
1802
1803static void canvas_copy(t_canvas *x)
1804{
1805 if (!x->gl_editor || !x->gl_editor->e_selection)
1806 return;
1807 binbuf_free(copy_binbuf);
1808 copy_binbuf = canvas_docopy(x);
1809}
1810
1811static void canvas_clearline(t_canvas *x)
1812{
1813 if (x->gl_editor->e_selectedline)
1814 {
1815 canvas_disconnect(x, x->gl_editor->e_selectline_index1,
1816 x->gl_editor->e_selectline_outno,
1817 x->gl_editor->e_selectline_index2,
1818 x->gl_editor->e_selectline_inno);
1819 canvas_setundo(x, canvas_undo_disconnect,
1820 canvas_undo_set_disconnect(x,
1821 x->gl_editor->e_selectline_index1,
1822 x->gl_editor->e_selectline_outno,
1823 x->gl_editor->e_selectline_index2,
1824 x->gl_editor->e_selectline_inno),
1825 "disconnect");
1826 }
1827}
1828
1829extern t_pd *newest;
1830static void canvas_doclear(t_canvas *x)
1831{
1832 t_gobj *y, *y2;
1833 int dspstate;
1834
1835 dspstate = canvas_suspend_dsp();
1836 if (x->gl_editor->e_selectedline)
1837 {
1838 canvas_disconnect(x, x->gl_editor->e_selectline_index1,
1839 x->gl_editor->e_selectline_outno,
1840 x->gl_editor->e_selectline_index2,
1841 x->gl_editor->e_selectline_inno);
1842 canvas_setundo(x, canvas_undo_disconnect,
1843 canvas_undo_set_disconnect(x,
1844 x->gl_editor->e_selectline_index1,
1845 x->gl_editor->e_selectline_outno,
1846 x->gl_editor->e_selectline_index2,
1847 x->gl_editor->e_selectline_inno),
1848 "disconnect");
1849 }
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)
1854 {
1855 newest = 0;
1856 glist_noselect(x);
1857 if (newest)
1858 {
1859 for (y = x->gl_list; y; y = y->g_next)
1860 if (&y->g_pd == newest) glist_select(x, y);
1861 }
1862 }
1863 while (1) /* this is pretty wierd... should rewrite it */
1864 {
1865 for (y = x->gl_list; y; y = y2)
1866 {
1867 y2 = y->g_next;
1868 if (glist_isselected(x, y))
1869 {
1870 glist_delete(x, y);
1871#if 0
1872 if (y2) post("cut 5 %x %x", y2, y2->g_next);
1873 else post("cut 6");
1874#endif
1875 goto next;
1876 }
1877 }
1878 goto restore;
1879 next: ;
1880 }
1881restore:
1882 canvas_resume_dsp(dspstate);
1883 canvas_dirty(x, 1);
1884}
1885
1886static void canvas_cut(t_canvas *x)
1887{
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)
1891 {
1892 canvas_setundo(x, canvas_undo_cut,
1893 canvas_undo_set_cut(x, UCUT_CUT), "cut");
1894 canvas_copy(x);
1895 canvas_doclear(x);
1896 }
1897}
1898
1899static int paste_onset;
1900static t_canvas *paste_canvas;
1901
1902static void glist_donewloadbangs(t_glist *x)
1903{
1904 if (x->gl_editor)
1905 {
1906 t_selection *sel;
1907 for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
1908 if (pd_class(&sel->sel_what->g_pd) == canvas_class)
1909 canvas_loadbang((t_canvas *)(&sel->sel_what->g_pd));
1910 }
1911}
1912
1913static void canvas_dopaste(t_canvas *x, t_binbuf *b)
1914{
1915 t_gobj *newgobj, *last, *g2;
1916 int dspstate = canvas_suspend_dsp(), nbox, count;
1917
1918 canvas_editmode(x, 1.);
1919 glist_noselect(x);
1920 for (g2 = x->gl_list, nbox = 0; g2; g2 = g2->g_next) nbox++;
1921
1922 paste_onset = nbox;
1923 paste_canvas = x;
1924
1925 pd_bind(&x->gl_pd, gensym("#X"));
1926 binbuf_eval(b, 0, 0, 0);
1927 pd_unbind(&x->gl_pd, gensym("#X"));
1928 for (g2 = x->gl_list, count = 0; g2; g2 = g2->g_next, count++)
1929 if (count >= nbox)
1930 glist_select(x, g2);
1931 paste_canvas = 0;
1932 canvas_resume_dsp(dspstate);
1933 canvas_dirty(x, 1);
1934 glist_donewloadbangs(x);
1935}
1936
1937static void canvas_paste(t_canvas *x)
1938{
1939 canvas_setundo(x, canvas_undo_paste, canvas_undo_set_paste(x), "paste");
1940 canvas_dopaste(x, copy_binbuf);
1941}
1942
1943static void canvas_duplicate(t_canvas *x)
1944{
1945 if (x->gl_editor->e_onmotion == MA_NONE)
1946 {
1947 t_selection *y;
1948 canvas_copy(x);
1949 canvas_setundo(x, canvas_undo_paste, canvas_undo_set_paste(x),
1950 "duplicate");
1951 canvas_dopaste(x, copy_binbuf);
1952 for (y = x->gl_editor->e_selection; y; y = y->sel_next)
1953 gobj_displace(y->sel_what, x,
1954 10, 10);
1955 canvas_dirty(x, 1);
1956 }
1957}
1958
1959static void canvas_selectall(t_canvas *x)
1960{
1961 t_gobj *y;
1962 if (!x->gl_edit)
1963 canvas_editmode(x, 1);
1964 for (y = x->gl_list; y; y = y->g_next)
1965 {
1966 if (!glist_isselected(x, y))
1967 glist_select(x, y);
1968 }
1969}
1970
1971void canvas_connect(t_canvas *x, t_floatarg fwhoout, t_floatarg foutno,
1972 t_floatarg fwhoin, t_floatarg finno)
1973{
1974 int whoout = fwhoout, outno = foutno, whoin = fwhoin, inno = finno;
1975 t_gobj *src = 0, *sink = 0;
1976 t_object *objsrc, *objsink;
1977 t_outconnect *oc;
1978 int nin = whoin, nout = whoout;
1979 if (paste_canvas == x) whoout += paste_onset, whoin += paste_onset;
1980 for (src = x->gl_list; whoout; src = src->g_next, whoout--)
1981 if (!src->g_next) goto bad; /* bug fix thanks to Hannes */
1982 for (sink = x->gl_list; whoin; sink = sink->g_next, whoin--)
1983 if (!sink->g_next) goto bad;
1984 if (!(objsrc = pd_checkobject(&src->g_pd)) ||
1985 !(objsink = pd_checkobject(&sink->g_pd)))
1986 goto bad;
1987 if (!(oc = obj_connect(objsrc, outno, objsink, inno))) goto bad;
1988 if (glist_isvisible(x))
1989 {
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);
1994 }
1995 return;
1996
1997bad:
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)) : "???"));
2002}
2003
2004#define XTOLERANCE 4
2005#define YTOLERANCE 3
2006#define NHIST 15
2007
2008 /* LATER might have to speed this up */
2009static void canvas_tidy(t_canvas *x)
2010{
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);
2017
2018 canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, !all),
2019 "motion");
2020
2021 /* tidy horizontally */
2022 for (y = x->gl_list; y; y = y->g_next)
2023 if (all || glist_isselected(x, y))
2024 {
2025 gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);
2026
2027 for (y2 = x->gl_list; y2; y2 = y2->g_next)
2028 if (all || glist_isselected(x, y2))
2029 {
2030 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
2031 if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE &&
2032 bx1 < ax1)
2033 goto nothorizhead;
2034 }
2035
2036 for (y2 = x->gl_list; y2; y2 = y2->g_next)
2037 if (all || glist_isselected(x, y2))
2038 {
2039 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
2040 if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE
2041 && by1 != ay1)
2042 gobj_displace(y2, x, 0, ay1-by1);
2043 }
2044 nothorizhead: ;
2045 }
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))
2050 {
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))
2054 {
2055 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
2056 if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE)
2057 {
2058 int distance = by1-ay2;
2059 if (distance >= 0 && distance < NHIST)
2060 histogram[distance]++;
2061 }
2062 }
2063 }
2064 for (i = 1, besthist = 0, bestdist = 4, ip = histogram + 1;
2065 i < (NHIST-1); i++, ip++)
2066 {
2067 int hit = ip[-1] + 2 * ip[0] + ip[1];
2068 if (hit > besthist)
2069 {
2070 besthist = hit;
2071 bestdist = i;
2072 }
2073 }
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))
2077 {
2078 int keep = 1;
2079 gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);
2080 for (y2 = x->gl_list; y2; y2 = y2->g_next)
2081 if (all || glist_isselected(x, y2))
2082 {
2083 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
2084 if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE &&
2085 ay1 >= by2 - 10 && ay1 < by2 + NHIST)
2086 goto nothead;
2087 }
2088 while (keep)
2089 {
2090 keep = 0;
2091 for (y2 = x->gl_list; y2; y2 = y2->g_next)
2092 if (all || glist_isselected(x, y2))
2093 {
2094 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
2095 if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE &&
2096 by1 > ay1 && by1 < ay2 + NHIST)
2097 {
2098 int vmove = ay2 + bestdist - by1;
2099 gobj_displace(y2, x, ax1-bx1, vmove);
2100 ay1 = by1 + vmove;
2101 ay2 = by2 + vmove;
2102 keep = 1;
2103 break;
2104 }
2105 }
2106 }
2107 nothead: ;
2108 }
2109 canvas_dirty(x, 1);
2110}
2111
2112static void canvas_texteditor(t_canvas *x)
2113{
2114 t_rtext *foo;
2115 char *buf;
2116 int bufsize;
2117 if (foo = x->gl_editor->e_textedfor)
2118 rtext_gettext(foo, &buf, &bufsize);
2119 else buf = "", bufsize = 0;
2120 sys_vgui("pdtk_pd_texteditor {%.*s}\n", bufsize, buf);
2121
2122}
2123
2124void glob_key(void *dummy, t_symbol *s, int ac, t_atom *av)
2125{
2126 /* canvas_editing can be zero; canvas_key checks for that */
2127 canvas_key(canvas_editing, s, ac, av);
2128}
2129
2130void canvas_editmode(t_canvas *x, t_floatarg fyesplease)
2131{
2132 int yesplease = fyesplease;
2133 if (yesplease && x->gl_edit)
2134 return;
2135 x->gl_edit = !x->gl_edit;
2136 if (x->gl_edit && glist_isvisible(x) && glist_istoplevel(x))
2137 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
2138 else
2139 {
2140 glist_noselect(x);
2141 if (glist_isvisible(x) && glist_istoplevel(x))
2142 canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
2143 }
2144 sys_vgui("pdtk_canvas_editval .x%x %d\n",
2145 glist_getcanvas(x), x->gl_edit);
2146}
2147
2148 /* called by canvas_font below */
2149static void canvas_dofont(t_canvas *x, t_floatarg font, t_floatarg xresize,
2150 t_floatarg yresize)
2151{
2152 t_gobj *y;
2153 x->gl_font = font;
2154 if (xresize != 1 || yresize != 1)
2155 {
2156 canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, 0),
2157 "motion");
2158 for (y = x->gl_list; y; y = y->g_next)
2159 {
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);
2165 }
2166 }
2167 if (glist_isvisible(x))
2168 glist_redraw(x);
2169 for (y = x->gl_list; y; y = y->g_next)
2170 if (pd_class(&y->g_pd) == canvas_class
2171 && !canvas_isabstraction((t_canvas *)y))
2172 canvas_dofont((t_canvas *)y, font, xresize, yresize);
2173}
2174
2175 /* canvas_menufont calls up a TK dialog which calls this back */
2176static void canvas_font(t_canvas *x, t_floatarg font, t_floatarg resize,
2177 t_floatarg whichresize)
2178{
2179 float realresize, realresx = 1, realresy = 1;
2180 t_canvas *x2 = canvas_getrootfor(x);
2181 if (!resize) realresize = 1;
2182 else
2183 {
2184 if (resize < 20) resize = 20;
2185 if (resize > 500) resize = 500;
2186 realresize = resize * 0.01;
2187 }
2188 if (whichresize != 3) realresx = realresize;
2189 if (whichresize != 2) realresy = realresize;
2190 canvas_dofont(x2, font, realresx, realresy);
2191 sys_defaultfont = font;
2192}
2193
2194static t_glist *canvas_last_glist;
2195static int canvas_last_glist_x, canvas_last_glist_y;
2196
2197void glist_getnextxy(t_glist *gl, int *xpix, int *ypix)
2198{
2199 if (canvas_last_glist == gl)
2200 *xpix = canvas_last_glist_x, *ypix = canvas_last_glist_y;
2201 else *xpix = *ypix = 40;
2202}
2203
2204static void glist_setlastxy(t_glist *gl, int xval, int yval)
2205{
2206 canvas_last_glist = gl;
2207 canvas_last_glist_x = xval;
2208 canvas_last_glist_y = yval;
2209}
2210
2211
2212void g_editor_setup(void)
2213{
2214/* ------------------------ events ---------------------------------- */
2215 class_addmethod(canvas_class, (t_method)canvas_mousedown, gensym("mouse"),
2216 A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2217 class_addmethod(canvas_class, (t_method)canvas_mouseup, gensym("mouseup"),
2218 A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2219 class_addmethod(canvas_class, (t_method)canvas_key, gensym("key"),
2220 A_GIMME, A_NULL);
2221 class_addmethod(canvas_class, (t_method)canvas_motion, gensym("motion"),
2222 A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2223
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);
2265
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);
2269
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();
2274}
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. */
2278
2279#include <stdlib.h>
2280#include <stdio.h>
2281#include "m_pd.h"
2282#include "m_imp.h"
2283#include "s_stuff.h"
2284#include "g_canvas.h"
2285#include <string.h>
2286
2287void glist_readfrombinbuf(t_glist *x, t_binbuf *b, char *filename,
2288 int selectem);
2289
2290void open_via_helppath(const char *name, const char *dir);
2291char *class_gethelpdir(t_class *c);
2292
2293/* ------------------ forward declarations --------------- */
2294static void canvas_doclear(t_canvas *x);
2295static void glist_setlastxy(t_glist *gl, int xval, int yval);
2296static void glist_donewloadbangs(t_glist *x);
2297static t_binbuf *canvas_docopy(t_canvas *x);
2298static void canvas_dopaste(t_canvas *x, t_binbuf *b);
2299static void canvas_paste(t_canvas *x);
2300static void canvas_clearline(t_canvas *x);
2301static t_binbuf *copy_binbuf;
2302
2303/* ---------------- generic widget behavior ------------------------- */
2304
2305void gobj_getrect(t_gobj *x, t_glist *glist, int *x1, int *y1,
2306 int *x2, int *y2)
2307{
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);
2310}
2311
2312void gobj_displace(t_gobj *x, t_glist *glist, int dx, int dy)
2313{
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);
2316}
2317
2318void gobj_select(t_gobj *x, t_glist *glist, int state)
2319{
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);
2322}
2323
2324void gobj_activate(t_gobj *x, t_glist *glist, int state)
2325{
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);
2328}
2329
2330void gobj_delete(t_gobj *x, t_glist *glist)
2331{
2332 if (x->g_pd->c_wb && x->g_pd->c_wb->w_deletefn)
2333 (*x->g_pd->c_wb->w_deletefn)(x, glist);
2334}
2335
2336void gobj_vis(t_gobj *x, struct _glist *glist, int flag)
2337{
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);
2340}
2341
2342int gobj_click(t_gobj *x, struct _glist *glist,
2343 int xpix, int ypix, int shift, int alt, int dbl, int doit)
2344{
2345 if (x->g_pd->c_wb && x->g_pd->c_wb->w_clickfn)
2346 return ((*x->g_pd->c_wb->w_clickfn)(x,
2347 glist, xpix, ypix, shift, alt, dbl, doit));
2348 else return (0);
2349}
2350
2351/* ------------------------ managing the selection ----------------- */
2352
2353void glist_selectline(t_glist *x, t_outconnect *oc, int index1,
2354 int outno, int index2, int inno)
2355{
2356 if (x->gl_editor)
2357 {
2358 glist_noselect(x);
2359 x->gl_editor->e_selectedline = 1;
2360 x->gl_editor->e_selectline_index1 = index1;
2361 x->gl_editor->e_selectline_outno = outno;
2362 x->gl_editor->e_selectline_index2 = index2;
2363 x->gl_editor->e_selectline_inno = inno;
2364 x->gl_editor->e_selectline_tag = oc;
2365 sys_vgui(".x%x.c itemconfigure l%x -fill blue\n",
2366 x, x->gl_editor->e_selectline_tag);
2367 }
2368}
2369
2370void glist_deselectline(t_glist *x)
2371{
2372 if (x->gl_editor)
2373 {
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);
2377 }
2378}
2379
2380int glist_isselected(t_glist *x, t_gobj *y)
2381{
2382 if (x->gl_editor)
2383 {
2384 t_selection *sel;
2385 for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
2386 if (sel->sel_what == y) return (1);
2387 }
2388 return (0);
2389}
2390
2391 /* call this for unselected objects only */
2392void glist_select(t_glist *x, t_gobj *y)
2393{
2394 if (x->gl_editor)
2395 {
2396 t_selection *sel = (t_selection *)getbytes(sizeof(*sel));
2397 if (x->gl_editor->e_selectedline)
2398 glist_deselectline(x);
2399 /* LATER #ifdef out the following check */
2400 if (glist_isselected(x, y)) bug("glist_select");
2401 sel->sel_next = x->gl_editor->e_selection;
2402 sel->sel_what = y;
2403 x->gl_editor->e_selection = sel;
2404 gobj_select(y, x, 1);
2405 }
2406}
2407
2408 /* call this for selected objects only */
2409void glist_deselect(t_glist *x, t_gobj *y)
2410{
2411 int fixdsp = 0;
2412 static int reenter = 0;
2413 if (reenter) return;
2414 reenter = 1;
2415 if (x->gl_editor)
2416 {
2417 t_selection *sel, *sel2;
2418 t_rtext *z = 0;
2419 if (!glist_isselected(x, y)) bug("glist_deselect");
2420 if (x->gl_editor->e_textedfor)
2421 {
2422 t_rtext *fuddy = glist_findrtext(x, (t_text *)y);
2423 if (x->gl_editor->e_textedfor == fuddy)
2424 {
2425 if (x->gl_editor->e_textdirty)
2426 {
2427 z = fuddy;
2428 canvas_stowconnections(glist_getcanvas(x));
2429 }
2430 gobj_activate(y, x, 0);
2431 }
2432 if (zgetfn(&y->g_pd, gensym("dsp")))
2433 fixdsp = 1;
2434 }
2435 if ((sel = x->gl_editor->e_selection)->sel_what == y)
2436 {
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));
2440 }
2441 else
2442 {
2443 for (sel = x->gl_editor->e_selection; sel2 = sel->sel_next;
2444 sel = sel2)
2445 {
2446 if (sel2->sel_what == y)
2447 {
2448 sel->sel_next = sel2->sel_next;
2449 gobj_select(sel2->sel_what, x, 0);
2450 freebytes(sel2, sizeof(*sel2));
2451 break;
2452 }
2453 }
2454 }
2455 if (z)
2456 {
2457 char *buf;
2458 int bufsize;
2459
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;
2464 }
2465 if (fixdsp)
2466 canvas_update_dsp();
2467 }
2468 reenter = 0;
2469}
2470
2471void glist_noselect(t_glist *x)
2472{
2473 if (x->gl_editor)
2474 {
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);
2479 }
2480}
2481
2482void glist_selectall(t_glist *x)
2483{
2484 if (x->gl_editor)
2485 {
2486 glist_noselect(x);
2487 if (x->gl_list)
2488 {
2489 t_selection *sel = (t_selection *)getbytes(sizeof(*sel));
2490 t_gobj *y = x->gl_list;
2491 x->gl_editor->e_selection = sel;
2492 sel->sel_what = y;
2493 gobj_select(y, x, 1);
2494 while (y = y->g_next)
2495 {
2496 t_selection *sel2 = (t_selection *)getbytes(sizeof(*sel2));
2497 sel->sel_next = sel2;
2498 sel = sel2;
2499 sel->sel_what = y;
2500 gobj_select(y, x, 1);
2501 }
2502 sel->sel_next = 0;
2503 }
2504 }
2505}
2506
2507 /* get the index of a gobj in a glist. If y is zero, return the
2508 total number of objects. */
2509int glist_getindex(t_glist *x, t_gobj *y)
2510{
2511 t_gobj *y2;
2512 int indx;
2513
2514 for (y2 = x->gl_list, indx = 0; y2 && y2 != y; y2 = y2->g_next)
2515 indx++;
2516 return (indx);
2517}
2518
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. */
2522int glist_selectionindex(t_glist *x, t_gobj *y, int selected)
2523{
2524 t_gobj *y2;
2525 int indx;
2526
2527 for (y2 = x->gl_list, indx = 0; y2 && y2 != y; y2 = y2->g_next)
2528 if (selected == glist_isselected(x, y2))
2529 indx++;
2530 return (indx);
2531}
2532
2533static t_gobj *glist_nth(t_glist *x, int n)
2534{
2535 t_gobj *y;
2536 int indx;
2537 for (y = x->gl_list, indx = 0; y; y = y->g_next, indx++)
2538 if (indx == n)
2539 return (y);
2540 return (0);
2541}
2542
2543/* ------------------- support for undo/redo -------------------------- */
2544
2545static t_undofn canvas_undo_fn; /* current undo function if any */
2546static int canvas_undo_whatnext; /* whether we can now UNDO or REDO */
2547static void *canvas_undo_buf; /* data private to the undo function */
2548static t_canvas *canvas_undo_canvas; /* which canvas we can undo on */
2549static const char *canvas_undo_name;
2550
2551void canvas_setundo(t_canvas *x, t_undofn undofn, void *buf,
2552 const char *name)
2553{
2554 int hadone = 0;
2555 /* blow away the old undo information. In one special case the
2556 old undo info is re-used; if so we shouldn't free it here. */
2557 if (canvas_undo_fn && canvas_undo_buf && (buf != canvas_undo_buf))
2558 {
2559 (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_FREE);
2560 hadone = 1;
2561 }
2562 canvas_undo_canvas = x;
2563 canvas_undo_fn = undofn;
2564 canvas_undo_buf = buf;
2565 canvas_undo_whatnext = UNDO_UNDO;
2566 canvas_undo_name = name;
2567 if (x && glist_isvisible(x) && glist_istoplevel(x))
2568 /* enable undo in menu */
2569 sys_vgui("pdtk_undomenu .x%x %s no\n", x, name);
2570 else if (hadone)
2571 sys_vgui("pdtk_undomenu nobody no no\n");
2572}
2573
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.) */
2576void canvas_noundo(t_canvas *x)
2577{
2578 if (!x || (x == canvas_undo_canvas))
2579 canvas_setundo(0, 0, 0, "foo");
2580}
2581
2582static void canvas_undo(t_canvas *x)
2583{
2584 if (x != canvas_undo_canvas)
2585 bug("canvas_undo 1");
2586 else if (canvas_undo_whatnext != UNDO_UNDO)
2587 bug("canvas_undo 2");
2588 else
2589 {
2590 /* post("undo"); */
2591 (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_UNDO);
2592 /* enable redo in menu */
2593 if (glist_isvisible(x) && glist_istoplevel(x))
2594 sys_vgui("pdtk_undomenu .x%x no %s\n", x, canvas_undo_name);
2595 canvas_undo_whatnext = UNDO_REDO;
2596 }
2597}
2598
2599static void canvas_redo(t_canvas *x)
2600{
2601 if (x != canvas_undo_canvas)
2602 bug("canvas_undo 1");
2603 else if (canvas_undo_whatnext != UNDO_REDO)
2604 bug("canvas_undo 2");
2605 else
2606 {
2607 /* post("redo"); */
2608 (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_REDO);
2609 /* enable undo in menu */
2610 if (glist_isvisible(x) && glist_istoplevel(x))
2611 sys_vgui("pdtk_undomenu .x%x %s no\n", x, canvas_undo_name);
2612 canvas_undo_whatnext = UNDO_UNDO;
2613 }
2614}
2615
2616/* ------- specific undo methods: 1. connect and disconnect -------- */
2617
2618typedef struct _undo_connect
2619{
2620 int u_index1;
2621 int u_outletno;
2622 int u_index2;
2623 int u_inletno;
2624} t_undo_connect;
2625
2626static void *canvas_undo_set_disconnect(t_canvas *x,
2627 int index1, int outno, int index2, int inno)
2628{
2629 t_undo_connect *buf = (t_undo_connect *)getbytes(sizeof(*buf));
2630 buf->u_index1 = index1;
2631 buf->u_outletno = outno;
2632 buf->u_index2 = index2;
2633 buf->u_inletno = inno;
2634 return (buf);
2635}
2636
2637void canvas_disconnect(t_canvas *x,
2638 float index1, float outno, float index2, float inno)
2639{
2640 t_linetraverser t;
2641 t_outconnect *oc;
2642 linetraverser_start(&t, x);
2643 while (oc = linetraverser_next(&t))
2644 {
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)
2649 {
2650 sys_vgui(".x%x.c delete l%x\n", x, oc);
2651 obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);
2652 break;
2653 }
2654 }
2655}
2656
2657static void canvas_undo_disconnect(t_canvas *x, void *z, int action)
2658{
2659 t_undo_connect *buf = z;
2660 if (action == UNDO_UNDO)
2661 {
2662 canvas_connect(x, buf->u_index1, buf->u_outletno,
2663 buf->u_index2, buf->u_inletno);
2664 }
2665 else if (action == UNDO_REDO)
2666 {
2667 canvas_disconnect(x, buf->u_index1, buf->u_outletno,
2668 buf->u_index2, buf->u_inletno);
2669 }
2670 else if (action == UNDO_FREE)
2671 t_freebytes(buf, sizeof(*buf));
2672}
2673
2674 /* connect just calls disconnect actions backward... */
2675static void *canvas_undo_set_connect(t_canvas *x,
2676 int index1, int outno, int index2, int inno)
2677{
2678 return (canvas_undo_set_disconnect(x, index1, outno, index2, inno));
2679}
2680
2681static void canvas_undo_connect(t_canvas *x, void *z, int action)
2682{
2683 int myaction;
2684 if (action == UNDO_UNDO)
2685 myaction = UNDO_REDO;
2686 else if (action == UNDO_REDO)
2687 myaction = UNDO_UNDO;
2688 else myaction = action;
2689 canvas_undo_disconnect(x, z, myaction);
2690}
2691
2692/* ---------- ... 2. cut, clear, and typing into objects: -------- */
2693
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 */
2697
2698typedef struct _undo_cut
2699{
2700 t_binbuf *u_objectbuf; /* the object cleared or typed into */
2701 t_binbuf *u_reconnectbuf; /* connections into and out of object */
2702 t_binbuf *u_redotextbuf; /* buffer to paste back for redo if TEXT */
2703 int u_mode; /* from flags above */
2704} t_undo_cut;
2705
2706static void *canvas_undo_set_cut(t_canvas *x, int mode)
2707{
2708 t_undo_cut *buf;
2709 t_gobj *y;
2710 t_linetraverser t;
2711 t_outconnect *oc;
2712 int nnotsel= glist_selectionindex(x, 0, 0);
2713 buf = (t_undo_cut *)getbytes(sizeof(*buf));
2714 buf->u_mode = mode;
2715 buf->u_redotextbuf = 0;
2716
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))
2721 {
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)
2725 {
2726 binbuf_addv(buf->u_reconnectbuf, "ssiiii;",
2727 gensym("#X"), gensym("connect"),
2728 (issel1 ? nnotsel : 0)
2729 + glist_selectionindex(x, &t.tr_ob->ob_g, issel1),
2730 t.tr_outno,
2731 (issel2 ? nnotsel : 0) +
2732 glist_selectionindex(x, &t.tr_ob2->ob_g, issel2),
2733 t.tr_inno);
2734 }
2735 }
2736 if (mode == UCUT_TEXT)
2737 {
2738 buf->u_objectbuf = canvas_docopy(x);
2739 }
2740 else if (mode == UCUT_CUT)
2741 {
2742 buf->u_objectbuf = 0;
2743 }
2744 else if (mode == UCUT_CLEAR)
2745 {
2746 buf->u_objectbuf = canvas_docopy(x);
2747 }
2748 return (buf);
2749}
2750
2751static void canvas_undo_cut(t_canvas *x, void *z, int action)
2752{
2753 t_undo_cut *buf = z;
2754 int mode = buf->u_mode;
2755 if (action == UNDO_UNDO)
2756 {
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)
2762 {
2763 t_gobj *y1, *y2;
2764 glist_noselect(x);
2765 for (y1 = x->gl_list; y2 = y1->g_next; y1 = y2)
2766 ;
2767 if (y1)
2768 {
2769 if (!buf->u_redotextbuf)
2770 {
2771 glist_noselect(x);
2772 glist_select(x, y1);
2773 buf->u_redotextbuf = canvas_docopy(x);
2774 glist_noselect(x);
2775 }
2776 glist_delete(x, y1);
2777 }
2778 canvas_dopaste(x, buf->u_objectbuf);
2779 }
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"));
2783 }
2784 else if (action == UNDO_REDO)
2785 {
2786 if (mode == UCUT_CUT || mode == UCUT_CLEAR)
2787 canvas_doclear(x);
2788 else if (mode == UCUT_TEXT)
2789 {
2790 t_gobj *y1, *y2;
2791 for (y1 = x->gl_list; y2 = y1->g_next; y1 = y2)
2792 ;
2793 if (y1)
2794 glist_delete(x, y1);
2795 canvas_dopaste(x, buf->u_redotextbuf);
2796 pd_bind(&x->gl_pd, gensym("#X"));
2797 binbuf_eval(buf->u_reconnectbuf, 0, 0, 0);
2798 pd_unbind(&x->gl_pd, gensym("#X"));
2799 }
2800 }
2801 else if (action == UNDO_FREE)
2802 {
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));
2810 }
2811}
2812
2813/* --------- 3. motion, including "tidy up" and stretching ----------- */
2814
2815typedef struct _undo_move_elem
2816{
2817 int e_index;
2818 int e_xpix;
2819 int e_ypix;
2820} t_undo_move_elem;
2821
2822typedef struct _undo_move
2823{
2824 t_undo_move_elem *u_vec;
2825 int u_n;
2826} t_undo_move;
2827
2828static int canvas_undo_already_set_move;
2829
2830static void *canvas_undo_set_move(t_canvas *x, int selected)
2831{
2832 int x1, y1, x2, y2, i, indx;
2833 t_gobj *y;
2834 t_undo_move *buf = (t_undo_move *)getbytes(sizeof(*buf));
2835 buf->u_n = selected ? glist_selectionindex(x, 0, 1) : glist_getindex(x, 0);
2836 buf->u_vec = (t_undo_move_elem *)getbytes(sizeof(*buf->u_vec) *
2837 (selected ? glist_selectionindex(x, 0, 1) : glist_getindex(x, 0)));
2838 if (selected)
2839 {
2840 for (y = x->gl_list, i = indx = 0; y; y = y->g_next, indx++)
2841 if (glist_isselected(x, y))
2842 {
2843 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
2844 buf->u_vec[i].e_index = indx;
2845 buf->u_vec[i].e_xpix = x1;
2846 buf->u_vec[i].e_ypix = y1;
2847 i++;
2848 }
2849 }
2850 else
2851 {
2852 for (y = x->gl_list, indx = 0; y; y = y->g_next, indx++)
2853 {
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;
2858 }
2859 }
2860 canvas_undo_already_set_move = 1;
2861 return (buf);
2862}
2863
2864static void canvas_undo_move(t_canvas *x, void *z, int action)
2865{
2866 t_undo_move *buf = z;
2867 if (action == UNDO_UNDO || action == UNDO_REDO)
2868 {
2869 int i;
2870 for (i = 0; i < buf->u_n; i++)
2871 {
2872 int x1, y1, x2, y2, newx, newy;
2873 t_gobj *y;
2874 newx = buf->u_vec[i].e_xpix;
2875 newy = buf->u_vec[i].e_ypix;
2876 y = glist_nth(x, buf->u_vec[i].e_index);
2877 if (y)
2878 {
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;
2883 }
2884 }
2885 }
2886 else if (action == UNDO_FREE)
2887 {
2888 t_freebytes(buf->u_vec, buf->u_n * sizeof(*buf->u_vec));
2889 t_freebytes(buf, sizeof(*buf));
2890 }
2891}
2892
2893/* --------- 4. paste (also duplicate) ----------- */
2894
2895typedef struct _undo_paste
2896{
2897 int u_index; /* index of first object pasted */
2898} t_undo_paste;
2899
2900static void *canvas_undo_set_paste(t_canvas *x)
2901{
2902 t_undo_paste *buf = (t_undo_paste *)getbytes(sizeof(*buf));
2903 buf->u_index = glist_getindex(x, 0);
2904 return (buf);
2905}
2906
2907static void canvas_undo_paste(t_canvas *x, void *z, int action)
2908{
2909 t_undo_paste *buf = z;
2910 if (action == UNDO_UNDO)
2911 {
2912 t_gobj *y;
2913 glist_noselect(x);
2914 for (y = glist_nth(x, buf->u_index); y; y = y->g_next)
2915 glist_select(x, y);
2916 canvas_doclear(x);
2917 }
2918 else if (action == UNDO_REDO)
2919 {
2920 t_selection *sel;
2921 canvas_dopaste(x, copy_binbuf);
2922 /* if it was "duplicate" have to re-enact the displacement. */
2923 if (canvas_undo_name && canvas_undo_name[0] == 'd')
2924 for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
2925 gobj_displace(sel->sel_what, x, 10, 10);
2926 }
2927else if (action == UNDO_FREE)
2928 t_freebytes(buf, sizeof(*buf));
2929}
2930
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. */
2934static void glist_doreload(t_glist *gl, t_symbol *name, t_symbol *dir,
2935 t_gobj *except)
2936{
2937 t_gobj *g;
2938 int i, nobj = glist_getindex(gl, 0); /* number of objects */
2939 for (g = gl->gl_list, i = 0; g && i < nobj; i++)
2940 {
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)
2945 {
2946 /* we're going to remake the object, so "g" will go stale.
2947 Get its index here, and afterward restore g. Also, the
2948 replacement will be at teh end of the list, so we don't
2949 do g = g->g_next in this case. */
2950 int j = glist_getindex(gl, g);
2951 if (!gl->gl_havewindow)
2952 canvas_vis(glist_getcanvas(gl), 1);
2953 glist_noselect(gl);
2954 glist_select(gl, g);
2955 canvas_setundo(gl, canvas_undo_cut,
2956 canvas_undo_set_cut(gl, UCUT_CLEAR), "clear");
2957 canvas_doclear(gl);
2958 canvas_undo(gl);
2959 glist_noselect(gl);
2960 g = glist_nth(gl, j);
2961 }
2962 else
2963 {
2964 if (g != except && pd_class(&g->g_pd) == canvas_class)
2965 glist_doreload((t_canvas *)g, name, dir, except);
2966 g = g->g_next;
2967 }
2968 }
2969}
2970
2971 /* call canvas_doreload on everyone */
2972void canvas_reload(t_symbol *name, t_symbol *dir, t_gobj *except)
2973{
2974 t_canvas *x;
2975 /* find all root canvases */
2976 for (x = canvas_list; x; x = x->gl_next)
2977 glist_doreload(x, name, dir, except);
2978}
2979
2980/* ------------------------ event handling ------------------------ */
2981
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
2989
2990static char *cursorlist[] = {
2991#ifdef MSW
2992 "right_ptr", /* CURSOR_RUNMODE_NOTHING */
2993#else
2994 "left_ptr", /* CURSOR_RUNMODE_NOTHING */
2995#endif
2996 "arrow", /* CURSOR_RUNMODE_CLICKME */
2997 "sb_v_double_arrow", /* CURSOR_RUNMODE_THICKEN */
2998 "plus", /* CURSOR_RUNMODE_ADDPOINT */
2999 "hand2", /* CURSOR_EDITMODE_NOTHING */
3000 "circle", /* CURSOR_EDITMODE_CONNECT */
3001 "X_cursor" /* CURSOR_EDITMODE_DISCONNECT */
3002};
3003
3004void canvas_setcursor(t_canvas *x, unsigned int cursornum)
3005{
3006 static t_canvas *xwas;
3007 static unsigned int cursorwas;
3008 if (cursornum >= sizeof(cursorlist)/sizeof *cursorlist)
3009 {
3010 bug("canvas_setcursor");
3011 return;
3012 }
3013 if (xwas != x || cursorwas != cursornum)
3014 {
3015 sys_vgui(".x%x configure -cursor %s\n", x, cursorlist[cursornum]);
3016 xwas = x;
3017 cursorwas = cursornum;
3018 }
3019}
3020
3021 /* check if a point lies in a gobj. */
3022int canvas_hitbox(t_canvas *x, t_gobj *y, int xpos, int ypos,
3023 int *x1p, int *y1p, int *x2p, int *y2p)
3024{
3025 int x1, y1, x2, y2;
3026 t_text *ob;
3027 if ((ob = pd_checkobject(&y->g_pd)) &&
3028 !text_shouldvis(ob, x))
3029 return (0);
3030 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
3031 if (xpos >= x1 && xpos <= x2 && ypos >= y1 && ypos <= y2)
3032 {
3033 *x1p = x1;
3034 *y1p = y1;
3035 *x2p = x2;
3036 *y2p = y2;
3037 return (1);
3038 }
3039 else return (0);
3040}
3041
3042 /* find the last gobj, if any, containing the point. */
3043static t_gobj *canvas_findhitbox(t_canvas *x, int xpos, int ypos,
3044 int *x1p, int *y1p, int *x2p, int *y2p)
3045{
3046 t_gobj *y, *rval = 0;
3047 for (y = x->gl_list; y; y = y->g_next)
3048 {
3049 if (canvas_hitbox(x, y, xpos, ypos, x1p, y1p, x2p, y2p))
3050 rval = y;
3051 }
3052 return (rval);
3053}
3054
3055 /* right-clicking on a canvas object pops up a menu. */
3056static void canvas_rightclick(t_canvas *x, int xpos, int ypos, t_gobj *y)
3057{
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);
3063}
3064
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. */
3068static void canvas_properties(t_glist *x)
3069{
3070 char graphbuf[200];
3071 sprintf(graphbuf, "pdtk_canvas_dialog %%s %g %g %g %g \n",
3072 glist_dpixtodx(x, 1), -glist_dpixtody(x, 1),
3073 (float)glist_isgraph(x), (float)x->gl_stretch);
3074 gfxstub_new(&x->gl_pd, x, graphbuf);
3075}
3076
3077
3078void canvas_setgraph(t_glist *x, int flag)
3079{
3080 if (!flag && glist_isgraph(x))
3081 {
3082 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
3083 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
3084 x->gl_isgraph = 0;
3085 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
3086 {
3087 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
3088 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
3089 }
3090 }
3091 else if (flag && !glist_isgraph(x))
3092 {
3093 if (x->gl_pixwidth <= 0)
3094 x->gl_pixwidth = GLIST_DEFGRAPHWIDTH;
3095
3096 if (x->gl_pixheight <= 0)
3097 x->gl_pixheight = GLIST_DEFGRAPHHEIGHT;
3098
3099 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
3100 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
3101 x->gl_isgraph = 1;
3102 /* if (x->gl_owner && glist_isvisible(x->gl_owner))
3103 canvas_vis(x, 1); */
3104 if (x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner))
3105 canvas_create_editor(x, 1);
3106 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
3107 {
3108 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
3109 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
3110 }
3111 }
3112}
3113
3114 /* called from the gui when "OK" is selected on the canvas properties
3115 dialog. Again we negate "y" scale. */
3116static void canvas_donecanvasdialog(t_glist *x, t_floatarg xperpix,
3117 t_floatarg yperpix, t_floatarg fgraphme)
3118{
3119 int graphme = (fgraphme != 0), redraw = 0;
3120 yperpix = -yperpix;
3121 if (xperpix == 0)
3122 xperpix = 1;
3123 if (yperpix == 0)
3124 yperpix = 1;
3125 canvas_setgraph(x, graphme);
3126 if (!x->gl_isgraph && (xperpix != glist_dpixtodx(x, 1)))
3127 {
3128 if (xperpix > 0)
3129 {
3130 x->gl_x1 = 0;
3131 x->gl_x2 = xperpix;
3132 }
3133 else
3134 {
3135 x->gl_x1 = -xperpix * (x->gl_screenx2 - x->gl_screenx1);
3136 x->gl_x2 = x->gl_x1 + xperpix;
3137 }
3138 redraw = 1;
3139 }
3140 if (!x->gl_isgraph && (yperpix != glist_dpixtody(x, 1)))
3141 {
3142 if (yperpix > 0)
3143 {
3144 x->gl_y1 = 0;
3145 x->gl_y2 = yperpix;
3146 }
3147 else
3148 {
3149 x->gl_y1 = -yperpix * (x->gl_screeny2 - x->gl_screeny1);
3150 x->gl_y2 = x->gl_y1 + yperpix;
3151 }
3152 redraw = 1;
3153 }
3154 if (redraw)
3155 canvas_redraw(x);
3156}
3157
3158 /* called from the gui when a popup menu comes back with "properties,"
3159 "open," or "help." */
3160static void canvas_done_popup(t_canvas *x, float which, float xpos, float ypos)
3161{
3162 char pathbuf[MAXPDSTRING], namebuf[MAXPDSTRING];
3163 t_gobj *y;
3164 for (y = x->gl_list; y; y = y->g_next)
3165 {
3166 int x1, y1, x2, y2;
3167 if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2))
3168 {
3169 if (which == 0) /* properties */
3170 {
3171 if (!class_getpropertiesfn(pd_class(&y->g_pd)))
3172 continue;
3173 (*class_getpropertiesfn(pd_class(&y->g_pd)))(y, x);
3174 return;
3175 }
3176 else if (which == 1) /* open */
3177 {
3178 if (!zgetfn(&y->g_pd, gensym("menu-open")))
3179 continue;
3180 vmess(&y->g_pd, gensym("menu-open"), "");
3181 return;
3182 }
3183 else /* help */
3184 {
3185 char *dir;
3186 if (pd_class(&y->g_pd) == canvas_class &&
3187 canvas_isabstraction((t_canvas *)y))
3188 {
3189 t_object *ob = (t_object *)y;
3190 int ac = binbuf_getnatom(ob->te_binbuf);
3191 t_atom *av = binbuf_getvec(ob->te_binbuf);
3192 if (ac < 1)
3193 return;
3194 atom_string(av, namebuf, MAXPDSTRING);
3195 dir = canvas_getdir((t_canvas *)y)->s_name;
3196 }
3197 else
3198 {
3199 strcpy(namebuf, class_gethelpname(pd_class(&y->g_pd)));
3200 dir = class_gethelpdir(pd_class(&y->g_pd));
3201 }
3202 if (strcmp(namebuf + strlen(namebuf) - 3, ".pd"))
3203 strcat(namebuf, ".pd");
3204 open_via_helppath(namebuf, dir);
3205 return;
3206 }
3207 }
3208 }
3209 if (which == 0)
3210 canvas_properties(x);
3211 else if (which == 2)
3212 {
3213 strcpy(pathbuf, sys_libdir->s_name);
3214 strcat(pathbuf, "/doc/5.reference/0.INTRO.txt");
3215 sys_vgui("menu_opentext %s\n", pathbuf);
3216 }
3217}
3218
3219#define NOMOD 0
3220#define SHIFTMOD 1
3221#define CTRLMOD 2
3222#define ALTMOD 4
3223#define RIGHTCLICK 8
3224
3225/* on one-button-mouse machines, you can use double click to
3226 mean right click (which gets the popup menu.) Do this for Mac. */
3227#ifdef MACOSX
3228#define SIMULATERIGHTCLICK
3229#endif
3230
3231#ifdef SIMULATERIGHTCLICK
3232static double canvas_upclicktime;
3233static int canvas_upx, canvas_upy;
3234#define DCLICKINTERVAL 0.25
3235#endif
3236
3237 /* mouse click */
3238void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
3239 int mod, int doit)
3240{
3241 t_gobj *y;
3242 int shiftmod, runmode, altmod, rightclick;
3243 int x1, y1, x2, y2, clickreturned = 0;
3244
3245 if (!x->gl_editor)
3246 {
3247 bug("editor");
3248 return;
3249 }
3250
3251 shiftmod = (mod & SHIFTMOD);
3252 runmode = ((mod & CTRLMOD) || (!x->gl_edit));
3253 altmod = (mod & ALTMOD);
3254 rightclick = (mod & RIGHTCLICK);
3255
3256 canvas_undo_already_set_move = 0;
3257
3258 /* if keyboard was grabbed, notify grabber and cancel the grab */
3259 if (doit && x->gl_editor->e_grab && x->gl_editor->e_keyfn)
3260 {
3261 (* x->gl_editor->e_keyfn) (x->gl_editor->e_grab, 0);
3262 glist_grab(x, 0, 0, 0, 0, 0);
3263 }
3264
3265#ifdef SIMULATERIGHTCLICK
3266 if (doit && !runmode && xpos == canvas_upx && ypos == canvas_upy &&
3267 sys_getrealtime() - canvas_upclicktime < DCLICKINTERVAL)
3268 rightclick = 1;
3269#endif
3270
3271 x->gl_editor->e_lastmoved = 0;
3272 if (doit)
3273 {
3274 x->gl_editor->e_grab = 0;
3275 x->gl_editor->e_onmotion = MA_NONE;
3276 }
3277 /* post("click %d %d %d %d", xpos, ypos, which, mod); */
3278
3279 if (x->gl_editor->e_onmotion != MA_NONE)
3280 return;
3281
3282 x->gl_editor->e_xwas = xpos;
3283 x->gl_editor->e_ywas = ypos;
3284
3285 if (runmode && !rightclick)
3286 {
3287 for (y = x->gl_list; y; y = y->g_next)
3288 {
3289 /* check if the object wants to be clicked */
3290 if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2)
3291 && (clickreturned = gobj_click(y, x, xpos, ypos,
3292 shiftmod, altmod, 0, doit)))
3293 break;
3294 }
3295 if (!doit)
3296 {
3297 if (y)
3298 canvas_setcursor(x, clickreturned);
3299 else canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
3300 }
3301 return;
3302 }
3303 /* if not a runmode left click, fall here. */
3304 if (y = canvas_findhitbox(x, xpos, ypos, &x1, &y1, &x2, &y2))
3305 {
3306 t_object *ob = pd_checkobject(&y->g_pd);
3307 /* check you're in the rectangle */
3308 ob = pd_checkobject(&y->g_pd);
3309 if (rightclick)
3310 canvas_rightclick(x, xpos, ypos, y);
3311 else if (shiftmod)
3312 {
3313 if (doit)
3314 {
3315 t_rtext *rt;
3316 if (ob && (rt = x->gl_editor->e_textedfor) &&
3317 rt == glist_findrtext(x, ob))
3318 {
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;
3323 }
3324 else
3325 {
3326 if (glist_isselected(x, y))
3327 glist_deselect(x, y);
3328 else glist_select(x, y);
3329 }
3330 }
3331 }
3332 else
3333 {
3334 /* look for an outlet */
3335 int noutlet;
3336 if (ob && (noutlet = obj_noutlets(ob)) && ypos >= y2-4)
3337 {
3338 int width = x2 - x1;
3339 int nout1 = (noutlet > 1 ? noutlet - 1 : 1);
3340 int closest = ((xpos-x1) * (nout1) + width/2)/width;
3341 int hotspot = x1 +
3342 (width - IOWIDTH) * closest / (nout1);
3343 if (closest < noutlet &&
3344 xpos >= (hotspot-1) && xpos <= hotspot + (IOWIDTH+1))
3345 {
3346 if (doit)
3347 {
3348 int issignal = obj_issignaloutlet(ob, closest);
3349 x->gl_editor->e_onmotion = MA_CONNECT;
3350 x->gl_editor->e_xwas = xpos;
3351 x->gl_editor->e_ywas = ypos;
3352 sys_vgui(
3353 ".x%x.c create line %d %d %d %d -width %d -tags x\n",
3354 x, xpos, ypos, xpos, ypos,
3355 (issignal ? 2 : 1));
3356 }
3357 else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT);
3358 }
3359 else if (doit)
3360 goto nooutletafterall;
3361 }
3362 /* not in an outlet; select and move */
3363 else if (doit)
3364 {
3365 t_rtext *rt;
3366 /* check if the box is being text edited */
3367 nooutletafterall:
3368 if (ob && (rt = x->gl_editor->e_textedfor) &&
3369 rt == glist_findrtext(x, ob))
3370 {
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;
3375 }
3376 else
3377 {
3378 /* otherwise select and drag to displace */
3379 if (!glist_isselected(x, y))
3380 {
3381 glist_noselect(x);
3382 glist_select(x, y);
3383 }
3384 x->gl_editor->e_onmotion = MA_MOVE;
3385 }
3386 }
3387 else canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
3388 }
3389 return;
3390 }
3391 /* if right click doesn't hit any boxes, call rightclick
3392 routine anyway */
3393 if (rightclick)
3394 canvas_rightclick(x, xpos, ypos, 0);
3395
3396 /* if not an editing action, and if we didn't hit a
3397 box, set cursor and return */
3398 if (runmode || rightclick)
3399 {
3400 canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
3401 return;
3402 }
3403 /* having failed to find a box, we try lines now. */
3404 if (!runmode && !altmod && !shiftmod)
3405 {
3406 t_linetraverser t;
3407 t_outconnect *oc;
3408 float fx = xpos, fy = ypos;
3409 t_glist *glist2 = glist_getcanvas(x);
3410 linetraverser_start(&t, glist2);
3411 while (oc = linetraverser_next(&t))
3412 {
3413 float lx1 = t.tr_lx1, ly1 = t.tr_ly1,
3414 lx2 = t.tr_lx2, ly2 = t.tr_ly2;
3415 float area = (lx2 - lx1) * (fy - ly1) -
3416 (ly2 - ly1) * (fx - lx1);
3417 float dsquare = (lx2-lx1) * (lx2-lx1) + (ly2-ly1) * (ly2-ly1);
3418 if (area * area >= 50 * dsquare) continue;
3419 if ((lx2-lx1) * (fx-lx1) + (ly2-ly1) * (fy-ly1) < 0) continue;
3420 if ((lx2-lx1) * (lx2-fx) + (ly2-ly1) * (ly2-fy) < 0) continue;
3421 if (doit)
3422 {
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);
3426 }
3427 canvas_setcursor(x, CURSOR_EDITMODE_DISCONNECT);
3428 return;
3429 }
3430 }
3431 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
3432 if (doit)
3433 {
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;
3440 }
3441}
3442
3443void canvas_mousedown(t_canvas *x, t_floatarg xpos, t_floatarg ypos,
3444 t_floatarg which, t_floatarg mod)
3445{
3446 canvas_doclick(x, xpos, ypos, which, mod, 1);
3447}
3448
3449int canvas_isconnected (t_canvas *x, t_text *ob1, int n1,
3450 t_text *ob2, int n2)
3451{
3452 t_linetraverser t;
3453 t_outconnect *oc;
3454 linetraverser_start(&t, x);
3455 while (oc = linetraverser_next(&t))
3456 if (t.tr_ob == ob1 && t.tr_outno == n1 &&
3457 t.tr_ob2 == ob2 && t.tr_inno == n2)
3458 return (1);
3459 return (0);
3460}
3461
3462void canvas_doconnect(t_canvas *x, int xpos, int ypos, int which, int doit)
3463{
3464 int x11, y11, x12, y12;
3465 t_gobj *y1;
3466 int x21, y21, x22, y22;
3467 t_gobj *y2;
3468 int xwas = x->gl_editor->e_xwas,
3469 ywas = x->gl_editor->e_ywas;
3470 if (doit) sys_vgui(".x%x.c delete x\n", x);
3471 else sys_vgui(".x%x.c coords x %d %d %d %d\n",
3472 x, x->gl_editor->e_xwas,
3473 x->gl_editor->e_ywas, xpos, ypos);
3474
3475 if ((y1 = canvas_findhitbox(x, xwas, ywas, &x11, &y11, &x12, &y12))
3476 && (y2 = canvas_findhitbox(x, xpos, ypos, &x21, &y21, &x22, &y22)))
3477 {
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)))
3484 {
3485 int width1 = x12 - x11, closest1, hotspot1;
3486 int width2 = x22 - x21, closest2, hotspot2;
3487 int lx1, lx2, ly1, ly2;
3488 t_outconnect *oc;
3489
3490 if (noutlet1 > 1)
3491 {
3492 closest1 = ((xwas-x11) * (noutlet1-1) + width1/2)/width1;
3493 hotspot1 = x11 +
3494 (width1 - IOWIDTH) * closest1 / (noutlet1-1);
3495 }
3496 else closest1 = 0, hotspot1 = x11;
3497
3498 if (ninlet2 > 1)
3499 {
3500 closest2 = ((xpos-x21) * (ninlet2-1) + width2/2)/width2;
3501 hotspot2 = x21 +
3502 (width2 - IOWIDTH) * closest2 / (ninlet2-1);
3503 }
3504 else closest2 = 0, hotspot2 = x21;
3505
3506 if (closest1 >= noutlet1)
3507 closest1 = noutlet1 - 1;
3508 if (closest2 >= ninlet2)
3509 closest2 = ninlet2 - 1;
3510
3511 if (canvas_isconnected (x, ob1, closest1, ob2, closest2))
3512 {
3513 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
3514 return;
3515 }
3516 if (obj_issignaloutlet(ob1, closest1) &&
3517 !obj_issignalinlet(ob2, closest2))
3518 {
3519 if (doit)
3520 error("can't connect signal outlet to control inlet");
3521 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
3522 return;
3523 }
3524 if (doit)
3525 {
3526 oc = obj_connect(ob1, closest1, ob2, closest2);
3527 lx1 = x11 + (noutlet1 > 1 ?
3528 ((x12-x11-IOWIDTH) * closest1)/(noutlet1-1) : 0)
3529 + IOMIDDLE;
3530 ly1 = y12;
3531 lx2 = x21 + (ninlet2 > 1 ?
3532 ((x22-x21-IOWIDTH) * closest2)/(ninlet2-1) : 0)
3533 + IOMIDDLE;
3534 ly2 = y21;
3535 sys_vgui(".x%x.c create line %d %d %d %d -width %d -tags l%x\n",
3536 glist_getcanvas(x),
3537 lx1, ly1, lx2, ly2,
3538 (obj_issignaloutlet(ob1, closest1) ? 2 : 1), oc);
3539 canvas_setundo(x, canvas_undo_connect,
3540 canvas_undo_set_connect(x,
3541 canvas_getindex(x, &ob1->ob_g), closest1,
3542 canvas_getindex(x, &ob2->ob_g), closest2),
3543 "connect");
3544 }
3545 else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT);
3546 return;
3547 }
3548 }
3549 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
3550}
3551
3552void canvas_selectinrect(t_canvas *x, int lox, int loy, int hix, int hiy)
3553{
3554 t_gobj *y;
3555 for (y = x->gl_list; y; y = y->g_next)
3556 {
3557 int x1, y1, x2, y2;
3558 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
3559 if (hix >= x1 && lox <= x2 && hiy >= y1 && loy <= y2
3560 && !glist_isselected(x, y))
3561 glist_select(x, y);
3562 }
3563}
3564
3565static void canvas_doregion(t_canvas *x, int xpos, int ypos, int doit)
3566{
3567 if (doit)
3568 {
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;
3579 }
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);
3583}
3584
3585void canvas_mouseup(t_canvas *x,
3586 t_floatarg fxpos, t_floatarg fypos, t_floatarg fwhich)
3587{
3588 t_gobj *y;
3589
3590 int xpos = fxpos, ypos = fypos, which = fwhich;
3591
3592 if (!x->gl_editor)
3593 {
3594 bug("editor");
3595 return;
3596 }
3597
3598#ifdef SIMULATERIGHTCLICK
3599 canvas_upclicktime = sys_getrealtime();
3600 canvas_upx = xpos;
3601 canvas_upy = ypos;
3602#endif
3603
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)
3609 {
3610 /* after motion, if there's only one item selected, activate it */
3611 if (x->gl_editor->e_selection &&
3612 !(x->gl_editor->e_selection->sel_next))
3613 gobj_activate(x->gl_editor->e_selection->sel_what,
3614 x, 1);
3615 }
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;
3619
3620
3621#if 1
3622 /* GG misused the (unused) dbl parameter to tell if mouseup */
3623
3624 for (y = x->gl_list; y; y = y->g_next) {
3625 int x1, y1, x2, y2, clickreturned = 0;
3626
3627 /* check if the object wants to be clicked */
3628 if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2)
3629 && (clickreturned = gobj_click(y, x, xpos, ypos,
3630 0, 0, 1, 0)))
3631 break;
3632 }
3633#endif
3634
3635
3636}
3637
3638 /* displace the selection by (dx, dy) pixels */
3639static void canvas_displaceselection(t_canvas *x, int dx, int dy)
3640{
3641 t_selection *y;
3642 int resortin = 0, resortout = 0;
3643 if (!canvas_undo_already_set_move)
3644 {
3645 canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, 1),
3646 "motion");
3647 canvas_undo_already_set_move = 1;
3648 }
3649 for (y = x->gl_editor->e_selection; y; y = y->sel_next)
3650 {
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;
3655 }
3656 if (resortin) canvas_resortinlets(x);
3657 if (resortout) canvas_resortoutlets(x);
3658 canvas_dirty(x, 1);
3659}
3660
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. */
3665void canvas_key(t_canvas *x, t_symbol *s, int ac, t_atom *av)
3666{
3667 static t_symbol *keynumsym, *keyupsym, *keynamesym;
3668 int keynum, fflag;
3669 t_symbol *gotkeysym;
3670
3671 int down, shift;
3672
3673 if (ac < 3)
3674 return;
3675 if (!x->gl_editor)
3676 {
3677 bug("editor");
3678 return;
3679 }
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)
3686 {
3687 char buf[3];
3688 sprintf(buf, "%c", (int)(av[1].a_w.w_float));
3689 gotkeysym = gensym(buf);
3690 }
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 == '}')
3695 {
3696 post("%c: dropped", (int)keynum);
3697 return;
3698 }
3699 if (keynum == '\r') keynum = '\n';
3700 if (av[1].a_type == A_SYMBOL &&
3701 !strcmp(av[1].a_w.w_symbol->s_name, "Return"))
3702 keynum = '\n';
3703 if (!keynumsym)
3704 {
3705 keynumsym = gensym("#key");
3706 keyupsym = gensym("#keyup");
3707 keynamesym = gensym("#keyname");
3708 }
3709#ifdef MACOSX
3710 if (keynum == 30)
3711 keynum = 0, gotkeysym = gensym("Up");
3712 else if (keynum == 31)
3713 keynum = 0, gotkeysym = gensym("Down");
3714 else if (keynum == 28)
3715 keynum = 0, gotkeysym = gensym("Left");
3716 else if (keynum == 29)
3717 keynum = 0, gotkeysym = gensym("Right");
3718#endif
3719 if (keynumsym->s_thing && down)
3720 pd_float(keynumsym->s_thing, (float)keynum);
3721 if (keyupsym->s_thing && !down)
3722 pd_float(keyupsym->s_thing, (float)keynum);
3723 if (keynamesym->s_thing)
3724 {
3725 t_atom at[2];
3726 at[0] = av[0];
3727 SETFLOAT(at, down);
3728 SETSYMBOL(at+1, gotkeysym);
3729 pd_list(keynamesym->s_thing, 0, 2, at);
3730 }
3731 if (!x->gl_editor) /* if that 'invis'ed the window, we'd better stop. */
3732 return;
3733 if (x && down)
3734 {
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)
3742 {
3743 if (!x->gl_editor->e_textdirty)
3744 {
3745 canvas_setundo(x, canvas_undo_cut,
3746 canvas_undo_set_cut(x, UCUT_TEXT), "typing");
3747 }
3748 rtext_key(x->gl_editor->e_textedfor,
3749 (int)keynum, gotkeysym);
3750 if (x->gl_editor->e_textdirty)
3751 canvas_dirty(x, 1);
3752 }
3753 /* check for backspace or clear */
3754 else if (keynum == 8 || keynum == 127)
3755 {
3756 if (x->gl_editor->e_selectedline)
3757 canvas_clearline(x);
3758 else if (x->gl_editor->e_selection)
3759 {
3760 canvas_setundo(x, canvas_undo_cut,
3761 canvas_undo_set_cut(x, UCUT_CLEAR), "clear");
3762 canvas_doclear(x);
3763 }
3764 }
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);
3774 }
3775}
3776
3777void canvas_motion(t_canvas *x, t_floatarg xpos, t_floatarg ypos,
3778 t_floatarg fmod)
3779{
3780 /* post("motion %d %d", xpos, ypos); */
3781 int mod = fmod;
3782 if (!x->gl_editor)
3783 {
3784 bug("editor");
3785 return;
3786 }
3787 glist_setlastxy(x, xpos, ypos);
3788 if (x->gl_editor->e_onmotion == MA_MOVE)
3789 {
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;
3794 }
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)
3800 {
3801 if (!x->gl_editor->e_motionfn)
3802 bug("e_motionfn");
3803 (*x->gl_editor->e_motionfn)(&x->gl_editor->e_grab->g_pd,
3804 xpos - x->gl_editor->e_xwas,
3805 ypos - x->gl_editor->e_ywas);
3806 x->gl_editor->e_xwas = xpos;
3807 x->gl_editor->e_ywas = ypos;
3808 }
3809 else if (x->gl_editor->e_onmotion == MA_DRAGTEXT)
3810 {
3811 t_rtext *rt = x->gl_editor->e_textedfor;
3812 if (rt)
3813 rtext_mouse(rt, xpos - x->gl_editor->e_xwas,
3814 ypos - x->gl_editor->e_ywas, RTEXT_DRAG);
3815 }
3816 else canvas_doclick(x, xpos, ypos, 0, mod, 0);
3817
3818 x->gl_editor->e_lastmoved = 1;
3819}
3820
3821void canvas_startmotion(t_canvas *x)
3822{
3823 int xval, yval;
3824 if (!x->gl_editor) return;
3825 glist_getnextxy(x, &xval, &yval);
3826 if (xval == 0 && yval == 0) return;
3827 x->gl_editor->e_onmotion = MA_MOVE;
3828 x->gl_editor->e_xwas = xval;
3829 x->gl_editor->e_ywas = yval;
3830}
3831
3832/* ----------------------------- window stuff ----------------------- */
3833
3834void canvas_print(t_canvas *x, t_symbol *s)
3835{
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);
3838}
3839
3840void canvas_menuclose(t_canvas *x, t_floatarg force)
3841{
3842 if (x->gl_owner)
3843 canvas_vis(x, 0);
3844 else if ((force != 0) || (!x->gl_dirty))
3845 pd_free(&x->gl_pd);
3846 else sys_vgui("pdtk_check {This window has been modified. Close anyway?}\
3847 {.x%x menuclose 1;\n}\n", x);
3848}
3849
3850 /* put up a dialog which may call canvas_font back to do the work */
3851static void canvas_menufont(t_canvas *x)
3852{
3853 char buf[80];
3854 t_canvas *x2 = canvas_getrootfor(x);
3855 gfxstub_deleteforkey(x2);
3856 sprintf(buf, "pdtk_canvas_dofont %%s %d\n", x2->gl_font);
3857 gfxstub_new(&x2->gl_pd, &x2->gl_pd, buf);
3858}
3859
3860static int canvas_find_index1, canvas_find_index2;
3861static t_binbuf *canvas_findbuf;
3862int binbuf_match(t_binbuf *inbuf, t_binbuf *searchbuf);
3863
3864 /* find an atom or string of atoms */
3865static int canvas_dofind(t_canvas *x, int *myindex1p)
3866{
3867 t_gobj *y;
3868 int myindex1 = *myindex1p, myindex2;
3869 if (myindex1 >= canvas_find_index1)
3870 {
3871 for (y = x->gl_list, myindex2 = 0; y;
3872 y = y->g_next, myindex2++)
3873 {
3874 t_object *ob = 0;
3875 if (ob = pd_checkobject(&y->g_pd))
3876 {
3877 if (binbuf_match(ob->ob_binbuf, canvas_findbuf))
3878 {
3879 if (myindex1 > canvas_find_index1 ||
3880 myindex1 == canvas_find_index1 &&
3881 myindex2 > canvas_find_index2)
3882 {
3883 canvas_find_index1 = myindex1;
3884 canvas_find_index2 = myindex2;
3885 glist_noselect(x);
3886 canvas_vis(x, 1);
3887 canvas_editmode(x, 1.);
3888 glist_select(x, y);
3889 return (1);
3890 }
3891 }
3892 }
3893 }
3894 }
3895 for (y = x->gl_list, myindex2 = 0; y; y = y->g_next, myindex2++)
3896 {
3897 if (pd_class(&y->g_pd) == canvas_class)
3898 {
3899 (*myindex1p)++;
3900 if (canvas_dofind((t_canvas *)y, myindex1p))
3901 return (1);
3902 }
3903 }
3904 return (0);
3905}
3906
3907static void canvas_find(t_canvas *x, t_symbol *s, int ac, t_atom *av)
3908{
3909 int myindex1 = 0, i;
3910 for (i = 0; i < ac; i++)
3911 {
3912 if (av[i].a_type == A_SYMBOL)
3913 {
3914 if (!strcmp(av[i].a_w.w_symbol->s_name, "_semi_"))
3915 SETSEMI(&av[i]);
3916 else if (!strcmp(av[i].a_w.w_symbol->s_name, "_comma_"))
3917 SETCOMMA(&av[i]);
3918 }
3919 }
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))
3928 {
3929 binbuf_print(canvas_findbuf);
3930 post("... couldn't find");
3931 }
3932}
3933
3934static void canvas_find_again(t_canvas *x)
3935{
3936 int myindex1 = 0;
3937 if (!canvas_findbuf || !canvas_whichfind)
3938 return;
3939 if (!canvas_dofind(canvas_whichfind, &myindex1))
3940 {
3941 binbuf_print(canvas_findbuf);
3942 post("... couldn't find");
3943 }
3944}
3945
3946static void canvas_find_parent(t_canvas *x)
3947{
3948 if (x->gl_owner)
3949 canvas_vis(glist_getcanvas(x->gl_owner), 1);
3950}
3951
3952static int glist_dofinderror(t_glist *gl, void *error_object)
3953{
3954 t_gobj *g;
3955 for (g = gl->gl_list; g; g = g->g_next)
3956 {
3957 if ((void *)g == error_object)
3958 {
3959 /* got it... now show it. */
3960 glist_noselect(gl);
3961 canvas_vis(glist_getcanvas(gl), 1);
3962 canvas_editmode(glist_getcanvas(gl), 1.);
3963 glist_select(gl, g);
3964 return (1);
3965 }
3966 else if (g->g_pd == canvas_class)
3967 {
3968 if (glist_dofinderror((t_canvas *)g, error_object))
3969 return (1);
3970 }
3971 }
3972 return (0);
3973}
3974
3975void canvas_finderror(void *error_object)
3976{
3977 t_canvas *x;
3978 /* find all root canvases */
3979 for (x = canvas_list; x; x = x->gl_next)
3980 {
3981 if (glist_dofinderror(x, error_object))
3982 return;
3983 }
3984 post("... sorry, I couldn't find the source of that error.");
3985}
3986
3987void canvas_stowconnections(t_canvas *x)
3988{
3989 t_gobj *selhead = 0, *seltail = 0, *nonhead = 0, *nontail = 0, *y, *y2;
3990 t_linetraverser t;
3991 t_outconnect *oc;
3992 if (!x->gl_editor) return;
3993 /* split list to "selected" and "unselected" parts */
3994 for (y = x->gl_list; y; y = y2)
3995 {
3996 y2 = y->g_next;
3997 if (glist_isselected(x, y))
3998 {
3999 if (seltail)
4000 {
4001 seltail->g_next = y;
4002 seltail = y;
4003 y->g_next = 0;
4004 }
4005 else
4006 {
4007 selhead = seltail = y;
4008 seltail->g_next = 0;
4009 }
4010 }
4011 else
4012 {
4013 if (nontail)
4014 {
4015 nontail->g_next = y;
4016 nontail = y;
4017 y->g_next = 0;
4018 }
4019 else
4020 {
4021 nonhead = nontail = y;
4022 nontail->g_next = 0;
4023 }
4024 }
4025 }
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;
4029
4030 /* add connections to binbuf */
4031 binbuf_clear(x->gl_editor->e_connectbuf);
4032 linetraverser_start(&t, x);
4033 while (oc = linetraverser_next(&t))
4034 {
4035 int s1 = glist_isselected(x, &t.tr_ob->ob_g);
4036 int s2 = glist_isselected(x, &t.tr_ob2->ob_g);
4037 if (s1 != s2)
4038 binbuf_addv(x->gl_editor->e_connectbuf, "ssiiii;",
4039 gensym("#X"), gensym("connect"),
4040 glist_getindex(x, &t.tr_ob->ob_g), t.tr_outno,
4041 glist_getindex(x, &t.tr_ob2->ob_g), t.tr_inno);
4042 }
4043}
4044
4045void canvas_restoreconnections(t_canvas *x)
4046{
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"));
4050}
4051
4052static t_binbuf *canvas_docopy(t_canvas *x)
4053{
4054 t_gobj *y;
4055 t_linetraverser t;
4056 t_outconnect *oc;
4057 t_binbuf *b = binbuf_new();
4058 for (y = x->gl_list; y; y = y->g_next)
4059 {
4060 if (glist_isselected(x, y))
4061 gobj_save(y, b);
4062 }
4063 linetraverser_start(&t, x);
4064 while (oc = linetraverser_next(&t))
4065 {
4066 if (glist_isselected(x, &t.tr_ob->ob_g)
4067 && glist_isselected(x, &t.tr_ob2->ob_g))
4068 {
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);
4072 }
4073 }
4074 return (b);
4075}
4076
4077static void canvas_copy(t_canvas *x)
4078{
4079 if (!x->gl_editor || !x->gl_editor->e_selection)
4080 return;
4081 binbuf_free(copy_binbuf);
4082 copy_binbuf = canvas_docopy(x);
4083}
4084
4085static void canvas_clearline(t_canvas *x)
4086{
4087 if (x->gl_editor->e_selectedline)
4088 {
4089 canvas_disconnect(x, x->gl_editor->e_selectline_index1,
4090 x->gl_editor->e_selectline_outno,
4091 x->gl_editor->e_selectline_index2,
4092 x->gl_editor->e_selectline_inno);
4093 canvas_setundo(x, canvas_undo_disconnect,
4094 canvas_undo_set_disconnect(x,
4095 x->gl_editor->e_selectline_index1,
4096 x->gl_editor->e_selectline_outno,
4097 x->gl_editor->e_selectline_index2,
4098 x->gl_editor->e_selectline_inno),
4099 "disconnect");
4100 }
4101}
4102
4103extern t_pd *newest;
4104static void canvas_doclear(t_canvas *x)
4105{
4106 t_gobj *y, *y2;
4107 int dspstate;
4108
4109 dspstate = canvas_suspend_dsp();
4110 if (x->gl_editor->e_selectedline)
4111 {
4112 canvas_disconnect(x, x->gl_editor->e_selectline_index1,
4113 x->gl_editor->e_selectline_outno,
4114 x->gl_editor->e_selectline_index2,
4115 x->gl_editor->e_selectline_inno);
4116 canvas_setundo(x, canvas_undo_disconnect,
4117 canvas_undo_set_disconnect(x,
4118 x->gl_editor->e_selectline_index1,
4119 x->gl_editor->e_selectline_outno,
4120 x->gl_editor->e_selectline_index2,
4121 x->gl_editor->e_selectline_inno),
4122 "disconnect");
4123 }
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)
4128 {
4129 newest = 0;
4130 glist_noselect(x);
4131 if (newest)
4132 {
4133 for (y = x->gl_list; y; y = y->g_next)
4134 if (&y->g_pd == newest) glist_select(x, y);
4135 }
4136 }
4137 while (1) /* this is pretty wierd... should rewrite it */
4138 {
4139 for (y = x->gl_list; y; y = y2)
4140 {
4141 y2 = y->g_next;
4142 if (glist_isselected(x, y))
4143 {
4144 glist_delete(x, y);
4145#if 0
4146 if (y2) post("cut 5 %x %x", y2, y2->g_next);
4147 else post("cut 6");
4148#endif
4149 goto next;
4150 }
4151 }
4152 goto restore;
4153 next: ;
4154 }
4155restore:
4156 canvas_resume_dsp(dspstate);
4157 canvas_dirty(x, 1);
4158}
4159
4160static void canvas_cut(t_canvas *x)
4161{
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)
4165 {
4166 canvas_setundo(x, canvas_undo_cut,
4167 canvas_undo_set_cut(x, UCUT_CUT), "cut");
4168 canvas_copy(x);
4169 canvas_doclear(x);
4170 }
4171}
4172
4173static int paste_onset;
4174static t_canvas *paste_canvas;
4175
4176static void glist_donewloadbangs(t_glist *x)
4177{
4178 if (x->gl_editor)
4179 {
4180 t_selection *sel;
4181 for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
4182 if (pd_class(&sel->sel_what->g_pd) == canvas_class)
4183 canvas_loadbang((t_canvas *)(&sel->sel_what->g_pd));
4184 }
4185}
4186
4187static void canvas_dopaste(t_canvas *x, t_binbuf *b)
4188{
4189 t_gobj *newgobj, *last, *g2;
4190 int dspstate = canvas_suspend_dsp(), nbox, count;
4191
4192 canvas_editmode(x, 1.);
4193 glist_noselect(x);
4194 for (g2 = x->gl_list, nbox = 0; g2; g2 = g2->g_next) nbox++;
4195
4196 paste_onset = nbox;
4197 paste_canvas = x;
4198
4199 pd_bind(&x->gl_pd, gensym("#X"));
4200 binbuf_eval(b, 0, 0, 0);
4201 pd_unbind(&x->gl_pd, gensym("#X"));
4202 for (g2 = x->gl_list, count = 0; g2; g2 = g2->g_next, count++)
4203 if (count >= nbox)
4204 glist_select(x, g2);
4205 paste_canvas = 0;
4206 canvas_resume_dsp(dspstate);
4207 canvas_dirty(x, 1);
4208 glist_donewloadbangs(x);
4209}
4210
4211static void canvas_paste(t_canvas *x)
4212{
4213 canvas_setundo(x, canvas_undo_paste, canvas_undo_set_paste(x), "paste");
4214 canvas_dopaste(x, copy_binbuf);
4215}
4216
4217static void canvas_duplicate(t_canvas *x)
4218{
4219 if (x->gl_editor->e_onmotion == MA_NONE)
4220 {
4221 t_selection *y;
4222 canvas_copy(x);
4223 canvas_setundo(x, canvas_undo_paste, canvas_undo_set_paste(x),
4224 "duplicate");
4225 canvas_dopaste(x, copy_binbuf);
4226 for (y = x->gl_editor->e_selection; y; y = y->sel_next)
4227 gobj_displace(y->sel_what, x,
4228 10, 10);
4229 canvas_dirty(x, 1);
4230 }
4231}
4232
4233static void canvas_selectall(t_canvas *x)
4234{
4235 t_gobj *y;
4236 if (!x->gl_edit)
4237 canvas_editmode(x, 1);
4238 for (y = x->gl_list; y; y = y->g_next)
4239 {
4240 if (!glist_isselected(x, y))
4241 glist_select(x, y);
4242 }
4243}
4244
4245void canvas_connect(t_canvas *x, t_floatarg fwhoout, t_floatarg foutno,
4246 t_floatarg fwhoin, t_floatarg finno)
4247{
4248 int whoout = fwhoout, outno = foutno, whoin = fwhoin, inno = finno;
4249 t_gobj *src = 0, *sink = 0;
4250 t_object *objsrc, *objsink;
4251 t_outconnect *oc;
4252 int nin = whoin, nout = whoout;
4253 if (paste_canvas == x) whoout += paste_onset, whoin += paste_onset;
4254 for (src = x->gl_list; whoout; src = src->g_next, whoout--)
4255 if (!src->g_next) goto bad; /* bug fix thanks to Hannes */
4256 for (sink = x->gl_list; whoin; sink = sink->g_next, whoin--)
4257 if (!sink->g_next) goto bad;
4258 if (!(objsrc = pd_checkobject(&src->g_pd)) ||
4259 !(objsink = pd_checkobject(&sink->g_pd)))
4260 goto bad;
4261 if (!(oc = obj_connect(objsrc, outno, objsink, inno))) goto bad;
4262 if (glist_isvisible(x))
4263 {
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);
4268 }
4269 return;
4270
4271bad:
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)) : "???"));
4276}
4277
4278#define XTOLERANCE 4
4279#define YTOLERANCE 3
4280#define NHIST 15
4281
4282 /* LATER might have to speed this up */
4283static void canvas_tidy(t_canvas *x)
4284{
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);
4291
4292 canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, !all),
4293 "motion");
4294
4295 /* tidy horizontally */
4296 for (y = x->gl_list; y; y = y->g_next)
4297 if (all || glist_isselected(x, y))
4298 {
4299 gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);
4300
4301 for (y2 = x->gl_list; y2; y2 = y2->g_next)
4302 if (all || glist_isselected(x, y2))
4303 {
4304 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
4305 if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE &&
4306 bx1 < ax1)
4307 goto nothorizhead;
4308 }
4309
4310 for (y2 = x->gl_list; y2; y2 = y2->g_next)
4311 if (all || glist_isselected(x, y2))
4312 {
4313 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
4314 if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE
4315 && by1 != ay1)
4316 gobj_displace(y2, x, 0, ay1-by1);
4317 }
4318 nothorizhead: ;
4319 }
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))
4324 {
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))
4328 {
4329 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
4330 if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE)
4331 {
4332 int distance = by1-ay2;
4333 if (distance >= 0 && distance < NHIST)
4334 histogram[distance]++;
4335 }
4336 }
4337 }
4338 for (i = 1, besthist = 0, bestdist = 4, ip = histogram + 1;
4339 i < (NHIST-1); i++, ip++)
4340 {
4341 int hit = ip[-1] + 2 * ip[0] + ip[1];
4342 if (hit > besthist)
4343 {
4344 besthist = hit;
4345 bestdist = i;
4346 }
4347 }
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))
4351 {
4352 int keep = 1;
4353 gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);
4354 for (y2 = x->gl_list; y2; y2 = y2->g_next)
4355 if (all || glist_isselected(x, y2))
4356 {
4357 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
4358 if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE &&
4359 ay1 >= by2 - 10 && ay1 < by2 + NHIST)
4360 goto nothead;
4361 }
4362 while (keep)
4363 {
4364 keep = 0;
4365 for (y2 = x->gl_list; y2; y2 = y2->g_next)
4366 if (all || glist_isselected(x, y2))
4367 {
4368 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
4369 if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE &&
4370 by1 > ay1 && by1 < ay2 + NHIST)
4371 {
4372 int vmove = ay2 + bestdist - by1;
4373 gobj_displace(y2, x, ax1-bx1, vmove);
4374 ay1 = by1 + vmove;
4375 ay2 = by2 + vmove;
4376 keep = 1;
4377 break;
4378 }
4379 }
4380 }
4381 nothead: ;
4382 }
4383 canvas_dirty(x, 1);
4384}
4385
4386static void canvas_texteditor(t_canvas *x)
4387{
4388 t_rtext *foo;
4389 char *buf;
4390 int bufsize;
4391 if (foo = x->gl_editor->e_textedfor)
4392 rtext_gettext(foo, &buf, &bufsize);
4393 else buf = "", bufsize = 0;
4394 sys_vgui("pdtk_pd_texteditor {%.*s}\n", bufsize, buf);
4395
4396}
4397
4398void glob_key(void *dummy, t_symbol *s, int ac, t_atom *av)
4399{
4400 /* canvas_editing can be zero; canvas_key checks for that */
4401 canvas_key(canvas_editing, s, ac, av);
4402}
4403
4404void canvas_editmode(t_canvas *x, t_floatarg fyesplease)
4405{
4406 int yesplease = fyesplease;
4407 if (yesplease && x->gl_edit)
4408 return;
4409 x->gl_edit = !x->gl_edit;
4410 if (x->gl_edit && glist_isvisible(x) && glist_istoplevel(x))
4411 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
4412 else
4413 {
4414 glist_noselect(x);
4415 if (glist_isvisible(x) && glist_istoplevel(x))
4416 canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
4417 }
4418 sys_vgui("pdtk_canvas_editval .x%x %d\n",
4419 glist_getcanvas(x), x->gl_edit);
4420}
4421
4422 /* called by canvas_font below */
4423static void canvas_dofont(t_canvas *x, t_floatarg font, t_floatarg xresize,
4424 t_floatarg yresize)
4425{
4426 t_gobj *y;
4427 x->gl_font = font;
4428 if (xresize != 1 || yresize != 1)
4429 {
4430 canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, 0),
4431 "motion");
4432 for (y = x->gl_list; y; y = y->g_next)
4433 {
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);
4439 }
4440 }
4441 if (glist_isvisible(x))
4442 glist_redraw(x);
4443 for (y = x->gl_list; y; y = y->g_next)
4444 if (pd_class(&y->g_pd) == canvas_class
4445 && !canvas_isabstraction((t_canvas *)y))
4446 canvas_dofont((t_canvas *)y, font, xresize, yresize);
4447}
4448
4449 /* canvas_menufont calls up a TK dialog which calls this back */
4450static void canvas_font(t_canvas *x, t_floatarg font, t_floatarg resize,
4451 t_floatarg whichresize)
4452{
4453 float realresize, realresx = 1, realresy = 1;
4454 t_canvas *x2 = canvas_getrootfor(x);
4455 if (!resize) realresize = 1;
4456 else
4457 {
4458 if (resize < 20) resize = 20;
4459 if (resize > 500) resize = 500;
4460 realresize = resize * 0.01;
4461 }
4462 if (whichresize != 3) realresx = realresize;
4463 if (whichresize != 2) realresy = realresize;
4464 canvas_dofont(x2, font, realresx, realresy);
4465 sys_defaultfont = font;
4466}
4467
4468static t_glist *canvas_last_glist;
4469static int canvas_last_glist_x, canvas_last_glist_y;
4470
4471void glist_getnextxy(t_glist *gl, int *xpix, int *ypix)
4472{
4473 if (canvas_last_glist == gl)
4474 *xpix = canvas_last_glist_x, *ypix = canvas_last_glist_y;
4475 else *xpix = *ypix = 40;
4476}
4477
4478static void glist_setlastxy(t_glist *gl, int xval, int yval)
4479{
4480 canvas_last_glist = gl;
4481 canvas_last_glist_x = xval;
4482 canvas_last_glist_y = yval;
4483}
4484
4485
4486void g_editor_setup(void)
4487{
4488/* ------------------------ events ---------------------------------- */
4489 class_addmethod(canvas_class, (t_method)canvas_mousedown, gensym("mouse"),
4490 A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
4491 class_addmethod(canvas_class, (t_method)canvas_mouseup, gensym("mouseup"),
4492 A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
4493 class_addmethod(canvas_class, (t_method)canvas_key, gensym("key"),
4494 A_GIMME, A_NULL);
4495 class_addmethod(canvas_class, (t_method)canvas_motion, gensym("motion"),
4496 A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
4497
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);
4539
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);
4543
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();
4548}