summaryrefslogtreecommitdiff
path: root/apps/plugins/pdbox/PDa/src/g_editor.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/pdbox/PDa/src/g_editor.c')
-rw-r--r--apps/plugins/pdbox/PDa/src/g_editor.c2273
1 files changed, 0 insertions, 2273 deletions
diff --git a/apps/plugins/pdbox/PDa/src/g_editor.c b/apps/plugins/pdbox/PDa/src/g_editor.c
index a1dea79df1..1190739e1a 100644
--- a/apps/plugins/pdbox/PDa/src/g_editor.c
+++ b/apps/plugins/pdbox/PDa/src/g_editor.c
@@ -2272,2277 +2272,4 @@ void g_editor_setup(void)
2272/* -------------- copy buffer ------------------ */ 2272/* -------------- copy buffer ------------------ */
2273 copy_binbuf = binbuf_new(); 2273 copy_binbuf = binbuf_new();
2274} 2274}
2275/* Copyright (c) 1997-2001 Miller Puckette and others.
2276* For information on usage and redistribution, and for a DISCLAIMER OF ALL
2277* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
2278
2279#include <stdlib.h>
2280#include <stdio.h>
2281#include "m_pd.h"
2282#include "m_imp.h"
2283#include "s_stuff.h"
2284#include "g_canvas.h"
2285#include <string.h>
2286
2287void glist_readfrombinbuf(t_glist *x, t_binbuf *b, char *filename,
2288 int selectem);
2289
2290void open_via_helppath(const char *name, const char *dir);
2291char *class_gethelpdir(t_class *c);
2292
2293/* ------------------ forward declarations --------------- */
2294static void canvas_doclear(t_canvas *x);
2295static void glist_setlastxy(t_glist *gl, int xval, int yval);
2296static void glist_donewloadbangs(t_glist *x);
2297static t_binbuf *canvas_docopy(t_canvas *x);
2298static void canvas_dopaste(t_canvas *x, t_binbuf *b);
2299static void canvas_paste(t_canvas *x);
2300static void canvas_clearline(t_canvas *x);
2301static t_binbuf *copy_binbuf;
2302
2303/* ---------------- generic widget behavior ------------------------- */
2304
2305void gobj_getrect(t_gobj *x, t_glist *glist, int *x1, int *y1,
2306 int *x2, int *y2)
2307{
2308 if (x->g_pd->c_wb && x->g_pd->c_wb->w_getrectfn)
2309 (*x->g_pd->c_wb->w_getrectfn)(x, glist, x1, y1, x2, y2);
2310}
2311 2275
2312void gobj_displace(t_gobj *x, t_glist *glist, int dx, int dy)
2313{
2314 if (x->g_pd->c_wb && x->g_pd->c_wb->w_displacefn)
2315 (*x->g_pd->c_wb->w_displacefn)(x, glist, dx, dy);
2316}
2317
2318void gobj_select(t_gobj *x, t_glist *glist, int state)
2319{
2320 if (x->g_pd->c_wb && x->g_pd->c_wb->w_selectfn)
2321 (*x->g_pd->c_wb->w_selectfn)(x, glist, state);
2322}
2323
2324void gobj_activate(t_gobj *x, t_glist *glist, int state)
2325{
2326 if (x->g_pd->c_wb && x->g_pd->c_wb->w_activatefn)
2327 (*x->g_pd->c_wb->w_activatefn)(x, glist, state);
2328}
2329
2330void gobj_delete(t_gobj *x, t_glist *glist)
2331{
2332 if (x->g_pd->c_wb && x->g_pd->c_wb->w_deletefn)
2333 (*x->g_pd->c_wb->w_deletefn)(x, glist);
2334}
2335
2336void gobj_vis(t_gobj *x, struct _glist *glist, int flag)
2337{
2338 if (x->g_pd->c_wb && x->g_pd->c_wb->w_visfn)
2339 (*x->g_pd->c_wb->w_visfn)(x, glist, flag);
2340}
2341
2342int gobj_click(t_gobj *x, struct _glist *glist,
2343 int xpix, int ypix, int shift, int alt, int dbl, int doit)
2344{
2345 if (x->g_pd->c_wb && x->g_pd->c_wb->w_clickfn)
2346 return ((*x->g_pd->c_wb->w_clickfn)(x,
2347 glist, xpix, ypix, shift, alt, dbl, doit));
2348 else return (0);
2349}
2350
2351/* ------------------------ managing the selection ----------------- */
2352
2353void glist_selectline(t_glist *x, t_outconnect *oc, int index1,
2354 int outno, int index2, int inno)
2355{
2356 if (x->gl_editor)
2357 {
2358 glist_noselect(x);
2359 x->gl_editor->e_selectedline = 1;
2360 x->gl_editor->e_selectline_index1 = index1;
2361 x->gl_editor->e_selectline_outno = outno;
2362 x->gl_editor->e_selectline_index2 = index2;
2363 x->gl_editor->e_selectline_inno = inno;
2364 x->gl_editor->e_selectline_tag = oc;
2365 sys_vgui(".x%x.c itemconfigure l%x -fill blue\n",
2366 x, x->gl_editor->e_selectline_tag);
2367 }
2368}
2369
2370void glist_deselectline(t_glist *x)
2371{
2372 if (x->gl_editor)
2373 {
2374 x->gl_editor->e_selectedline = 0;
2375 sys_vgui(".x%x.c itemconfigure l%x -fill black\n",
2376 x, x->gl_editor->e_selectline_tag);
2377 }
2378}
2379
2380int glist_isselected(t_glist *x, t_gobj *y)
2381{
2382 if (x->gl_editor)
2383 {
2384 t_selection *sel;
2385 for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
2386 if (sel->sel_what == y) return (1);
2387 }
2388 return (0);
2389}
2390
2391 /* call this for unselected objects only */
2392void glist_select(t_glist *x, t_gobj *y)
2393{
2394 if (x->gl_editor)
2395 {
2396 t_selection *sel = (t_selection *)getbytes(sizeof(*sel));
2397 if (x->gl_editor->e_selectedline)
2398 glist_deselectline(x);
2399 /* LATER #ifdef out the following check */
2400 if (glist_isselected(x, y)) bug("glist_select");
2401 sel->sel_next = x->gl_editor->e_selection;
2402 sel->sel_what = y;
2403 x->gl_editor->e_selection = sel;
2404 gobj_select(y, x, 1);
2405 }
2406}
2407
2408 /* call this for selected objects only */
2409void glist_deselect(t_glist *x, t_gobj *y)
2410{
2411 int fixdsp = 0;
2412 static int reenter = 0;
2413 if (reenter) return;
2414 reenter = 1;
2415 if (x->gl_editor)
2416 {
2417 t_selection *sel, *sel2;
2418 t_rtext *z = 0;
2419 if (!glist_isselected(x, y)) bug("glist_deselect");
2420 if (x->gl_editor->e_textedfor)
2421 {
2422 t_rtext *fuddy = glist_findrtext(x, (t_text *)y);
2423 if (x->gl_editor->e_textedfor == fuddy)
2424 {
2425 if (x->gl_editor->e_textdirty)
2426 {
2427 z = fuddy;
2428 canvas_stowconnections(glist_getcanvas(x));
2429 }
2430 gobj_activate(y, x, 0);
2431 }
2432 if (zgetfn(&y->g_pd, gensym("dsp")))
2433 fixdsp = 1;
2434 }
2435 if ((sel = x->gl_editor->e_selection)->sel_what == y)
2436 {
2437 x->gl_editor->e_selection = x->gl_editor->e_selection->sel_next;
2438 gobj_select(sel->sel_what, x, 0);
2439 freebytes(sel, sizeof(*sel));
2440 }
2441 else
2442 {
2443 for (sel = x->gl_editor->e_selection; sel2 = sel->sel_next;
2444 sel = sel2)
2445 {
2446 if (sel2->sel_what == y)
2447 {
2448 sel->sel_next = sel2->sel_next;
2449 gobj_select(sel2->sel_what, x, 0);
2450 freebytes(sel2, sizeof(*sel2));
2451 break;
2452 }
2453 }
2454 }
2455 if (z)
2456 {
2457 char *buf;
2458 int bufsize;
2459
2460 rtext_gettext(z, &buf, &bufsize);
2461 text_setto((t_text *)y, x, buf, bufsize);
2462 canvas_fixlinesfor(glist_getcanvas(x), (t_text *)y);
2463 x->gl_editor->e_textedfor = 0;
2464 }
2465 if (fixdsp)
2466 canvas_update_dsp();
2467 }
2468 reenter = 0;
2469}
2470
2471void glist_noselect(t_glist *x)
2472{
2473 if (x->gl_editor)
2474 {
2475 while (x->gl_editor->e_selection)
2476 glist_deselect(x, x->gl_editor->e_selection->sel_what);
2477 if (x->gl_editor->e_selectedline)
2478 glist_deselectline(x);
2479 }
2480}
2481
2482void glist_selectall(t_glist *x)
2483{
2484 if (x->gl_editor)
2485 {
2486 glist_noselect(x);
2487 if (x->gl_list)
2488 {
2489 t_selection *sel = (t_selection *)getbytes(sizeof(*sel));
2490 t_gobj *y = x->gl_list;
2491 x->gl_editor->e_selection = sel;
2492 sel->sel_what = y;
2493 gobj_select(y, x, 1);
2494 while (y = y->g_next)
2495 {
2496 t_selection *sel2 = (t_selection *)getbytes(sizeof(*sel2));
2497 sel->sel_next = sel2;
2498 sel = sel2;
2499 sel->sel_what = y;
2500 gobj_select(y, x, 1);
2501 }
2502 sel->sel_next = 0;
2503 }
2504 }
2505}
2506
2507 /* get the index of a gobj in a glist. If y is zero, return the
2508 total number of objects. */
2509int glist_getindex(t_glist *x, t_gobj *y)
2510{
2511 t_gobj *y2;
2512 int indx;
2513
2514 for (y2 = x->gl_list, indx = 0; y2 && y2 != y; y2 = y2->g_next)
2515 indx++;
2516 return (indx);
2517}
2518
2519 /* get the index of the object, among selected items, if "selected"
2520 is set; otherwise, among unselected ones. If y is zero, just
2521 counts the selected or unselected objects. */
2522int glist_selectionindex(t_glist *x, t_gobj *y, int selected)
2523{
2524 t_gobj *y2;
2525 int indx;
2526
2527 for (y2 = x->gl_list, indx = 0; y2 && y2 != y; y2 = y2->g_next)
2528 if (selected == glist_isselected(x, y2))
2529 indx++;
2530 return (indx);
2531}
2532
2533static t_gobj *glist_nth(t_glist *x, int n)
2534{
2535 t_gobj *y;
2536 int indx;
2537 for (y = x->gl_list, indx = 0; y; y = y->g_next, indx++)
2538 if (indx == n)
2539 return (y);
2540 return (0);
2541}
2542
2543/* ------------------- support for undo/redo -------------------------- */
2544
2545static t_undofn canvas_undo_fn; /* current undo function if any */
2546static int canvas_undo_whatnext; /* whether we can now UNDO or REDO */
2547static void *canvas_undo_buf; /* data private to the undo function */
2548static t_canvas *canvas_undo_canvas; /* which canvas we can undo on */
2549static const char *canvas_undo_name;
2550
2551void canvas_setundo(t_canvas *x, t_undofn undofn, void *buf,
2552 const char *name)
2553{
2554 int hadone = 0;
2555 /* blow away the old undo information. In one special case the
2556 old undo info is re-used; if so we shouldn't free it here. */
2557 if (canvas_undo_fn && canvas_undo_buf && (buf != canvas_undo_buf))
2558 {
2559 (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_FREE);
2560 hadone = 1;
2561 }
2562 canvas_undo_canvas = x;
2563 canvas_undo_fn = undofn;
2564 canvas_undo_buf = buf;
2565 canvas_undo_whatnext = UNDO_UNDO;
2566 canvas_undo_name = name;
2567 if (x && glist_isvisible(x) && glist_istoplevel(x))
2568 /* enable undo in menu */
2569 sys_vgui("pdtk_undomenu .x%x %s no\n", x, name);
2570 else if (hadone)
2571 sys_vgui("pdtk_undomenu nobody no no\n");
2572}
2573
2574 /* clear undo if it happens to be for the canvas x.
2575 (but if x is 0, clear it regardless of who owns it.) */
2576void canvas_noundo(t_canvas *x)
2577{
2578 if (!x || (x == canvas_undo_canvas))
2579 canvas_setundo(0, 0, 0, "foo");
2580}
2581
2582static void canvas_undo(t_canvas *x)
2583{
2584 if (x != canvas_undo_canvas)
2585 bug("canvas_undo 1");
2586 else if (canvas_undo_whatnext != UNDO_UNDO)
2587 bug("canvas_undo 2");
2588 else
2589 {
2590 /* post("undo"); */
2591 (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_UNDO);
2592 /* enable redo in menu */
2593 if (glist_isvisible(x) && glist_istoplevel(x))
2594 sys_vgui("pdtk_undomenu .x%x no %s\n", x, canvas_undo_name);
2595 canvas_undo_whatnext = UNDO_REDO;
2596 }
2597}
2598
2599static void canvas_redo(t_canvas *x)
2600{
2601 if (x != canvas_undo_canvas)
2602 bug("canvas_undo 1");
2603 else if (canvas_undo_whatnext != UNDO_REDO)
2604 bug("canvas_undo 2");
2605 else
2606 {
2607 /* post("redo"); */
2608 (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_REDO);
2609 /* enable undo in menu */
2610 if (glist_isvisible(x) && glist_istoplevel(x))
2611 sys_vgui("pdtk_undomenu .x%x %s no\n", x, canvas_undo_name);
2612 canvas_undo_whatnext = UNDO_UNDO;
2613 }
2614}
2615
2616/* ------- specific undo methods: 1. connect and disconnect -------- */
2617
2618typedef struct _undo_connect
2619{
2620 int u_index1;
2621 int u_outletno;
2622 int u_index2;
2623 int u_inletno;
2624} t_undo_connect;
2625
2626static void *canvas_undo_set_disconnect(t_canvas *x,
2627 int index1, int outno, int index2, int inno)
2628{
2629 t_undo_connect *buf = (t_undo_connect *)getbytes(sizeof(*buf));
2630 buf->u_index1 = index1;
2631 buf->u_outletno = outno;
2632 buf->u_index2 = index2;
2633 buf->u_inletno = inno;
2634 return (buf);
2635}
2636
2637void canvas_disconnect(t_canvas *x,
2638 float index1, float outno, float index2, float inno)
2639{
2640 t_linetraverser t;
2641 t_outconnect *oc;
2642 linetraverser_start(&t, x);
2643 while (oc = linetraverser_next(&t))
2644 {
2645 int srcno = canvas_getindex(x, &t.tr_ob->ob_g);
2646 int sinkno = canvas_getindex(x, &t.tr_ob2->ob_g);
2647 if (srcno == index1 && t.tr_outno == outno &&
2648 sinkno == index2 && t.tr_inno == inno)
2649 {
2650 sys_vgui(".x%x.c delete l%x\n", x, oc);
2651 obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno);
2652 break;
2653 }
2654 }
2655}
2656
2657static void canvas_undo_disconnect(t_canvas *x, void *z, int action)
2658{
2659 t_undo_connect *buf = z;
2660 if (action == UNDO_UNDO)
2661 {
2662 canvas_connect(x, buf->u_index1, buf->u_outletno,
2663 buf->u_index2, buf->u_inletno);
2664 }
2665 else if (action == UNDO_REDO)
2666 {
2667 canvas_disconnect(x, buf->u_index1, buf->u_outletno,
2668 buf->u_index2, buf->u_inletno);
2669 }
2670 else if (action == UNDO_FREE)
2671 t_freebytes(buf, sizeof(*buf));
2672}
2673
2674 /* connect just calls disconnect actions backward... */
2675static void *canvas_undo_set_connect(t_canvas *x,
2676 int index1, int outno, int index2, int inno)
2677{
2678 return (canvas_undo_set_disconnect(x, index1, outno, index2, inno));
2679}
2680
2681static void canvas_undo_connect(t_canvas *x, void *z, int action)
2682{
2683 int myaction;
2684 if (action == UNDO_UNDO)
2685 myaction = UNDO_REDO;
2686 else if (action == UNDO_REDO)
2687 myaction = UNDO_UNDO;
2688 else myaction = action;
2689 canvas_undo_disconnect(x, z, myaction);
2690}
2691
2692/* ---------- ... 2. cut, clear, and typing into objects: -------- */
2693
2694#define UCUT_CUT 1 /* operation was a cut */
2695#define UCUT_CLEAR 2 /* .. a clear */
2696#define UCUT_TEXT 3 /* text typed into a box */
2697
2698typedef struct _undo_cut
2699{
2700 t_binbuf *u_objectbuf; /* the object cleared or typed into */
2701 t_binbuf *u_reconnectbuf; /* connections into and out of object */
2702 t_binbuf *u_redotextbuf; /* buffer to paste back for redo if TEXT */
2703 int u_mode; /* from flags above */
2704} t_undo_cut;
2705
2706static void *canvas_undo_set_cut(t_canvas *x, int mode)
2707{
2708 t_undo_cut *buf;
2709 t_gobj *y;
2710 t_linetraverser t;
2711 t_outconnect *oc;
2712 int nnotsel= glist_selectionindex(x, 0, 0);
2713 buf = (t_undo_cut *)getbytes(sizeof(*buf));
2714 buf->u_mode = mode;
2715 buf->u_redotextbuf = 0;
2716
2717 /* store connections into/out of the selection */
2718 buf->u_reconnectbuf = binbuf_new();
2719 linetraverser_start(&t, x);
2720 while (oc = linetraverser_next(&t))
2721 {
2722 int issel1 = glist_isselected(x, &t.tr_ob->ob_g);
2723 int issel2 = glist_isselected(x, &t.tr_ob2->ob_g);
2724 if (issel1 != issel2)
2725 {
2726 binbuf_addv(buf->u_reconnectbuf, "ssiiii;",
2727 gensym("#X"), gensym("connect"),
2728 (issel1 ? nnotsel : 0)
2729 + glist_selectionindex(x, &t.tr_ob->ob_g, issel1),
2730 t.tr_outno,
2731 (issel2 ? nnotsel : 0) +
2732 glist_selectionindex(x, &t.tr_ob2->ob_g, issel2),
2733 t.tr_inno);
2734 }
2735 }
2736 if (mode == UCUT_TEXT)
2737 {
2738 buf->u_objectbuf = canvas_docopy(x);
2739 }
2740 else if (mode == UCUT_CUT)
2741 {
2742 buf->u_objectbuf = 0;
2743 }
2744 else if (mode == UCUT_CLEAR)
2745 {
2746 buf->u_objectbuf = canvas_docopy(x);
2747 }
2748 return (buf);
2749}
2750
2751static void canvas_undo_cut(t_canvas *x, void *z, int action)
2752{
2753 t_undo_cut *buf = z;
2754 int mode = buf->u_mode;
2755 if (action == UNDO_UNDO)
2756 {
2757 if (mode == UCUT_CUT)
2758 canvas_dopaste(x, copy_binbuf);
2759 else if (mode == UCUT_CLEAR)
2760 canvas_dopaste(x, buf->u_objectbuf);
2761 else if (mode == UCUT_TEXT)
2762 {
2763 t_gobj *y1, *y2;
2764 glist_noselect(x);
2765 for (y1 = x->gl_list; y2 = y1->g_next; y1 = y2)
2766 ;
2767 if (y1)
2768 {
2769 if (!buf->u_redotextbuf)
2770 {
2771 glist_noselect(x);
2772 glist_select(x, y1);
2773 buf->u_redotextbuf = canvas_docopy(x);
2774 glist_noselect(x);
2775 }
2776 glist_delete(x, y1);
2777 }
2778 canvas_dopaste(x, buf->u_objectbuf);
2779 }
2780 pd_bind(&x->gl_pd, gensym("#X"));
2781 binbuf_eval(buf->u_reconnectbuf, 0, 0, 0);
2782 pd_unbind(&x->gl_pd, gensym("#X"));
2783 }
2784 else if (action == UNDO_REDO)
2785 {
2786 if (mode == UCUT_CUT || mode == UCUT_CLEAR)
2787 canvas_doclear(x);
2788 else if (mode == UCUT_TEXT)
2789 {
2790 t_gobj *y1, *y2;
2791 for (y1 = x->gl_list; y2 = y1->g_next; y1 = y2)
2792 ;
2793 if (y1)
2794 glist_delete(x, y1);
2795 canvas_dopaste(x, buf->u_redotextbuf);
2796 pd_bind(&x->gl_pd, gensym("#X"));
2797 binbuf_eval(buf->u_reconnectbuf, 0, 0, 0);
2798 pd_unbind(&x->gl_pd, gensym("#X"));
2799 }
2800 }
2801 else if (action == UNDO_FREE)
2802 {
2803 if (buf->u_objectbuf)
2804 binbuf_free(buf->u_objectbuf);
2805 if (buf->u_reconnectbuf)
2806 binbuf_free(buf->u_reconnectbuf);
2807 if (buf->u_redotextbuf)
2808 binbuf_free(buf->u_redotextbuf);
2809 t_freebytes(buf, sizeof(*buf));
2810 }
2811}
2812
2813/* --------- 3. motion, including "tidy up" and stretching ----------- */
2814
2815typedef struct _undo_move_elem
2816{
2817 int e_index;
2818 int e_xpix;
2819 int e_ypix;
2820} t_undo_move_elem;
2821
2822typedef struct _undo_move
2823{
2824 t_undo_move_elem *u_vec;
2825 int u_n;
2826} t_undo_move;
2827
2828static int canvas_undo_already_set_move;
2829
2830static void *canvas_undo_set_move(t_canvas *x, int selected)
2831{
2832 int x1, y1, x2, y2, i, indx;
2833 t_gobj *y;
2834 t_undo_move *buf = (t_undo_move *)getbytes(sizeof(*buf));
2835 buf->u_n = selected ? glist_selectionindex(x, 0, 1) : glist_getindex(x, 0);
2836 buf->u_vec = (t_undo_move_elem *)getbytes(sizeof(*buf->u_vec) *
2837 (selected ? glist_selectionindex(x, 0, 1) : glist_getindex(x, 0)));
2838 if (selected)
2839 {
2840 for (y = x->gl_list, i = indx = 0; y; y = y->g_next, indx++)
2841 if (glist_isselected(x, y))
2842 {
2843 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
2844 buf->u_vec[i].e_index = indx;
2845 buf->u_vec[i].e_xpix = x1;
2846 buf->u_vec[i].e_ypix = y1;
2847 i++;
2848 }
2849 }
2850 else
2851 {
2852 for (y = x->gl_list, indx = 0; y; y = y->g_next, indx++)
2853 {
2854 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
2855 buf->u_vec[indx].e_index = indx;
2856 buf->u_vec[indx].e_xpix = x1;
2857 buf->u_vec[indx].e_ypix = y1;
2858 }
2859 }
2860 canvas_undo_already_set_move = 1;
2861 return (buf);
2862}
2863
2864static void canvas_undo_move(t_canvas *x, void *z, int action)
2865{
2866 t_undo_move *buf = z;
2867 if (action == UNDO_UNDO || action == UNDO_REDO)
2868 {
2869 int i;
2870 for (i = 0; i < buf->u_n; i++)
2871 {
2872 int x1, y1, x2, y2, newx, newy;
2873 t_gobj *y;
2874 newx = buf->u_vec[i].e_xpix;
2875 newy = buf->u_vec[i].e_ypix;
2876 y = glist_nth(x, buf->u_vec[i].e_index);
2877 if (y)
2878 {
2879 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
2880 gobj_displace(y, x, newx-x1, newy - y1);
2881 buf->u_vec[i].e_xpix = x1;
2882 buf->u_vec[i].e_ypix = y1;
2883 }
2884 }
2885 }
2886 else if (action == UNDO_FREE)
2887 {
2888 t_freebytes(buf->u_vec, buf->u_n * sizeof(*buf->u_vec));
2889 t_freebytes(buf, sizeof(*buf));
2890 }
2891}
2892
2893/* --------- 4. paste (also duplicate) ----------- */
2894
2895typedef struct _undo_paste
2896{
2897 int u_index; /* index of first object pasted */
2898} t_undo_paste;
2899
2900static void *canvas_undo_set_paste(t_canvas *x)
2901{
2902 t_undo_paste *buf = (t_undo_paste *)getbytes(sizeof(*buf));
2903 buf->u_index = glist_getindex(x, 0);
2904 return (buf);
2905}
2906
2907static void canvas_undo_paste(t_canvas *x, void *z, int action)
2908{
2909 t_undo_paste *buf = z;
2910 if (action == UNDO_UNDO)
2911 {
2912 t_gobj *y;
2913 glist_noselect(x);
2914 for (y = glist_nth(x, buf->u_index); y; y = y->g_next)
2915 glist_select(x, y);
2916 canvas_doclear(x);
2917 }
2918 else if (action == UNDO_REDO)
2919 {
2920 t_selection *sel;
2921 canvas_dopaste(x, copy_binbuf);
2922 /* if it was "duplicate" have to re-enact the displacement. */
2923 if (canvas_undo_name && canvas_undo_name[0] == 'd')
2924 for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
2925 gobj_displace(sel->sel_what, x, 10, 10);
2926 }
2927else if (action == UNDO_FREE)
2928 t_freebytes(buf, sizeof(*buf));
2929}
2930
2931 /* recursively check for abstractions to reload as result of a save.
2932 Don't reload the one we just saved ("except") though. */
2933 /* LATER try to do the same trick for externs. */
2934static void glist_doreload(t_glist *gl, t_symbol *name, t_symbol *dir,
2935 t_gobj *except)
2936{
2937 t_gobj *g;
2938 int i, nobj = glist_getindex(gl, 0); /* number of objects */
2939 for (g = gl->gl_list, i = 0; g && i < nobj; i++)
2940 {
2941 if (g != except && pd_class(&g->g_pd) == canvas_class &&
2942 canvas_isabstraction((t_canvas *)g) &&
2943 ((t_canvas *)g)->gl_name == name &&
2944 canvas_getdir((t_canvas *)g) == dir)
2945 {
2946 /* we're going to remake the object, so "g" will go stale.
2947 Get its index here, and afterward restore g. Also, the
2948 replacement will be at teh end of the list, so we don't
2949 do g = g->g_next in this case. */
2950 int j = glist_getindex(gl, g);
2951 if (!gl->gl_havewindow)
2952 canvas_vis(glist_getcanvas(gl), 1);
2953 glist_noselect(gl);
2954 glist_select(gl, g);
2955 canvas_setundo(gl, canvas_undo_cut,
2956 canvas_undo_set_cut(gl, UCUT_CLEAR), "clear");
2957 canvas_doclear(gl);
2958 canvas_undo(gl);
2959 glist_noselect(gl);
2960 g = glist_nth(gl, j);
2961 }
2962 else
2963 {
2964 if (g != except && pd_class(&g->g_pd) == canvas_class)
2965 glist_doreload((t_canvas *)g, name, dir, except);
2966 g = g->g_next;
2967 }
2968 }
2969}
2970
2971 /* call canvas_doreload on everyone */
2972void canvas_reload(t_symbol *name, t_symbol *dir, t_gobj *except)
2973{
2974 t_canvas *x;
2975 /* find all root canvases */
2976 for (x = canvas_list; x; x = x->gl_next)
2977 glist_doreload(x, name, dir, except);
2978}
2979
2980/* ------------------------ event handling ------------------------ */
2981
2982#define CURSOR_RUNMODE_NOTHING 0
2983#define CURSOR_RUNMODE_CLICKME 1
2984#define CURSOR_RUNMODE_THICKEN 2
2985#define CURSOR_RUNMODE_ADDPOINT 3
2986#define CURSOR_EDITMODE_NOTHING 4
2987#define CURSOR_EDITMODE_CONNECT 5
2988#define CURSOR_EDITMODE_DISCONNECT 6
2989
2990static char *cursorlist[] = {
2991#ifdef MSW
2992 "right_ptr", /* CURSOR_RUNMODE_NOTHING */
2993#else
2994 "left_ptr", /* CURSOR_RUNMODE_NOTHING */
2995#endif
2996 "arrow", /* CURSOR_RUNMODE_CLICKME */
2997 "sb_v_double_arrow", /* CURSOR_RUNMODE_THICKEN */
2998 "plus", /* CURSOR_RUNMODE_ADDPOINT */
2999 "hand2", /* CURSOR_EDITMODE_NOTHING */
3000 "circle", /* CURSOR_EDITMODE_CONNECT */
3001 "X_cursor" /* CURSOR_EDITMODE_DISCONNECT */
3002};
3003
3004void canvas_setcursor(t_canvas *x, unsigned int cursornum)
3005{
3006 static t_canvas *xwas;
3007 static unsigned int cursorwas;
3008 if (cursornum >= sizeof(cursorlist)/sizeof *cursorlist)
3009 {
3010 bug("canvas_setcursor");
3011 return;
3012 }
3013 if (xwas != x || cursorwas != cursornum)
3014 {
3015 sys_vgui(".x%x configure -cursor %s\n", x, cursorlist[cursornum]);
3016 xwas = x;
3017 cursorwas = cursornum;
3018 }
3019}
3020
3021 /* check if a point lies in a gobj. */
3022int canvas_hitbox(t_canvas *x, t_gobj *y, int xpos, int ypos,
3023 int *x1p, int *y1p, int *x2p, int *y2p)
3024{
3025 int x1, y1, x2, y2;
3026 t_text *ob;
3027 if ((ob = pd_checkobject(&y->g_pd)) &&
3028 !text_shouldvis(ob, x))
3029 return (0);
3030 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
3031 if (xpos >= x1 && xpos <= x2 && ypos >= y1 && ypos <= y2)
3032 {
3033 *x1p = x1;
3034 *y1p = y1;
3035 *x2p = x2;
3036 *y2p = y2;
3037 return (1);
3038 }
3039 else return (0);
3040}
3041
3042 /* find the last gobj, if any, containing the point. */
3043static t_gobj *canvas_findhitbox(t_canvas *x, int xpos, int ypos,
3044 int *x1p, int *y1p, int *x2p, int *y2p)
3045{
3046 t_gobj *y, *rval = 0;
3047 for (y = x->gl_list; y; y = y->g_next)
3048 {
3049 if (canvas_hitbox(x, y, xpos, ypos, x1p, y1p, x2p, y2p))
3050 rval = y;
3051 }
3052 return (rval);
3053}
3054
3055 /* right-clicking on a canvas object pops up a menu. */
3056static void canvas_rightclick(t_canvas *x, int xpos, int ypos, t_gobj *y)
3057{
3058 int canprop, canopen;
3059 canprop = (!y || (y && class_getpropertiesfn(pd_class(&y->g_pd))));
3060 canopen = (y && zgetfn(&y->g_pd, gensym("menu-open")));
3061 sys_vgui("pdtk_canvas_popup .x%x %d %d %d %d\n",
3062 x, xpos, ypos, canprop, canopen);
3063}
3064
3065 /* tell GUI to create a properties dialog on the canvas. We tell
3066 the user the negative of the "pixel" y scale to make it appear to grow
3067 naturally upward, whereas pixels grow downward. */
3068static void canvas_properties(t_glist *x)
3069{
3070 char graphbuf[200];
3071 sprintf(graphbuf, "pdtk_canvas_dialog %%s %g %g %g %g \n",
3072 glist_dpixtodx(x, 1), -glist_dpixtody(x, 1),
3073 (float)glist_isgraph(x), (float)x->gl_stretch);
3074 gfxstub_new(&x->gl_pd, x, graphbuf);
3075}
3076
3077
3078void canvas_setgraph(t_glist *x, int flag)
3079{
3080 if (!flag && glist_isgraph(x))
3081 {
3082 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
3083 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
3084 x->gl_isgraph = 0;
3085 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
3086 {
3087 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
3088 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
3089 }
3090 }
3091 else if (flag && !glist_isgraph(x))
3092 {
3093 if (x->gl_pixwidth <= 0)
3094 x->gl_pixwidth = GLIST_DEFGRAPHWIDTH;
3095
3096 if (x->gl_pixheight <= 0)
3097 x->gl_pixheight = GLIST_DEFGRAPHHEIGHT;
3098
3099 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
3100 gobj_vis(&x->gl_gobj, x->gl_owner, 0);
3101 x->gl_isgraph = 1;
3102 /* if (x->gl_owner && glist_isvisible(x->gl_owner))
3103 canvas_vis(x, 1); */
3104 if (x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner))
3105 canvas_create_editor(x, 1);
3106 if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner))
3107 {
3108 gobj_vis(&x->gl_gobj, x->gl_owner, 1);
3109 canvas_fixlinesfor(x->gl_owner, &x->gl_obj);
3110 }
3111 }
3112}
3113
3114 /* called from the gui when "OK" is selected on the canvas properties
3115 dialog. Again we negate "y" scale. */
3116static void canvas_donecanvasdialog(t_glist *x, t_floatarg xperpix,
3117 t_floatarg yperpix, t_floatarg fgraphme)
3118{
3119 int graphme = (fgraphme != 0), redraw = 0;
3120 yperpix = -yperpix;
3121 if (xperpix == 0)
3122 xperpix = 1;
3123 if (yperpix == 0)
3124 yperpix = 1;
3125 canvas_setgraph(x, graphme);
3126 if (!x->gl_isgraph && (xperpix != glist_dpixtodx(x, 1)))
3127 {
3128 if (xperpix > 0)
3129 {
3130 x->gl_x1 = 0;
3131 x->gl_x2 = xperpix;
3132 }
3133 else
3134 {
3135 x->gl_x1 = -xperpix * (x->gl_screenx2 - x->gl_screenx1);
3136 x->gl_x2 = x->gl_x1 + xperpix;
3137 }
3138 redraw = 1;
3139 }
3140 if (!x->gl_isgraph && (yperpix != glist_dpixtody(x, 1)))
3141 {
3142 if (yperpix > 0)
3143 {
3144 x->gl_y1 = 0;
3145 x->gl_y2 = yperpix;
3146 }
3147 else
3148 {
3149 x->gl_y1 = -yperpix * (x->gl_screeny2 - x->gl_screeny1);
3150 x->gl_y2 = x->gl_y1 + yperpix;
3151 }
3152 redraw = 1;
3153 }
3154 if (redraw)
3155 canvas_redraw(x);
3156}
3157
3158 /* called from the gui when a popup menu comes back with "properties,"
3159 "open," or "help." */
3160static void canvas_done_popup(t_canvas *x, float which, float xpos, float ypos)
3161{
3162 char pathbuf[MAXPDSTRING], namebuf[MAXPDSTRING];
3163 t_gobj *y;
3164 for (y = x->gl_list; y; y = y->g_next)
3165 {
3166 int x1, y1, x2, y2;
3167 if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2))
3168 {
3169 if (which == 0) /* properties */
3170 {
3171 if (!class_getpropertiesfn(pd_class(&y->g_pd)))
3172 continue;
3173 (*class_getpropertiesfn(pd_class(&y->g_pd)))(y, x);
3174 return;
3175 }
3176 else if (which == 1) /* open */
3177 {
3178 if (!zgetfn(&y->g_pd, gensym("menu-open")))
3179 continue;
3180 vmess(&y->g_pd, gensym("menu-open"), "");
3181 return;
3182 }
3183 else /* help */
3184 {
3185 char *dir;
3186 if (pd_class(&y->g_pd) == canvas_class &&
3187 canvas_isabstraction((t_canvas *)y))
3188 {
3189 t_object *ob = (t_object *)y;
3190 int ac = binbuf_getnatom(ob->te_binbuf);
3191 t_atom *av = binbuf_getvec(ob->te_binbuf);
3192 if (ac < 1)
3193 return;
3194 atom_string(av, namebuf, MAXPDSTRING);
3195 dir = canvas_getdir((t_canvas *)y)->s_name;
3196 }
3197 else
3198 {
3199 strcpy(namebuf, class_gethelpname(pd_class(&y->g_pd)));
3200 dir = class_gethelpdir(pd_class(&y->g_pd));
3201 }
3202 if (strcmp(namebuf + strlen(namebuf) - 3, ".pd"))
3203 strcat(namebuf, ".pd");
3204 open_via_helppath(namebuf, dir);
3205 return;
3206 }
3207 }
3208 }
3209 if (which == 0)
3210 canvas_properties(x);
3211 else if (which == 2)
3212 {
3213 strcpy(pathbuf, sys_libdir->s_name);
3214 strcat(pathbuf, "/doc/5.reference/0.INTRO.txt");
3215 sys_vgui("menu_opentext %s\n", pathbuf);
3216 }
3217}
3218
3219#define NOMOD 0
3220#define SHIFTMOD 1
3221#define CTRLMOD 2
3222#define ALTMOD 4
3223#define RIGHTCLICK 8
3224
3225/* on one-button-mouse machines, you can use double click to
3226 mean right click (which gets the popup menu.) Do this for Mac. */
3227#ifdef MACOSX
3228#define SIMULATERIGHTCLICK
3229#endif
3230
3231#ifdef SIMULATERIGHTCLICK
3232static double canvas_upclicktime;
3233static int canvas_upx, canvas_upy;
3234#define DCLICKINTERVAL 0.25
3235#endif
3236
3237 /* mouse click */
3238void canvas_doclick(t_canvas *x, int xpos, int ypos, int which,
3239 int mod, int doit)
3240{
3241 t_gobj *y;
3242 int shiftmod, runmode, altmod, rightclick;
3243 int x1, y1, x2, y2, clickreturned = 0;
3244
3245 if (!x->gl_editor)
3246 {
3247 bug("editor");
3248 return;
3249 }
3250
3251 shiftmod = (mod & SHIFTMOD);
3252 runmode = ((mod & CTRLMOD) || (!x->gl_edit));
3253 altmod = (mod & ALTMOD);
3254 rightclick = (mod & RIGHTCLICK);
3255
3256 canvas_undo_already_set_move = 0;
3257
3258 /* if keyboard was grabbed, notify grabber and cancel the grab */
3259 if (doit && x->gl_editor->e_grab && x->gl_editor->e_keyfn)
3260 {
3261 (* x->gl_editor->e_keyfn) (x->gl_editor->e_grab, 0);
3262 glist_grab(x, 0, 0, 0, 0, 0);
3263 }
3264
3265#ifdef SIMULATERIGHTCLICK
3266 if (doit && !runmode && xpos == canvas_upx && ypos == canvas_upy &&
3267 sys_getrealtime() - canvas_upclicktime < DCLICKINTERVAL)
3268 rightclick = 1;
3269#endif
3270
3271 x->gl_editor->e_lastmoved = 0;
3272 if (doit)
3273 {
3274 x->gl_editor->e_grab = 0;
3275 x->gl_editor->e_onmotion = MA_NONE;
3276 }
3277 /* post("click %d %d %d %d", xpos, ypos, which, mod); */
3278
3279 if (x->gl_editor->e_onmotion != MA_NONE)
3280 return;
3281
3282 x->gl_editor->e_xwas = xpos;
3283 x->gl_editor->e_ywas = ypos;
3284
3285 if (runmode && !rightclick)
3286 {
3287 for (y = x->gl_list; y; y = y->g_next)
3288 {
3289 /* check if the object wants to be clicked */
3290 if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2)
3291 && (clickreturned = gobj_click(y, x, xpos, ypos,
3292 shiftmod, altmod, 0, doit)))
3293 break;
3294 }
3295 if (!doit)
3296 {
3297 if (y)
3298 canvas_setcursor(x, clickreturned);
3299 else canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
3300 }
3301 return;
3302 }
3303 /* if not a runmode left click, fall here. */
3304 if (y = canvas_findhitbox(x, xpos, ypos, &x1, &y1, &x2, &y2))
3305 {
3306 t_object *ob = pd_checkobject(&y->g_pd);
3307 /* check you're in the rectangle */
3308 ob = pd_checkobject(&y->g_pd);
3309 if (rightclick)
3310 canvas_rightclick(x, xpos, ypos, y);
3311 else if (shiftmod)
3312 {
3313 if (doit)
3314 {
3315 t_rtext *rt;
3316 if (ob && (rt = x->gl_editor->e_textedfor) &&
3317 rt == glist_findrtext(x, ob))
3318 {
3319 rtext_mouse(rt, xpos - x1, ypos - y1, RTEXT_SHIFT);
3320 x->gl_editor->e_onmotion = MA_DRAGTEXT;
3321 x->gl_editor->e_xwas = x1;
3322 x->gl_editor->e_ywas = y1;
3323 }
3324 else
3325 {
3326 if (glist_isselected(x, y))
3327 glist_deselect(x, y);
3328 else glist_select(x, y);
3329 }
3330 }
3331 }
3332 else
3333 {
3334 /* look for an outlet */
3335 int noutlet;
3336 if (ob && (noutlet = obj_noutlets(ob)) && ypos >= y2-4)
3337 {
3338 int width = x2 - x1;
3339 int nout1 = (noutlet > 1 ? noutlet - 1 : 1);
3340 int closest = ((xpos-x1) * (nout1) + width/2)/width;
3341 int hotspot = x1 +
3342 (width - IOWIDTH) * closest / (nout1);
3343 if (closest < noutlet &&
3344 xpos >= (hotspot-1) && xpos <= hotspot + (IOWIDTH+1))
3345 {
3346 if (doit)
3347 {
3348 int issignal = obj_issignaloutlet(ob, closest);
3349 x->gl_editor->e_onmotion = MA_CONNECT;
3350 x->gl_editor->e_xwas = xpos;
3351 x->gl_editor->e_ywas = ypos;
3352 sys_vgui(
3353 ".x%x.c create line %d %d %d %d -width %d -tags x\n",
3354 x, xpos, ypos, xpos, ypos,
3355 (issignal ? 2 : 1));
3356 }
3357 else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT);
3358 }
3359 else if (doit)
3360 goto nooutletafterall;
3361 }
3362 /* not in an outlet; select and move */
3363 else if (doit)
3364 {
3365 t_rtext *rt;
3366 /* check if the box is being text edited */
3367 nooutletafterall:
3368 if (ob && (rt = x->gl_editor->e_textedfor) &&
3369 rt == glist_findrtext(x, ob))
3370 {
3371 rtext_mouse(rt, xpos - x1, ypos - y1, RTEXT_DOWN);
3372 x->gl_editor->e_onmotion = MA_DRAGTEXT;
3373 x->gl_editor->e_xwas = x1;
3374 x->gl_editor->e_ywas = y1;
3375 }
3376 else
3377 {
3378 /* otherwise select and drag to displace */
3379 if (!glist_isselected(x, y))
3380 {
3381 glist_noselect(x);
3382 glist_select(x, y);
3383 }
3384 x->gl_editor->e_onmotion = MA_MOVE;
3385 }
3386 }
3387 else canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
3388 }
3389 return;
3390 }
3391 /* if right click doesn't hit any boxes, call rightclick
3392 routine anyway */
3393 if (rightclick)
3394 canvas_rightclick(x, xpos, ypos, 0);
3395
3396 /* if not an editing action, and if we didn't hit a
3397 box, set cursor and return */
3398 if (runmode || rightclick)
3399 {
3400 canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
3401 return;
3402 }
3403 /* having failed to find a box, we try lines now. */
3404 if (!runmode && !altmod && !shiftmod)
3405 {
3406 t_linetraverser t;
3407 t_outconnect *oc;
3408 float fx = xpos, fy = ypos;
3409 t_glist *glist2 = glist_getcanvas(x);
3410 linetraverser_start(&t, glist2);
3411 while (oc = linetraverser_next(&t))
3412 {
3413 float lx1 = t.tr_lx1, ly1 = t.tr_ly1,
3414 lx2 = t.tr_lx2, ly2 = t.tr_ly2;
3415 float area = (lx2 - lx1) * (fy - ly1) -
3416 (ly2 - ly1) * (fx - lx1);
3417 float dsquare = (lx2-lx1) * (lx2-lx1) + (ly2-ly1) * (ly2-ly1);
3418 if (area * area >= 50 * dsquare) continue;
3419 if ((lx2-lx1) * (fx-lx1) + (ly2-ly1) * (fy-ly1) < 0) continue;
3420 if ((lx2-lx1) * (lx2-fx) + (ly2-ly1) * (ly2-fy) < 0) continue;
3421 if (doit)
3422 {
3423 glist_selectline(glist2, oc,
3424 canvas_getindex(glist2, &t.tr_ob->ob_g), t.tr_outno,
3425 canvas_getindex(glist2, &t.tr_ob2->ob_g), t.tr_inno);
3426 }
3427 canvas_setcursor(x, CURSOR_EDITMODE_DISCONNECT);
3428 return;
3429 }
3430 }
3431 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
3432 if (doit)
3433 {
3434 if (!shiftmod) glist_noselect(x);
3435 sys_vgui(".x%x.c create rectangle %d %d %d %d -tags x\n",
3436 x, xpos, ypos, xpos, ypos);
3437 x->gl_editor->e_xwas = xpos;
3438 x->gl_editor->e_ywas = ypos;
3439 x->gl_editor->e_onmotion = MA_REGION;
3440 }
3441}
3442
3443void canvas_mousedown(t_canvas *x, t_floatarg xpos, t_floatarg ypos,
3444 t_floatarg which, t_floatarg mod)
3445{
3446 canvas_doclick(x, xpos, ypos, which, mod, 1);
3447}
3448
3449int canvas_isconnected (t_canvas *x, t_text *ob1, int n1,
3450 t_text *ob2, int n2)
3451{
3452 t_linetraverser t;
3453 t_outconnect *oc;
3454 linetraverser_start(&t, x);
3455 while (oc = linetraverser_next(&t))
3456 if (t.tr_ob == ob1 && t.tr_outno == n1 &&
3457 t.tr_ob2 == ob2 && t.tr_inno == n2)
3458 return (1);
3459 return (0);
3460}
3461
3462void canvas_doconnect(t_canvas *x, int xpos, int ypos, int which, int doit)
3463{
3464 int x11, y11, x12, y12;
3465 t_gobj *y1;
3466 int x21, y21, x22, y22;
3467 t_gobj *y2;
3468 int xwas = x->gl_editor->e_xwas,
3469 ywas = x->gl_editor->e_ywas;
3470 if (doit) sys_vgui(".x%x.c delete x\n", x);
3471 else sys_vgui(".x%x.c coords x %d %d %d %d\n",
3472 x, x->gl_editor->e_xwas,
3473 x->gl_editor->e_ywas, xpos, ypos);
3474
3475 if ((y1 = canvas_findhitbox(x, xwas, ywas, &x11, &y11, &x12, &y12))
3476 && (y2 = canvas_findhitbox(x, xpos, ypos, &x21, &y21, &x22, &y22)))
3477 {
3478 t_object *ob1 = pd_checkobject(&y1->g_pd);
3479 t_object *ob2 = pd_checkobject(&y2->g_pd);
3480 int noutlet1, ninlet2;
3481 if (ob1 && ob2 && ob1 != ob2 &&
3482 (noutlet1 = obj_noutlets(ob1))
3483 && (ninlet2 = obj_ninlets(ob2)))
3484 {
3485 int width1 = x12 - x11, closest1, hotspot1;
3486 int width2 = x22 - x21, closest2, hotspot2;
3487 int lx1, lx2, ly1, ly2;
3488 t_outconnect *oc;
3489
3490 if (noutlet1 > 1)
3491 {
3492 closest1 = ((xwas-x11) * (noutlet1-1) + width1/2)/width1;
3493 hotspot1 = x11 +
3494 (width1 - IOWIDTH) * closest1 / (noutlet1-1);
3495 }
3496 else closest1 = 0, hotspot1 = x11;
3497
3498 if (ninlet2 > 1)
3499 {
3500 closest2 = ((xpos-x21) * (ninlet2-1) + width2/2)/width2;
3501 hotspot2 = x21 +
3502 (width2 - IOWIDTH) * closest2 / (ninlet2-1);
3503 }
3504 else closest2 = 0, hotspot2 = x21;
3505
3506 if (closest1 >= noutlet1)
3507 closest1 = noutlet1 - 1;
3508 if (closest2 >= ninlet2)
3509 closest2 = ninlet2 - 1;
3510
3511 if (canvas_isconnected (x, ob1, closest1, ob2, closest2))
3512 {
3513 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
3514 return;
3515 }
3516 if (obj_issignaloutlet(ob1, closest1) &&
3517 !obj_issignalinlet(ob2, closest2))
3518 {
3519 if (doit)
3520 error("can't connect signal outlet to control inlet");
3521 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
3522 return;
3523 }
3524 if (doit)
3525 {
3526 oc = obj_connect(ob1, closest1, ob2, closest2);
3527 lx1 = x11 + (noutlet1 > 1 ?
3528 ((x12-x11-IOWIDTH) * closest1)/(noutlet1-1) : 0)
3529 + IOMIDDLE;
3530 ly1 = y12;
3531 lx2 = x21 + (ninlet2 > 1 ?
3532 ((x22-x21-IOWIDTH) * closest2)/(ninlet2-1) : 0)
3533 + IOMIDDLE;
3534 ly2 = y21;
3535 sys_vgui(".x%x.c create line %d %d %d %d -width %d -tags l%x\n",
3536 glist_getcanvas(x),
3537 lx1, ly1, lx2, ly2,
3538 (obj_issignaloutlet(ob1, closest1) ? 2 : 1), oc);
3539 canvas_setundo(x, canvas_undo_connect,
3540 canvas_undo_set_connect(x,
3541 canvas_getindex(x, &ob1->ob_g), closest1,
3542 canvas_getindex(x, &ob2->ob_g), closest2),
3543 "connect");
3544 }
3545 else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT);
3546 return;
3547 }
3548 }
3549 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
3550}
3551
3552void canvas_selectinrect(t_canvas *x, int lox, int loy, int hix, int hiy)
3553{
3554 t_gobj *y;
3555 for (y = x->gl_list; y; y = y->g_next)
3556 {
3557 int x1, y1, x2, y2;
3558 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
3559 if (hix >= x1 && lox <= x2 && hiy >= y1 && loy <= y2
3560 && !glist_isselected(x, y))
3561 glist_select(x, y);
3562 }
3563}
3564
3565static void canvas_doregion(t_canvas *x, int xpos, int ypos, int doit)
3566{
3567 if (doit)
3568 {
3569 int lox, loy, hix, hiy;
3570 if (x->gl_editor->e_xwas < xpos)
3571 lox = x->gl_editor->e_xwas, hix = xpos;
3572 else hix = x->gl_editor->e_xwas, lox = xpos;
3573 if (x->gl_editor->e_ywas < ypos)
3574 loy = x->gl_editor->e_ywas, hiy = ypos;
3575 else hiy = x->gl_editor->e_ywas, loy = ypos;
3576 canvas_selectinrect(x, lox, loy, hix, hiy);
3577 sys_vgui(".x%x.c delete x\n", x);
3578 x->gl_editor->e_onmotion = 0;
3579 }
3580 else sys_vgui(".x%x.c coords x %d %d %d %d\n",
3581 x, x->gl_editor->e_xwas,
3582 x->gl_editor->e_ywas, xpos, ypos);
3583}
3584
3585void canvas_mouseup(t_canvas *x,
3586 t_floatarg fxpos, t_floatarg fypos, t_floatarg fwhich)
3587{
3588 t_gobj *y;
3589
3590 int xpos = fxpos, ypos = fypos, which = fwhich;
3591
3592 if (!x->gl_editor)
3593 {
3594 bug("editor");
3595 return;
3596 }
3597
3598#ifdef SIMULATERIGHTCLICK
3599 canvas_upclicktime = sys_getrealtime();
3600 canvas_upx = xpos;
3601 canvas_upy = ypos;
3602#endif
3603
3604 if (x->gl_editor->e_onmotion == MA_CONNECT)
3605 canvas_doconnect(x, xpos, ypos, which, 1);
3606 else if (x->gl_editor->e_onmotion == MA_REGION)
3607 canvas_doregion(x, xpos, ypos, 1);
3608 else if (x->gl_editor->e_onmotion == MA_MOVE)
3609 {
3610 /* after motion, if there's only one item selected, activate it */
3611 if (x->gl_editor->e_selection &&
3612 !(x->gl_editor->e_selection->sel_next))
3613 gobj_activate(x->gl_editor->e_selection->sel_what,
3614 x, 1);
3615 }
3616 else if (x->gl_editor->e_onmotion == MA_PASSOUT)
3617 x->gl_editor->e_onmotion = 0;
3618 x->gl_editor->e_onmotion = MA_NONE;
3619
3620
3621#if 1
3622 /* GG misused the (unused) dbl parameter to tell if mouseup */
3623
3624 for (y = x->gl_list; y; y = y->g_next) {
3625 int x1, y1, x2, y2, clickreturned = 0;
3626
3627 /* check if the object wants to be clicked */
3628 if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2)
3629 && (clickreturned = gobj_click(y, x, xpos, ypos,
3630 0, 0, 1, 0)))
3631 break;
3632 }
3633#endif
3634
3635
3636}
3637
3638 /* displace the selection by (dx, dy) pixels */
3639static void canvas_displaceselection(t_canvas *x, int dx, int dy)
3640{
3641 t_selection *y;
3642 int resortin = 0, resortout = 0;
3643 if (!canvas_undo_already_set_move)
3644 {
3645 canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, 1),
3646 "motion");
3647 canvas_undo_already_set_move = 1;
3648 }
3649 for (y = x->gl_editor->e_selection; y; y = y->sel_next)
3650 {
3651 t_class *cl = pd_class(&y->sel_what->g_pd);
3652 gobj_displace(y->sel_what, x, dx, dy);
3653 if (cl == vinlet_class) resortin = 1;
3654 else if (cl == voutlet_class) resortout = 1;
3655 }
3656 if (resortin) canvas_resortinlets(x);
3657 if (resortout) canvas_resortoutlets(x);
3658 canvas_dirty(x, 1);
3659}
3660
3661 /* this routine is called whenever a key is pressed or released. "x"
3662 may be zero if there's no current canvas. The first argument is true or
3663 fals for down/up; the second one is either a symbolic key name (e.g.,
3664 "Right" or an Ascii key number. */
3665void canvas_key(t_canvas *x, t_symbol *s, int ac, t_atom *av)
3666{
3667 static t_symbol *keynumsym, *keyupsym, *keynamesym;
3668 int keynum, fflag;
3669 t_symbol *gotkeysym;
3670
3671 int down, shift;
3672
3673 if (ac < 3)
3674 return;
3675 if (!x->gl_editor)
3676 {
3677 bug("editor");
3678 return;
3679 }
3680 canvas_undo_already_set_move = 0;
3681 down = (atom_getfloat(av) != 0); /* nonzero if it's a key down */
3682 shift = (atom_getfloat(av+2) != 0); /* nonzero if shift-ed */
3683 if (av[1].a_type == A_SYMBOL)
3684 gotkeysym = av[1].a_w.w_symbol;
3685 else if (av[1].a_type == A_FLOAT)
3686 {
3687 char buf[3];
3688 sprintf(buf, "%c", (int)(av[1].a_w.w_float));
3689 gotkeysym = gensym(buf);
3690 }
3691 else gotkeysym = gensym("?");
3692 fflag = (av[0].a_type == A_FLOAT ? av[0].a_w.w_float : 0);
3693 keynum = (av[1].a_type == A_FLOAT ? av[1].a_w.w_float : 0);
3694 if (keynum == '\\' || keynum == '{' || keynum == '}')
3695 {
3696 post("%c: dropped", (int)keynum);
3697 return;
3698 }
3699 if (keynum == '\r') keynum = '\n';
3700 if (av[1].a_type == A_SYMBOL &&
3701 !strcmp(av[1].a_w.w_symbol->s_name, "Return"))
3702 keynum = '\n';
3703 if (!keynumsym)
3704 {
3705 keynumsym = gensym("#key");
3706 keyupsym = gensym("#keyup");
3707 keynamesym = gensym("#keyname");
3708 }
3709#ifdef MACOSX
3710 if (keynum == 30)
3711 keynum = 0, gotkeysym = gensym("Up");
3712 else if (keynum == 31)
3713 keynum = 0, gotkeysym = gensym("Down");
3714 else if (keynum == 28)
3715 keynum = 0, gotkeysym = gensym("Left");
3716 else if (keynum == 29)
3717 keynum = 0, gotkeysym = gensym("Right");
3718#endif
3719 if (keynumsym->s_thing && down)
3720 pd_float(keynumsym->s_thing, (float)keynum);
3721 if (keyupsym->s_thing && !down)
3722 pd_float(keyupsym->s_thing, (float)keynum);
3723 if (keynamesym->s_thing)
3724 {
3725 t_atom at[2];
3726 at[0] = av[0];
3727 SETFLOAT(at, down);
3728 SETSYMBOL(at+1, gotkeysym);
3729 pd_list(keynamesym->s_thing, 0, 2, at);
3730 }
3731 if (!x->gl_editor) /* if that 'invis'ed the window, we'd better stop. */
3732 return;
3733 if (x && down)
3734 {
3735 /* if an object has "grabbed" keys just send them on */
3736 if (x->gl_editor->e_grab
3737 && x->gl_editor->e_keyfn && keynum)
3738 (* x->gl_editor->e_keyfn)
3739 (x->gl_editor->e_grab, (float)keynum);
3740 /* if a text editor is open send it on */
3741 else if (x->gl_editor->e_textedfor)
3742 {
3743 if (!x->gl_editor->e_textdirty)
3744 {
3745 canvas_setundo(x, canvas_undo_cut,
3746 canvas_undo_set_cut(x, UCUT_TEXT), "typing");
3747 }
3748 rtext_key(x->gl_editor->e_textedfor,
3749 (int)keynum, gotkeysym);
3750 if (x->gl_editor->e_textdirty)
3751 canvas_dirty(x, 1);
3752 }
3753 /* check for backspace or clear */
3754 else if (keynum == 8 || keynum == 127)
3755 {
3756 if (x->gl_editor->e_selectedline)
3757 canvas_clearline(x);
3758 else if (x->gl_editor->e_selection)
3759 {
3760 canvas_setundo(x, canvas_undo_cut,
3761 canvas_undo_set_cut(x, UCUT_CLEAR), "clear");
3762 canvas_doclear(x);
3763 }
3764 }
3765 /* check for arrow keys */
3766 else if (!strcmp(gotkeysym->s_name, "Up"))
3767 canvas_displaceselection(x, 0, shift ? -10 : -1);
3768 else if (!strcmp(gotkeysym->s_name, "Down"))
3769 canvas_displaceselection(x, 0, shift ? 10 : 1);
3770 else if (!strcmp(gotkeysym->s_name, "Left"))
3771 canvas_displaceselection(x, shift ? -10 : -1, 0);
3772 else if (!strcmp(gotkeysym->s_name, "Right"))
3773 canvas_displaceselection(x, shift ? 10 : 1, 0);
3774 }
3775}
3776
3777void canvas_motion(t_canvas *x, t_floatarg xpos, t_floatarg ypos,
3778 t_floatarg fmod)
3779{
3780 /* post("motion %d %d", xpos, ypos); */
3781 int mod = fmod;
3782 if (!x->gl_editor)
3783 {
3784 bug("editor");
3785 return;
3786 }
3787 glist_setlastxy(x, xpos, ypos);
3788 if (x->gl_editor->e_onmotion == MA_MOVE)
3789 {
3790 canvas_displaceselection(x,
3791 xpos - x->gl_editor->e_xwas, ypos - x->gl_editor->e_ywas);
3792 x->gl_editor->e_xwas = xpos;
3793 x->gl_editor->e_ywas = ypos;
3794 }
3795 else if (x->gl_editor->e_onmotion == MA_REGION)
3796 canvas_doregion(x, xpos, ypos, 0);
3797 else if (x->gl_editor->e_onmotion == MA_CONNECT)
3798 canvas_doconnect(x, xpos, ypos, 0, 0);
3799 else if (x->gl_editor->e_onmotion == MA_PASSOUT)
3800 {
3801 if (!x->gl_editor->e_motionfn)
3802 bug("e_motionfn");
3803 (*x->gl_editor->e_motionfn)(&x->gl_editor->e_grab->g_pd,
3804 xpos - x->gl_editor->e_xwas,
3805 ypos - x->gl_editor->e_ywas);
3806 x->gl_editor->e_xwas = xpos;
3807 x->gl_editor->e_ywas = ypos;
3808 }
3809 else if (x->gl_editor->e_onmotion == MA_DRAGTEXT)
3810 {
3811 t_rtext *rt = x->gl_editor->e_textedfor;
3812 if (rt)
3813 rtext_mouse(rt, xpos - x->gl_editor->e_xwas,
3814 ypos - x->gl_editor->e_ywas, RTEXT_DRAG);
3815 }
3816 else canvas_doclick(x, xpos, ypos, 0, mod, 0);
3817
3818 x->gl_editor->e_lastmoved = 1;
3819}
3820
3821void canvas_startmotion(t_canvas *x)
3822{
3823 int xval, yval;
3824 if (!x->gl_editor) return;
3825 glist_getnextxy(x, &xval, &yval);
3826 if (xval == 0 && yval == 0) return;
3827 x->gl_editor->e_onmotion = MA_MOVE;
3828 x->gl_editor->e_xwas = xval;
3829 x->gl_editor->e_ywas = yval;
3830}
3831
3832/* ----------------------------- window stuff ----------------------- */
3833
3834void canvas_print(t_canvas *x, t_symbol *s)
3835{
3836 if (*s->s_name) sys_vgui(".x%x.c postscript -file %s\n", x, s->s_name);
3837 else sys_vgui(".x%x.c postscript -file x.ps\n", x);
3838}
3839
3840void canvas_menuclose(t_canvas *x, t_floatarg force)
3841{
3842 if (x->gl_owner)
3843 canvas_vis(x, 0);
3844 else if ((force != 0) || (!x->gl_dirty))
3845 pd_free(&x->gl_pd);
3846 else sys_vgui("pdtk_check {This window has been modified. Close anyway?}\
3847 {.x%x menuclose 1;\n}\n", x);
3848}
3849
3850 /* put up a dialog which may call canvas_font back to do the work */
3851static void canvas_menufont(t_canvas *x)
3852{
3853 char buf[80];
3854 t_canvas *x2 = canvas_getrootfor(x);
3855 gfxstub_deleteforkey(x2);
3856 sprintf(buf, "pdtk_canvas_dofont %%s %d\n", x2->gl_font);
3857 gfxstub_new(&x2->gl_pd, &x2->gl_pd, buf);
3858}
3859
3860static int canvas_find_index1, canvas_find_index2;
3861static t_binbuf *canvas_findbuf;
3862int binbuf_match(t_binbuf *inbuf, t_binbuf *searchbuf);
3863
3864 /* find an atom or string of atoms */
3865static int canvas_dofind(t_canvas *x, int *myindex1p)
3866{
3867 t_gobj *y;
3868 int myindex1 = *myindex1p, myindex2;
3869 if (myindex1 >= canvas_find_index1)
3870 {
3871 for (y = x->gl_list, myindex2 = 0; y;
3872 y = y->g_next, myindex2++)
3873 {
3874 t_object *ob = 0;
3875 if (ob = pd_checkobject(&y->g_pd))
3876 {
3877 if (binbuf_match(ob->ob_binbuf, canvas_findbuf))
3878 {
3879 if (myindex1 > canvas_find_index1 ||
3880 myindex1 == canvas_find_index1 &&
3881 myindex2 > canvas_find_index2)
3882 {
3883 canvas_find_index1 = myindex1;
3884 canvas_find_index2 = myindex2;
3885 glist_noselect(x);
3886 canvas_vis(x, 1);
3887 canvas_editmode(x, 1.);
3888 glist_select(x, y);
3889 return (1);
3890 }
3891 }
3892 }
3893 }
3894 }
3895 for (y = x->gl_list, myindex2 = 0; y; y = y->g_next, myindex2++)
3896 {
3897 if (pd_class(&y->g_pd) == canvas_class)
3898 {
3899 (*myindex1p)++;
3900 if (canvas_dofind((t_canvas *)y, myindex1p))
3901 return (1);
3902 }
3903 }
3904 return (0);
3905}
3906
3907static void canvas_find(t_canvas *x, t_symbol *s, int ac, t_atom *av)
3908{
3909 int myindex1 = 0, i;
3910 for (i = 0; i < ac; i++)
3911 {
3912 if (av[i].a_type == A_SYMBOL)
3913 {
3914 if (!strcmp(av[i].a_w.w_symbol->s_name, "_semi_"))
3915 SETSEMI(&av[i]);
3916 else if (!strcmp(av[i].a_w.w_symbol->s_name, "_comma_"))
3917 SETCOMMA(&av[i]);
3918 }
3919 }
3920 if (!canvas_findbuf)
3921 canvas_findbuf = binbuf_new();
3922 binbuf_clear(canvas_findbuf);
3923 binbuf_add(canvas_findbuf, ac, av);
3924 canvas_find_index1 = 0;
3925 canvas_find_index2 = -1;
3926 canvas_whichfind = x;
3927 if (!canvas_dofind(x, &myindex1))
3928 {
3929 binbuf_print(canvas_findbuf);
3930 post("... couldn't find");
3931 }
3932}
3933
3934static void canvas_find_again(t_canvas *x)
3935{
3936 int myindex1 = 0;
3937 if (!canvas_findbuf || !canvas_whichfind)
3938 return;
3939 if (!canvas_dofind(canvas_whichfind, &myindex1))
3940 {
3941 binbuf_print(canvas_findbuf);
3942 post("... couldn't find");
3943 }
3944}
3945
3946static void canvas_find_parent(t_canvas *x)
3947{
3948 if (x->gl_owner)
3949 canvas_vis(glist_getcanvas(x->gl_owner), 1);
3950}
3951
3952static int glist_dofinderror(t_glist *gl, void *error_object)
3953{
3954 t_gobj *g;
3955 for (g = gl->gl_list; g; g = g->g_next)
3956 {
3957 if ((void *)g == error_object)
3958 {
3959 /* got it... now show it. */
3960 glist_noselect(gl);
3961 canvas_vis(glist_getcanvas(gl), 1);
3962 canvas_editmode(glist_getcanvas(gl), 1.);
3963 glist_select(gl, g);
3964 return (1);
3965 }
3966 else if (g->g_pd == canvas_class)
3967 {
3968 if (glist_dofinderror((t_canvas *)g, error_object))
3969 return (1);
3970 }
3971 }
3972 return (0);
3973}
3974
3975void canvas_finderror(void *error_object)
3976{
3977 t_canvas *x;
3978 /* find all root canvases */
3979 for (x = canvas_list; x; x = x->gl_next)
3980 {
3981 if (glist_dofinderror(x, error_object))
3982 return;
3983 }
3984 post("... sorry, I couldn't find the source of that error.");
3985}
3986
3987void canvas_stowconnections(t_canvas *x)
3988{
3989 t_gobj *selhead = 0, *seltail = 0, *nonhead = 0, *nontail = 0, *y, *y2;
3990 t_linetraverser t;
3991 t_outconnect *oc;
3992 if (!x->gl_editor) return;
3993 /* split list to "selected" and "unselected" parts */
3994 for (y = x->gl_list; y; y = y2)
3995 {
3996 y2 = y->g_next;
3997 if (glist_isselected(x, y))
3998 {
3999 if (seltail)
4000 {
4001 seltail->g_next = y;
4002 seltail = y;
4003 y->g_next = 0;
4004 }
4005 else
4006 {
4007 selhead = seltail = y;
4008 seltail->g_next = 0;
4009 }
4010 }
4011 else
4012 {
4013 if (nontail)
4014 {
4015 nontail->g_next = y;
4016 nontail = y;
4017 y->g_next = 0;
4018 }
4019 else
4020 {
4021 nonhead = nontail = y;
4022 nontail->g_next = 0;
4023 }
4024 }
4025 }
4026 /* move the selected part to the end */
4027 if (!nonhead) x->gl_list = selhead;
4028 else x->gl_list = nonhead, nontail->g_next = selhead;
4029
4030 /* add connections to binbuf */
4031 binbuf_clear(x->gl_editor->e_connectbuf);
4032 linetraverser_start(&t, x);
4033 while (oc = linetraverser_next(&t))
4034 {
4035 int s1 = glist_isselected(x, &t.tr_ob->ob_g);
4036 int s2 = glist_isselected(x, &t.tr_ob2->ob_g);
4037 if (s1 != s2)
4038 binbuf_addv(x->gl_editor->e_connectbuf, "ssiiii;",
4039 gensym("#X"), gensym("connect"),
4040 glist_getindex(x, &t.tr_ob->ob_g), t.tr_outno,
4041 glist_getindex(x, &t.tr_ob2->ob_g), t.tr_inno);
4042 }
4043}
4044
4045void canvas_restoreconnections(t_canvas *x)
4046{
4047 pd_bind(&x->gl_pd, gensym("#X"));
4048 binbuf_eval(x->gl_editor->e_connectbuf, 0, 0, 0);
4049 pd_unbind(&x->gl_pd, gensym("#X"));
4050}
4051
4052static t_binbuf *canvas_docopy(t_canvas *x)
4053{
4054 t_gobj *y;
4055 t_linetraverser t;
4056 t_outconnect *oc;
4057 t_binbuf *b = binbuf_new();
4058 for (y = x->gl_list; y; y = y->g_next)
4059 {
4060 if (glist_isselected(x, y))
4061 gobj_save(y, b);
4062 }
4063 linetraverser_start(&t, x);
4064 while (oc = linetraverser_next(&t))
4065 {
4066 if (glist_isselected(x, &t.tr_ob->ob_g)
4067 && glist_isselected(x, &t.tr_ob2->ob_g))
4068 {
4069 binbuf_addv(b, "ssiiii;", gensym("#X"), gensym("connect"),
4070 glist_selectionindex(x, &t.tr_ob->ob_g, 1), t.tr_outno,
4071 glist_selectionindex(x, &t.tr_ob2->ob_g, 1), t.tr_inno);
4072 }
4073 }
4074 return (b);
4075}
4076
4077static void canvas_copy(t_canvas *x)
4078{
4079 if (!x->gl_editor || !x->gl_editor->e_selection)
4080 return;
4081 binbuf_free(copy_binbuf);
4082 copy_binbuf = canvas_docopy(x);
4083}
4084
4085static void canvas_clearline(t_canvas *x)
4086{
4087 if (x->gl_editor->e_selectedline)
4088 {
4089 canvas_disconnect(x, x->gl_editor->e_selectline_index1,
4090 x->gl_editor->e_selectline_outno,
4091 x->gl_editor->e_selectline_index2,
4092 x->gl_editor->e_selectline_inno);
4093 canvas_setundo(x, canvas_undo_disconnect,
4094 canvas_undo_set_disconnect(x,
4095 x->gl_editor->e_selectline_index1,
4096 x->gl_editor->e_selectline_outno,
4097 x->gl_editor->e_selectline_index2,
4098 x->gl_editor->e_selectline_inno),
4099 "disconnect");
4100 }
4101}
4102
4103extern t_pd *newest;
4104static void canvas_doclear(t_canvas *x)
4105{
4106 t_gobj *y, *y2;
4107 int dspstate;
4108
4109 dspstate = canvas_suspend_dsp();
4110 if (x->gl_editor->e_selectedline)
4111 {
4112 canvas_disconnect(x, x->gl_editor->e_selectline_index1,
4113 x->gl_editor->e_selectline_outno,
4114 x->gl_editor->e_selectline_index2,
4115 x->gl_editor->e_selectline_inno);
4116 canvas_setundo(x, canvas_undo_disconnect,
4117 canvas_undo_set_disconnect(x,
4118 x->gl_editor->e_selectline_index1,
4119 x->gl_editor->e_selectline_outno,
4120 x->gl_editor->e_selectline_index2,
4121 x->gl_editor->e_selectline_inno),
4122 "disconnect");
4123 }
4124 /* if text is selected, deselecting it might remake the
4125 object. So we deselect it and hunt for a "new" object on
4126 the glist to reselect. */
4127 if (x->gl_editor->e_textedfor)
4128 {
4129 newest = 0;
4130 glist_noselect(x);
4131 if (newest)
4132 {
4133 for (y = x->gl_list; y; y = y->g_next)
4134 if (&y->g_pd == newest) glist_select(x, y);
4135 }
4136 }
4137 while (1) /* this is pretty wierd... should rewrite it */
4138 {
4139 for (y = x->gl_list; y; y = y2)
4140 {
4141 y2 = y->g_next;
4142 if (glist_isselected(x, y))
4143 {
4144 glist_delete(x, y);
4145#if 0
4146 if (y2) post("cut 5 %x %x", y2, y2->g_next);
4147 else post("cut 6");
4148#endif
4149 goto next;
4150 }
4151 }
4152 goto restore;
4153 next: ;
4154 }
4155restore:
4156 canvas_resume_dsp(dspstate);
4157 canvas_dirty(x, 1);
4158}
4159
4160static void canvas_cut(t_canvas *x)
4161{
4162 if (x->gl_editor && x->gl_editor->e_selectedline)
4163 canvas_clearline(x);
4164 else if (x->gl_editor && x->gl_editor->e_selection)
4165 {
4166 canvas_setundo(x, canvas_undo_cut,
4167 canvas_undo_set_cut(x, UCUT_CUT), "cut");
4168 canvas_copy(x);
4169 canvas_doclear(x);
4170 }
4171}
4172
4173static int paste_onset;
4174static t_canvas *paste_canvas;
4175
4176static void glist_donewloadbangs(t_glist *x)
4177{
4178 if (x->gl_editor)
4179 {
4180 t_selection *sel;
4181 for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next)
4182 if (pd_class(&sel->sel_what->g_pd) == canvas_class)
4183 canvas_loadbang((t_canvas *)(&sel->sel_what->g_pd));
4184 }
4185}
4186
4187static void canvas_dopaste(t_canvas *x, t_binbuf *b)
4188{
4189 t_gobj *newgobj, *last, *g2;
4190 int dspstate = canvas_suspend_dsp(), nbox, count;
4191
4192 canvas_editmode(x, 1.);
4193 glist_noselect(x);
4194 for (g2 = x->gl_list, nbox = 0; g2; g2 = g2->g_next) nbox++;
4195
4196 paste_onset = nbox;
4197 paste_canvas = x;
4198
4199 pd_bind(&x->gl_pd, gensym("#X"));
4200 binbuf_eval(b, 0, 0, 0);
4201 pd_unbind(&x->gl_pd, gensym("#X"));
4202 for (g2 = x->gl_list, count = 0; g2; g2 = g2->g_next, count++)
4203 if (count >= nbox)
4204 glist_select(x, g2);
4205 paste_canvas = 0;
4206 canvas_resume_dsp(dspstate);
4207 canvas_dirty(x, 1);
4208 glist_donewloadbangs(x);
4209}
4210
4211static void canvas_paste(t_canvas *x)
4212{
4213 canvas_setundo(x, canvas_undo_paste, canvas_undo_set_paste(x), "paste");
4214 canvas_dopaste(x, copy_binbuf);
4215}
4216
4217static void canvas_duplicate(t_canvas *x)
4218{
4219 if (x->gl_editor->e_onmotion == MA_NONE)
4220 {
4221 t_selection *y;
4222 canvas_copy(x);
4223 canvas_setundo(x, canvas_undo_paste, canvas_undo_set_paste(x),
4224 "duplicate");
4225 canvas_dopaste(x, copy_binbuf);
4226 for (y = x->gl_editor->e_selection; y; y = y->sel_next)
4227 gobj_displace(y->sel_what, x,
4228 10, 10);
4229 canvas_dirty(x, 1);
4230 }
4231}
4232
4233static void canvas_selectall(t_canvas *x)
4234{
4235 t_gobj *y;
4236 if (!x->gl_edit)
4237 canvas_editmode(x, 1);
4238 for (y = x->gl_list; y; y = y->g_next)
4239 {
4240 if (!glist_isselected(x, y))
4241 glist_select(x, y);
4242 }
4243}
4244
4245void canvas_connect(t_canvas *x, t_floatarg fwhoout, t_floatarg foutno,
4246 t_floatarg fwhoin, t_floatarg finno)
4247{
4248 int whoout = fwhoout, outno = foutno, whoin = fwhoin, inno = finno;
4249 t_gobj *src = 0, *sink = 0;
4250 t_object *objsrc, *objsink;
4251 t_outconnect *oc;
4252 int nin = whoin, nout = whoout;
4253 if (paste_canvas == x) whoout += paste_onset, whoin += paste_onset;
4254 for (src = x->gl_list; whoout; src = src->g_next, whoout--)
4255 if (!src->g_next) goto bad; /* bug fix thanks to Hannes */
4256 for (sink = x->gl_list; whoin; sink = sink->g_next, whoin--)
4257 if (!sink->g_next) goto bad;
4258 if (!(objsrc = pd_checkobject(&src->g_pd)) ||
4259 !(objsink = pd_checkobject(&sink->g_pd)))
4260 goto bad;
4261 if (!(oc = obj_connect(objsrc, outno, objsink, inno))) goto bad;
4262 if (glist_isvisible(x))
4263 {
4264 sys_vgui(".x%x.c create line %d %d %d %d -width %d -tags l%x\n",
4265 glist_getcanvas(x), 0, 0, 0, 0,
4266 (obj_issignaloutlet(objsrc, outno) ? 2 : 1),oc);
4267 canvas_fixlinesfor(x, objsrc);
4268 }
4269 return;
4270
4271bad:
4272 post("%s %d %d %d %d (%s->%s) connection failed",
4273 x->gl_name->s_name, nout, outno, nin, inno,
4274 (src? class_getname(pd_class(&src->g_pd)) : "???"),
4275 (sink? class_getname(pd_class(&sink->g_pd)) : "???"));
4276}
4277
4278#define XTOLERANCE 4
4279#define YTOLERANCE 3
4280#define NHIST 15
4281
4282 /* LATER might have to speed this up */
4283static void canvas_tidy(t_canvas *x)
4284{
4285 t_gobj *y, *y2, *y3;
4286 int ax1, ay1, ax2, ay2, bx1, by1, bx2, by2;
4287 int histogram[NHIST], *ip, i, besthist, bestdist;
4288 /* if nobody is selected, this means do it to all boxes;
4289 othewise just the selection */
4290 int all = (x->gl_editor ? (x->gl_editor->e_selection == 0) : 1);
4291
4292 canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, !all),
4293 "motion");
4294
4295 /* tidy horizontally */
4296 for (y = x->gl_list; y; y = y->g_next)
4297 if (all || glist_isselected(x, y))
4298 {
4299 gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);
4300
4301 for (y2 = x->gl_list; y2; y2 = y2->g_next)
4302 if (all || glist_isselected(x, y2))
4303 {
4304 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
4305 if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE &&
4306 bx1 < ax1)
4307 goto nothorizhead;
4308 }
4309
4310 for (y2 = x->gl_list; y2; y2 = y2->g_next)
4311 if (all || glist_isselected(x, y2))
4312 {
4313 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
4314 if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE
4315 && by1 != ay1)
4316 gobj_displace(y2, x, 0, ay1-by1);
4317 }
4318 nothorizhead: ;
4319 }
4320 /* tidy vertically. First guess the user's favorite vertical spacing */
4321 for (i = NHIST, ip = histogram; i--; ip++) *ip = 0;
4322 for (y = x->gl_list; y; y = y->g_next)
4323 if (all || glist_isselected(x, y))
4324 {
4325 gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);
4326 for (y2 = x->gl_list; y2; y2 = y2->g_next)
4327 if (all || glist_isselected(x, y2))
4328 {
4329 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
4330 if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE)
4331 {
4332 int distance = by1-ay2;
4333 if (distance >= 0 && distance < NHIST)
4334 histogram[distance]++;
4335 }
4336 }
4337 }
4338 for (i = 1, besthist = 0, bestdist = 4, ip = histogram + 1;
4339 i < (NHIST-1); i++, ip++)
4340 {
4341 int hit = ip[-1] + 2 * ip[0] + ip[1];
4342 if (hit > besthist)
4343 {
4344 besthist = hit;
4345 bestdist = i;
4346 }
4347 }
4348 post("best vertical distance %d", bestdist);
4349 for (y = x->gl_list; y; y = y->g_next)
4350 if (all || glist_isselected(x, y))
4351 {
4352 int keep = 1;
4353 gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2);
4354 for (y2 = x->gl_list; y2; y2 = y2->g_next)
4355 if (all || glist_isselected(x, y2))
4356 {
4357 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
4358 if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE &&
4359 ay1 >= by2 - 10 && ay1 < by2 + NHIST)
4360 goto nothead;
4361 }
4362 while (keep)
4363 {
4364 keep = 0;
4365 for (y2 = x->gl_list; y2; y2 = y2->g_next)
4366 if (all || glist_isselected(x, y2))
4367 {
4368 gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2);
4369 if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE &&
4370 by1 > ay1 && by1 < ay2 + NHIST)
4371 {
4372 int vmove = ay2 + bestdist - by1;
4373 gobj_displace(y2, x, ax1-bx1, vmove);
4374 ay1 = by1 + vmove;
4375 ay2 = by2 + vmove;
4376 keep = 1;
4377 break;
4378 }
4379 }
4380 }
4381 nothead: ;
4382 }
4383 canvas_dirty(x, 1);
4384}
4385
4386static void canvas_texteditor(t_canvas *x)
4387{
4388 t_rtext *foo;
4389 char *buf;
4390 int bufsize;
4391 if (foo = x->gl_editor->e_textedfor)
4392 rtext_gettext(foo, &buf, &bufsize);
4393 else buf = "", bufsize = 0;
4394 sys_vgui("pdtk_pd_texteditor {%.*s}\n", bufsize, buf);
4395
4396}
4397
4398void glob_key(void *dummy, t_symbol *s, int ac, t_atom *av)
4399{
4400 /* canvas_editing can be zero; canvas_key checks for that */
4401 canvas_key(canvas_editing, s, ac, av);
4402}
4403
4404void canvas_editmode(t_canvas *x, t_floatarg fyesplease)
4405{
4406 int yesplease = fyesplease;
4407 if (yesplease && x->gl_edit)
4408 return;
4409 x->gl_edit = !x->gl_edit;
4410 if (x->gl_edit && glist_isvisible(x) && glist_istoplevel(x))
4411 canvas_setcursor(x, CURSOR_EDITMODE_NOTHING);
4412 else
4413 {
4414 glist_noselect(x);
4415 if (glist_isvisible(x) && glist_istoplevel(x))
4416 canvas_setcursor(x, CURSOR_RUNMODE_NOTHING);
4417 }
4418 sys_vgui("pdtk_canvas_editval .x%x %d\n",
4419 glist_getcanvas(x), x->gl_edit);
4420}
4421
4422 /* called by canvas_font below */
4423static void canvas_dofont(t_canvas *x, t_floatarg font, t_floatarg xresize,
4424 t_floatarg yresize)
4425{
4426 t_gobj *y;
4427 x->gl_font = font;
4428 if (xresize != 1 || yresize != 1)
4429 {
4430 canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, 0),
4431 "motion");
4432 for (y = x->gl_list; y; y = y->g_next)
4433 {
4434 int x1, x2, y1, y2, nx1, ny1;
4435 gobj_getrect(y, x, &x1, &y1, &x2, &y2);
4436 nx1 = x1 * xresize + 0.5;
4437 ny1 = y1 * yresize + 0.5;
4438 gobj_displace(y, x, nx1-x1, ny1-y1);
4439 }
4440 }
4441 if (glist_isvisible(x))
4442 glist_redraw(x);
4443 for (y = x->gl_list; y; y = y->g_next)
4444 if (pd_class(&y->g_pd) == canvas_class
4445 && !canvas_isabstraction((t_canvas *)y))
4446 canvas_dofont((t_canvas *)y, font, xresize, yresize);
4447}
4448
4449 /* canvas_menufont calls up a TK dialog which calls this back */
4450static void canvas_font(t_canvas *x, t_floatarg font, t_floatarg resize,
4451 t_floatarg whichresize)
4452{
4453 float realresize, realresx = 1, realresy = 1;
4454 t_canvas *x2 = canvas_getrootfor(x);
4455 if (!resize) realresize = 1;
4456 else
4457 {
4458 if (resize < 20) resize = 20;
4459 if (resize > 500) resize = 500;
4460 realresize = resize * 0.01;
4461 }
4462 if (whichresize != 3) realresx = realresize;
4463 if (whichresize != 2) realresy = realresize;
4464 canvas_dofont(x2, font, realresx, realresy);
4465 sys_defaultfont = font;
4466}
4467
4468static t_glist *canvas_last_glist;
4469static int canvas_last_glist_x, canvas_last_glist_y;
4470
4471void glist_getnextxy(t_glist *gl, int *xpix, int *ypix)
4472{
4473 if (canvas_last_glist == gl)
4474 *xpix = canvas_last_glist_x, *ypix = canvas_last_glist_y;
4475 else *xpix = *ypix = 40;
4476}
4477
4478static void glist_setlastxy(t_glist *gl, int xval, int yval)
4479{
4480 canvas_last_glist = gl;
4481 canvas_last_glist_x = xval;
4482 canvas_last_glist_y = yval;
4483}
4484
4485
4486void g_editor_setup(void)
4487{
4488/* ------------------------ events ---------------------------------- */
4489 class_addmethod(canvas_class, (t_method)canvas_mousedown, gensym("mouse"),
4490 A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
4491 class_addmethod(canvas_class, (t_method)canvas_mouseup, gensym("mouseup"),
4492 A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
4493 class_addmethod(canvas_class, (t_method)canvas_key, gensym("key"),
4494 A_GIMME, A_NULL);
4495 class_addmethod(canvas_class, (t_method)canvas_motion, gensym("motion"),
4496 A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
4497
4498/* ------------------------ menu actions ---------------------------- */
4499 class_addmethod(canvas_class, (t_method)canvas_menuclose,
4500 gensym("menuclose"), A_DEFFLOAT, 0);
4501 class_addmethod(canvas_class, (t_method)canvas_cut,
4502 gensym("cut"), A_NULL);
4503 class_addmethod(canvas_class, (t_method)canvas_copy,
4504 gensym("copy"), A_NULL);
4505 class_addmethod(canvas_class, (t_method)canvas_paste,
4506 gensym("paste"), A_NULL);
4507 class_addmethod(canvas_class, (t_method)canvas_duplicate,
4508 gensym("duplicate"), A_NULL);
4509 class_addmethod(canvas_class, (t_method)canvas_selectall,
4510 gensym("selectall"), A_NULL);
4511 class_addmethod(canvas_class, (t_method)canvas_undo,
4512 gensym("undo"), A_NULL);
4513 class_addmethod(canvas_class, (t_method)canvas_redo,
4514 gensym("redo"), A_NULL);
4515 class_addmethod(canvas_class, (t_method)canvas_tidy,
4516 gensym("tidy"), A_NULL);
4517 class_addmethod(canvas_class, (t_method)canvas_texteditor,
4518 gensym("texteditor"), A_NULL);
4519 class_addmethod(canvas_class, (t_method)canvas_editmode,
4520 gensym("editmode"), A_DEFFLOAT, A_NULL);
4521 class_addmethod(canvas_class, (t_method)canvas_print,
4522 gensym("print"), A_SYMBOL, A_NULL);
4523 class_addmethod(canvas_class, (t_method)canvas_menufont,
4524 gensym("menufont"), A_NULL);
4525 class_addmethod(canvas_class, (t_method)canvas_font,
4526 gensym("font"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
4527 class_addmethod(canvas_class, (t_method)canvas_find,
4528 gensym("find"), A_GIMME, A_NULL);
4529 class_addmethod(canvas_class, (t_method)canvas_find_again,
4530 gensym("findagain"), A_NULL);
4531 class_addmethod(canvas_class, (t_method)canvas_find_parent,
4532 gensym("findparent"), A_NULL);
4533 class_addmethod(canvas_class, (t_method)canvas_done_popup,
4534 gensym("done-popup"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
4535 class_addmethod(canvas_class, (t_method)canvas_donecanvasdialog,
4536 gensym("donecanvasdialog"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
4537 class_addmethod(canvas_class, (t_method)glist_arraydialog,
4538 gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
4539
4540/* -------------- connect method used in reading files ------------------ */
4541 class_addmethod(canvas_class, (t_method)canvas_connect,
4542 gensym("connect"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
4543
4544 class_addmethod(canvas_class, (t_method)canvas_disconnect,
4545 gensym("disconnect"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
4546/* -------------- copy buffer ------------------ */
4547 copy_binbuf = binbuf_new();
4548}