A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
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#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 */
21
22void glist_readfrombinbuf(t_glist *x, t_binbuf *b, char *filename,
23 int selectem);
24
25void open_via_helppath(const char *name, const char *dir);
26char *class_gethelpdir(t_class *c);
27
28/* ------------------ forward declarations --------------- */
29static void canvas_doclear(t_canvas *x);
30static void glist_setlastxy(t_glist *gl, int xval, int yval);
31static void glist_donewloadbangs(t_glist *x);
32static t_binbuf *canvas_docopy(t_canvas *x);
33static void canvas_dopaste(t_canvas *x, t_binbuf *b);
34static void canvas_paste(t_canvas *x);
35static void canvas_clearline(t_canvas *x);
36static t_binbuf *copy_binbuf;
37
38/* ---------------- generic widget behavior ------------------------- */
39
40void gobj_getrect(t_gobj *x, t_glist *glist, int *x1, int *y1,
41 int *x2, int *y2)
42{
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);
45}
46
47void gobj_displace(t_gobj *x, t_glist *glist, int dx, int dy)
48{
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);
51}
52
53void gobj_select(t_gobj *x, t_glist *glist, int state)
54{
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);
57}
58
59void gobj_activate(t_gobj *x, t_glist *glist, int state)
60{
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);
63}
64
65void gobj_delete(t_gobj *x, t_glist *glist)
66{
67 if (x->g_pd->c_wb && x->g_pd->c_wb->w_deletefn)
68 (*x->g_pd->c_wb->w_deletefn)(x, glist);
69}
70
71void gobj_vis(t_gobj *x, struct _glist *glist, int flag)
72{
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);
75}
76
77int gobj_click(t_gobj *x, struct _glist *glist,
78 int xpix, int ypix, int shift, int alt, int dbl, int doit)
79{
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);
84}
85
86/* ------------------------ managing the selection ----------------- */
87
88void glist_selectline(t_glist *x, t_outconnect *oc, int index1,
89 int outno, int index2, int inno)
90{
91 if (x->gl_editor)
92 {
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
104 }
105}
106
107void glist_deselectline(t_glist *x)
108{
109 if (x->gl_editor)
110 {
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
116 }
117}
118
119int glist_isselected(t_glist *x, t_gobj *y)
120{
121 if (x->gl_editor)
122 {
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);
126 }
127 return (0);
128}
129
130 /* call this for unselected objects only */
131void glist_select(t_glist *x, t_gobj *y)
132{
133 if (x->gl_editor)
134 {
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);
144 }
145}
146
147 /* call this for selected objects only */
148void glist_deselect(t_glist *x, t_gobj *y)
149{
150 int fixdsp = 0;
151 static int reenter = 0;
152 if (reenter) return;
153 reenter = 1;
154 if (x->gl_editor)
155 {
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)
160 {
161 t_rtext *fuddy = glist_findrtext(x, (t_text *)y);
162 if (x->gl_editor->e_textedfor == fuddy)
163 {
164 if (x->gl_editor->e_textdirty)
165 {
166 z = fuddy;
167 canvas_stowconnections(glist_getcanvas(x));
168 }
169 gobj_activate(y, x, 0);
170 }
171 if (zgetfn(&y->g_pd, gensym("dsp")))
172 fixdsp = 1;
173 }
174 if ((sel = x->gl_editor->e_selection)->sel_what == y)
175 {
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));
179 }
180 else
181 {
182 for(sel = x->gl_editor->e_selection; (sel2 = sel->sel_next);
183 sel = sel2)
184 {
185 if (sel2->sel_what == y)
186 {
187 sel->sel_next = sel2->sel_next;
188 gobj_select(sel2->sel_what, x, 0);
189 freebytes(sel2, sizeof(*sel2));
190 break;
191 }
192 }
193 }
194 if (z)
195 {
196 char *buf;
197 int bufsize;
198
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;
203 }
204 if (fixdsp)
205 canvas_update_dsp();
206 }
207 reenter = 0;
208}
209
210void glist_noselect(t_glist *x)
211{
212 if (x->gl_editor)
213 {
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);
218 }
219}
220
221void glist_selectall(t_glist *x)
222{
223 if (x->gl_editor)
224 {
225 glist_noselect(x);
226 if (x->gl_list)
227 {
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))
234 {
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);
240 }
241 sel->sel_next = 0;
242 }
243 }
244}
245
246 /* get the index of a gobj in a glist. If y is zero, return the
247 total number of objects. */
248int glist_getindex(t_glist *x, t_gobj *y)
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 indx++;
255 return (indx);
256}
257
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. */
261int glist_selectionindex(t_glist *x, t_gobj *y, int selected)
262{
263 t_gobj *y2;
264 int indx;
265
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);
270}
271
272static t_gobj *glist_nth(t_glist *x, int n)
273{
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);
280}
281
282/* ------------------- support for undo/redo -------------------------- */
283
284static t_undofn canvas_undo_fn; /* current undo function if any */
285static int canvas_undo_whatnext; /* whether we can now UNDO or REDO */
286static void *canvas_undo_buf; /* data private to the undo function */
287static t_canvas *canvas_undo_canvas; /* which canvas we can undo on */
288static const char *canvas_undo_name;
289
290void canvas_setundo(t_canvas *x, t_undofn undofn, void *buf,
291 const char *name)
292{
293#ifndef ROCKBOX
294 int hadone = 0;
295#endif
296 /* blow away the old undo information. In one special case the
297 old undo info is re-used; if so we shouldn't free it here. */
298 if (canvas_undo_fn && canvas_undo_buf && (buf != canvas_undo_buf))
299 {
300 (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_FREE);
301#ifndef ROCKBOX
302 hadone = 1;
303#endif
304 }
305 canvas_undo_canvas = x;
306 canvas_undo_fn = undofn;
307 canvas_undo_buf = buf;
308 canvas_undo_whatnext = UNDO_UNDO;
309 canvas_undo_name = name;
310#ifndef ROCKBOX
311 if (x && glist_isvisible(x) && glist_istoplevel(x))
312 /* enable undo in menu */
313 sys_vgui("pdtk_undomenu .x%x %s no\n", x, name);
314 else if (hadone)
315 sys_vgui("pdtk_undomenu nobody no no\n");
316#endif
317}
318
319 /* clear undo if it happens to be for the canvas x.
320 (but if x is 0, clear it regardless of who owns it.) */
321void canvas_noundo(t_canvas *x)
322{
323 if (!x || (x == canvas_undo_canvas))
324 canvas_setundo(0, 0, 0, "foo");
325}
326
327static void canvas_undo(t_canvas *x)
328{
329 if (x != canvas_undo_canvas)
330 bug("canvas_undo 1");
331 else if (canvas_undo_whatnext != UNDO_UNDO)
332 bug("canvas_undo 2");
333 else
334 {
335 /* post("undo"); */
336 (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_UNDO);
337 /* enable redo in menu */
338#ifndef ROCKBOX
339 if (glist_isvisible(x) && glist_istoplevel(x))
340 sys_vgui("pdtk_undomenu .x%x no %s\n", x, canvas_undo_name);
341#endif
342 canvas_undo_whatnext = UNDO_REDO;
343 }
344}
345
346static void canvas_redo(t_canvas *x)
347{
348 if (x != canvas_undo_canvas)
349 bug("canvas_undo 1");
350 else if (canvas_undo_whatnext != UNDO_REDO)
351 bug("canvas_undo 2");
352 else
353 {
354 /* post("redo"); */
355 (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_REDO);
356 /* enable undo in menu */
357#ifndef ROCKBOX
358 if (glist_isvisible(x) && glist_istoplevel(x))
359 sys_vgui("pdtk_undomenu .x%x %s no\n", x, canvas_undo_name);
360#endif
361 canvas_undo_whatnext = UNDO_UNDO;
362 }
363}
364
365/* ------- specific undo methods: 1. connect and disconnect -------- */
366
367typedef struct _undo_connect
368{
369 int u_index1;
370 int u_outletno;
371 int u_index2;
372 int u_inletno;
373} t_undo_connect;
374
375static void *canvas_undo_set_disconnect(t_canvas *x,
376 int index1, int outno, int index2, int inno)
377{
378#ifdef ROCKBOX
379 (void) x;
380#endif
381 t_undo_connect *buf = (t_undo_connect *)getbytes(sizeof(*buf));
382 buf->u_index1 = index1;
383 buf->u_outletno = outno;
384 buf->u_index2 = index2;
385 buf->u_inletno = inno;
386 return (buf);
387}
388
389void canvas_disconnect(t_canvas *x,
390 float index1, float outno, float index2, float inno)
391{
392 t_linetraverser t;
393 t_outconnect *oc;
394 linetraverser_start(&t, x);
395 while((oc = linetraverser_next(&t)))
396 {
397 int srcno = canvas_getindex(x, &t.tr_ob->ob_g);
398 int sinkno = canvas_getindex(x, &t.tr_ob2->ob_g);
399 if (srcno == index1 && t.tr_outno == outno &&
400 sinkno == index2 && t.tr_inno == inno)
401 {
402#ifndef ROCKBOX
403 sys_vgui(".x%x.c delete l%x\n", x, oc);
404#endif
405 obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);
406 break;
407 }
408 }
409}
410
411static void canvas_undo_disconnect(t_canvas *x, void *z, int action)
412{
413 t_undo_connect *buf = z;
414 if (action == UNDO_UNDO)
415 {
416 canvas_connect(x, buf->u_index1, buf->u_outletno,
417 buf->u_index2, buf->u_inletno);
418 }
419 else if (action == UNDO_REDO)
420 {
421 canvas_disconnect(x, buf->u_index1, buf->u_outletno,
422 buf->u_index2, buf->u_inletno);
423 }
424 else if (action == UNDO_FREE)
425 t_freebytes(buf, sizeof(*buf));
426}
427
428 /* connect just calls disconnect actions backward... */
429static void *canvas_undo_set_connect(t_canvas *x,
430 int index1, int outno, int index2, int inno)
431{
432 return (canvas_undo_set_disconnect(x, index1, outno, index2, inno));
433}
434
435static void canvas_undo_connect(t_canvas *x, void *z, int action)
436{
437 int myaction;
438 if (action == UNDO_UNDO)
439 myaction = UNDO_REDO;
440 else if (action == UNDO_REDO)
441 myaction = UNDO_UNDO;
442 else myaction = action;
443 canvas_undo_disconnect(x, z, myaction);
444}
445
446/* ---------- ... 2. cut, clear, and typing into objects: -------- */
447
448#define UCUT_CUT 1 /* operation was a cut */
449#define UCUT_CLEAR 2 /* .. a clear */
450#define UCUT_TEXT 3 /* text typed into a box */
451
452typedef struct _undo_cut
453{
454 t_binbuf *u_objectbuf; /* the object cleared or typed into */
455 t_binbuf *u_reconnectbuf; /* connections into and out of object */
456 t_binbuf *u_redotextbuf; /* buffer to paste back for redo if TEXT */
457 int u_mode; /* from flags above */
458} t_undo_cut;
459
460static void *canvas_undo_set_cut(t_canvas *x, int mode)
461{
462 t_undo_cut *buf;
463#ifndef ROCKBOX
464 t_gobj *y;
465#endif
466 t_linetraverser t;
467 t_outconnect *oc;
468 int nnotsel= glist_selectionindex(x, 0, 0);
469 buf = (t_undo_cut *)getbytes(sizeof(*buf));
470 buf->u_mode = mode;
471 buf->u_redotextbuf = 0;
472
473 /* store connections into/out of the selection */
474 buf->u_reconnectbuf = binbuf_new();
475 linetraverser_start(&t, x);
476 while((oc = linetraverser_next(&t)))
477 {
478 int issel1 = glist_isselected(x, &t.tr_ob->ob_g);
479 int issel2 = glist_isselected(x, &t.tr_ob2->ob_g);
480 if (issel1 != issel2)
481 {
482 binbuf_addv(buf->u_reconnectbuf, "ssiiii;",
483 gensym("#X"), gensym("connect"),
484 (issel1 ? nnotsel : 0)
485 + glist_selectionindex(x, &t.tr_ob->ob_g, issel1),
486 t.tr_outno,
487 (issel2 ? nnotsel : 0) +
488 glist_selectionindex(x, &t.tr_ob2->ob_g, issel2),
489 t.tr_inno);
490 }
491 }
492 if (mode == UCUT_TEXT)
493 {
494 buf->u_objectbuf = canvas_docopy(x);
495 }
496 else if (mode == UCUT_CUT)
497 {
498 buf->u_objectbuf = 0;
499 }
500 else if (mode == UCUT_CLEAR)
501 {
502 buf->u_objectbuf = canvas_docopy(x);
503 }
504 return (buf);
505}
506
507static void canvas_undo_cut(t_canvas *x, void *z, int action)
508{
509 t_undo_cut *buf = z;
510 int mode = buf->u_mode;
511 if (action == UNDO_UNDO)
512 {
513 if (mode == UCUT_CUT)
514 canvas_dopaste(x, copy_binbuf);
515 else if (mode == UCUT_CLEAR)
516 canvas_dopaste(x, buf->u_objectbuf);
517 else if (mode == UCUT_TEXT)
518 {
519 t_gobj *y1, *y2;
520 glist_noselect(x);
521 for(y1 = x->gl_list; (y2 = y1->g_next); y1 = y2)
522 ;
523 if (y1)
524 {
525 if (!buf->u_redotextbuf)
526 {
527 glist_noselect(x);
528 glist_select(x, y1);
529 buf->u_redotextbuf = canvas_docopy(x);
530 glist_noselect(x);
531 }
532 glist_delete(x, y1);
533 }
534 canvas_dopaste(x, buf->u_objectbuf);
535 }
536 pd_bind(&x->gl_pd, gensym("#X"));
537 binbuf_eval(buf->u_reconnectbuf, 0, 0, 0);
538 pd_unbind(&x->gl_pd, gensym("#X"));
539 }
540 else if (action == UNDO_REDO)
541 {
542 if (mode == UCUT_CUT || mode == UCUT_CLEAR)
543 canvas_doclear(x);
544 else if (mode == UCUT_TEXT)
545 {
546 t_gobj *y1, *y2;
547 for(y1 = x->gl_list; (y2 = y1->g_next); y1 = y2)
548 ;
549 if (y1)
550 glist_delete(x, y1);
551 canvas_dopaste(x, buf->u_redotextbuf);
552 pd_bind(&x->gl_pd, gensym("#X"));
553 binbuf_eval(buf->u_reconnectbuf, 0, 0, 0);
554 pd_unbind(&x->gl_pd, gensym("#X"));
555 }
556 }
557 else if (action == UNDO_FREE)
558 {
559 if (buf->u_objectbuf)
560 binbuf_free(buf->u_objectbuf);
561 if (buf->u_reconnectbuf)
562 binbuf_free(buf->u_reconnectbuf);
563 if (buf->u_redotextbuf)
564 binbuf_free(buf->u_redotextbuf);
565 t_freebytes(buf, sizeof(*buf));
566 }
567}
568
569/* --------- 3. motion, including "tidy up" and stretching ----------- */
570
571typedef struct _undo_move_elem
572{
573 int e_index;
574 int e_xpix;
575 int e_ypix;
576} t_undo_move_elem;
577
578typedef struct _undo_move
579{
580 t_undo_move_elem *u_vec;
581 int u_n;
582} t_undo_move;
583
584static int canvas_undo_already_set_move;
585
586static void *canvas_undo_set_move(t_canvas *x, int selected)
587{
588 int x1, y1, x2, y2, i, indx;
589 t_gobj *y;
590 t_undo_move *buf = (t_undo_move *)getbytes(sizeof(*buf));
591 buf->u_n = selected ? glist_selectionindex(x, 0, 1) : glist_getindex(x, 0);
592 buf->u_vec = (t_undo_move_elem *)getbytes(sizeof(*buf->u_vec) *
593 (selected ? glist_selectionindex(x, 0, 1) : glist_getindex(x, 0)));
594 if (selected)
595 {
596 for (y = x->gl_list, i = indx = 0; y; y = y->g_next, indx++)
597 if (glist_isselected(x, y))
598 {
599 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
600 buf->u_vec[i].e_index = indx;
601 buf->u_vec[i].e_xpix = x1;
602 buf->u_vec[i].e_ypix = y1;
603 i++;
604 }
605 }
606 else
607 {
608 for (y = x->gl_list, indx = 0; y; y = y->g_next, indx++)
609 {
610 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
611 buf->u_vec[indx].e_index = indx;
612 buf->u_vec[indx].e_xpix = x1;
613 buf->u_vec[indx].e_ypix = y1;
614 }
615 }
616 canvas_undo_already_set_move = 1;
617 return (buf);
618}
619
620static void canvas_undo_move(t_canvas *x, void *z, int action)
621{
622 t_undo_move *buf = z;
623 if (action == UNDO_UNDO || action == UNDO_REDO)
624 {
625 int i;
626 for (i = 0; i < buf->u_n; i++)
627 {
628 int x1, y1, x2, y2, newx, newy;
629 t_gobj *y;
630 newx = buf->u_vec[i].e_xpix;
631 newy = buf->u_vec[i].e_ypix;
632 y = glist_nth(x, buf->u_vec[i].e_index);
633 if (y)
634 {
635 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
636 gobj_displace(y, x, newx-x1, newy - y1);
637 buf->u_vec[i].e_xpix = x1;
638 buf->u_vec[i].e_ypix = y1;
639 }
640 }
641 }
642 else if (action == UNDO_FREE)
643 {
644 t_freebytes(buf->u_vec, buf->u_n * sizeof(*buf->u_vec));
645 t_freebytes(buf, sizeof(*buf));
646 }
647}
648
649/* --------- 4. paste (also duplicate) ----------- */
650
651typedef struct _undo_paste
652{
653 int u_index; /* index of first object pasted */
654} t_undo_paste;
655
656static void *canvas_undo_set_paste(t_canvas *x)
657{
658 t_undo_paste *buf = (t_undo_paste *)getbytes(sizeof(*buf));
659 buf->u_index = glist_getindex(x, 0);
660 return (buf);
661}
662
663static void canvas_undo_paste(t_canvas *x, void *z, int action)
664{
665 t_undo_paste *buf = z;
666 if (action == UNDO_UNDO)
667 {
668 t_gobj *y;
669 glist_noselect(x);
670 for (y = glist_nth(x, buf->u_index); y; y = y->g_next)
671 glist_select(x, y);
672 canvas_doclear(x);
673 }
674 else if (action == UNDO_REDO)
675 {
676 t_selection *sel;
677 canvas_dopaste(x, copy_binbuf);
678 /* if it was "duplicate" have to re-enact the displacement. */
679 if (canvas_undo_name && canvas_undo_name[0] == 'd')
680 for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
681 gobj_displace(sel->sel_what, x, 10, 10);
682 }
683else if (action == UNDO_FREE)
684 t_freebytes(buf, sizeof(*buf));
685}
686
687 /* recursively check for abstractions to reload as result of a save.
688 Don't reload the one we just saved ("except") though. */
689 /* LATER try to do the same trick for externs. */
690static void glist_doreload(t_glist *gl, t_symbol *name, t_symbol *dir,
691 t_gobj *except)
692{
693 t_gobj *g;
694 int i, nobj = glist_getindex(gl, 0); /* number of objects */
695 for (g = gl->gl_list, i = 0; g && i < nobj; i++)
696 {
697 if (g != except && pd_class(&g->g_pd) == canvas_class &&
698 canvas_isabstraction((t_canvas *)g) &&
699 ((t_canvas *)g)->gl_name == name &&
700 canvas_getdir((t_canvas *)g) == dir)
701 {
702 /* we're going to remake the object, so "g" will go stale.
703 Get its index here, and afterward restore g. Also, the
704 replacement will be at teh end of the list, so we don't
705 do g = g->g_next in this case. */
706 int j = glist_getindex(gl, g);
707 if (!gl->gl_havewindow)
708 canvas_vis(glist_getcanvas(gl), 1);
709 glist_noselect(gl);
710 glist_select(gl, g);
711 canvas_setundo(gl, canvas_undo_cut,
712 canvas_undo_set_cut(gl, UCUT_CLEAR), "clear");
713 canvas_doclear(gl);
714 canvas_undo(gl);
715 glist_noselect(gl);
716 g = glist_nth(gl, j);
717 }
718 else
719 {
720 if (g != except && pd_class(&g->g_pd) == canvas_class)
721 glist_doreload((t_canvas *)g, name, dir, except);
722 g = g->g_next;
723 }
724 }
725}
726
727 /* call canvas_doreload on everyone */
728void canvas_reload(t_symbol *name, t_symbol *dir, t_gobj *except)
729{
730 t_canvas *x;
731 /* find all root canvases */
732 for (x = canvas_list; x; x = x->gl_next)
733 glist_doreload(x, name, dir, except);
734}
735
736/* ------------------------ event handling ------------------------ */
737
738#define CURSOR_RUNMODE_NOTHING 0
739#define CURSOR_RUNMODE_CLICKME 1
740#define CURSOR_RUNMODE_THICKEN 2
741#define CURSOR_RUNMODE_ADDPOINT 3
742#define CURSOR_EDITMODE_NOTHING 4
743#define CURSOR_EDITMODE_CONNECT 5
744#define CURSOR_EDITMODE_DISCONNECT 6
745
746static char *cursorlist[] = {
747#ifdef MSW
748 "right_ptr", /* CURSOR_RUNMODE_NOTHING */
749#else
750 "left_ptr", /* CURSOR_RUNMODE_NOTHING */
751#endif
752 "arrow", /* CURSOR_RUNMODE_CLICKME */
753 "sb_v_double_arrow", /* CURSOR_RUNMODE_THICKEN */
754 "plus", /* CURSOR_RUNMODE_ADDPOINT */
755 "hand2", /* CURSOR_EDITMODE_NOTHING */
756 "circle", /* CURSOR_EDITMODE_CONNECT */
757 "X_cursor" /* CURSOR_EDITMODE_DISCONNECT */
758};
759
760void canvas_setcursor(t_canvas *x, unsigned int cursornum)
761{
762 static t_canvas *xwas;
763 static unsigned int cursorwas;
764 if (cursornum >= sizeof(cursorlist)/sizeof *cursorlist)
765 {
766 bug("canvas_setcursor");
767 return;
768 }
769 if (xwas != x || cursorwas != cursornum)
770 {
771#ifndef ROCKBOX
772 sys_vgui(".x%x configure -cursor %s\n", x, cursorlist[cursornum]);
773#endif
774 xwas = x;
775 cursorwas = cursornum;
776 }
777}
778
779 /* check if a point lies in a gobj. */
780int canvas_hitbox(t_canvas *x, t_gobj *y, int xpos, int ypos,
781 int *x1p, int *y1p, int *x2p, int *y2p)
782{
783 int x1, y1, x2, y2;
784 t_text *ob;
785 if ((ob = pd_checkobject(&y->g_pd)) &&
786 !text_shouldvis(ob, x))
787 return (0);
788 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
789 if (xpos >= x1 && xpos <= x2 && ypos >= y1 && ypos <= y2)
790 {
791 *x1p = x1;
792 *y1p = y1;
793 *x2p = x2;
794 *y2p = y2;
795 return (1);
796 }
797 else return (0);
798}
799
800 /* find the last gobj, if any, containing the point. */
801static t_gobj *canvas_findhitbox(t_canvas *x, int xpos, int ypos,
802 int *x1p, int *y1p, int *x2p, int *y2p)
803{
804 t_gobj *y, *rval = 0;
805 for (y = x->gl_list; y; y = y->g_next)
806 {
807 if (canvas_hitbox(x, y, xpos, ypos, x1p, y1p, x2p, y2p))
808 rval = y;
809 }
810 return (rval);
811}
812
813 /* right-clicking on a canvas object pops up a menu. */
814static void canvas_rightclick(t_canvas *x, int xpos, int ypos, t_gobj *y)
815{
816#ifdef ROCKBOX
817 (void) x;
818 (void) xpos;
819 (void) ypos;
820 (void) y;
821#else /* ROCKBOX */
822 int canprop, canopen;
823 canprop = (!y || (y && class_getpropertiesfn(pd_class(&y->g_pd))));
824 canopen = (y && zgetfn(&y->g_pd, gensym("menu-open")));
825 sys_vgui("pdtk_canvas_popup .x%x %d %d %d %d\n",
826 x, xpos, ypos, canprop, canopen);
827#endif /* ROCKBOX */
828}
829
830 /* tell GUI to create a properties dialog on the canvas. We tell
831 the user the negative of the "pixel" y scale to make it appear to grow
832 naturally upward, whereas pixels grow downward. */
833static void canvas_properties(t_glist *x)
834{
835#ifdef ROCKBOX
836 (void) x;
837#else /* ROCKBOX */
838 char graphbuf[200];
839 sprintf(graphbuf, "pdtk_canvas_dialog %%s %g %g %g %g \n",
840 glist_dpixtodx(x, 1), -glist_dpixtody(x, 1),
841 (float)glist_isgraph(x), (float)x->gl_stretch);
842 gfxstub_new(&x->gl_pd, x, graphbuf);
843#endif /* ROCKBOX */
844}
845
846
847void canvas_setgraph(t_glist *x, int flag)
848{
849 if (!flag && glist_isgraph(x))
850 {
851 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
852 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
853 x->gl_isgraph = 0;
854 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
855 {
856 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
857 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
858 }
859 }
860 else if (flag && !glist_isgraph(x))
861 {
862 if (x->gl_pixwidth <= 0)
863 x->gl_pixwidth = GLIST_DEFGRAPHWIDTH;
864
865 if (x->gl_pixheight <= 0)
866 x->gl_pixheight = GLIST_DEFGRAPHHEIGHT;
867
868 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
869 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
870 x->gl_isgraph = 1;
871 /* if (x->gl_owner && glist_isvisible(x->gl_owner))
872 canvas_vis(x, 1); */
873 if (x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner))
874 canvas_create_editor(x, 1);
875 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
876 {
877 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
878 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
879 }
880 }
881}
882
883 /* called from the gui when "OK" is selected on the canvas properties
884 dialog. Again we negate "y" scale. */
885static void canvas_donecanvasdialog(t_glist *x, t_floatarg xperpix,
886 t_floatarg yperpix, t_floatarg fgraphme)
887{
888 int graphme = (fgraphme != 0), redraw = 0;
889 yperpix = -yperpix;
890 if (xperpix == 0)
891 xperpix = 1;
892 if (yperpix == 0)
893 yperpix = 1;
894 canvas_setgraph(x, graphme);
895 if (!x->gl_isgraph && (xperpix != glist_dpixtodx(x, 1)))
896 {
897 if (xperpix > 0)
898 {
899 x->gl_x1 = 0;
900 x->gl_x2 = xperpix;
901 }
902 else
903 {
904 x->gl_x1 = -xperpix * (x->gl_screenx2 - x->gl_screenx1);
905 x->gl_x2 = x->gl_x1 + xperpix;
906 }
907 redraw = 1;
908 }
909 if (!x->gl_isgraph && (yperpix != glist_dpixtody(x, 1)))
910 {
911 if (yperpix > 0)
912 {
913 x->gl_y1 = 0;
914 x->gl_y2 = yperpix;
915 }
916 else
917 {
918 x->gl_y1 = -yperpix * (x->gl_screeny2 - x->gl_screeny1);
919 x->gl_y2 = x->gl_y1 + yperpix;
920 }
921 redraw = 1;
922 }
923 if (redraw)
924 canvas_redraw(x);
925}
926
927 /* called from the gui when a popup menu comes back with "properties,"
928 "open," or "help." */
929static void canvas_done_popup(t_canvas *x, float which, float xpos, float ypos)
930{
931#ifdef ROCKBOX
932 char namebuf[MAXPDSTRING];
933#else
934 char pathbuf[MAXPDSTRING], namebuf[MAXPDSTRING];
935#endif
936 t_gobj *y;
937 for (y = x->gl_list; y; y = y->g_next)
938 {
939 int x1, y1, x2, y2;
940 if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2))
941 {
942 if (which == 0) /* properties */
943 {
944 if (!class_getpropertiesfn(pd_class(&y->g_pd)))
945 continue;
946 (*class_getpropertiesfn(pd_class(&y->g_pd)))(y, x);
947 return;
948 }
949 else if (which == 1) /* open */
950 {
951 if (!zgetfn(&y->g_pd, gensym("menu-open")))
952 continue;
953 vmess(&y->g_pd, gensym("menu-open"), "");
954 return;
955 }
956 else /* help */
957 {
958 char *dir;
959 if (pd_class(&y->g_pd) == canvas_class &&
960 canvas_isabstraction((t_canvas *)y))
961 {
962 t_object *ob = (t_object *)y;
963 int ac = binbuf_getnatom(ob->te_binbuf);
964 t_atom *av = binbuf_getvec(ob->te_binbuf);
965 if (ac < 1)
966 return;
967 atom_string(av, namebuf, MAXPDSTRING);
968 dir = canvas_getdir((t_canvas *)y)->s_name;
969 }
970 else
971 {
972 strcpy(namebuf, class_gethelpname(pd_class(&y->g_pd)));
973 dir = class_gethelpdir(pd_class(&y->g_pd));
974 }
975 if (strcmp(namebuf + strlen(namebuf) - 3, ".pd"))
976 strcat(namebuf, ".pd");
977 open_via_helppath(namebuf, dir);
978 return;
979 }
980 }
981 }
982 if (which == 0)
983 canvas_properties(x);
984 else if (which == 2)
985 {
986#ifndef ROCKBOX
987 strcpy(pathbuf, sys_libdir->s_name);
988 strcat(pathbuf, "/doc/5.reference/0.INTRO.txt");
989 sys_vgui("menu_opentext %s\n", pathbuf);
990#endif
991 }
992}
993
994#define NOMOD 0
995#define SHIFTMOD 1
996#define CTRLMOD 2
997#define ALTMOD 4
998#define RIGHTCLICK 8
999
1000/* on one-button-mouse machines, you can use double click to
1001 mean right click (which gets the popup menu.) Do this for Mac. */
1002#ifdef MACOSX
1003#define SIMULATERIGHTCLICK
1004#endif
1005
1006#ifdef SIMULATERIGHTCLICK
1007static double canvas_upclicktime;
1008static int canvas_upx, canvas_upy;
1009#define DCLICKINTERVAL 0.25
1010#endif
1011
1012 /* mouse click */
1013void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
1014 int mod, int doit)
1015{
1016 t_gobj *y;
1017 int shiftmod, runmode, altmod, rightclick;
1018 int x1, y1, x2, y2, clickreturned = 0;
1019
1020#ifdef ROCKBOX
1021 (void) which;
1022#endif
1023
1024 if (!x->gl_editor)
1025 {
1026 bug("editor");
1027 return;
1028 }
1029
1030 shiftmod = (mod & SHIFTMOD);
1031 runmode = ((mod & CTRLMOD) || (!x->gl_edit));
1032 altmod = (mod & ALTMOD);
1033 rightclick = (mod & RIGHTCLICK);
1034
1035 canvas_undo_already_set_move = 0;
1036
1037 /* if keyboard was grabbed, notify grabber and cancel the grab */
1038 if (doit && x->gl_editor->e_grab && x->gl_editor->e_keyfn)
1039 {
1040 (* x->gl_editor->e_keyfn) (x->gl_editor->e_grab, 0);
1041 glist_grab(x, 0, 0, 0, 0, 0);
1042 }
1043
1044#ifdef SIMULATERIGHTCLICK
1045 if (doit && !runmode && xpos == canvas_upx && ypos == canvas_upy &&
1046 sys_getrealtime() - canvas_upclicktime < DCLICKINTERVAL)
1047 rightclick = 1;
1048#endif
1049
1050 x->gl_editor->e_lastmoved = 0;
1051 if (doit)
1052 {
1053 x->gl_editor->e_grab = 0;
1054 x->gl_editor->e_onmotion = MA_NONE;
1055 }
1056 /* post("click %d %d %d %d", xpos, ypos, which, mod); */
1057
1058 if (x->gl_editor->e_onmotion != MA_NONE)
1059 return;
1060
1061 x->gl_editor->e_xwas = xpos;
1062 x->gl_editor->e_ywas = ypos;
1063
1064 if (runmode && !rightclick)
1065 {
1066 for (y = x->gl_list; y; y = y->g_next)
1067 {
1068 /* check if the object wants to be clicked */
1069 if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2)
1070 && (clickreturned = gobj_click(y, x, xpos, ypos,
1071 shiftmod, altmod, 0, doit)))
1072 break;
1073 }
1074 if (!doit)
1075 {
1076 if (y)
1077 canvas_setcursor(x, clickreturned);
1078 else canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
1079 }
1080 return;
1081 }
1082 /* if not a runmode left click, fall here. */
1083 if((y = canvas_findhitbox(x, xpos, ypos, &x1, &y1, &x2, &y2)))
1084 {
1085 t_object *ob = pd_checkobject(&y->g_pd);
1086 /* check you're in the rectangle */
1087 ob = pd_checkobject(&y->g_pd);
1088 if (rightclick)
1089 canvas_rightclick(x, xpos, ypos, y);
1090 else if (shiftmod)
1091 {
1092 if (doit)
1093 {
1094 t_rtext *rt;
1095 if (ob && (rt = x->gl_editor->e_textedfor) &&
1096 rt == glist_findrtext(x, ob))
1097 {
1098 rtext_mouse(rt, xpos - x1, ypos - y1, RTEXT_SHIFT);
1099 x->gl_editor->e_onmotion = MA_DRAGTEXT;
1100 x->gl_editor->e_xwas = x1;
1101 x->gl_editor->e_ywas = y1;
1102 }
1103 else
1104 {
1105 if (glist_isselected(x, y))
1106 glist_deselect(x, y);
1107 else glist_select(x, y);
1108 }
1109 }
1110 }
1111 else
1112 {
1113 /* look for an outlet */
1114 int noutlet;
1115 if (ob && (noutlet = obj_noutlets(ob)) && ypos >= y2-4)
1116 {
1117 int width = x2 - x1;
1118 int nout1 = (noutlet > 1 ? noutlet - 1 : 1);
1119 int closest = ((xpos-x1) * (nout1) + width/2)/width;
1120 int hotspot = x1 +
1121 (width - IOWIDTH) * closest / (nout1);
1122 if (closest < noutlet &&
1123 xpos >= (hotspot-1) && xpos <= hotspot + (IOWIDTH+1))
1124 {
1125 if (doit)
1126 {
1127#ifndef ROCKBOX
1128 int issignal = obj_issignaloutlet(ob, closest);
1129#endif
1130 x->gl_editor->e_onmotion = MA_CONNECT;
1131 x->gl_editor->e_xwas = xpos;
1132 x->gl_editor->e_ywas = ypos;
1133#ifndef ROCKBOX
1134 sys_vgui(
1135 ".x%x.c create line %d %d %d %d -width %d -tags x\n",
1136 x, xpos, ypos, xpos, ypos,
1137 (issignal ? 2 : 1));
1138#endif
1139 }
1140 else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT);
1141 }
1142 else if (doit)
1143 goto nooutletafterall;
1144 }
1145 /* not in an outlet; select and move */
1146 else if (doit)
1147 {
1148 t_rtext *rt;
1149 /* check if the box is being text edited */
1150 nooutletafterall:
1151 if (ob && (rt = x->gl_editor->e_textedfor) &&
1152 rt == glist_findrtext(x, ob))
1153 {
1154 rtext_mouse(rt, xpos - x1, ypos - y1, RTEXT_DOWN);
1155 x->gl_editor->e_onmotion = MA_DRAGTEXT;
1156 x->gl_editor->e_xwas = x1;
1157 x->gl_editor->e_ywas = y1;
1158 }
1159 else
1160 {
1161 /* otherwise select and drag to displace */
1162 if (!glist_isselected(x, y))
1163 {
1164 glist_noselect(x);
1165 glist_select(x, y);
1166 }
1167 x->gl_editor->e_onmotion = MA_MOVE;
1168 }
1169 }
1170 else canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
1171 }
1172 return;
1173 }
1174 /* if right click doesn't hit any boxes, call rightclick
1175 routine anyway */
1176 if (rightclick)
1177 canvas_rightclick(x, xpos, ypos, 0);
1178
1179 /* if not an editing action, and if we didn't hit a
1180 box, set cursor and return */
1181 if (runmode || rightclick)
1182 {
1183 canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
1184 return;
1185 }
1186 /* having failed to find a box, we try lines now. */
1187 if (!runmode && !altmod && !shiftmod)
1188 {
1189 t_linetraverser t;
1190 t_outconnect *oc;
1191 float fx = xpos, fy = ypos;
1192 t_glist *glist2 = glist_getcanvas(x);
1193 linetraverser_start(&t, glist2);
1194 while((oc = linetraverser_next(&t)))
1195 {
1196 float lx1 = t.tr_lx1, ly1 = t.tr_ly1,
1197 lx2 = t.tr_lx2, ly2 = t.tr_ly2;
1198 float area = (lx2 - lx1) * (fy - ly1) -
1199 (ly2 - ly1) * (fx - lx1);
1200 float dsquare = (lx2-lx1) * (lx2-lx1) + (ly2-ly1) * (ly2-ly1);
1201 if (area * area >= 50 * dsquare) continue;
1202 if ((lx2-lx1) * (fx-lx1) + (ly2-ly1) * (fy-ly1) < 0) continue;
1203 if ((lx2-lx1) * (lx2-fx) + (ly2-ly1) * (ly2-fy) < 0) continue;
1204 if (doit)
1205 {
1206 glist_selectline(glist2, oc,
1207 canvas_getindex(glist2, &t.tr_ob->ob_g), t.tr_outno,
1208 canvas_getindex(glist2, &t.tr_ob2->ob_g), t.tr_inno);
1209 }
1210 canvas_setcursor(x, CURSOR_EDITMODE_DISCONNECT);
1211 return;
1212 }
1213 }
1214 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
1215 if (doit)
1216 {
1217 if (!shiftmod) glist_noselect(x);
1218#ifndef ROCKBOX
1219 sys_vgui(".x%x.c create rectangle %d %d %d %d -tags x\n",
1220 x, xpos, ypos, xpos, ypos);
1221#endif
1222 x->gl_editor->e_xwas = xpos;
1223 x->gl_editor->e_ywas = ypos;
1224 x->gl_editor->e_onmotion = MA_REGION;
1225 }
1226}
1227
1228void canvas_mousedown(t_canvas *x, t_floatarg xpos, t_floatarg ypos,
1229 t_floatarg which, t_floatarg mod)
1230{
1231 canvas_doclick(x, xpos, ypos, which, mod, 1);
1232}
1233
1234int canvas_isconnected (t_canvas *x, t_text *ob1, int n1,
1235 t_text *ob2, int n2)
1236{
1237 t_linetraverser t;
1238 t_outconnect *oc;
1239 linetraverser_start(&t, x);
1240 while((oc = linetraverser_next(&t)))
1241 if (t.tr_ob == ob1 && t.tr_outno == n1 &&
1242 t.tr_ob2 == ob2 && t.tr_inno == n2)
1243 return (1);
1244 return (0);
1245}
1246
1247void canvas_doconnect(t_canvas *x, int xpos, int ypos, int which, int doit)
1248{
1249 int x11, y11, x12, y12;
1250 t_gobj *y1;
1251 int x21, y21, x22, y22;
1252 t_gobj *y2;
1253 int xwas = x->gl_editor->e_xwas,
1254 ywas = x->gl_editor->e_ywas;
1255#ifdef ROCKBOX
1256 (void) which;
1257#endif /* ROCKBOX */
1258#ifndef ROCKBOX
1259 if (doit) sys_vgui(".x%x.c delete x\n", x);
1260 else sys_vgui(".x%x.c coords x %d %d %d %d\n",
1261 x, x->gl_editor->e_xwas,
1262 x->gl_editor->e_ywas, xpos, ypos);
1263#endif /* ROCKBOX */
1264
1265 if ((y1 = canvas_findhitbox(x, xwas, ywas, &x11, &y11, &x12, &y12))
1266 && (y2 = canvas_findhitbox(x, xpos, ypos, &x21, &y21, &x22, &y22)))
1267 {
1268 t_object *ob1 = pd_checkobject(&y1->g_pd);
1269 t_object *ob2 = pd_checkobject(&y2->g_pd);
1270 int noutlet1, ninlet2;
1271 if (ob1 && ob2 && ob1 != ob2 &&
1272 (noutlet1 = obj_noutlets(ob1))
1273 && (ninlet2 = obj_ninlets(ob2)))
1274 {
1275#ifndef ROCKBOX
1276 int hotspot1;
1277 int hotspot2;
1278 int lx1, lx2, ly1, ly2;
1279 t_outconnect *oc;
1280#else
1281 int width1 = x12 - x11, closest1;
1282 int width2 = x22 - x21, closest2;
1283#endif
1284 if (noutlet1 > 1)
1285 {
1286 closest1 = ((xwas-x11) * (noutlet1-1) + width1/2)/width1;
1287#ifndef ROCKBOX
1288 hotspot1 = x11 +
1289 (width1 - IOWIDTH) * closest1 / (noutlet1-1);
1290#endif
1291 }
1292 else
1293 {
1294 closest1 = 0;
1295#ifndef ROCKBOX
1296 hotspot1 = x11;
1297#endif
1298 }
1299
1300 if (ninlet2 > 1)
1301 {
1302 closest2 = ((xpos-x21) * (ninlet2-1) + width2/2)/width2;
1303#ifndef ROCKBOX
1304 hotspot2 = x21 +
1305 (width2 - IOWIDTH) * closest2 / (ninlet2-1);
1306#endif
1307 }
1308 else
1309 {
1310 closest2 = 0;
1311#ifndef ROCKBOX
1312 hotspot2 = x21;
1313#endif
1314 }
1315
1316 if (closest1 >= noutlet1)
1317 closest1 = noutlet1 - 1;
1318 if (closest2 >= ninlet2)
1319 closest2 = ninlet2 - 1;
1320
1321 if (canvas_isconnected (x, ob1, closest1, ob2, closest2))
1322 {
1323 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
1324 return;
1325 }
1326 if (obj_issignaloutlet(ob1, closest1) &&
1327 !obj_issignalinlet(ob2, closest2))
1328 {
1329 if (doit)
1330 error("can't connect signal outlet to control inlet");
1331 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
1332 return;
1333 }
1334 if (doit)
1335 {
1336#ifndef ROCKBOX
1337 oc = obj_connect(ob1, closest1, ob2, closest2);
1338 lx1 = x11 + (noutlet1 > 1 ?
1339 ((x12-x11-IOWIDTH) * closest1)/(noutlet1-1) : 0)
1340 + IOMIDDLE;
1341 ly1 = y12;
1342 lx2 = x21 + (ninlet2 > 1 ?
1343 ((x22-x21-IOWIDTH) * closest2)/(ninlet2-1) : 0)
1344 + IOMIDDLE;
1345 ly2 = y21;
1346 sys_vgui(".x%x.c create line %d %d %d %d -width %d -tags l%x\n",
1347 glist_getcanvas(x),
1348 lx1, ly1, lx2, ly2,
1349 (obj_issignaloutlet(ob1, closest1) ? 2 : 1), oc);
1350#endif /* ROCKBOX */
1351 canvas_setundo(x, canvas_undo_connect,
1352 canvas_undo_set_connect(x,
1353 canvas_getindex(x, &ob1->ob_g), closest1,
1354 canvas_getindex(x, &ob2->ob_g), closest2),
1355 "connect");
1356 }
1357 else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT);
1358 return;
1359 }
1360 }
1361 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
1362}
1363
1364void canvas_selectinrect(t_canvas *x, int lox, int loy, int hix, int hiy)
1365{
1366 t_gobj *y;
1367 for (y = x->gl_list; y; y = y->g_next)
1368 {
1369 int x1, y1, x2, y2;
1370 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
1371 if (hix >= x1 && lox <= x2 && hiy >= y1 && loy <= y2
1372 && !glist_isselected(x, y))
1373 glist_select(x, y);
1374 }
1375}
1376
1377static void canvas_doregion(t_canvas *x, int xpos, int ypos, int doit)
1378{
1379 if (doit)
1380 {
1381 int lox, loy, hix, hiy;
1382 if (x->gl_editor->e_xwas < xpos)
1383 lox = x->gl_editor->e_xwas, hix = xpos;
1384 else hix = x->gl_editor->e_xwas, lox = xpos;
1385 if (x->gl_editor->e_ywas < ypos)
1386 loy = x->gl_editor->e_ywas, hiy = ypos;
1387 else hiy = x->gl_editor->e_ywas, loy = ypos;
1388 canvas_selectinrect(x, lox, loy, hix, hiy);
1389#ifndef ROCKBOX
1390 sys_vgui(".x%x.c delete x\n", x);
1391#endif
1392 x->gl_editor->e_onmotion = 0;
1393 }
1394#ifndef ROCKBOX
1395 else sys_vgui(".x%x.c coords x %d %d %d %d\n",
1396 x, x->gl_editor->e_xwas,
1397 x->gl_editor->e_ywas, xpos, ypos);
1398#endif
1399}
1400
1401void canvas_mouseup(t_canvas *x,
1402 t_floatarg fxpos, t_floatarg fypos, t_floatarg fwhich)
1403{
1404 t_gobj *y;
1405
1406 int xpos = fxpos, ypos = fypos, which = fwhich;
1407
1408 if (!x->gl_editor)
1409 {
1410 bug("editor");
1411 return;
1412 }
1413
1414#ifdef SIMULATERIGHTCLICK
1415 canvas_upclicktime = sys_getrealtime();
1416 canvas_upx = xpos;
1417 canvas_upy = ypos;
1418#endif
1419
1420 if (x->gl_editor->e_onmotion == MA_CONNECT)
1421 canvas_doconnect(x, xpos, ypos, which, 1);
1422 else if (x->gl_editor->e_onmotion == MA_REGION)
1423 canvas_doregion(x, xpos, ypos, 1);
1424 else if (x->gl_editor->e_onmotion == MA_MOVE)
1425 {
1426 /* after motion, if there's only one item selected, activate it */
1427 if (x->gl_editor->e_selection &&
1428 !(x->gl_editor->e_selection->sel_next))
1429 gobj_activate(x->gl_editor->e_selection->sel_what,
1430 x, 1);
1431 }
1432 else if (x->gl_editor->e_onmotion == MA_PASSOUT)
1433 x->gl_editor->e_onmotion = 0;
1434 x->gl_editor->e_onmotion = MA_NONE;
1435
1436
1437#if 1
1438 /* GG misused the (unused) dbl parameter to tell if mouseup */
1439
1440 for (y = x->gl_list; y; y = y->g_next) {
1441 int x1, y1, x2, y2, clickreturned = 0;
1442
1443 /* check if the object wants to be clicked */
1444 if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2)
1445 && (clickreturned = gobj_click(y, x, xpos, ypos,
1446 0, 0, 1, 0)))
1447 break;
1448 }
1449#endif
1450
1451
1452}
1453
1454 /* displace the selection by (dx, dy) pixels */
1455static void canvas_displaceselection(t_canvas *x, int dx, int dy)
1456{
1457 t_selection *y;
1458 int resortin = 0, resortout = 0;
1459 if (!canvas_undo_already_set_move)
1460 {
1461 canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, 1),
1462 "motion");
1463 canvas_undo_already_set_move = 1;
1464 }
1465 for (y = x->gl_editor->e_selection; y; y = y->sel_next)
1466 {
1467 t_class *cl = pd_class(&y->sel_what->g_pd);
1468 gobj_displace(y->sel_what, x, dx, dy);
1469 if (cl == vinlet_class) resortin = 1;
1470 else if (cl == voutlet_class) resortout = 1;
1471 }
1472 if (resortin) canvas_resortinlets(x);
1473 if (resortout) canvas_resortoutlets(x);
1474 canvas_dirty(x, 1);
1475}
1476
1477 /* this routine is called whenever a key is pressed or released. "x"
1478 may be zero if there's no current canvas. The first argument is true or
1479 fals for down/up; the second one is either a symbolic key name (e.g.,
1480 "Right" or an Ascii key number. */
1481void canvas_key(t_canvas *x, t_symbol *s, int ac, t_atom *av)
1482{
1483 static t_symbol *keynumsym, *keyupsym, *keynamesym;
1484#ifndef ROCKBOX
1485 int fflag;
1486#endif
1487 int keynum;
1488 t_symbol *gotkeysym;
1489
1490 int down, shift;
1491
1492#ifdef ROCKBOX
1493 (void) s;
1494#endif
1495
1496 if (ac < 3)
1497 return;
1498 if (!x->gl_editor)
1499 {
1500 bug("editor");
1501 return;
1502 }
1503 canvas_undo_already_set_move = 0;
1504 down = (atom_getfloat(av) != 0); /* nonzero if it's a key down */
1505 shift = (atom_getfloat(av+2) != 0); /* nonzero if shift-ed */
1506 if (av[1].a_type == A_SYMBOL)
1507 gotkeysym = av[1].a_w.w_symbol;
1508 else if (av[1].a_type == A_FLOAT)
1509 {
1510 char buf[3];
1511#ifdef ROCKBOX
1512 snprintf(buf, sizeof(buf), "%c", (int) (av[1].a_w.w_float));
1513#else /* ROCKBOX */
1514 sprintf(buf, "%c", (int)(av[1].a_w.w_float));
1515#endif /* ROCKBOX */
1516 gotkeysym = gensym(buf);
1517 }
1518 else gotkeysym = gensym("?");
1519#ifndef ROCKBOX
1520 fflag = (av[0].a_type == A_FLOAT ? av[0].a_w.w_float : 0);
1521#endif
1522 keynum = (av[1].a_type == A_FLOAT ? av[1].a_w.w_float : 0);
1523 if (keynum == '\\' || keynum == '{' || keynum == '}')
1524 {
1525 post("%c: dropped", (int)keynum);
1526 return;
1527 }
1528 if (keynum == '\r') keynum = '\n';
1529 if (av[1].a_type == A_SYMBOL &&
1530 !strcmp(av[1].a_w.w_symbol->s_name, "Return"))
1531 keynum = '\n';
1532 if (!keynumsym)
1533 {
1534 keynumsym = gensym("#key");
1535 keyupsym = gensym("#keyup");
1536 keynamesym = gensym("#keyname");
1537 }
1538#ifdef MACOSX
1539 if (keynum == 30)
1540 keynum = 0, gotkeysym = gensym("Up");
1541 else if (keynum == 31)
1542 keynum = 0, gotkeysym = gensym("Down");
1543 else if (keynum == 28)
1544 keynum = 0, gotkeysym = gensym("Left");
1545 else if (keynum == 29)
1546 keynum = 0, gotkeysym = gensym("Right");
1547#endif
1548 if (keynumsym->s_thing && down)
1549 pd_float(keynumsym->s_thing, (float)keynum);
1550 if (keyupsym->s_thing && !down)
1551 pd_float(keyupsym->s_thing, (float)keynum);
1552 if (keynamesym->s_thing)
1553 {
1554 t_atom at[2];
1555 at[0] = av[0];
1556 SETFLOAT(at, down);
1557 SETSYMBOL(at+1, gotkeysym);
1558 pd_list(keynamesym->s_thing, 0, 2, at);
1559 }
1560 if (!x->gl_editor) /* if that 'invis'ed the window, we'd better stop. */
1561 return;
1562 if (x && down)
1563 {
1564 /* if an object has "grabbed" keys just send them on */
1565 if (x->gl_editor->e_grab
1566 && x->gl_editor->e_keyfn && keynum)
1567 (* x->gl_editor->e_keyfn)
1568 (x->gl_editor->e_grab, (float)keynum);
1569 /* if a text editor is open send it on */
1570 else if (x->gl_editor->e_textedfor)
1571 {
1572 if (!x->gl_editor->e_textdirty)
1573 {
1574 canvas_setundo(x, canvas_undo_cut,
1575 canvas_undo_set_cut(x, UCUT_TEXT), "typing");
1576 }
1577 rtext_key(x->gl_editor->e_textedfor,
1578 (int)keynum, gotkeysym);
1579 if (x->gl_editor->e_textdirty)
1580 canvas_dirty(x, 1);
1581 }
1582 /* check for backspace or clear */
1583 else if (keynum == 8 || keynum == 127)
1584 {
1585 if (x->gl_editor->e_selectedline)
1586 canvas_clearline(x);
1587 else if (x->gl_editor->e_selection)
1588 {
1589 canvas_setundo(x, canvas_undo_cut,
1590 canvas_undo_set_cut(x, UCUT_CLEAR), "clear");
1591 canvas_doclear(x);
1592 }
1593 }
1594 /* check for arrow keys */
1595 else if (!strcmp(gotkeysym->s_name, "Up"))
1596 canvas_displaceselection(x, 0, shift ? -10 : -1);
1597 else if (!strcmp(gotkeysym->s_name, "Down"))
1598 canvas_displaceselection(x, 0, shift ? 10 : 1);
1599 else if (!strcmp(gotkeysym->s_name, "Left"))
1600 canvas_displaceselection(x, shift ? -10 : -1, 0);
1601 else if (!strcmp(gotkeysym->s_name, "Right"))
1602 canvas_displaceselection(x, shift ? 10 : 1, 0);
1603 }
1604}
1605
1606void canvas_motion(t_canvas *x, t_floatarg xpos, t_floatarg ypos,
1607 t_floatarg fmod)
1608{
1609 /* post("motion %d %d", xpos, ypos); */
1610 int mod = fmod;
1611 if (!x->gl_editor)
1612 {
1613 bug("editor");
1614 return;
1615 }
1616 glist_setlastxy(x, xpos, ypos);
1617 if (x->gl_editor->e_onmotion == MA_MOVE)
1618 {
1619 canvas_displaceselection(x,
1620 xpos - x->gl_editor->e_xwas, ypos - x->gl_editor->e_ywas);
1621 x->gl_editor->e_xwas = xpos;
1622 x->gl_editor->e_ywas = ypos;
1623 }
1624 else if (x->gl_editor->e_onmotion == MA_REGION)
1625 canvas_doregion(x, xpos, ypos, 0);
1626 else if (x->gl_editor->e_onmotion == MA_CONNECT)
1627 canvas_doconnect(x, xpos, ypos, 0, 0);
1628 else if (x->gl_editor->e_onmotion == MA_PASSOUT)
1629 {
1630 if (!x->gl_editor->e_motionfn)
1631 bug("e_motionfn");
1632 (*x->gl_editor->e_motionfn)(&x->gl_editor->e_grab->g_pd,
1633 xpos - x->gl_editor->e_xwas,
1634 ypos - x->gl_editor->e_ywas);
1635 x->gl_editor->e_xwas = xpos;
1636 x->gl_editor->e_ywas = ypos;
1637 }
1638 else if (x->gl_editor->e_onmotion == MA_DRAGTEXT)
1639 {
1640 t_rtext *rt = x->gl_editor->e_textedfor;
1641 if (rt)
1642 rtext_mouse(rt, xpos - x->gl_editor->e_xwas,
1643 ypos - x->gl_editor->e_ywas, RTEXT_DRAG);
1644 }
1645 else canvas_doclick(x, xpos, ypos, 0, mod, 0);
1646
1647 x->gl_editor->e_lastmoved = 1;
1648}
1649
1650void canvas_startmotion(t_canvas *x)
1651{
1652 int xval, yval;
1653 if (!x->gl_editor) return;
1654 glist_getnextxy(x, &xval, &yval);
1655 if (xval == 0 && yval == 0) return;
1656 x->gl_editor->e_onmotion = MA_MOVE;
1657 x->gl_editor->e_xwas = xval;
1658 x->gl_editor->e_ywas = yval;
1659}
1660
1661/* ----------------------------- window stuff ----------------------- */
1662
1663void canvas_print(t_canvas *x, t_symbol *s)
1664{
1665#ifdef ROCKBOX
1666 (void) x;
1667 (void) s;
1668#else /* ROCKBOX */
1669 if (*s->s_name) sys_vgui(".x%x.c postscript -file %s\n", x, s->s_name);
1670 else sys_vgui(".x%x.c postscript -file x.ps\n", x);
1671#endif /* ROCKBOX */
1672}
1673
1674void canvas_menuclose(t_canvas *x, t_floatarg force)
1675{
1676 if (x->gl_owner)
1677 canvas_vis(x, 0);
1678 else if ((force != 0) || (!x->gl_dirty))
1679 pd_free(&x->gl_pd);
1680#ifndef ROCKBOX
1681 else sys_vgui("pdtk_check {This window has been modified. Close anyway?}\
1682 {.x%x menuclose 1;\n}\n", x);
1683#endif
1684}
1685
1686 /* put up a dialog which may call canvas_font back to do the work */
1687static void canvas_menufont(t_canvas *x)
1688{
1689#ifdef ROCKBOX
1690 (void) x;
1691#else /* ROCKBOX */
1692 char buf[80];
1693 t_canvas *x2 = canvas_getrootfor(x);
1694 gfxstub_deleteforkey(x2);
1695 sprintf(buf, "pdtk_canvas_dofont %%s %d\n", x2->gl_font);
1696 gfxstub_new(&x2->gl_pd, &x2->gl_pd, buf);
1697#endif /* ROCKBOX */
1698}
1699
1700static int canvas_find_index1, canvas_find_index2;
1701static t_binbuf *canvas_findbuf;
1702int binbuf_match(t_binbuf *inbuf, t_binbuf *searchbuf);
1703
1704 /* find an atom or string of atoms */
1705static int canvas_dofind(t_canvas *x, int *myindex1p)
1706{
1707 t_gobj *y;
1708 int myindex1 = *myindex1p, myindex2;
1709 if (myindex1 >= canvas_find_index1)
1710 {
1711 for (y = x->gl_list, myindex2 = 0; y;
1712 y = y->g_next, myindex2++)
1713 {
1714 t_object *ob = 0;
1715 if((ob = pd_checkobject(&y->g_pd)))
1716 {
1717 if (binbuf_match(ob->ob_binbuf, canvas_findbuf))
1718 {
1719 if (myindex1 > canvas_find_index1 ||
1720 (myindex1 == canvas_find_index1 &&
1721 myindex2 > canvas_find_index2))
1722 {
1723 canvas_find_index1 = myindex1;
1724 canvas_find_index2 = myindex2;
1725 glist_noselect(x);
1726 canvas_vis(x, 1);
1727 canvas_editmode(x, 1.);
1728 glist_select(x, y);
1729 return (1);
1730 }
1731 }
1732 }
1733 }
1734 }
1735 for (y = x->gl_list, myindex2 = 0; y; y = y->g_next, myindex2++)
1736 {
1737 if (pd_class(&y->g_pd) == canvas_class)
1738 {
1739 (*myindex1p)++;
1740 if (canvas_dofind((t_canvas *)y, myindex1p))
1741 return (1);
1742 }
1743 }
1744 return (0);
1745}
1746
1747static void canvas_find(t_canvas *x, t_symbol *s, int ac, t_atom *av)
1748{
1749 int myindex1 = 0, i;
1750#ifdef ROCKBOX
1751 (void) s;
1752#endif
1753 for (i = 0; i < ac; i++)
1754 {
1755 if (av[i].a_type == A_SYMBOL)
1756 {
1757 if (!strcmp(av[i].a_w.w_symbol->s_name, "_semi_"))
1758 SETSEMI(&av[i]);
1759 else if (!strcmp(av[i].a_w.w_symbol->s_name, "_comma_"))
1760 SETCOMMA(&av[i]);
1761 }
1762 }
1763 if (!canvas_findbuf)
1764 canvas_findbuf = binbuf_new();
1765 binbuf_clear(canvas_findbuf);
1766 binbuf_add(canvas_findbuf, ac, av);
1767 canvas_find_index1 = 0;
1768 canvas_find_index2 = -1;
1769 canvas_whichfind = x;
1770 if (!canvas_dofind(x, &myindex1))
1771 {
1772 binbuf_print(canvas_findbuf);
1773 post("... couldn't find");
1774 }
1775}
1776
1777static void canvas_find_again(t_canvas *x)
1778{
1779 int myindex1 = 0;
1780#ifdef ROCKBOX
1781 (void) x;
1782#endif
1783 if (!canvas_findbuf || !canvas_whichfind)
1784 return;
1785 if (!canvas_dofind(canvas_whichfind, &myindex1))
1786 {
1787 binbuf_print(canvas_findbuf);
1788 post("... couldn't find");
1789 }
1790}
1791
1792static void canvas_find_parent(t_canvas *x)
1793{
1794 if (x->gl_owner)
1795 canvas_vis(glist_getcanvas(x->gl_owner), 1);
1796}
1797
1798static int glist_dofinderror(t_glist *gl, void *error_object)
1799{
1800 t_gobj *g;
1801 for (g = gl->gl_list; g; g = g->g_next)
1802 {
1803 if ((void *)g == error_object)
1804 {
1805 /* got it... now show it. */
1806 glist_noselect(gl);
1807 canvas_vis(glist_getcanvas(gl), 1);
1808 canvas_editmode(glist_getcanvas(gl), 1.);
1809 glist_select(gl, g);
1810 return (1);
1811 }
1812 else if (g->g_pd == canvas_class)
1813 {
1814 if (glist_dofinderror((t_canvas *)g, error_object))
1815 return (1);
1816 }
1817 }
1818 return (0);
1819}
1820
1821void canvas_finderror(void *error_object)
1822{
1823 t_canvas *x;
1824 /* find all root canvases */
1825 for (x = canvas_list; x; x = x->gl_next)
1826 {
1827 if (glist_dofinderror(x, error_object))
1828 return;
1829 }
1830 post("... sorry, I couldn't find the source of that error.");
1831}
1832
1833void canvas_stowconnections(t_canvas *x)
1834{
1835 t_gobj *selhead = 0, *seltail = 0, *nonhead = 0, *nontail = 0, *y, *y2;
1836 t_linetraverser t;
1837 t_outconnect *oc;
1838 if (!x->gl_editor) return;
1839 /* split list to "selected" and "unselected" parts */
1840 for (y = x->gl_list; y; y = y2)
1841 {
1842 y2 = y->g_next;
1843 if (glist_isselected(x, y))
1844 {
1845 if (seltail)
1846 {
1847 seltail->g_next = y;
1848 seltail = y;
1849 y->g_next = 0;
1850 }
1851 else
1852 {
1853 selhead = seltail = y;
1854 seltail->g_next = 0;
1855 }
1856 }
1857 else
1858 {
1859 if (nontail)
1860 {
1861 nontail->g_next = y;
1862 nontail = y;
1863 y->g_next = 0;
1864 }
1865 else
1866 {
1867 nonhead = nontail = y;
1868 nontail->g_next = 0;
1869 }
1870 }
1871 }
1872 /* move the selected part to the end */
1873 if (!nonhead) x->gl_list = selhead;
1874 else x->gl_list = nonhead, nontail->g_next = selhead;
1875
1876 /* add connections to binbuf */
1877 binbuf_clear(x->gl_editor->e_connectbuf);
1878 linetraverser_start(&t, x);
1879 while((oc = linetraverser_next(&t)))
1880 {
1881 int s1 = glist_isselected(x, &t.tr_ob->ob_g);
1882 int s2 = glist_isselected(x, &t.tr_ob2->ob_g);
1883 if (s1 != s2)
1884 binbuf_addv(x->gl_editor->e_connectbuf, "ssiiii;",
1885 gensym("#X"), gensym("connect"),
1886 glist_getindex(x, &t.tr_ob->ob_g), t.tr_outno,
1887 glist_getindex(x, &t.tr_ob2->ob_g), t.tr_inno);
1888 }
1889}
1890
1891void canvas_restoreconnections(t_canvas *x)
1892{
1893 pd_bind(&x->gl_pd, gensym("#X"));
1894 binbuf_eval(x->gl_editor->e_connectbuf, 0, 0, 0);
1895 pd_unbind(&x->gl_pd, gensym("#X"));
1896}
1897
1898static t_binbuf *canvas_docopy(t_canvas *x)
1899{
1900 t_gobj *y;
1901 t_linetraverser t;
1902 t_outconnect *oc;
1903 t_binbuf *b = binbuf_new();
1904 for (y = x->gl_list; y; y = y->g_next)
1905 {
1906 if (glist_isselected(x, y))
1907 gobj_save(y, b);
1908 }
1909 linetraverser_start(&t, x);
1910 while((oc = linetraverser_next(&t)))
1911 {
1912 if (glist_isselected(x, &t.tr_ob->ob_g)
1913 && glist_isselected(x, &t.tr_ob2->ob_g))
1914 {
1915 binbuf_addv(b, "ssiiii;", gensym("#X"), gensym("connect"),
1916 glist_selectionindex(x, &t.tr_ob->ob_g, 1), t.tr_outno,
1917 glist_selectionindex(x, &t.tr_ob2->ob_g, 1), t.tr_inno);
1918 }
1919 }
1920 return (b);
1921}
1922
1923static void canvas_copy(t_canvas *x)
1924{
1925 if (!x->gl_editor || !x->gl_editor->e_selection)
1926 return;
1927 binbuf_free(copy_binbuf);
1928 copy_binbuf = canvas_docopy(x);
1929}
1930
1931static void canvas_clearline(t_canvas *x)
1932{
1933 if (x->gl_editor->e_selectedline)
1934 {
1935 canvas_disconnect(x, x->gl_editor->e_selectline_index1,
1936 x->gl_editor->e_selectline_outno,
1937 x->gl_editor->e_selectline_index2,
1938 x->gl_editor->e_selectline_inno);
1939 canvas_setundo(x, canvas_undo_disconnect,
1940 canvas_undo_set_disconnect(x,
1941 x->gl_editor->e_selectline_index1,
1942 x->gl_editor->e_selectline_outno,
1943 x->gl_editor->e_selectline_index2,
1944 x->gl_editor->e_selectline_inno),
1945 "disconnect");
1946 }
1947}
1948
1949extern t_pd *newest;
1950static void canvas_doclear(t_canvas *x)
1951{
1952 t_gobj *y, *y2;
1953 int dspstate;
1954
1955 dspstate = canvas_suspend_dsp();
1956 if (x->gl_editor->e_selectedline)
1957 {
1958 canvas_disconnect(x, x->gl_editor->e_selectline_index1,
1959 x->gl_editor->e_selectline_outno,
1960 x->gl_editor->e_selectline_index2,
1961 x->gl_editor->e_selectline_inno);
1962 canvas_setundo(x, canvas_undo_disconnect,
1963 canvas_undo_set_disconnect(x,
1964 x->gl_editor->e_selectline_index1,
1965 x->gl_editor->e_selectline_outno,
1966 x->gl_editor->e_selectline_index2,
1967 x->gl_editor->e_selectline_inno),
1968 "disconnect");
1969 }
1970 /* if text is selected, deselecting it might remake the
1971 object. So we deselect it and hunt for a "new" object on
1972 the glist to reselect. */
1973 if (x->gl_editor->e_textedfor)
1974 {
1975 newest = 0;
1976 glist_noselect(x);
1977 if (newest)
1978 {
1979 for (y = x->gl_list; y; y = y->g_next)
1980 if (&y->g_pd == newest) glist_select(x, y);
1981 }
1982 }
1983 while (1) /* this is pretty wierd... should rewrite it */
1984 {
1985 for (y = x->gl_list; y; y = y2)
1986 {
1987 y2 = y->g_next;
1988 if (glist_isselected(x, y))
1989 {
1990 glist_delete(x, y);
1991#if 0
1992 if (y2) post("cut 5 %x %x", y2, y2->g_next);
1993 else post("cut 6");
1994#endif
1995 goto next;
1996 }
1997 }
1998 goto restore;
1999 next: ;
2000 }
2001restore:
2002 canvas_resume_dsp(dspstate);
2003 canvas_dirty(x, 1);
2004}
2005
2006static void canvas_cut(t_canvas *x)
2007{
2008 if (x->gl_editor && x->gl_editor->e_selectedline)
2009 canvas_clearline(x);
2010 else if (x->gl_editor && x->gl_editor->e_selection)
2011 {
2012 canvas_setundo(x, canvas_undo_cut,
2013 canvas_undo_set_cut(x, UCUT_CUT), "cut");
2014 canvas_copy(x);
2015 canvas_doclear(x);
2016 }
2017}
2018
2019static int paste_onset;
2020static t_canvas *paste_canvas;
2021
2022static void glist_donewloadbangs(t_glist *x)
2023{
2024 if (x->gl_editor)
2025 {
2026 t_selection *sel;
2027 for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
2028 if (pd_class(&sel->sel_what->g_pd) == canvas_class)
2029 canvas_loadbang((t_canvas *)(&sel->sel_what->g_pd));
2030 }
2031}
2032
2033static void canvas_dopaste(t_canvas *x, t_binbuf *b)
2034{
2035#ifdef ROCKBOX
2036 t_gobj *g2;
2037#else /* ROCKBOX */
2038 t_gobj *newgobj, *last, *g2;
2039#endif /* ROCKBOX */
2040 int dspstate = canvas_suspend_dsp(), nbox, count;
2041
2042 canvas_editmode(x, 1.);
2043 glist_noselect(x);
2044 for (g2 = x->gl_list, nbox = 0; g2; g2 = g2->g_next) nbox++;
2045
2046 paste_onset = nbox;
2047 paste_canvas = x;
2048
2049 pd_bind(&x->gl_pd, gensym("#X"));
2050 binbuf_eval(b, 0, 0, 0);
2051 pd_unbind(&x->gl_pd, gensym("#X"));
2052 for (g2 = x->gl_list, count = 0; g2; g2 = g2->g_next, count++)
2053 if (count >= nbox)
2054 glist_select(x, g2);
2055 paste_canvas = 0;
2056 canvas_resume_dsp(dspstate);
2057 canvas_dirty(x, 1);
2058 glist_donewloadbangs(x);
2059}
2060
2061static void canvas_paste(t_canvas *x)
2062{
2063 canvas_setundo(x, canvas_undo_paste, canvas_undo_set_paste(x), "paste");
2064 canvas_dopaste(x, copy_binbuf);
2065}
2066
2067static void canvas_duplicate(t_canvas *x)
2068{
2069 if (x->gl_editor->e_onmotion == MA_NONE)
2070 {
2071 t_selection *y;
2072 canvas_copy(x);
2073 canvas_setundo(x, canvas_undo_paste, canvas_undo_set_paste(x),
2074 "duplicate");
2075 canvas_dopaste(x, copy_binbuf);
2076 for (y = x->gl_editor->e_selection; y; y = y->sel_next)
2077 gobj_displace(y->sel_what, x,
2078 10, 10);
2079 canvas_dirty(x, 1);
2080 }
2081}
2082
2083static void canvas_selectall(t_canvas *x)
2084{
2085 t_gobj *y;
2086 if (!x->gl_edit)
2087 canvas_editmode(x, 1);
2088 for (y = x->gl_list; y; y = y->g_next)
2089 {
2090 if (!glist_isselected(x, y))
2091 glist_select(x, y);
2092 }
2093}
2094
2095void canvas_connect(t_canvas *x, t_floatarg fwhoout, t_floatarg foutno,
2096 t_floatarg fwhoin, t_floatarg finno)
2097{
2098 int whoout = fwhoout, outno = foutno, whoin = fwhoin, inno = finno;
2099 t_gobj *src = 0, *sink = 0;
2100 t_object *objsrc, *objsink;
2101 t_outconnect *oc;
2102 int nin = whoin, nout = whoout;
2103 if (paste_canvas == x) whoout += paste_onset, whoin += paste_onset;
2104 for (src = x->gl_list; whoout; src = src->g_next, whoout--)
2105 if (!src->g_next) goto bad; /* bug fix thanks to Hannes */
2106 for (sink = x->gl_list; whoin; sink = sink->g_next, whoin--)
2107 if (!sink->g_next) goto bad;
2108 if (!(objsrc = pd_checkobject(&src->g_pd)) ||
2109 !(objsink = pd_checkobject(&sink->g_pd)))
2110 goto bad;
2111 if (!(oc = obj_connect(objsrc, outno, objsink, inno))) goto bad;
2112 if (glist_isvisible(x))
2113 {
2114#ifndef ROCKBOX
2115 sys_vgui(".x%x.c create line %d %d %d %d -width %d -tags l%x\n",
2116 glist_getcanvas(x), 0, 0, 0, 0,
2117 (obj_issignaloutlet(objsrc, outno) ? 2 : 1),oc);
2118#endif
2119 canvas_fixlinesfor(x, objsrc);
2120 }
2121 return;
2122
2123bad:
2124 post("%s %d %d %d %d (%s->%s) connection failed",
2125 x->gl_name->s_name, nout, outno, nin, inno,
2126 (src? class_getname(pd_class(&src->g_pd)) : "???"),
2127 (sink? class_getname(pd_class(&sink->g_pd)) : "???"));
2128}
2129
2130#define XTOLERANCE 4
2131#define YTOLERANCE 3
2132#define NHIST 15
2133
2134 /* LATER might have to speed this up */
2135static void canvas_tidy(t_canvas *x)
2136{
2137#ifdef ROCKBOX
2138 t_gobj *y, *y2;
2139#else /* ROCKBOX */
2140 t_gobj *y, *y2, *y3;
2141#endif /* ROCKBOX */
2142 int ax1, ay1, ax2, ay2, bx1, by1, bx2, by2;
2143 int histogram[NHIST], *ip, i, besthist, bestdist;
2144 /* if nobody is selected, this means do it to all boxes;
2145 othewise just the selection */
2146 int all = (x->gl_editor ? (x->gl_editor->e_selection == 0) : 1);
2147
2148 canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, !all),
2149 "motion");
2150
2151 /* tidy horizontally */
2152 for (y = x->gl_list; y; y = y->g_next)
2153 if (all || glist_isselected(x, y))
2154 {
2155 gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);
2156
2157 for (y2 = x->gl_list; y2; y2 = y2->g_next)
2158 if (all || glist_isselected(x, y2))
2159 {
2160 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
2161 if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE &&
2162 bx1 < ax1)
2163 goto nothorizhead;
2164 }
2165
2166 for (y2 = x->gl_list; y2; y2 = y2->g_next)
2167 if (all || glist_isselected(x, y2))
2168 {
2169 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
2170 if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE
2171 && by1 != ay1)
2172 gobj_displace(y2, x, 0, ay1-by1);
2173 }
2174 nothorizhead: ;
2175 }
2176 /* tidy vertically. First guess the user's favorite vertical spacing */
2177 for (i = NHIST, ip = histogram; i--; ip++) *ip = 0;
2178 for (y = x->gl_list; y; y = y->g_next)
2179 if (all || glist_isselected(x, y))
2180 {
2181 gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);
2182 for (y2 = x->gl_list; y2; y2 = y2->g_next)
2183 if (all || glist_isselected(x, y2))
2184 {
2185 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
2186 if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE)
2187 {
2188 int distance = by1-ay2;
2189 if (distance >= 0 && distance < NHIST)
2190 histogram[distance]++;
2191 }
2192 }
2193 }
2194 for (i = 1, besthist = 0, bestdist = 4, ip = histogram + 1;
2195 i < (NHIST-1); i++, ip++)
2196 {
2197 int hit = ip[-1] + 2 * ip[0] + ip[1];
2198 if (hit > besthist)
2199 {
2200 besthist = hit;
2201 bestdist = i;
2202 }
2203 }
2204 post("best vertical distance %d", bestdist);
2205 for (y = x->gl_list; y; y = y->g_next)
2206 if (all || glist_isselected(x, y))
2207 {
2208 int keep = 1;
2209 gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);
2210 for (y2 = x->gl_list; y2; y2 = y2->g_next)
2211 if (all || glist_isselected(x, y2))
2212 {
2213 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
2214 if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE &&
2215 ay1 >= by2 - 10 && ay1 < by2 + NHIST)
2216 goto nothead;
2217 }
2218 while (keep)
2219 {
2220 keep = 0;
2221 for (y2 = x->gl_list; y2; y2 = y2->g_next)
2222 if (all || glist_isselected(x, y2))
2223 {
2224 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
2225 if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE &&
2226 by1 > ay1 && by1 < ay2 + NHIST)
2227 {
2228 int vmove = ay2 + bestdist - by1;
2229 gobj_displace(y2, x, ax1-bx1, vmove);
2230 ay1 = by1 + vmove;
2231 ay2 = by2 + vmove;
2232 keep = 1;
2233 break;
2234 }
2235 }
2236 }
2237 nothead: ;
2238 }
2239 canvas_dirty(x, 1);
2240}
2241
2242static void canvas_texteditor(t_canvas *x)
2243{
2244 t_rtext *foo;
2245 char *buf;
2246 int bufsize;
2247 if((foo = x->gl_editor->e_textedfor))
2248 rtext_gettext(foo, &buf, &bufsize);
2249 else buf = "", bufsize = 0;
2250#ifndef ROCKBOX
2251 sys_vgui("pdtk_pd_texteditor {%.*s}\n", bufsize, buf);
2252#endif
2253}
2254
2255void glob_key(void *dummy, t_symbol *s, int ac, t_atom *av)
2256{
2257#ifdef ROCKBOX
2258 (void) dummy;
2259#endif
2260 /* canvas_editing can be zero; canvas_key checks for that */
2261 canvas_key(canvas_editing, s, ac, av);
2262}
2263
2264void canvas_editmode(t_canvas *x, t_floatarg fyesplease)
2265{
2266 int yesplease = fyesplease;
2267 if (yesplease && x->gl_edit)
2268 return;
2269 x->gl_edit = !x->gl_edit;
2270 if (x->gl_edit && glist_isvisible(x) && glist_istoplevel(x))
2271 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
2272 else
2273 {
2274 glist_noselect(x);
2275 if (glist_isvisible(x) && glist_istoplevel(x))
2276 canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
2277 }
2278#ifndef ROCKBOX
2279 sys_vgui("pdtk_canvas_editval .x%x %d\n",
2280 glist_getcanvas(x), x->gl_edit);
2281#endif
2282}
2283
2284 /* called by canvas_font below */
2285static void canvas_dofont(t_canvas *x, t_floatarg font, t_floatarg xresize,
2286 t_floatarg yresize)
2287{
2288 t_gobj *y;
2289 x->gl_font = font;
2290 if (xresize != 1 || yresize != 1)
2291 {
2292 canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, 0),
2293 "motion");
2294 for (y = x->gl_list; y; y = y->g_next)
2295 {
2296 int x1, x2, y1, y2, nx1, ny1;
2297 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
2298 nx1 = x1 * xresize + 0.5;
2299 ny1 = y1 * yresize + 0.5;
2300 gobj_displace(y, x, nx1-x1, ny1-y1);
2301 }
2302 }
2303 if (glist_isvisible(x))
2304 glist_redraw(x);
2305 for (y = x->gl_list; y; y = y->g_next)
2306 if (pd_class(&y->g_pd) == canvas_class
2307 && !canvas_isabstraction((t_canvas *)y))
2308 canvas_dofont((t_canvas *)y, font, xresize, yresize);
2309}
2310
2311 /* canvas_menufont calls up a TK dialog which calls this back */
2312static void canvas_font(t_canvas *x, t_floatarg font, t_floatarg resize,
2313 t_floatarg whichresize)
2314{
2315 float realresize, realresx = 1, realresy = 1;
2316 t_canvas *x2 = canvas_getrootfor(x);
2317 if (!resize) realresize = 1;
2318 else
2319 {
2320 if (resize < 20) resize = 20;
2321 if (resize > 500) resize = 500;
2322 realresize = resize * 0.01;
2323 }
2324 if (whichresize != 3) realresx = realresize;
2325 if (whichresize != 2) realresy = realresize;
2326 canvas_dofont(x2, font, realresx, realresy);
2327#ifndef ROCKBOX
2328 sys_defaultfont = font;
2329#endif
2330}
2331
2332static t_glist *canvas_last_glist;
2333static int canvas_last_glist_x, canvas_last_glist_y;
2334
2335void glist_getnextxy(t_glist *gl, int *xpix, int *ypix)
2336{
2337 if (canvas_last_glist == gl)
2338 *xpix = canvas_last_glist_x, *ypix = canvas_last_glist_y;
2339 else *xpix = *ypix = 40;
2340}
2341
2342static void glist_setlastxy(t_glist *gl, int xval, int yval)
2343{
2344 canvas_last_glist = gl;
2345 canvas_last_glist_x = xval;
2346 canvas_last_glist_y = yval;
2347}
2348
2349
2350void g_editor_setup(void)
2351{
2352/* ------------------------ events ---------------------------------- */
2353 class_addmethod(canvas_class, (t_method)canvas_mousedown, gensym("mouse"),
2354 A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2355 class_addmethod(canvas_class, (t_method)canvas_mouseup, gensym("mouseup"),
2356 A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2357 class_addmethod(canvas_class, (t_method)canvas_key, gensym("key"),
2358 A_GIMME, A_NULL);
2359 class_addmethod(canvas_class, (t_method)canvas_motion, gensym("motion"),
2360 A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2361
2362/* ------------------------ menu actions ---------------------------- */
2363 class_addmethod(canvas_class, (t_method)canvas_menuclose,
2364 gensym("menuclose"), A_DEFFLOAT, 0);
2365 class_addmethod(canvas_class, (t_method)canvas_cut,
2366 gensym("cut"), A_NULL);
2367 class_addmethod(canvas_class, (t_method)canvas_copy,
2368 gensym("copy"), A_NULL);
2369 class_addmethod(canvas_class, (t_method)canvas_paste,
2370 gensym("paste"), A_NULL);
2371 class_addmethod(canvas_class, (t_method)canvas_duplicate,
2372 gensym("duplicate"), A_NULL);
2373 class_addmethod(canvas_class, (t_method)canvas_selectall,
2374 gensym("selectall"), A_NULL);
2375 class_addmethod(canvas_class, (t_method)canvas_undo,
2376 gensym("undo"), A_NULL);
2377 class_addmethod(canvas_class, (t_method)canvas_redo,
2378 gensym("redo"), A_NULL);
2379 class_addmethod(canvas_class, (t_method)canvas_tidy,
2380 gensym("tidy"), A_NULL);
2381 class_addmethod(canvas_class, (t_method)canvas_texteditor,
2382 gensym("texteditor"), A_NULL);
2383 class_addmethod(canvas_class, (t_method)canvas_editmode,
2384 gensym("editmode"), A_DEFFLOAT, A_NULL);
2385 class_addmethod(canvas_class, (t_method)canvas_print,
2386 gensym("print"), A_SYMBOL, A_NULL);
2387 class_addmethod(canvas_class, (t_method)canvas_menufont,
2388 gensym("menufont"), A_NULL);
2389 class_addmethod(canvas_class, (t_method)canvas_font,
2390 gensym("font"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2391 class_addmethod(canvas_class, (t_method)canvas_find,
2392 gensym("find"), A_GIMME, A_NULL);
2393 class_addmethod(canvas_class, (t_method)canvas_find_again,
2394 gensym("findagain"), A_NULL);
2395 class_addmethod(canvas_class, (t_method)canvas_find_parent,
2396 gensym("findparent"), A_NULL);
2397 class_addmethod(canvas_class, (t_method)canvas_done_popup,
2398 gensym("done-popup"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2399 class_addmethod(canvas_class, (t_method)canvas_donecanvasdialog,
2400 gensym("donecanvasdialog"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2401 class_addmethod(canvas_class, (t_method)glist_arraydialog,
2402 gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2403
2404/* -------------- connect method used in reading files ------------------ */
2405 class_addmethod(canvas_class, (t_method)canvas_connect,
2406 gensym("connect"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2407
2408 class_addmethod(canvas_class, (t_method)canvas_disconnect,
2409 gensym("disconnect"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
2410/* -------------- copy buffer ------------------ */
2411 copy_binbuf = binbuf_new();
2412}
2413