Fix pdbox makefile to actually take part in dependency generation
[kugel-rb.git] / apps / plugins / pdbox / PDa / src / g_editor.c
blob1446a451fdba3c52509e06da033994356fc8c003
1 /* Copyright (c) 1997-2001 Miller Puckette and others.
2 * For information on usage and redistribution, and for a DISCLAIMER OF ALL
3 * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
5 #ifdef ROCKBOX
6 #include "plugin.h"
7 #include "../../pdbox.h"
8 #include "m_pd.h"
9 #include "m_imp.h"
10 #include "s_stuff.h"
11 #include "g_canvas.h"
12 #else /* ROCKBOX */
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include "m_pd.h"
16 #include "m_imp.h"
17 #include "s_stuff.h"
18 #include "g_canvas.h"
19 #include <string.h>
20 #endif /* ROCKBOX */
22 void glist_readfrombinbuf(t_glist *x, t_binbuf *b, char *filename,
23 int selectem);
25 void open_via_helppath(const char *name, const char *dir);
26 char *class_gethelpdir(t_class *c);
28 /* ------------------ forward declarations --------------- */
29 static void canvas_doclear(t_canvas *x);
30 static void glist_setlastxy(t_glist *gl, int xval, int yval);
31 static void glist_donewloadbangs(t_glist *x);
32 static t_binbuf *canvas_docopy(t_canvas *x);
33 static void canvas_dopaste(t_canvas *x, t_binbuf *b);
34 static void canvas_paste(t_canvas *x);
35 static void canvas_clearline(t_canvas *x);
36 static t_binbuf *copy_binbuf;
38 /* ---------------- generic widget behavior ------------------------- */
40 void gobj_getrect(t_gobj *x, t_glist *glist, int *x1, int *y1,
41 int *x2, int *y2)
43 if (x->g_pd->c_wb && x->g_pd->c_wb->w_getrectfn)
44 (*x->g_pd->c_wb->w_getrectfn)(x, glist, x1, y1, x2, y2);
47 void gobj_displace(t_gobj *x, t_glist *glist, int dx, int dy)
49 if (x->g_pd->c_wb && x->g_pd->c_wb->w_displacefn)
50 (*x->g_pd->c_wb->w_displacefn)(x, glist, dx, dy);
53 void gobj_select(t_gobj *x, t_glist *glist, int state)
55 if (x->g_pd->c_wb && x->g_pd->c_wb->w_selectfn)
56 (*x->g_pd->c_wb->w_selectfn)(x, glist, state);
59 void gobj_activate(t_gobj *x, t_glist *glist, int state)
61 if (x->g_pd->c_wb && x->g_pd->c_wb->w_activatefn)
62 (*x->g_pd->c_wb->w_activatefn)(x, glist, state);
65 void gobj_delete(t_gobj *x, t_glist *glist)
67 if (x->g_pd->c_wb && x->g_pd->c_wb->w_deletefn)
68 (*x->g_pd->c_wb->w_deletefn)(x, glist);
71 void gobj_vis(t_gobj *x, struct _glist *glist, int flag)
73 if (x->g_pd->c_wb && x->g_pd->c_wb->w_visfn)
74 (*x->g_pd->c_wb->w_visfn)(x, glist, flag);
77 int gobj_click(t_gobj *x, struct _glist *glist,
78 int xpix, int ypix, int shift, int alt, int dbl, int doit)
80 if (x->g_pd->c_wb && x->g_pd->c_wb->w_clickfn)
81 return ((*x->g_pd->c_wb->w_clickfn)(x,
82 glist, xpix, ypix, shift, alt, dbl, doit));
83 else return (0);
86 /* ------------------------ managing the selection ----------------- */
88 void glist_selectline(t_glist *x, t_outconnect *oc, int index1,
89 int outno, int index2, int inno)
91 if (x->gl_editor)
93 glist_noselect(x);
94 x->gl_editor->e_selectedline = 1;
95 x->gl_editor->e_selectline_index1 = index1;
96 x->gl_editor->e_selectline_outno = outno;
97 x->gl_editor->e_selectline_index2 = index2;
98 x->gl_editor->e_selectline_inno = inno;
99 x->gl_editor->e_selectline_tag = oc;
100 #ifndef ROCKBOX
101 sys_vgui(".x%x.c itemconfigure l%x -fill blue\n",
102 x, x->gl_editor->e_selectline_tag);
103 #endif
107 void glist_deselectline(t_glist *x)
109 if (x->gl_editor)
111 x->gl_editor->e_selectedline = 0;
112 #ifndef ROCKBOX
113 sys_vgui(".x%x.c itemconfigure l%x -fill black\n",
114 x, x->gl_editor->e_selectline_tag);
115 #endif
119 int glist_isselected(t_glist *x, t_gobj *y)
121 if (x->gl_editor)
123 t_selection *sel;
124 for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
125 if (sel->sel_what == y) return (1);
127 return (0);
130 /* call this for unselected objects only */
131 void glist_select(t_glist *x, t_gobj *y)
133 if (x->gl_editor)
135 t_selection *sel = (t_selection *)getbytes(sizeof(*sel));
136 if (x->gl_editor->e_selectedline)
137 glist_deselectline(x);
138 /* LATER #ifdef out the following check */
139 if (glist_isselected(x, y)) bug("glist_select");
140 sel->sel_next = x->gl_editor->e_selection;
141 sel->sel_what = y;
142 x->gl_editor->e_selection = sel;
143 gobj_select(y, x, 1);
147 /* call this for selected objects only */
148 void glist_deselect(t_glist *x, t_gobj *y)
150 int fixdsp = 0;
151 static int reenter = 0;
152 if (reenter) return;
153 reenter = 1;
154 if (x->gl_editor)
156 t_selection *sel, *sel2;
157 t_rtext *z = 0;
158 if (!glist_isselected(x, y)) bug("glist_deselect");
159 if (x->gl_editor->e_textedfor)
161 t_rtext *fuddy = glist_findrtext(x, (t_text *)y);
162 if (x->gl_editor->e_textedfor == fuddy)
164 if (x->gl_editor->e_textdirty)
166 z = fuddy;
167 canvas_stowconnections(glist_getcanvas(x));
169 gobj_activate(y, x, 0);
171 if (zgetfn(&y->g_pd, gensym("dsp")))
172 fixdsp = 1;
174 if ((sel = x->gl_editor->e_selection)->sel_what == y)
176 x->gl_editor->e_selection = x->gl_editor->e_selection->sel_next;
177 gobj_select(sel->sel_what, x, 0);
178 freebytes(sel, sizeof(*sel));
180 else
182 for(sel = x->gl_editor->e_selection; (sel2 = sel->sel_next);
183 sel = sel2)
185 if (sel2->sel_what == y)
187 sel->sel_next = sel2->sel_next;
188 gobj_select(sel2->sel_what, x, 0);
189 freebytes(sel2, sizeof(*sel2));
190 break;
194 if (z)
196 char *buf;
197 int bufsize;
199 rtext_gettext(z, &buf, &bufsize);
200 text_setto((t_text *)y, x, buf, bufsize);
201 canvas_fixlinesfor(glist_getcanvas(x), (t_text *)y);
202 x->gl_editor->e_textedfor = 0;
204 if (fixdsp)
205 canvas_update_dsp();
207 reenter = 0;
210 void glist_noselect(t_glist *x)
212 if (x->gl_editor)
214 while (x->gl_editor->e_selection)
215 glist_deselect(x, x->gl_editor->e_selection->sel_what);
216 if (x->gl_editor->e_selectedline)
217 glist_deselectline(x);
221 void glist_selectall(t_glist *x)
223 if (x->gl_editor)
225 glist_noselect(x);
226 if (x->gl_list)
228 t_selection *sel = (t_selection *)getbytes(sizeof(*sel));
229 t_gobj *y = x->gl_list;
230 x->gl_editor->e_selection = sel;
231 sel->sel_what = y;
232 gobj_select(y, x, 1);
233 while((y = y->g_next))
235 t_selection *sel2 = (t_selection *)getbytes(sizeof(*sel2));
236 sel->sel_next = sel2;
237 sel = sel2;
238 sel->sel_what = y;
239 gobj_select(y, x, 1);
241 sel->sel_next = 0;
246 /* get the index of a gobj in a glist. If y is zero, return the
247 total number of objects. */
248 int glist_getindex(t_glist *x, t_gobj *y)
250 t_gobj *y2;
251 int indx;
253 for (y2 = x->gl_list, indx = 0; y2 && y2 != y; y2 = y2->g_next)
254 indx++;
255 return (indx);
258 /* get the index of the object, among selected items, if "selected"
259 is set; otherwise, among unselected ones. If y is zero, just
260 counts the selected or unselected objects. */
261 int glist_selectionindex(t_glist *x, t_gobj *y, int selected)
263 t_gobj *y2;
264 int indx;
266 for (y2 = x->gl_list, indx = 0; y2 && y2 != y; y2 = y2->g_next)
267 if (selected == glist_isselected(x, y2))
268 indx++;
269 return (indx);
272 static t_gobj *glist_nth(t_glist *x, int n)
274 t_gobj *y;
275 int indx;
276 for (y = x->gl_list, indx = 0; y; y = y->g_next, indx++)
277 if (indx == n)
278 return (y);
279 return (0);
282 /* ------------------- support for undo/redo -------------------------- */
284 static t_undofn canvas_undo_fn; /* current undo function if any */
285 static int canvas_undo_whatnext; /* whether we can now UNDO or REDO */
286 static void *canvas_undo_buf; /* data private to the undo function */
287 static t_canvas *canvas_undo_canvas; /* which canvas we can undo on */
288 static const char *canvas_undo_name;
290 void canvas_setundo(t_canvas *x, t_undofn undofn, void *buf,
291 const char *name)
293 int hadone = 0;
294 /* blow away the old undo information. In one special case the
295 old undo info is re-used; if so we shouldn't free it here. */
296 if (canvas_undo_fn && canvas_undo_buf && (buf != canvas_undo_buf))
298 (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_FREE);
299 hadone = 1;
301 canvas_undo_canvas = x;
302 canvas_undo_fn = undofn;
303 canvas_undo_buf = buf;
304 canvas_undo_whatnext = UNDO_UNDO;
305 canvas_undo_name = name;
306 #ifndef ROCKBOX
307 if (x && glist_isvisible(x) && glist_istoplevel(x))
308 /* enable undo in menu */
309 sys_vgui("pdtk_undomenu .x%x %s no\n", x, name);
310 else if (hadone)
311 sys_vgui("pdtk_undomenu nobody no no\n");
312 #endif
315 /* clear undo if it happens to be for the canvas x.
316 (but if x is 0, clear it regardless of who owns it.) */
317 void canvas_noundo(t_canvas *x)
319 if (!x || (x == canvas_undo_canvas))
320 canvas_setundo(0, 0, 0, "foo");
323 static void canvas_undo(t_canvas *x)
325 if (x != canvas_undo_canvas)
326 bug("canvas_undo 1");
327 else if (canvas_undo_whatnext != UNDO_UNDO)
328 bug("canvas_undo 2");
329 else
331 /* post("undo"); */
332 (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_UNDO);
333 /* enable redo in menu */
334 #ifndef ROCKBOX
335 if (glist_isvisible(x) && glist_istoplevel(x))
336 sys_vgui("pdtk_undomenu .x%x no %s\n", x, canvas_undo_name);
337 #endif
338 canvas_undo_whatnext = UNDO_REDO;
342 static void canvas_redo(t_canvas *x)
344 if (x != canvas_undo_canvas)
345 bug("canvas_undo 1");
346 else if (canvas_undo_whatnext != UNDO_REDO)
347 bug("canvas_undo 2");
348 else
350 /* post("redo"); */
351 (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_REDO);
352 /* enable undo in menu */
353 #ifndef ROCKBOX
354 if (glist_isvisible(x) && glist_istoplevel(x))
355 sys_vgui("pdtk_undomenu .x%x %s no\n", x, canvas_undo_name);
356 #endif
357 canvas_undo_whatnext = UNDO_UNDO;
361 /* ------- specific undo methods: 1. connect and disconnect -------- */
363 typedef struct _undo_connect
365 int u_index1;
366 int u_outletno;
367 int u_index2;
368 int u_inletno;
369 } t_undo_connect;
371 static void *canvas_undo_set_disconnect(t_canvas *x,
372 int index1, int outno, int index2, int inno)
374 #ifdef ROCKBOX
375 (void) x;
376 #endif
377 t_undo_connect *buf = (t_undo_connect *)getbytes(sizeof(*buf));
378 buf->u_index1 = index1;
379 buf->u_outletno = outno;
380 buf->u_index2 = index2;
381 buf->u_inletno = inno;
382 return (buf);
385 void canvas_disconnect(t_canvas *x,
386 float index1, float outno, float index2, float inno)
388 t_linetraverser t;
389 t_outconnect *oc;
390 linetraverser_start(&t, x);
391 while((oc = linetraverser_next(&t)))
393 int srcno = canvas_getindex(x, &t.tr_ob->ob_g);
394 int sinkno = canvas_getindex(x, &t.tr_ob2->ob_g);
395 if (srcno == index1 && t.tr_outno == outno &&
396 sinkno == index2 && t.tr_inno == inno)
398 #ifndef ROCKBOX
399 sys_vgui(".x%x.c delete l%x\n", x, oc);
400 #endif
401 obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);
402 break;
407 static void canvas_undo_disconnect(t_canvas *x, void *z, int action)
409 t_undo_connect *buf = z;
410 if (action == UNDO_UNDO)
412 canvas_connect(x, buf->u_index1, buf->u_outletno,
413 buf->u_index2, buf->u_inletno);
415 else if (action == UNDO_REDO)
417 canvas_disconnect(x, buf->u_index1, buf->u_outletno,
418 buf->u_index2, buf->u_inletno);
420 else if (action == UNDO_FREE)
421 t_freebytes(buf, sizeof(*buf));
424 /* connect just calls disconnect actions backward... */
425 static void *canvas_undo_set_connect(t_canvas *x,
426 int index1, int outno, int index2, int inno)
428 return (canvas_undo_set_disconnect(x, index1, outno, index2, inno));
431 static void canvas_undo_connect(t_canvas *x, void *z, int action)
433 int myaction;
434 if (action == UNDO_UNDO)
435 myaction = UNDO_REDO;
436 else if (action == UNDO_REDO)
437 myaction = UNDO_UNDO;
438 else myaction = action;
439 canvas_undo_disconnect(x, z, myaction);
442 /* ---------- ... 2. cut, clear, and typing into objects: -------- */
444 #define UCUT_CUT 1 /* operation was a cut */
445 #define UCUT_CLEAR 2 /* .. a clear */
446 #define UCUT_TEXT 3 /* text typed into a box */
448 typedef struct _undo_cut
450 t_binbuf *u_objectbuf; /* the object cleared or typed into */
451 t_binbuf *u_reconnectbuf; /* connections into and out of object */
452 t_binbuf *u_redotextbuf; /* buffer to paste back for redo if TEXT */
453 int u_mode; /* from flags above */
454 } t_undo_cut;
456 static void *canvas_undo_set_cut(t_canvas *x, int mode)
458 t_undo_cut *buf;
459 #ifndef ROCKBOX
460 t_gobj *y;
461 #endif
462 t_linetraverser t;
463 t_outconnect *oc;
464 int nnotsel= glist_selectionindex(x, 0, 0);
465 buf = (t_undo_cut *)getbytes(sizeof(*buf));
466 buf->u_mode = mode;
467 buf->u_redotextbuf = 0;
469 /* store connections into/out of the selection */
470 buf->u_reconnectbuf = binbuf_new();
471 linetraverser_start(&t, x);
472 while((oc = linetraverser_next(&t)))
474 int issel1 = glist_isselected(x, &t.tr_ob->ob_g);
475 int issel2 = glist_isselected(x, &t.tr_ob2->ob_g);
476 if (issel1 != issel2)
478 binbuf_addv(buf->u_reconnectbuf, "ssiiii;",
479 gensym("#X"), gensym("connect"),
480 (issel1 ? nnotsel : 0)
481 + glist_selectionindex(x, &t.tr_ob->ob_g, issel1),
482 t.tr_outno,
483 (issel2 ? nnotsel : 0) +
484 glist_selectionindex(x, &t.tr_ob2->ob_g, issel2),
485 t.tr_inno);
488 if (mode == UCUT_TEXT)
490 buf->u_objectbuf = canvas_docopy(x);
492 else if (mode == UCUT_CUT)
494 buf->u_objectbuf = 0;
496 else if (mode == UCUT_CLEAR)
498 buf->u_objectbuf = canvas_docopy(x);
500 return (buf);
503 static void canvas_undo_cut(t_canvas *x, void *z, int action)
505 t_undo_cut *buf = z;
506 int mode = buf->u_mode;
507 if (action == UNDO_UNDO)
509 if (mode == UCUT_CUT)
510 canvas_dopaste(x, copy_binbuf);
511 else if (mode == UCUT_CLEAR)
512 canvas_dopaste(x, buf->u_objectbuf);
513 else if (mode == UCUT_TEXT)
515 t_gobj *y1, *y2;
516 glist_noselect(x);
517 for(y1 = x->gl_list; (y2 = y1->g_next); y1 = y2)
519 if (y1)
521 if (!buf->u_redotextbuf)
523 glist_noselect(x);
524 glist_select(x, y1);
525 buf->u_redotextbuf = canvas_docopy(x);
526 glist_noselect(x);
528 glist_delete(x, y1);
530 canvas_dopaste(x, buf->u_objectbuf);
532 pd_bind(&x->gl_pd, gensym("#X"));
533 binbuf_eval(buf->u_reconnectbuf, 0, 0, 0);
534 pd_unbind(&x->gl_pd, gensym("#X"));
536 else if (action == UNDO_REDO)
538 if (mode == UCUT_CUT || mode == UCUT_CLEAR)
539 canvas_doclear(x);
540 else if (mode == UCUT_TEXT)
542 t_gobj *y1, *y2;
543 for(y1 = x->gl_list; (y2 = y1->g_next); y1 = y2)
545 if (y1)
546 glist_delete(x, y1);
547 canvas_dopaste(x, buf->u_redotextbuf);
548 pd_bind(&x->gl_pd, gensym("#X"));
549 binbuf_eval(buf->u_reconnectbuf, 0, 0, 0);
550 pd_unbind(&x->gl_pd, gensym("#X"));
553 else if (action == UNDO_FREE)
555 if (buf->u_objectbuf)
556 binbuf_free(buf->u_objectbuf);
557 if (buf->u_reconnectbuf)
558 binbuf_free(buf->u_reconnectbuf);
559 if (buf->u_redotextbuf)
560 binbuf_free(buf->u_redotextbuf);
561 t_freebytes(buf, sizeof(*buf));
565 /* --------- 3. motion, including "tidy up" and stretching ----------- */
567 typedef struct _undo_move_elem
569 int e_index;
570 int e_xpix;
571 int e_ypix;
572 } t_undo_move_elem;
574 typedef struct _undo_move
576 t_undo_move_elem *u_vec;
577 int u_n;
578 } t_undo_move;
580 static int canvas_undo_already_set_move;
582 static void *canvas_undo_set_move(t_canvas *x, int selected)
584 int x1, y1, x2, y2, i, indx;
585 t_gobj *y;
586 t_undo_move *buf = (t_undo_move *)getbytes(sizeof(*buf));
587 buf->u_n = selected ? glist_selectionindex(x, 0, 1) : glist_getindex(x, 0);
588 buf->u_vec = (t_undo_move_elem *)getbytes(sizeof(*buf->u_vec) *
589 (selected ? glist_selectionindex(x, 0, 1) : glist_getindex(x, 0)));
590 if (selected)
592 for (y = x->gl_list, i = indx = 0; y; y = y->g_next, indx++)
593 if (glist_isselected(x, y))
595 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
596 buf->u_vec[i].e_index = indx;
597 buf->u_vec[i].e_xpix = x1;
598 buf->u_vec[i].e_ypix = y1;
599 i++;
602 else
604 for (y = x->gl_list, indx = 0; y; y = y->g_next, indx++)
606 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
607 buf->u_vec[indx].e_index = indx;
608 buf->u_vec[indx].e_xpix = x1;
609 buf->u_vec[indx].e_ypix = y1;
612 canvas_undo_already_set_move = 1;
613 return (buf);
616 static void canvas_undo_move(t_canvas *x, void *z, int action)
618 t_undo_move *buf = z;
619 if (action == UNDO_UNDO || action == UNDO_REDO)
621 int i;
622 for (i = 0; i < buf->u_n; i++)
624 int x1, y1, x2, y2, newx, newy;
625 t_gobj *y;
626 newx = buf->u_vec[i].e_xpix;
627 newy = buf->u_vec[i].e_ypix;
628 y = glist_nth(x, buf->u_vec[i].e_index);
629 if (y)
631 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
632 gobj_displace(y, x, newx-x1, newy - y1);
633 buf->u_vec[i].e_xpix = x1;
634 buf->u_vec[i].e_ypix = y1;
638 else if (action == UNDO_FREE)
640 t_freebytes(buf->u_vec, buf->u_n * sizeof(*buf->u_vec));
641 t_freebytes(buf, sizeof(*buf));
645 /* --------- 4. paste (also duplicate) ----------- */
647 typedef struct _undo_paste
649 int u_index; /* index of first object pasted */
650 } t_undo_paste;
652 static void *canvas_undo_set_paste(t_canvas *x)
654 t_undo_paste *buf = (t_undo_paste *)getbytes(sizeof(*buf));
655 buf->u_index = glist_getindex(x, 0);
656 return (buf);
659 static void canvas_undo_paste(t_canvas *x, void *z, int action)
661 t_undo_paste *buf = z;
662 if (action == UNDO_UNDO)
664 t_gobj *y;
665 glist_noselect(x);
666 for (y = glist_nth(x, buf->u_index); y; y = y->g_next)
667 glist_select(x, y);
668 canvas_doclear(x);
670 else if (action == UNDO_REDO)
672 t_selection *sel;
673 canvas_dopaste(x, copy_binbuf);
674 /* if it was "duplicate" have to re-enact the displacement. */
675 if (canvas_undo_name && canvas_undo_name[0] == 'd')
676 for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
677 gobj_displace(sel->sel_what, x, 10, 10);
679 else if (action == UNDO_FREE)
680 t_freebytes(buf, sizeof(*buf));
683 /* recursively check for abstractions to reload as result of a save.
684 Don't reload the one we just saved ("except") though. */
685 /* LATER try to do the same trick for externs. */
686 static void glist_doreload(t_glist *gl, t_symbol *name, t_symbol *dir,
687 t_gobj *except)
689 t_gobj *g;
690 int i, nobj = glist_getindex(gl, 0); /* number of objects */
691 for (g = gl->gl_list, i = 0; g && i < nobj; i++)
693 if (g != except && pd_class(&g->g_pd) == canvas_class &&
694 canvas_isabstraction((t_canvas *)g) &&
695 ((t_canvas *)g)->gl_name == name &&
696 canvas_getdir((t_canvas *)g) == dir)
698 /* we're going to remake the object, so "g" will go stale.
699 Get its index here, and afterward restore g. Also, the
700 replacement will be at teh end of the list, so we don't
701 do g = g->g_next in this case. */
702 int j = glist_getindex(gl, g);
703 if (!gl->gl_havewindow)
704 canvas_vis(glist_getcanvas(gl), 1);
705 glist_noselect(gl);
706 glist_select(gl, g);
707 canvas_setundo(gl, canvas_undo_cut,
708 canvas_undo_set_cut(gl, UCUT_CLEAR), "clear");
709 canvas_doclear(gl);
710 canvas_undo(gl);
711 glist_noselect(gl);
712 g = glist_nth(gl, j);
714 else
716 if (g != except && pd_class(&g->g_pd) == canvas_class)
717 glist_doreload((t_canvas *)g, name, dir, except);
718 g = g->g_next;
723 /* call canvas_doreload on everyone */
724 void canvas_reload(t_symbol *name, t_symbol *dir, t_gobj *except)
726 t_canvas *x;
727 /* find all root canvases */
728 for (x = canvas_list; x; x = x->gl_next)
729 glist_doreload(x, name, dir, except);
732 /* ------------------------ event handling ------------------------ */
734 #define CURSOR_RUNMODE_NOTHING 0
735 #define CURSOR_RUNMODE_CLICKME 1
736 #define CURSOR_RUNMODE_THICKEN 2
737 #define CURSOR_RUNMODE_ADDPOINT 3
738 #define CURSOR_EDITMODE_NOTHING 4
739 #define CURSOR_EDITMODE_CONNECT 5
740 #define CURSOR_EDITMODE_DISCONNECT 6
742 static char *cursorlist[] = {
743 #ifdef MSW
744 "right_ptr", /* CURSOR_RUNMODE_NOTHING */
745 #else
746 "left_ptr", /* CURSOR_RUNMODE_NOTHING */
747 #endif
748 "arrow", /* CURSOR_RUNMODE_CLICKME */
749 "sb_v_double_arrow", /* CURSOR_RUNMODE_THICKEN */
750 "plus", /* CURSOR_RUNMODE_ADDPOINT */
751 "hand2", /* CURSOR_EDITMODE_NOTHING */
752 "circle", /* CURSOR_EDITMODE_CONNECT */
753 "X_cursor" /* CURSOR_EDITMODE_DISCONNECT */
756 void canvas_setcursor(t_canvas *x, unsigned int cursornum)
758 static t_canvas *xwas;
759 static unsigned int cursorwas;
760 if (cursornum >= sizeof(cursorlist)/sizeof *cursorlist)
762 bug("canvas_setcursor");
763 return;
765 if (xwas != x || cursorwas != cursornum)
767 #ifndef ROCKBOX
768 sys_vgui(".x%x configure -cursor %s\n", x, cursorlist[cursornum]);
769 #endif
770 xwas = x;
771 cursorwas = cursornum;
775 /* check if a point lies in a gobj. */
776 int canvas_hitbox(t_canvas *x, t_gobj *y, int xpos, int ypos,
777 int *x1p, int *y1p, int *x2p, int *y2p)
779 int x1, y1, x2, y2;
780 t_text *ob;
781 if ((ob = pd_checkobject(&y->g_pd)) &&
782 !text_shouldvis(ob, x))
783 return (0);
784 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
785 if (xpos >= x1 && xpos <= x2 && ypos >= y1 && ypos <= y2)
787 *x1p = x1;
788 *y1p = y1;
789 *x2p = x2;
790 *y2p = y2;
791 return (1);
793 else return (0);
796 /* find the last gobj, if any, containing the point. */
797 static t_gobj *canvas_findhitbox(t_canvas *x, int xpos, int ypos,
798 int *x1p, int *y1p, int *x2p, int *y2p)
800 t_gobj *y, *rval = 0;
801 for (y = x->gl_list; y; y = y->g_next)
803 if (canvas_hitbox(x, y, xpos, ypos, x1p, y1p, x2p, y2p))
804 rval = y;
806 return (rval);
809 /* right-clicking on a canvas object pops up a menu. */
810 static void canvas_rightclick(t_canvas *x, int xpos, int ypos, t_gobj *y)
812 int canprop, canopen;
813 canprop = (!y || (y && class_getpropertiesfn(pd_class(&y->g_pd))));
814 canopen = (y && zgetfn(&y->g_pd, gensym("menu-open")));
815 #ifdef ROCKBOX
816 (void) x;
817 (void) xpos;
818 (void) ypos;
819 #else /* ROCKBOX */
820 sys_vgui("pdtk_canvas_popup .x%x %d %d %d %d\n",
821 x, xpos, ypos, canprop, canopen);
822 #endif /* ROCKBOX */
825 /* tell GUI to create a properties dialog on the canvas. We tell
826 the user the negative of the "pixel" y scale to make it appear to grow
827 naturally upward, whereas pixels grow downward. */
828 static void canvas_properties(t_glist *x)
830 #ifdef ROCKBOX
831 (void) x;
832 #else /* ROCKBOX */
833 char graphbuf[200];
834 sprintf(graphbuf, "pdtk_canvas_dialog %%s %g %g %g %g \n",
835 glist_dpixtodx(x, 1), -glist_dpixtody(x, 1),
836 (float)glist_isgraph(x), (float)x->gl_stretch);
837 gfxstub_new(&x->gl_pd, x, graphbuf);
838 #endif /* ROCKBOX */
842 void canvas_setgraph(t_glist *x, int flag)
844 if (!flag && glist_isgraph(x))
846 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
847 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
848 x->gl_isgraph = 0;
849 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
851 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
852 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
855 else if (flag && !glist_isgraph(x))
857 if (x->gl_pixwidth <= 0)
858 x->gl_pixwidth = GLIST_DEFGRAPHWIDTH;
860 if (x->gl_pixheight <= 0)
861 x->gl_pixheight = GLIST_DEFGRAPHHEIGHT;
863 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
864 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
865 x->gl_isgraph = 1;
866 /* if (x->gl_owner && glist_isvisible(x->gl_owner))
867 canvas_vis(x, 1); */
868 if (x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner))
869 canvas_create_editor(x, 1);
870 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
872 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
873 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
878 /* called from the gui when "OK" is selected on the canvas properties
879 dialog. Again we negate "y" scale. */
880 static void canvas_donecanvasdialog(t_glist *x, t_floatarg xperpix,
881 t_floatarg yperpix, t_floatarg fgraphme)
883 int graphme = (fgraphme != 0), redraw = 0;
884 yperpix = -yperpix;
885 if (xperpix == 0)
886 xperpix = 1;
887 if (yperpix == 0)
888 yperpix = 1;
889 canvas_setgraph(x, graphme);
890 if (!x->gl_isgraph && (xperpix != glist_dpixtodx(x, 1)))
892 if (xperpix > 0)
894 x->gl_x1 = 0;
895 x->gl_x2 = xperpix;
897 else
899 x->gl_x1 = -xperpix * (x->gl_screenx2 - x->gl_screenx1);
900 x->gl_x2 = x->gl_x1 + xperpix;
902 redraw = 1;
904 if (!x->gl_isgraph && (yperpix != glist_dpixtody(x, 1)))
906 if (yperpix > 0)
908 x->gl_y1 = 0;
909 x->gl_y2 = yperpix;
911 else
913 x->gl_y1 = -yperpix * (x->gl_screeny2 - x->gl_screeny1);
914 x->gl_y2 = x->gl_y1 + yperpix;
916 redraw = 1;
918 if (redraw)
919 canvas_redraw(x);
922 /* called from the gui when a popup menu comes back with "properties,"
923 "open," or "help." */
924 static void canvas_done_popup(t_canvas *x, float which, float xpos, float ypos)
926 #ifdef ROCKBOX
927 char namebuf[MAXPDSTRING];
928 #else
929 char pathbuf[MAXPDSTRING], namebuf[MAXPDSTRING];
930 #endif
931 t_gobj *y;
932 for (y = x->gl_list; y; y = y->g_next)
934 int x1, y1, x2, y2;
935 if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2))
937 if (which == 0) /* properties */
939 if (!class_getpropertiesfn(pd_class(&y->g_pd)))
940 continue;
941 (*class_getpropertiesfn(pd_class(&y->g_pd)))(y, x);
942 return;
944 else if (which == 1) /* open */
946 if (!zgetfn(&y->g_pd, gensym("menu-open")))
947 continue;
948 vmess(&y->g_pd, gensym("menu-open"), "");
949 return;
951 else /* help */
953 char *dir;
954 if (pd_class(&y->g_pd) == canvas_class &&
955 canvas_isabstraction((t_canvas *)y))
957 t_object *ob = (t_object *)y;
958 int ac = binbuf_getnatom(ob->te_binbuf);
959 t_atom *av = binbuf_getvec(ob->te_binbuf);
960 if (ac < 1)
961 return;
962 atom_string(av, namebuf, MAXPDSTRING);
963 dir = canvas_getdir((t_canvas *)y)->s_name;
965 else
967 strcpy(namebuf, class_gethelpname(pd_class(&y->g_pd)));
968 dir = class_gethelpdir(pd_class(&y->g_pd));
970 if (strcmp(namebuf + strlen(namebuf) - 3, ".pd"))
971 strcat(namebuf, ".pd");
972 open_via_helppath(namebuf, dir);
973 return;
977 if (which == 0)
978 canvas_properties(x);
979 else if (which == 2)
981 #ifndef ROCKBOX
982 strcpy(pathbuf, sys_libdir->s_name);
983 strcat(pathbuf, "/doc/5.reference/0.INTRO.txt");
984 sys_vgui("menu_opentext %s\n", pathbuf);
985 #endif
989 #define NOMOD 0
990 #define SHIFTMOD 1
991 #define CTRLMOD 2
992 #define ALTMOD 4
993 #define RIGHTCLICK 8
995 /* on one-button-mouse machines, you can use double click to
996 mean right click (which gets the popup menu.) Do this for Mac. */
997 #ifdef MACOSX
998 #define SIMULATERIGHTCLICK
999 #endif
1001 #ifdef SIMULATERIGHTCLICK
1002 static double canvas_upclicktime;
1003 static int canvas_upx, canvas_upy;
1004 #define DCLICKINTERVAL 0.25
1005 #endif
1007 /* mouse click */
1008 void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
1009 int mod, int doit)
1011 t_gobj *y;
1012 int shiftmod, runmode, altmod, rightclick;
1013 int x1, y1, x2, y2, clickreturned = 0;
1015 #ifdef ROCKBOX
1016 (void) which;
1017 #endif
1019 if (!x->gl_editor)
1021 bug("editor");
1022 return;
1025 shiftmod = (mod & SHIFTMOD);
1026 runmode = ((mod & CTRLMOD) || (!x->gl_edit));
1027 altmod = (mod & ALTMOD);
1028 rightclick = (mod & RIGHTCLICK);
1030 canvas_undo_already_set_move = 0;
1032 /* if keyboard was grabbed, notify grabber and cancel the grab */
1033 if (doit && x->gl_editor->e_grab && x->gl_editor->e_keyfn)
1035 (* x->gl_editor->e_keyfn) (x->gl_editor->e_grab, 0);
1036 glist_grab(x, 0, 0, 0, 0, 0);
1039 #ifdef SIMULATERIGHTCLICK
1040 if (doit && !runmode && xpos == canvas_upx && ypos == canvas_upy &&
1041 sys_getrealtime() - canvas_upclicktime < DCLICKINTERVAL)
1042 rightclick = 1;
1043 #endif
1045 x->gl_editor->e_lastmoved = 0;
1046 if (doit)
1048 x->gl_editor->e_grab = 0;
1049 x->gl_editor->e_onmotion = MA_NONE;
1051 /* post("click %d %d %d %d", xpos, ypos, which, mod); */
1053 if (x->gl_editor->e_onmotion != MA_NONE)
1054 return;
1056 x->gl_editor->e_xwas = xpos;
1057 x->gl_editor->e_ywas = ypos;
1059 if (runmode && !rightclick)
1061 for (y = x->gl_list; y; y = y->g_next)
1063 /* check if the object wants to be clicked */
1064 if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2)
1065 && (clickreturned = gobj_click(y, x, xpos, ypos,
1066 shiftmod, altmod, 0, doit)))
1067 break;
1069 if (!doit)
1071 if (y)
1072 canvas_setcursor(x, clickreturned);
1073 else canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
1075 return;
1077 /* if not a runmode left click, fall here. */
1078 if((y = canvas_findhitbox(x, xpos, ypos, &x1, &y1, &x2, &y2)))
1080 t_object *ob = pd_checkobject(&y->g_pd);
1081 /* check you're in the rectangle */
1082 ob = pd_checkobject(&y->g_pd);
1083 if (rightclick)
1084 canvas_rightclick(x, xpos, ypos, y);
1085 else if (shiftmod)
1087 if (doit)
1089 t_rtext *rt;
1090 if (ob && (rt = x->gl_editor->e_textedfor) &&
1091 rt == glist_findrtext(x, ob))
1093 rtext_mouse(rt, xpos - x1, ypos - y1, RTEXT_SHIFT);
1094 x->gl_editor->e_onmotion = MA_DRAGTEXT;
1095 x->gl_editor->e_xwas = x1;
1096 x->gl_editor->e_ywas = y1;
1098 else
1100 if (glist_isselected(x, y))
1101 glist_deselect(x, y);
1102 else glist_select(x, y);
1106 else
1108 /* look for an outlet */
1109 int noutlet;
1110 if (ob && (noutlet = obj_noutlets(ob)) && ypos >= y2-4)
1112 int width = x2 - x1;
1113 int nout1 = (noutlet > 1 ? noutlet - 1 : 1);
1114 int closest = ((xpos-x1) * (nout1) + width/2)/width;
1115 int hotspot = x1 +
1116 (width - IOWIDTH) * closest / (nout1);
1117 if (closest < noutlet &&
1118 xpos >= (hotspot-1) && xpos <= hotspot + (IOWIDTH+1))
1120 if (doit)
1122 #ifndef ROCKBOX
1123 int issignal = obj_issignaloutlet(ob, closest);
1124 #endif
1125 x->gl_editor->e_onmotion = MA_CONNECT;
1126 x->gl_editor->e_xwas = xpos;
1127 x->gl_editor->e_ywas = ypos;
1128 #ifndef ROCKBOX
1129 sys_vgui(
1130 ".x%x.c create line %d %d %d %d -width %d -tags x\n",
1131 x, xpos, ypos, xpos, ypos,
1132 (issignal ? 2 : 1));
1133 #endif
1135 else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT);
1137 else if (doit)
1138 goto nooutletafterall;
1140 /* not in an outlet; select and move */
1141 else if (doit)
1143 t_rtext *rt;
1144 /* check if the box is being text edited */
1145 nooutletafterall:
1146 if (ob && (rt = x->gl_editor->e_textedfor) &&
1147 rt == glist_findrtext(x, ob))
1149 rtext_mouse(rt, xpos - x1, ypos - y1, RTEXT_DOWN);
1150 x->gl_editor->e_onmotion = MA_DRAGTEXT;
1151 x->gl_editor->e_xwas = x1;
1152 x->gl_editor->e_ywas = y1;
1154 else
1156 /* otherwise select and drag to displace */
1157 if (!glist_isselected(x, y))
1159 glist_noselect(x);
1160 glist_select(x, y);
1162 x->gl_editor->e_onmotion = MA_MOVE;
1165 else canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
1167 return;
1169 /* if right click doesn't hit any boxes, call rightclick
1170 routine anyway */
1171 if (rightclick)
1172 canvas_rightclick(x, xpos, ypos, 0);
1174 /* if not an editing action, and if we didn't hit a
1175 box, set cursor and return */
1176 if (runmode || rightclick)
1178 canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
1179 return;
1181 /* having failed to find a box, we try lines now. */
1182 if (!runmode && !altmod && !shiftmod)
1184 t_linetraverser t;
1185 t_outconnect *oc;
1186 float fx = xpos, fy = ypos;
1187 t_glist *glist2 = glist_getcanvas(x);
1188 linetraverser_start(&t, glist2);
1189 while((oc = linetraverser_next(&t)))
1191 float lx1 = t.tr_lx1, ly1 = t.tr_ly1,
1192 lx2 = t.tr_lx2, ly2 = t.tr_ly2;
1193 float area = (lx2 - lx1) * (fy - ly1) -
1194 (ly2 - ly1) * (fx - lx1);
1195 float dsquare = (lx2-lx1) * (lx2-lx1) + (ly2-ly1) * (ly2-ly1);
1196 if (area * area >= 50 * dsquare) continue;
1197 if ((lx2-lx1) * (fx-lx1) + (ly2-ly1) * (fy-ly1) < 0) continue;
1198 if ((lx2-lx1) * (lx2-fx) + (ly2-ly1) * (ly2-fy) < 0) continue;
1199 if (doit)
1201 glist_selectline(glist2, oc,
1202 canvas_getindex(glist2, &t.tr_ob->ob_g), t.tr_outno,
1203 canvas_getindex(glist2, &t.tr_ob2->ob_g), t.tr_inno);
1205 canvas_setcursor(x, CURSOR_EDITMODE_DISCONNECT);
1206 return;
1209 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
1210 if (doit)
1212 if (!shiftmod) glist_noselect(x);
1213 #ifndef ROCKBOX
1214 sys_vgui(".x%x.c create rectangle %d %d %d %d -tags x\n",
1215 x, xpos, ypos, xpos, ypos);
1216 #endif
1217 x->gl_editor->e_xwas = xpos;
1218 x->gl_editor->e_ywas = ypos;
1219 x->gl_editor->e_onmotion = MA_REGION;
1223 void canvas_mousedown(t_canvas *x, t_floatarg xpos, t_floatarg ypos,
1224 t_floatarg which, t_floatarg mod)
1226 canvas_doclick(x, xpos, ypos, which, mod, 1);
1229 int canvas_isconnected (t_canvas *x, t_text *ob1, int n1,
1230 t_text *ob2, int n2)
1232 t_linetraverser t;
1233 t_outconnect *oc;
1234 linetraverser_start(&t, x);
1235 while((oc = linetraverser_next(&t)))
1236 if (t.tr_ob == ob1 && t.tr_outno == n1 &&
1237 t.tr_ob2 == ob2 && t.tr_inno == n2)
1238 return (1);
1239 return (0);
1242 void canvas_doconnect(t_canvas *x, int xpos, int ypos, int which, int doit)
1244 int x11, y11, x12, y12;
1245 t_gobj *y1;
1246 int x21, y21, x22, y22;
1247 t_gobj *y2;
1248 int xwas = x->gl_editor->e_xwas,
1249 ywas = x->gl_editor->e_ywas;
1250 #ifdef ROCKBOX
1251 (void) which;
1252 #endif /* ROCKBOX */
1253 #ifndef ROCKBOX
1254 if (doit) sys_vgui(".x%x.c delete x\n", x);
1255 else sys_vgui(".x%x.c coords x %d %d %d %d\n",
1256 x, x->gl_editor->e_xwas,
1257 x->gl_editor->e_ywas, xpos, ypos);
1258 #endif /* ROCKBOX */
1260 if ((y1 = canvas_findhitbox(x, xwas, ywas, &x11, &y11, &x12, &y12))
1261 && (y2 = canvas_findhitbox(x, xpos, ypos, &x21, &y21, &x22, &y22)))
1263 t_object *ob1 = pd_checkobject(&y1->g_pd);
1264 t_object *ob2 = pd_checkobject(&y2->g_pd);
1265 int noutlet1, ninlet2;
1266 if (ob1 && ob2 && ob1 != ob2 &&
1267 (noutlet1 = obj_noutlets(ob1))
1268 && (ninlet2 = obj_ninlets(ob2)))
1270 int width1 = x12 - x11, closest1, hotspot1;
1271 int width2 = x22 - x21, closest2, hotspot2;
1272 int lx1, lx2, ly1, ly2;
1273 t_outconnect *oc;
1275 if (noutlet1 > 1)
1277 closest1 = ((xwas-x11) * (noutlet1-1) + width1/2)/width1;
1278 hotspot1 = x11 +
1279 (width1 - IOWIDTH) * closest1 / (noutlet1-1);
1281 else closest1 = 0, hotspot1 = x11;
1283 if (ninlet2 > 1)
1285 closest2 = ((xpos-x21) * (ninlet2-1) + width2/2)/width2;
1286 hotspot2 = x21 +
1287 (width2 - IOWIDTH) * closest2 / (ninlet2-1);
1289 else closest2 = 0, hotspot2 = x21;
1291 if (closest1 >= noutlet1)
1292 closest1 = noutlet1 - 1;
1293 if (closest2 >= ninlet2)
1294 closest2 = ninlet2 - 1;
1296 if (canvas_isconnected (x, ob1, closest1, ob2, closest2))
1298 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
1299 return;
1301 if (obj_issignaloutlet(ob1, closest1) &&
1302 !obj_issignalinlet(ob2, closest2))
1304 if (doit)
1305 error("can't connect signal outlet to control inlet");
1306 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
1307 return;
1309 if (doit)
1311 oc = obj_connect(ob1, closest1, ob2, closest2);
1312 lx1 = x11 + (noutlet1 > 1 ?
1313 ((x12-x11-IOWIDTH) * closest1)/(noutlet1-1) : 0)
1314 + IOMIDDLE;
1315 ly1 = y12;
1316 lx2 = x21 + (ninlet2 > 1 ?
1317 ((x22-x21-IOWIDTH) * closest2)/(ninlet2-1) : 0)
1318 + IOMIDDLE;
1319 ly2 = y21;
1320 #ifndef ROCKBOX
1321 sys_vgui(".x%x.c create line %d %d %d %d -width %d -tags l%x\n",
1322 glist_getcanvas(x),
1323 lx1, ly1, lx2, ly2,
1324 (obj_issignaloutlet(ob1, closest1) ? 2 : 1), oc);
1325 #endif /* ROCKBOX */
1326 canvas_setundo(x, canvas_undo_connect,
1327 canvas_undo_set_connect(x,
1328 canvas_getindex(x, &ob1->ob_g), closest1,
1329 canvas_getindex(x, &ob2->ob_g), closest2),
1330 "connect");
1332 else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT);
1333 return;
1336 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
1339 void canvas_selectinrect(t_canvas *x, int lox, int loy, int hix, int hiy)
1341 t_gobj *y;
1342 for (y = x->gl_list; y; y = y->g_next)
1344 int x1, y1, x2, y2;
1345 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
1346 if (hix >= x1 && lox <= x2 && hiy >= y1 && loy <= y2
1347 && !glist_isselected(x, y))
1348 glist_select(x, y);
1352 static void canvas_doregion(t_canvas *x, int xpos, int ypos, int doit)
1354 if (doit)
1356 int lox, loy, hix, hiy;
1357 if (x->gl_editor->e_xwas < xpos)
1358 lox = x->gl_editor->e_xwas, hix = xpos;
1359 else hix = x->gl_editor->e_xwas, lox = xpos;
1360 if (x->gl_editor->e_ywas < ypos)
1361 loy = x->gl_editor->e_ywas, hiy = ypos;
1362 else hiy = x->gl_editor->e_ywas, loy = ypos;
1363 canvas_selectinrect(x, lox, loy, hix, hiy);
1364 #ifndef ROCKBOX
1365 sys_vgui(".x%x.c delete x\n", x);
1366 #endif
1367 x->gl_editor->e_onmotion = 0;
1369 #ifndef ROCKBOX
1370 else sys_vgui(".x%x.c coords x %d %d %d %d\n",
1371 x, x->gl_editor->e_xwas,
1372 x->gl_editor->e_ywas, xpos, ypos);
1373 #endif
1376 void canvas_mouseup(t_canvas *x,
1377 t_floatarg fxpos, t_floatarg fypos, t_floatarg fwhich)
1379 t_gobj *y;
1381 int xpos = fxpos, ypos = fypos, which = fwhich;
1383 if (!x->gl_editor)
1385 bug("editor");
1386 return;
1389 #ifdef SIMULATERIGHTCLICK
1390 canvas_upclicktime = sys_getrealtime();
1391 canvas_upx = xpos;
1392 canvas_upy = ypos;
1393 #endif
1395 if (x->gl_editor->e_onmotion == MA_CONNECT)
1396 canvas_doconnect(x, xpos, ypos, which, 1);
1397 else if (x->gl_editor->e_onmotion == MA_REGION)
1398 canvas_doregion(x, xpos, ypos, 1);
1399 else if (x->gl_editor->e_onmotion == MA_MOVE)
1401 /* after motion, if there's only one item selected, activate it */
1402 if (x->gl_editor->e_selection &&
1403 !(x->gl_editor->e_selection->sel_next))
1404 gobj_activate(x->gl_editor->e_selection->sel_what,
1405 x, 1);
1407 else if (x->gl_editor->e_onmotion == MA_PASSOUT)
1408 x->gl_editor->e_onmotion = 0;
1409 x->gl_editor->e_onmotion = MA_NONE;
1412 #if 1
1413 /* GG misused the (unused) dbl parameter to tell if mouseup */
1415 for (y = x->gl_list; y; y = y->g_next) {
1416 int x1, y1, x2, y2, clickreturned = 0;
1418 /* check if the object wants to be clicked */
1419 if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2)
1420 && (clickreturned = gobj_click(y, x, xpos, ypos,
1421 0, 0, 1, 0)))
1422 break;
1424 #endif
1429 /* displace the selection by (dx, dy) pixels */
1430 static void canvas_displaceselection(t_canvas *x, int dx, int dy)
1432 t_selection *y;
1433 int resortin = 0, resortout = 0;
1434 if (!canvas_undo_already_set_move)
1436 canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, 1),
1437 "motion");
1438 canvas_undo_already_set_move = 1;
1440 for (y = x->gl_editor->e_selection; y; y = y->sel_next)
1442 t_class *cl = pd_class(&y->sel_what->g_pd);
1443 gobj_displace(y->sel_what, x, dx, dy);
1444 if (cl == vinlet_class) resortin = 1;
1445 else if (cl == voutlet_class) resortout = 1;
1447 if (resortin) canvas_resortinlets(x);
1448 if (resortout) canvas_resortoutlets(x);
1449 canvas_dirty(x, 1);
1452 /* this routine is called whenever a key is pressed or released. "x"
1453 may be zero if there's no current canvas. The first argument is true or
1454 fals for down/up; the second one is either a symbolic key name (e.g.,
1455 "Right" or an Ascii key number. */
1456 void canvas_key(t_canvas *x, t_symbol *s, int ac, t_atom *av)
1458 static t_symbol *keynumsym, *keyupsym, *keynamesym;
1459 int keynum, fflag;
1460 t_symbol *gotkeysym;
1462 int down, shift;
1464 #ifdef ROCKBOX
1465 (void) s;
1466 #endif
1468 if (ac < 3)
1469 return;
1470 if (!x->gl_editor)
1472 bug("editor");
1473 return;
1475 canvas_undo_already_set_move = 0;
1476 down = (atom_getfloat(av) != 0); /* nonzero if it's a key down */
1477 shift = (atom_getfloat(av+2) != 0); /* nonzero if shift-ed */
1478 if (av[1].a_type == A_SYMBOL)
1479 gotkeysym = av[1].a_w.w_symbol;
1480 else if (av[1].a_type == A_FLOAT)
1482 char buf[3];
1483 #ifdef ROCKBOX
1484 snprintf(buf, sizeof(buf), "%c", (int) (av[1].a_w.w_float));
1485 #else /* ROCKBOX */
1486 sprintf(buf, "%c", (int)(av[1].a_w.w_float));
1487 #endif /* ROCKBOX */
1488 gotkeysym = gensym(buf);
1490 else gotkeysym = gensym("?");
1491 fflag = (av[0].a_type == A_FLOAT ? av[0].a_w.w_float : 0);
1492 keynum = (av[1].a_type == A_FLOAT ? av[1].a_w.w_float : 0);
1493 if (keynum == '\\' || keynum == '{' || keynum == '}')
1495 post("%c: dropped", (int)keynum);
1496 return;
1498 if (keynum == '\r') keynum = '\n';
1499 if (av[1].a_type == A_SYMBOL &&
1500 !strcmp(av[1].a_w.w_symbol->s_name, "Return"))
1501 keynum = '\n';
1502 if (!keynumsym)
1504 keynumsym = gensym("#key");
1505 keyupsym = gensym("#keyup");
1506 keynamesym = gensym("#keyname");
1508 #ifdef MACOSX
1509 if (keynum == 30)
1510 keynum = 0, gotkeysym = gensym("Up");
1511 else if (keynum == 31)
1512 keynum = 0, gotkeysym = gensym("Down");
1513 else if (keynum == 28)
1514 keynum = 0, gotkeysym = gensym("Left");
1515 else if (keynum == 29)
1516 keynum = 0, gotkeysym = gensym("Right");
1517 #endif
1518 if (keynumsym->s_thing && down)
1519 pd_float(keynumsym->s_thing, (float)keynum);
1520 if (keyupsym->s_thing && !down)
1521 pd_float(keyupsym->s_thing, (float)keynum);
1522 if (keynamesym->s_thing)
1524 t_atom at[2];
1525 at[0] = av[0];
1526 SETFLOAT(at, down);
1527 SETSYMBOL(at+1, gotkeysym);
1528 pd_list(keynamesym->s_thing, 0, 2, at);
1530 if (!x->gl_editor) /* if that 'invis'ed the window, we'd better stop. */
1531 return;
1532 if (x && down)
1534 /* if an object has "grabbed" keys just send them on */
1535 if (x->gl_editor->e_grab
1536 && x->gl_editor->e_keyfn && keynum)
1537 (* x->gl_editor->e_keyfn)
1538 (x->gl_editor->e_grab, (float)keynum);
1539 /* if a text editor is open send it on */
1540 else if (x->gl_editor->e_textedfor)
1542 if (!x->gl_editor->e_textdirty)
1544 canvas_setundo(x, canvas_undo_cut,
1545 canvas_undo_set_cut(x, UCUT_TEXT), "typing");
1547 rtext_key(x->gl_editor->e_textedfor,
1548 (int)keynum, gotkeysym);
1549 if (x->gl_editor->e_textdirty)
1550 canvas_dirty(x, 1);
1552 /* check for backspace or clear */
1553 else if (keynum == 8 || keynum == 127)
1555 if (x->gl_editor->e_selectedline)
1556 canvas_clearline(x);
1557 else if (x->gl_editor->e_selection)
1559 canvas_setundo(x, canvas_undo_cut,
1560 canvas_undo_set_cut(x, UCUT_CLEAR), "clear");
1561 canvas_doclear(x);
1564 /* check for arrow keys */
1565 else if (!strcmp(gotkeysym->s_name, "Up"))
1566 canvas_displaceselection(x, 0, shift ? -10 : -1);
1567 else if (!strcmp(gotkeysym->s_name, "Down"))
1568 canvas_displaceselection(x, 0, shift ? 10 : 1);
1569 else if (!strcmp(gotkeysym->s_name, "Left"))
1570 canvas_displaceselection(x, shift ? -10 : -1, 0);
1571 else if (!strcmp(gotkeysym->s_name, "Right"))
1572 canvas_displaceselection(x, shift ? 10 : 1, 0);
1576 void canvas_motion(t_canvas *x, t_floatarg xpos, t_floatarg ypos,
1577 t_floatarg fmod)
1579 /* post("motion %d %d", xpos, ypos); */
1580 int mod = fmod;
1581 if (!x->gl_editor)
1583 bug("editor");
1584 return;
1586 glist_setlastxy(x, xpos, ypos);
1587 if (x->gl_editor->e_onmotion == MA_MOVE)
1589 canvas_displaceselection(x,
1590 xpos - x->gl_editor->e_xwas, ypos - x->gl_editor->e_ywas);
1591 x->gl_editor->e_xwas = xpos;
1592 x->gl_editor->e_ywas = ypos;
1594 else if (x->gl_editor->e_onmotion == MA_REGION)
1595 canvas_doregion(x, xpos, ypos, 0);
1596 else if (x->gl_editor->e_onmotion == MA_CONNECT)
1597 canvas_doconnect(x, xpos, ypos, 0, 0);
1598 else if (x->gl_editor->e_onmotion == MA_PASSOUT)
1600 if (!x->gl_editor->e_motionfn)
1601 bug("e_motionfn");
1602 (*x->gl_editor->e_motionfn)(&x->gl_editor->e_grab->g_pd,
1603 xpos - x->gl_editor->e_xwas,
1604 ypos - x->gl_editor->e_ywas);
1605 x->gl_editor->e_xwas = xpos;
1606 x->gl_editor->e_ywas = ypos;
1608 else if (x->gl_editor->e_onmotion == MA_DRAGTEXT)
1610 t_rtext *rt = x->gl_editor->e_textedfor;
1611 if (rt)
1612 rtext_mouse(rt, xpos - x->gl_editor->e_xwas,
1613 ypos - x->gl_editor->e_ywas, RTEXT_DRAG);
1615 else canvas_doclick(x, xpos, ypos, 0, mod, 0);
1617 x->gl_editor->e_lastmoved = 1;
1620 void canvas_startmotion(t_canvas *x)
1622 int xval, yval;
1623 if (!x->gl_editor) return;
1624 glist_getnextxy(x, &xval, &yval);
1625 if (xval == 0 && yval == 0) return;
1626 x->gl_editor->e_onmotion = MA_MOVE;
1627 x->gl_editor->e_xwas = xval;
1628 x->gl_editor->e_ywas = yval;
1631 /* ----------------------------- window stuff ----------------------- */
1633 void canvas_print(t_canvas *x, t_symbol *s)
1635 #ifdef ROCKBOX
1636 (void) x;
1637 (void) s;
1638 #else /* ROCKBOX */
1639 if (*s->s_name) sys_vgui(".x%x.c postscript -file %s\n", x, s->s_name);
1640 else sys_vgui(".x%x.c postscript -file x.ps\n", x);
1641 #endif /* ROCKBOX */
1644 void canvas_menuclose(t_canvas *x, t_floatarg force)
1646 if (x->gl_owner)
1647 canvas_vis(x, 0);
1648 else if ((force != 0) || (!x->gl_dirty))
1649 pd_free(&x->gl_pd);
1650 #ifndef ROCKBOX
1651 else sys_vgui("pdtk_check {This window has been modified. Close anyway?}\
1652 {.x%x menuclose 1;\n}\n", x);
1653 #endif
1656 /* put up a dialog which may call canvas_font back to do the work */
1657 static void canvas_menufont(t_canvas *x)
1659 #ifdef ROCKBOX
1660 (void) x;
1661 #else /* ROCKBOX */
1662 char buf[80];
1663 t_canvas *x2 = canvas_getrootfor(x);
1664 gfxstub_deleteforkey(x2);
1665 sprintf(buf, "pdtk_canvas_dofont %%s %d\n", x2->gl_font);
1666 gfxstub_new(&x2->gl_pd, &x2->gl_pd, buf);
1667 #endif /* ROCKBOX */
1670 static int canvas_find_index1, canvas_find_index2;
1671 static t_binbuf *canvas_findbuf;
1672 int binbuf_match(t_binbuf *inbuf, t_binbuf *searchbuf);
1674 /* find an atom or string of atoms */
1675 static int canvas_dofind(t_canvas *x, int *myindex1p)
1677 t_gobj *y;
1678 int myindex1 = *myindex1p, myindex2;
1679 if (myindex1 >= canvas_find_index1)
1681 for (y = x->gl_list, myindex2 = 0; y;
1682 y = y->g_next, myindex2++)
1684 t_object *ob = 0;
1685 if((ob = pd_checkobject(&y->g_pd)))
1687 if (binbuf_match(ob->ob_binbuf, canvas_findbuf))
1689 if (myindex1 > canvas_find_index1 ||
1690 (myindex1 == canvas_find_index1 &&
1691 myindex2 > canvas_find_index2))
1693 canvas_find_index1 = myindex1;
1694 canvas_find_index2 = myindex2;
1695 glist_noselect(x);
1696 canvas_vis(x, 1);
1697 canvas_editmode(x, 1.);
1698 glist_select(x, y);
1699 return (1);
1705 for (y = x->gl_list, myindex2 = 0; y; y = y->g_next, myindex2++)
1707 if (pd_class(&y->g_pd) == canvas_class)
1709 (*myindex1p)++;
1710 if (canvas_dofind((t_canvas *)y, myindex1p))
1711 return (1);
1714 return (0);
1717 static void canvas_find(t_canvas *x, t_symbol *s, int ac, t_atom *av)
1719 int myindex1 = 0, i;
1720 #ifdef ROCKBOX
1721 (void) s;
1722 #endif
1723 for (i = 0; i < ac; i++)
1725 if (av[i].a_type == A_SYMBOL)
1727 if (!strcmp(av[i].a_w.w_symbol->s_name, "_semi_"))
1728 SETSEMI(&av[i]);
1729 else if (!strcmp(av[i].a_w.w_symbol->s_name, "_comma_"))
1730 SETCOMMA(&av[i]);
1733 if (!canvas_findbuf)
1734 canvas_findbuf = binbuf_new();
1735 binbuf_clear(canvas_findbuf);
1736 binbuf_add(canvas_findbuf, ac, av);
1737 canvas_find_index1 = 0;
1738 canvas_find_index2 = -1;
1739 canvas_whichfind = x;
1740 if (!canvas_dofind(x, &myindex1))
1742 binbuf_print(canvas_findbuf);
1743 post("... couldn't find");
1747 static void canvas_find_again(t_canvas *x)
1749 int myindex1 = 0;
1750 #ifdef ROCKBOX
1751 (void) x;
1752 #endif
1753 if (!canvas_findbuf || !canvas_whichfind)
1754 return;
1755 if (!canvas_dofind(canvas_whichfind, &myindex1))
1757 binbuf_print(canvas_findbuf);
1758 post("... couldn't find");
1762 static void canvas_find_parent(t_canvas *x)
1764 if (x->gl_owner)
1765 canvas_vis(glist_getcanvas(x->gl_owner), 1);
1768 static int glist_dofinderror(t_glist *gl, void *error_object)
1770 t_gobj *g;
1771 for (g = gl->gl_list; g; g = g->g_next)
1773 if ((void *)g == error_object)
1775 /* got it... now show it. */
1776 glist_noselect(gl);
1777 canvas_vis(glist_getcanvas(gl), 1);
1778 canvas_editmode(glist_getcanvas(gl), 1.);
1779 glist_select(gl, g);
1780 return (1);
1782 else if (g->g_pd == canvas_class)
1784 if (glist_dofinderror((t_canvas *)g, error_object))
1785 return (1);
1788 return (0);
1791 void canvas_finderror(void *error_object)
1793 t_canvas *x;
1794 /* find all root canvases */
1795 for (x = canvas_list; x; x = x->gl_next)
1797 if (glist_dofinderror(x, error_object))
1798 return;
1800 post("... sorry, I couldn't find the source of that error.");
1803 void canvas_stowconnections(t_canvas *x)
1805 t_gobj *selhead = 0, *seltail = 0, *nonhead = 0, *nontail = 0, *y, *y2;
1806 t_linetraverser t;
1807 t_outconnect *oc;
1808 if (!x->gl_editor) return;
1809 /* split list to "selected" and "unselected" parts */
1810 for (y = x->gl_list; y; y = y2)
1812 y2 = y->g_next;
1813 if (glist_isselected(x, y))
1815 if (seltail)
1817 seltail->g_next = y;
1818 seltail = y;
1819 y->g_next = 0;
1821 else
1823 selhead = seltail = y;
1824 seltail->g_next = 0;
1827 else
1829 if (nontail)
1831 nontail->g_next = y;
1832 nontail = y;
1833 y->g_next = 0;
1835 else
1837 nonhead = nontail = y;
1838 nontail->g_next = 0;
1842 /* move the selected part to the end */
1843 if (!nonhead) x->gl_list = selhead;
1844 else x->gl_list = nonhead, nontail->g_next = selhead;
1846 /* add connections to binbuf */
1847 binbuf_clear(x->gl_editor->e_connectbuf);
1848 linetraverser_start(&t, x);
1849 while((oc = linetraverser_next(&t)))
1851 int s1 = glist_isselected(x, &t.tr_ob->ob_g);
1852 int s2 = glist_isselected(x, &t.tr_ob2->ob_g);
1853 if (s1 != s2)
1854 binbuf_addv(x->gl_editor->e_connectbuf, "ssiiii;",
1855 gensym("#X"), gensym("connect"),
1856 glist_getindex(x, &t.tr_ob->ob_g), t.tr_outno,
1857 glist_getindex(x, &t.tr_ob2->ob_g), t.tr_inno);
1861 void canvas_restoreconnections(t_canvas *x)
1863 pd_bind(&x->gl_pd, gensym("#X"));
1864 binbuf_eval(x->gl_editor->e_connectbuf, 0, 0, 0);
1865 pd_unbind(&x->gl_pd, gensym("#X"));
1868 static t_binbuf *canvas_docopy(t_canvas *x)
1870 t_gobj *y;
1871 t_linetraverser t;
1872 t_outconnect *oc;
1873 t_binbuf *b = binbuf_new();
1874 for (y = x->gl_list; y; y = y->g_next)
1876 if (glist_isselected(x, y))
1877 gobj_save(y, b);
1879 linetraverser_start(&t, x);
1880 while((oc = linetraverser_next(&t)))
1882 if (glist_isselected(x, &t.tr_ob->ob_g)
1883 && glist_isselected(x, &t.tr_ob2->ob_g))
1885 binbuf_addv(b, "ssiiii;", gensym("#X"), gensym("connect"),
1886 glist_selectionindex(x, &t.tr_ob->ob_g, 1), t.tr_outno,
1887 glist_selectionindex(x, &t.tr_ob2->ob_g, 1), t.tr_inno);
1890 return (b);
1893 static void canvas_copy(t_canvas *x)
1895 if (!x->gl_editor || !x->gl_editor->e_selection)
1896 return;
1897 binbuf_free(copy_binbuf);
1898 copy_binbuf = canvas_docopy(x);
1901 static void canvas_clearline(t_canvas *x)
1903 if (x->gl_editor->e_selectedline)
1905 canvas_disconnect(x, x->gl_editor->e_selectline_index1,
1906 x->gl_editor->e_selectline_outno,
1907 x->gl_editor->e_selectline_index2,
1908 x->gl_editor->e_selectline_inno);
1909 canvas_setundo(x, canvas_undo_disconnect,
1910 canvas_undo_set_disconnect(x,
1911 x->gl_editor->e_selectline_index1,
1912 x->gl_editor->e_selectline_outno,
1913 x->gl_editor->e_selectline_index2,
1914 x->gl_editor->e_selectline_inno),
1915 "disconnect");
1919 extern t_pd *newest;
1920 static void canvas_doclear(t_canvas *x)
1922 t_gobj *y, *y2;
1923 int dspstate;
1925 dspstate = canvas_suspend_dsp();
1926 if (x->gl_editor->e_selectedline)
1928 canvas_disconnect(x, x->gl_editor->e_selectline_index1,
1929 x->gl_editor->e_selectline_outno,
1930 x->gl_editor->e_selectline_index2,
1931 x->gl_editor->e_selectline_inno);
1932 canvas_setundo(x, canvas_undo_disconnect,
1933 canvas_undo_set_disconnect(x,
1934 x->gl_editor->e_selectline_index1,
1935 x->gl_editor->e_selectline_outno,
1936 x->gl_editor->e_selectline_index2,
1937 x->gl_editor->e_selectline_inno),
1938 "disconnect");
1940 /* if text is selected, deselecting it might remake the
1941 object. So we deselect it and hunt for a "new" object on
1942 the glist to reselect. */
1943 if (x->gl_editor->e_textedfor)
1945 newest = 0;
1946 glist_noselect(x);
1947 if (newest)
1949 for (y = x->gl_list; y; y = y->g_next)
1950 if (&y->g_pd == newest) glist_select(x, y);
1953 while (1) /* this is pretty wierd... should rewrite it */
1955 for (y = x->gl_list; y; y = y2)
1957 y2 = y->g_next;
1958 if (glist_isselected(x, y))
1960 glist_delete(x, y);
1961 #if 0
1962 if (y2) post("cut 5 %x %x", y2, y2->g_next);
1963 else post("cut 6");
1964 #endif
1965 goto next;
1968 goto restore;
1969 next: ;
1971 restore:
1972 canvas_resume_dsp(dspstate);
1973 canvas_dirty(x, 1);
1976 static void canvas_cut(t_canvas *x)
1978 if (x->gl_editor && x->gl_editor->e_selectedline)
1979 canvas_clearline(x);
1980 else if (x->gl_editor && x->gl_editor->e_selection)
1982 canvas_setundo(x, canvas_undo_cut,
1983 canvas_undo_set_cut(x, UCUT_CUT), "cut");
1984 canvas_copy(x);
1985 canvas_doclear(x);
1989 static int paste_onset;
1990 static t_canvas *paste_canvas;
1992 static void glist_donewloadbangs(t_glist *x)
1994 if (x->gl_editor)
1996 t_selection *sel;
1997 for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
1998 if (pd_class(&sel->sel_what->g_pd) == canvas_class)
1999 canvas_loadbang((t_canvas *)(&sel->sel_what->g_pd));
2003 static void canvas_dopaste(t_canvas *x, t_binbuf *b)
2005 #ifdef ROCKBOX
2006 t_gobj *g2;
2007 #else /* ROCKBOX */
2008 t_gobj *newgobj, *last, *g2;
2009 #endif /* ROCKBOX */
2010 int dspstate = canvas_suspend_dsp(), nbox, count;
2012 canvas_editmode(x, 1.);
2013 glist_noselect(x);
2014 for (g2 = x->gl_list, nbox = 0; g2; g2 = g2->g_next) nbox++;
2016 paste_onset = nbox;
2017 paste_canvas = x;
2019 pd_bind(&x->gl_pd, gensym("#X"));
2020 binbuf_eval(b, 0, 0, 0);
2021 pd_unbind(&x->gl_pd, gensym("#X"));
2022 for (g2 = x->gl_list, count = 0; g2; g2 = g2->g_next, count++)
2023 if (count >= nbox)
2024 glist_select(x, g2);
2025 paste_canvas = 0;
2026 canvas_resume_dsp(dspstate);
2027 canvas_dirty(x, 1);
2028 glist_donewloadbangs(x);
2031 static void canvas_paste(t_canvas *x)
2033 canvas_setundo(x, canvas_undo_paste, canvas_undo_set_paste(x), "paste");
2034 canvas_dopaste(x, copy_binbuf);
2037 static void canvas_duplicate(t_canvas *x)
2039 if (x->gl_editor->e_onmotion == MA_NONE)
2041 t_selection *y;
2042 canvas_copy(x);
2043 canvas_setundo(x, canvas_undo_paste, canvas_undo_set_paste(x),
2044 "duplicate");
2045 canvas_dopaste(x, copy_binbuf);
2046 for (y = x->gl_editor->e_selection; y; y = y->sel_next)
2047 gobj_displace(y->sel_what, x,
2048 10, 10);
2049 canvas_dirty(x, 1);
2053 static void canvas_selectall(t_canvas *x)
2055 t_gobj *y;
2056 if (!x->gl_edit)
2057 canvas_editmode(x, 1);
2058 for (y = x->gl_list; y; y = y->g_next)
2060 if (!glist_isselected(x, y))
2061 glist_select(x, y);
2065 void canvas_connect(t_canvas *x, t_floatarg fwhoout, t_floatarg foutno,
2066 t_floatarg fwhoin, t_floatarg finno)
2068 int whoout = fwhoout, outno = foutno, whoin = fwhoin, inno = finno;
2069 t_gobj *src = 0, *sink = 0;
2070 t_object *objsrc, *objsink;
2071 t_outconnect *oc;
2072 int nin = whoin, nout = whoout;
2073 if (paste_canvas == x) whoout += paste_onset, whoin += paste_onset;
2074 for (src = x->gl_list; whoout; src = src->g_next, whoout--)
2075 if (!src->g_next) goto bad; /* bug fix thanks to Hannes */
2076 for (sink = x->gl_list; whoin; sink = sink->g_next, whoin--)
2077 if (!sink->g_next) goto bad;
2078 if (!(objsrc = pd_checkobject(&src->g_pd)) ||
2079 !(objsink = pd_checkobject(&sink->g_pd)))
2080 goto bad;
2081 if (!(oc = obj_connect(objsrc, outno, objsink, inno))) goto bad;
2082 if (glist_isvisible(x))
2084 #ifndef ROCKBOX
2085 sys_vgui(".x%x.c create line %d %d %d %d -width %d -tags l%x\n",
2086 glist_getcanvas(x), 0, 0, 0, 0,
2087 (obj_issignaloutlet(objsrc, outno) ? 2 : 1),oc);
2088 #endif
2089 canvas_fixlinesfor(x, objsrc);
2091 return;
2093 bad:
2094 post("%s %d %d %d %d (%s->%s) connection failed",
2095 x->gl_name->s_name, nout, outno, nin, inno,
2096 (src? class_getname(pd_class(&src->g_pd)) : "???"),
2097 (sink? class_getname(pd_class(&sink->g_pd)) : "???"));
2100 #define XTOLERANCE 4
2101 #define YTOLERANCE 3
2102 #define NHIST 15
2104 /* LATER might have to speed this up */
2105 static void canvas_tidy(t_canvas *x)
2107 #ifdef ROCKBOX
2108 t_gobj *y, *y2;
2109 #else /* ROCKBOX */
2110 t_gobj *y, *y2, *y3;
2111 #endif /* ROCKBOX */
2112 int ax1, ay1, ax2, ay2, bx1, by1, bx2, by2;
2113 int histogram[NHIST], *ip, i, besthist, bestdist;
2114 /* if nobody is selected, this means do it to all boxes;
2115 othewise just the selection */
2116 int all = (x->gl_editor ? (x->gl_editor->e_selection == 0) : 1);
2118 canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, !all),
2119 "motion");
2121 /* tidy horizontally */
2122 for (y = x->gl_list; y; y = y->g_next)
2123 if (all || glist_isselected(x, y))
2125 gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);
2127 for (y2 = x->gl_list; y2; y2 = y2->g_next)
2128 if (all || glist_isselected(x, y2))
2130 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
2131 if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE &&
2132 bx1 < ax1)
2133 goto nothorizhead;
2136 for (y2 = x->gl_list; y2; y2 = y2->g_next)
2137 if (all || glist_isselected(x, y2))
2139 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
2140 if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE
2141 && by1 != ay1)
2142 gobj_displace(y2, x, 0, ay1-by1);
2144 nothorizhead: ;
2146 /* tidy vertically. First guess the user's favorite vertical spacing */
2147 for (i = NHIST, ip = histogram; i--; ip++) *ip = 0;
2148 for (y = x->gl_list; y; y = y->g_next)
2149 if (all || glist_isselected(x, y))
2151 gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);
2152 for (y2 = x->gl_list; y2; y2 = y2->g_next)
2153 if (all || glist_isselected(x, y2))
2155 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
2156 if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE)
2158 int distance = by1-ay2;
2159 if (distance >= 0 && distance < NHIST)
2160 histogram[distance]++;
2164 for (i = 1, besthist = 0, bestdist = 4, ip = histogram + 1;
2165 i < (NHIST-1); i++, ip++)
2167 int hit = ip[-1] + 2 * ip[0] + ip[1];
2168 if (hit > besthist)
2170 besthist = hit;
2171 bestdist = i;
2174 post("best vertical distance %d", bestdist);
2175 for (y = x->gl_list; y; y = y->g_next)
2176 if (all || glist_isselected(x, y))
2178 int keep = 1;
2179 gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);
2180 for (y2 = x->gl_list; y2; y2 = y2->g_next)
2181 if (all || glist_isselected(x, y2))
2183 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
2184 if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE &&
2185 ay1 >= by2 - 10 && ay1 < by2 + NHIST)
2186 goto nothead;
2188 while (keep)
2190 keep = 0;
2191 for (y2 = x->gl_list; y2; y2 = y2->g_next)
2192 if (all || glist_isselected(x, y2))
2194 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
2195 if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE &&
2196 by1 > ay1 && by1 < ay2 + NHIST)
2198 int vmove = ay2 + bestdist - by1;
2199 gobj_displace(y2, x, ax1-bx1, vmove);
2200 ay1 = by1 + vmove;
2201 ay2 = by2 + vmove;
2202 keep = 1;
2203 break;
2207 nothead: ;
2209 canvas_dirty(x, 1);
2212 static void canvas_texteditor(t_canvas *x)
2214 t_rtext *foo;
2215 char *buf;
2216 int bufsize;
2217 if((foo = x->gl_editor->e_textedfor))
2218 rtext_gettext(foo, &buf, &bufsize);
2219 else buf = "", bufsize = 0;
2220 #ifndef ROCKBOX
2221 sys_vgui("pdtk_pd_texteditor {%.*s}\n", bufsize, buf);
2222 #endif
2225 void glob_key(void *dummy, t_symbol *s, int ac, t_atom *av)
2227 #ifdef ROCKBOX
2228 (void) dummy;
2229 #endif
2230 /* canvas_editing can be zero; canvas_key checks for that */
2231 canvas_key(canvas_editing, s, ac, av);
2234 void canvas_editmode(t_canvas *x, t_floatarg fyesplease)
2236 int yesplease = fyesplease;
2237 if (yesplease && x->gl_edit)
2238 return;
2239 x->gl_edit = !x->gl_edit;
2240 if (x->gl_edit && glist_isvisible(x) && glist_istoplevel(x))
2241 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
2242 else
2244 glist_noselect(x);
2245 if (glist_isvisible(x) && glist_istoplevel(x))
2246 canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
2248 #ifndef ROCKBOX
2249 sys_vgui("pdtk_canvas_editval .x%x %d\n",
2250 glist_getcanvas(x), x->gl_edit);
2251 #endif
2254 /* called by canvas_font below */
2255 static void canvas_dofont(t_canvas *x, t_floatarg font, t_floatarg xresize,
2256 t_floatarg yresize)
2258 t_gobj *y;
2259 x->gl_font = font;
2260 if (xresize != 1 || yresize != 1)
2262 canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, 0),
2263 "motion");
2264 for (y = x->gl_list; y; y = y->g_next)
2266 int x1, x2, y1, y2, nx1, ny1;
2267 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
2268 nx1 = x1 * xresize + 0.5;
2269 ny1 = y1 * yresize + 0.5;
2270 gobj_displace(y, x, nx1-x1, ny1-y1);
2273 if (glist_isvisible(x))
2274 glist_redraw(x);
2275 for (y = x->gl_list; y; y = y->g_next)
2276 if (pd_class(&y->g_pd) == canvas_class
2277 && !canvas_isabstraction((t_canvas *)y))
2278 canvas_dofont((t_canvas *)y, font, xresize, yresize);
2281 /* canvas_menufont calls up a TK dialog which calls this back */
2282 static void canvas_font(t_canvas *x, t_floatarg font, t_floatarg resize,
2283 t_floatarg whichresize)
2285 float realresize, realresx = 1, realresy = 1;
2286 t_canvas *x2 = canvas_getrootfor(x);
2287 if (!resize) realresize = 1;
2288 else
2290 if (resize < 20) resize = 20;
2291 if (resize > 500) resize = 500;
2292 realresize = resize * 0.01;
2294 if (whichresize != 3) realresx = realresize;
2295 if (whichresize != 2) realresy = realresize;
2296 canvas_dofont(x2, font, realresx, realresy);
2297 #ifndef ROCKBOX
2298 sys_defaultfont = font;
2299 #endif
2302 static t_glist *canvas_last_glist;
2303 static int canvas_last_glist_x, canvas_last_glist_y;
2305 void glist_getnextxy(t_glist *gl, int *xpix, int *ypix)
2307 if (canvas_last_glist == gl)
2308 *xpix = canvas_last_glist_x, *ypix = canvas_last_glist_y;
2309 else *xpix = *ypix = 40;
2312 static void glist_setlastxy(t_glist *gl, int xval, int yval)
2314 canvas_last_glist = gl;
2315 canvas_last_glist_x = xval;
2316 canvas_last_glist_y = yval;
2320 void g_editor_setup(void)
2322 /* ------------------------ events ---------------------------------- */
2323 class_addmethod(canvas_class, (t_method)canvas_mousedown, gensym("mouse"),
2324 A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2325 class_addmethod(canvas_class, (t_method)canvas_mouseup, gensym("mouseup"),
2326 A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2327 class_addmethod(canvas_class, (t_method)canvas_key, gensym("key"),
2328 A_GIMME, A_NULL);
2329 class_addmethod(canvas_class, (t_method)canvas_motion, gensym("motion"),
2330 A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2332 /* ------------------------ menu actions ---------------------------- */
2333 class_addmethod(canvas_class, (t_method)canvas_menuclose,
2334 gensym("menuclose"), A_DEFFLOAT, 0);
2335 class_addmethod(canvas_class, (t_method)canvas_cut,
2336 gensym("cut"), A_NULL);
2337 class_addmethod(canvas_class, (t_method)canvas_copy,
2338 gensym("copy"), A_NULL);
2339 class_addmethod(canvas_class, (t_method)canvas_paste,
2340 gensym("paste"), A_NULL);
2341 class_addmethod(canvas_class, (t_method)canvas_duplicate,
2342 gensym("duplicate"), A_NULL);
2343 class_addmethod(canvas_class, (t_method)canvas_selectall,
2344 gensym("selectall"), A_NULL);
2345 class_addmethod(canvas_class, (t_method)canvas_undo,
2346 gensym("undo"), A_NULL);
2347 class_addmethod(canvas_class, (t_method)canvas_redo,
2348 gensym("redo"), A_NULL);
2349 class_addmethod(canvas_class, (t_method)canvas_tidy,
2350 gensym("tidy"), A_NULL);
2351 class_addmethod(canvas_class, (t_method)canvas_texteditor,
2352 gensym("texteditor"), A_NULL);
2353 class_addmethod(canvas_class, (t_method)canvas_editmode,
2354 gensym("editmode"), A_DEFFLOAT, A_NULL);
2355 class_addmethod(canvas_class, (t_method)canvas_print,
2356 gensym("print"), A_SYMBOL, A_NULL);
2357 class_addmethod(canvas_class, (t_method)canvas_menufont,
2358 gensym("menufont"), A_NULL);
2359 class_addmethod(canvas_class, (t_method)canvas_font,
2360 gensym("font"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2361 class_addmethod(canvas_class, (t_method)canvas_find,
2362 gensym("find"), A_GIMME, A_NULL);
2363 class_addmethod(canvas_class, (t_method)canvas_find_again,
2364 gensym("findagain"), A_NULL);
2365 class_addmethod(canvas_class, (t_method)canvas_find_parent,
2366 gensym("findparent"), A_NULL);
2367 class_addmethod(canvas_class, (t_method)canvas_done_popup,
2368 gensym("done-popup"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2369 class_addmethod(canvas_class, (t_method)canvas_donecanvasdialog,
2370 gensym("donecanvasdialog"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2371 class_addmethod(canvas_class, (t_method)glist_arraydialog,
2372 gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2374 /* -------------- connect method used in reading files ------------------ */
2375 class_addmethod(canvas_class, (t_method)canvas_connect,
2376 gensym("connect"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2378 class_addmethod(canvas_class, (t_method)canvas_disconnect,
2379 gensym("disconnect"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2380 /* -------------- copy buffer ------------------ */
2381 copy_binbuf = binbuf_new();