A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 2413 lines 67 kB view raw
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