/* Copyright (c) 1997-2002 Miller Puckette and others. * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ /* this file reads and writes the "data" portions of a canvas to a file. See also canvas_saveto(), etc., in g_editor.c. The data portion is a collection of "scalar" objects. Routines here can save collections of scalars into a file and reload them; also, support is included here for */ #ifdef ROCKBOX #include "plugin.h" #include "../../pdbox.h" #include "m_pd.h" #include "g_canvas.h" #else /* ROCKBOX */ #include #include #include "m_pd.h" #include "g_canvas.h" #include #endif /* ROCKBOX */ /* the following routines read "scalars" from a file into a canvas. */ static int canvas_scanbinbuf(int natoms, t_atom *vec, int *p_indexout, int *p_next) { #ifdef ROCKBOX int i; #else int i, j; #endif int indexwas = *p_next; *p_indexout = indexwas; if (indexwas >= natoms) return (0); for (i = indexwas; i < natoms && vec[i].a_type != A_SEMI; i++) ; if (i >= natoms) *p_next = i; else *p_next = i + 1; return (i - indexwas); } int glist_readscalar(t_glist *x, int natoms, t_atom *vec, int *p_nextmsg, int selectit); static void canvas_readerror(int natoms, t_atom *vec, int message, int nline, char *s) { #ifdef ROCKBOX (void) natoms; #endif error(s); startpost("line was:"); postatom(nline, vec + message); endpost(); } /* fill in the contents of the scalar into the vector w. */ static void glist_readatoms(t_glist *x, int natoms, t_atom *vec, int *p_nextmsg, t_symbol *templatesym, t_word *w, int argc, t_atom *argv) { #ifdef ROCKBOX int message, n, i; #else int message, nline, n, i; #endif t_template *template = template_findbyname(templatesym); if (!template) { error("%s: no such template", templatesym->s_name); *p_nextmsg = natoms; return; } word_restore(w, template, argc, argv); n = template->t_n; for (i = 0; i < n; i++) { if (template->t_vec[i].ds_type == DT_ARRAY) { #ifndef ROCKBOX int j; #endif t_array *a = w[i].w_array; int elemsize = a->a_elemsize, nitems = 0; t_symbol *arraytemplatesym = template->t_vec[i].ds_arraytemplate; t_template *arraytemplate = template_findbyname(arraytemplatesym); if (!arraytemplate) { error("%s: no such template", arraytemplatesym->s_name); } else while (1) { t_word *element; int nline = canvas_scanbinbuf(natoms, vec, &message, p_nextmsg); /* empty line terminates array */ if (!nline) break; array_resize(a, arraytemplate, nitems + 1); element = (t_word *)(((char *)a->a_vec) + nitems * elemsize); glist_readatoms(x, natoms, vec, p_nextmsg, arraytemplatesym, element, nline, vec + message); nitems++; } } else if (template->t_vec[i].ds_type == DT_LIST) { while (1) { if (!glist_readscalar(w->w_list, natoms, vec, p_nextmsg, 0)) break; } } } } int glist_readscalar(t_glist *x, int natoms, t_atom *vec, int *p_nextmsg, int selectit) { #ifdef ROCKBOX int message, nline; #else int message, i, j, nline; #endif t_template *template; t_symbol *templatesym; t_scalar *sc; int nextmsg = *p_nextmsg; int wasvis = glist_isvisible(x); if (nextmsg >= natoms || vec[nextmsg].a_type != A_SYMBOL) { if (nextmsg < natoms) post("stopping early: type %d", vec[nextmsg].a_type); *p_nextmsg = natoms; return (0); } templatesym = canvas_makebindsym(vec[nextmsg].a_w.w_symbol); *p_nextmsg = nextmsg + 1; if (!(template = template_findbyname(templatesym))) { error("canvas_read: %s: no such template", templatesym->s_name); *p_nextmsg = natoms; return (0); } sc = scalar_new(x, templatesym); if (!sc) { error("couldn't create scalar \"%s\"", templatesym->s_name); *p_nextmsg = natoms; return (0); } if (wasvis) { /* temporarily lie about vis flag while this is built */ glist_getcanvas(x)->gl_mapped = 0; } glist_add(x, &sc->sc_gobj); nline = canvas_scanbinbuf(natoms, vec, &message, p_nextmsg); glist_readatoms(x, natoms, vec, p_nextmsg, templatesym, sc->sc_vec, nline, vec + message); if (wasvis) { /* reset vis flag as before */ glist_getcanvas(x)->gl_mapped = 1; gobj_vis(&sc->sc_gobj, x, 1); } if (selectit) { glist_select(x, &sc->sc_gobj); } return (1); } void glist_readfrombinbuf(t_glist *x, t_binbuf *b, char *filename, int selectem) { #ifdef ROCKBOX int natoms, nline, message, nextmsg = 0; #else t_canvas *canvas = glist_getcanvas(x); int cr = 0, natoms, nline, message, nextmsg = 0, i, j, nitems; #endif t_atom *vec; #ifndef ROCKBOX t_gobj *gobj; #endif natoms = binbuf_getnatom(b); vec = binbuf_getvec(b); /* check for file type */ nline = canvas_scanbinbuf(natoms, vec, &message, &nextmsg); if (nline != 1 && vec[message].a_type != A_SYMBOL && strcmp(vec[message].a_w.w_symbol->s_name, "data")) { pd_error(x, "%s: file apparently of wrong type", filename); binbuf_free(b); return; } /* read in templates and check for consistency */ while (1) { t_template *newtemplate, *existtemplate; t_symbol *templatesym; t_atom *templateargs = getbytes(0); int ntemplateargs = 0, newnargs; nline = canvas_scanbinbuf(natoms, vec, &message, &nextmsg); if (nline < 2) break; else if (nline > 2) canvas_readerror(natoms, vec, message, nline, "extra items ignored"); else if (vec[message].a_type != A_SYMBOL || strcmp(vec[message].a_w.w_symbol->s_name, "template") || vec[message + 1].a_type != A_SYMBOL) { canvas_readerror(natoms, vec, message, nline, "bad template header"); continue; } templatesym = canvas_makebindsym(vec[message + 1].a_w.w_symbol); while (1) { nline = canvas_scanbinbuf(natoms, vec, &message, &nextmsg); if (nline != 2 && nline != 3) break; newnargs = ntemplateargs + nline; templateargs = (t_atom *)t_resizebytes(templateargs, sizeof(*templateargs) * ntemplateargs, sizeof(*templateargs) * newnargs); templateargs[ntemplateargs] = vec[message]; templateargs[ntemplateargs + 1] = vec[message + 1]; if (nline == 3) templateargs[ntemplateargs + 2] = vec[message + 2]; ntemplateargs = newnargs; } newtemplate = template_new(templatesym, ntemplateargs, templateargs); t_freebytes(templateargs, sizeof (*templateargs) * ntemplateargs); if (!(existtemplate = template_findbyname(templatesym))) { error("%s: template not found in current patch", templatesym->s_name); template_free(newtemplate); return; } if (!template_match(existtemplate, newtemplate)) { error("%s: template doesn't match current one", templatesym->s_name); template_free(newtemplate); return; } template_free(newtemplate); } while (nextmsg < natoms) { glist_readscalar(x, natoms, vec, &nextmsg, selectem); } } static void glist_doread(t_glist *x, t_symbol *filename, t_symbol *format, int clearme) { t_binbuf *b = binbuf_new(); t_canvas *canvas = glist_getcanvas(x); int wasvis = glist_isvisible(canvas); #ifdef ROCKBOX int cr = 0; #else int cr = 0, natoms, nline, message, nextmsg = 0, i, j; t_atom *vec; #endif if (!strcmp(format->s_name, "cr")) cr = 1; else if (*format->s_name) error("qlist_read: unknown flag: %s", format->s_name); if (binbuf_read_via_path(b, filename->s_name, canvas_getdir(canvas)->s_name, cr)) { pd_error(x, "read failed"); binbuf_free(b); return; } if (wasvis) canvas_vis(canvas, 0); if (clearme) glist_clear(x); glist_readfrombinbuf(x, b, filename->s_name, 0); if (wasvis) canvas_vis(canvas, 1); binbuf_free(b); } void glist_read(t_glist *x, t_symbol *filename, t_symbol *format) { glist_doread(x, filename, format, 1); } void glist_mergefile(t_glist *x, t_symbol *filename, t_symbol *format) { glist_doread(x, filename, format, 0); } /* read text from a "properties" window, called from a gfxstub set up in scalar_properties(). We try to restore the object; if successful we delete the scalar and put the new thing in its place on the list. */ void canvas_dataproperties(t_canvas *x, t_scalar *sc, t_binbuf *b) { int ntotal, nnew, scindex; t_gobj *y, *y2 = 0, *newone, *oldone = 0; for (y = x->gl_list, ntotal = 0, scindex = -1; y; y = y->g_next) { if (y == &sc->sc_gobj) scindex = ntotal, oldone = y; ntotal++; } if (scindex == -1) bug("data_properties: scalar disappeared"); glist_readfrombinbuf(x, b, "properties dialog", 0); newone = 0; if (scindex >= 0) { /* take the new object off the list */ if (ntotal) { for(y = x->gl_list, nnew = 1; (y2 = y->g_next); y = y2, nnew++) if (nnew == ntotal) { newone = y2; y->g_next = y2->g_next; break; } } else newone = x->gl_list, x->gl_list = newone->g_next; } if (!newone) error("couldn't update properties (perhaps a format problem?)"); else if (!oldone) bug("data_properties: couldn't find old element"); else { glist_delete(x, oldone); if (scindex > 0) { for (y = x->gl_list, nnew = 1; y; y = y->g_next, nnew++) if (nnew == scindex || !y->g_next) { newone->g_next = y->g_next; y->g_next = newone; goto didit; } bug("data_properties: can't reinsert"); } else newone->g_next = x->gl_list, x->gl_list = newone; } didit: ; } /* ----------- routines to write data to a binbuf ----------- */ void canvas_doaddtemplate(t_symbol *templatesym, int *p_ntemplates, t_symbol ***p_templatevec) { int n = *p_ntemplates, i; t_symbol **templatevec = *p_templatevec; for (i = 0; i < n; i++) if (templatevec[i] == templatesym) return; templatevec = (t_symbol **)t_resizebytes(templatevec, n * sizeof(*templatevec), (n+1) * sizeof(*templatevec)); templatevec[n] = templatesym; *p_templatevec = templatevec; *p_ntemplates = n+1; } static void glist_writelist(t_gobj *y, t_binbuf *b); void canvas_writescalar(t_symbol *templatesym, t_word *w, t_binbuf *b, int amarrayelement) { #ifndef ROCKBOX t_dataslot *ds; #endif t_template *template = template_findbyname(templatesym); t_atom *a = (t_atom *)t_getbytes(0); int i, n = template->t_n, natom = 0; if (!amarrayelement) { t_atom templatename; SETSYMBOL(&templatename, gensym(templatesym->s_name + 3)); binbuf_add(b, 1, &templatename); } if (!template) bug("canvas_writescalar"); /* write the atoms (floats and symbols) */ for (i = 0; i < n; i++) { if (template->t_vec[i].ds_type == DT_FLOAT || template->t_vec[i].ds_type == DT_SYMBOL) { a = (t_atom *)t_resizebytes(a, natom * sizeof(*a), (natom + 1) * sizeof (*a)); if (template->t_vec[i].ds_type == DT_FLOAT) SETFLOAT(a + natom, w[i].w_float); else SETSYMBOL(a + natom, w[i].w_symbol); natom++; } } /* array elements have to have at least something */ if (natom == 0 && amarrayelement) SETSYMBOL(a + natom, &s_bang), natom++; binbuf_add(b, natom, a); binbuf_addsemi(b); t_freebytes(a, natom * sizeof(*a)); for (i = 0; i < n; i++) { if (template->t_vec[i].ds_type == DT_ARRAY) { int j; t_array *a = w[i].w_array; int elemsize = a->a_elemsize, nitems = a->a_n; t_symbol *arraytemplatesym = template->t_vec[i].ds_arraytemplate; for (j = 0; j < nitems; j++) canvas_writescalar(arraytemplatesym, (t_word *)(((char *)a->a_vec) + elemsize * j), b, 1); binbuf_addsemi(b); } else if (template->t_vec[i].ds_type == DT_LIST) { glist_writelist(w->w_list->gl_list, b); binbuf_addsemi(b); } } } static void glist_writelist(t_gobj *y, t_binbuf *b) { for (; y; y = y->g_next) { if (pd_class(&y->g_pd) == scalar_class) { canvas_writescalar(((t_scalar *)y)->sc_template, ((t_scalar *)y)->sc_vec, b, 0); } } } /* ------------ routines to write out templates for data ------- */ static void canvas_addtemplatesforlist(t_gobj *y, int *p_ntemplates, t_symbol ***p_templatevec); static void canvas_addtemplatesforscalar(t_symbol *templatesym, t_word *w, int *p_ntemplates, t_symbol ***p_templatevec) { t_dataslot *ds; int i; t_template *template = template_findbyname(templatesym); canvas_doaddtemplate(templatesym, p_ntemplates, p_templatevec); if (!template) bug("canvas_addtemplatesforscalar"); else for (ds = template->t_vec, i = template->t_n; i--; ds++, w++) { if (ds->ds_type == DT_ARRAY) { int j; t_array *a = w->w_array; int elemsize = a->a_elemsize, nitems = a->a_n; t_symbol *arraytemplatesym = ds->ds_arraytemplate; canvas_doaddtemplate(arraytemplatesym, p_ntemplates, p_templatevec); for (j = 0; j < nitems; j++) canvas_addtemplatesforscalar(arraytemplatesym, (t_word *)(((char *)a->a_vec) + elemsize * j), p_ntemplates, p_templatevec); } else if (ds->ds_type == DT_LIST) canvas_addtemplatesforlist(w->w_list->gl_list, p_ntemplates, p_templatevec); } } static void canvas_addtemplatesforlist(t_gobj *y, int *p_ntemplates, t_symbol ***p_templatevec) { for (; y; y = y->g_next) { if (pd_class(&y->g_pd) == scalar_class) { canvas_addtemplatesforscalar(((t_scalar *)y)->sc_template, ((t_scalar *)y)->sc_vec, p_ntemplates, p_templatevec); } } } /* write all "scalars" in a glist to a binbuf. */ t_binbuf *glist_writetobinbuf(t_glist *x, int wholething) { int i; t_symbol **templatevec = getbytes(0); int ntemplates = 0; t_gobj *y; t_binbuf *b = binbuf_new(); for (y = x->gl_list; y; y = y->g_next) { if ((pd_class(&y->g_pd) == scalar_class) && (wholething || glist_isselected(x, y))) { canvas_addtemplatesforscalar(((t_scalar *)y)->sc_template, ((t_scalar *)y)->sc_vec, &ntemplates, &templatevec); } } binbuf_addv(b, "s;", gensym("data")); for (i = 0; i < ntemplates; i++) { t_template *template = template_findbyname(templatevec[i]); int j, m = template->t_n; /* drop "pd-" prefix from template symbol to print it: */ binbuf_addv(b, "ss;", gensym("template"), gensym(templatevec[i]->s_name + 3)); for (j = 0; j < m; j++) { t_symbol *type; switch (template->t_vec[j].ds_type) { case DT_FLOAT: type = &s_float; break; case DT_SYMBOL: type = &s_symbol; break; case DT_ARRAY: type = gensym("array"); break; case DT_LIST: type = &s_list; break; default: type = &s_float; bug("canvas_write"); } if (template->t_vec[j].ds_type == DT_ARRAY) binbuf_addv(b, "sss;", type, template->t_vec[j].ds_name, gensym(template->t_vec[j].ds_arraytemplate->s_name + 3)); else binbuf_addv(b, "ss;", type, template->t_vec[j].ds_name); } binbuf_addsemi(b); } binbuf_addsemi(b); /* now write out the objects themselves */ for (y = x->gl_list; y; y = y->g_next) { if ((pd_class(&y->g_pd) == scalar_class) && (wholething || glist_isselected(x, y))) { canvas_writescalar(((t_scalar *)y)->sc_template, ((t_scalar *)y)->sc_vec, b, 0); } } return (b); } static void glist_write(t_glist *x, t_symbol *filename, t_symbol *format) { #ifdef ROCKBOX int cr = 0; #else int cr = 0, i; #endif t_binbuf *b; char buf[MAXPDSTRING]; #ifndef ROCKBOX t_symbol **templatevec = getbytes(0); int ntemplates = 0; t_gobj *y; #endif t_canvas *canvas = glist_getcanvas(x); canvas_makefilename(canvas, filename->s_name, buf, MAXPDSTRING); if (!strcmp(format->s_name, "cr")) cr = 1; else if (*format->s_name) error("qlist_read: unknown flag: %s", format->s_name); b = glist_writetobinbuf(x, 1); if (b) { if (binbuf_write(b, buf, "", cr)) error("%s: write failed", filename->s_name); binbuf_free(b); } } /* ------ routines to save and restore canvases (patches) recursively. ----*/ /* save to a binbuf, called recursively; cf. canvas_savetofile() which saves the document, and is only called on root canvases. */ static void canvas_saveto(t_canvas *x, t_binbuf *b) { t_gobj *y; t_linetraverser t; t_outconnect *oc; /* subpatch */ if (x->gl_owner && !x->gl_env) { binbuf_addv(b, "ssiiiisi;", gensym("#N"), gensym("canvas"), (t_int)(x->gl_screenx1), (t_int)(x->gl_screeny1), (t_int)(x->gl_screenx2 - x->gl_screenx1), (t_int)(x->gl_screeny2 - x->gl_screeny1), (*x->gl_name->s_name ? x->gl_name: gensym("(subpatch)")), x->gl_mapped); } /* root or abstraction */ else binbuf_addv(b, "ssiiiii;", gensym("#N"), gensym("canvas"), (t_int)(x->gl_screenx1), (t_int)(x->gl_screeny1), (t_int)(x->gl_screenx2 - x->gl_screenx1), (t_int)(x->gl_screeny2 - x->gl_screeny1), x->gl_font); for (y = x->gl_list; y; y = y->g_next) gobj_save(y, b); linetraverser_start(&t, x); while((oc = linetraverser_next(&t))) { int srcno = canvas_getindex(x, &t.tr_ob->ob_g); int sinkno = canvas_getindex(x, &t.tr_ob2->ob_g); binbuf_addv(b, "ssiiii;", gensym("#X"), gensym("connect"), srcno, t.tr_outno, sinkno, t.tr_inno); } /* unless everything is the default (as in ordinary subpatches) print out a "coords" message to set up the coordinate systems */ if (x->gl_isgraph || x->gl_x1 || x->gl_y1 || x->gl_x2 != 1 || x->gl_y2 != 1 || x->gl_pixwidth || x->gl_pixheight) binbuf_addv(b, "ssfffffff;", gensym("#X"), gensym("coords"), x->gl_x1, x->gl_y1, x->gl_x2, x->gl_y2, (float)x->gl_pixwidth, (float)x->gl_pixheight, (float)x->gl_isgraph); } /* call this recursively to collect all the template names for a canvas or for the selection. */ static void canvas_collecttemplatesfor(t_canvas *x, int *ntemplatesp, t_symbol ***templatevecp, int wholething) { t_gobj *y; for (y = x->gl_list; y; y = y->g_next) { if ((pd_class(&y->g_pd) == scalar_class) && (wholething || glist_isselected(x, y))) canvas_addtemplatesforscalar(((t_scalar *)y)->sc_template, ((t_scalar *)y)->sc_vec, ntemplatesp, templatevecp); else if ((pd_class(&y->g_pd) == canvas_class) && (wholething || glist_isselected(x, y))) canvas_collecttemplatesfor((t_canvas *)y, ntemplatesp, templatevecp, 1); } } /* save the templates needed by a canvas to a binbuf. */ static void canvas_savetemplatesto(t_canvas *x, t_binbuf *b, int wholething) { t_symbol **templatevec = getbytes(0); int i, ntemplates = 0; #ifndef ROCKBOX t_gobj *y; #endif canvas_collecttemplatesfor(x, &ntemplates, &templatevec, wholething); for (i = 0; i < ntemplates; i++) { t_template *template = template_findbyname(templatevec[i]); int j, m = template->t_n; if (!template) { bug("canvas_savetemplatesto"); continue; } /* drop "pd-" prefix from template symbol to print */ binbuf_addv(b, "sss", &s__N, gensym("struct"), gensym(templatevec[i]->s_name + 3)); for (j = 0; j < m; j++) { t_symbol *type; switch (template->t_vec[j].ds_type) { case DT_FLOAT: type = &s_float; break; case DT_SYMBOL: type = &s_symbol; break; case DT_ARRAY: type = gensym("array"); break; case DT_LIST: type = &s_list; break; default: type = &s_float; bug("canvas_write"); } if (template->t_vec[j].ds_type == DT_ARRAY) binbuf_addv(b, "sss", type, template->t_vec[j].ds_name, gensym(template->t_vec[j].ds_arraytemplate->s_name + 3)); else binbuf_addv(b, "ss", type, template->t_vec[j].ds_name); } binbuf_addsemi(b); } } void canvas_reload(t_symbol *name, t_symbol *dir, t_gobj *except); /* save a "root" canvas to a file; cf. canvas_saveto() which saves the body (and which is called recursively.) */ static void canvas_savetofile(t_canvas *x, t_symbol *filename, t_symbol *dir) { t_binbuf *b = binbuf_new(); canvas_savetemplatesto(x, b, 1); canvas_saveto(x, b); if (binbuf_write(b, filename->s_name, dir->s_name, 0)) sys_ouch(); else { /* if not an abstraction, reset title bar and directory */ if (!x->gl_owner) canvas_rename(x, filename, dir); post("saved to: %s/%s", dir->s_name, filename->s_name); canvas_dirty(x, 0); canvas_reload(filename, dir, &x->gl_gobj); } binbuf_free(b); } static void canvas_menusaveas(t_canvas *x) { #ifdef ROCKBOX (void) x; #else /* ROCKBOX */ t_canvas *x2 = canvas_getrootfor(x); sys_vgui("pdtk_canvas_saveas .x%x \"%s\" \"%s\"\n", x2, x2->gl_name->s_name, canvas_getdir(x2)->s_name); #endif /* ROCKBOX */ } static void canvas_menusave(t_canvas *x) { t_canvas *x2 = canvas_getrootfor(x); char *name = x2->gl_name->s_name; if (*name && strncmp(name, "Untitled", 8) && (strlen(name) < 4 || strcmp(name + strlen(name)-4, ".pat"))) canvas_savetofile(x2, x2->gl_name, canvas_getdir(x2)); else canvas_menusaveas(x2); } void g_readwrite_setup(void) { class_addmethod(canvas_class, (t_method)glist_write, gensym("write"), A_SYMBOL, A_DEFSYM, A_NULL); class_addmethod(canvas_class, (t_method)glist_read, gensym("read"), A_SYMBOL, A_DEFSYM, A_NULL); class_addmethod(canvas_class, (t_method)glist_mergefile, gensym("mergefile"), A_SYMBOL, A_DEFSYM, A_NULL); class_addmethod(canvas_class, (t_method)canvas_savetofile, gensym("savetofile"), A_SYMBOL, A_SYMBOL, 0); class_addmethod(canvas_class, (t_method)canvas_saveto, gensym("saveto"), A_CANT, 0); /* ------------------ from the menu ------------------------- */ class_addmethod(canvas_class, (t_method)canvas_menusave, gensym("menusave"), 0); class_addmethod(canvas_class, (t_method)canvas_menusaveas, gensym("menusaveas"), 0); }