/* * GRAPH, Version 1.00 - 4 August 1989 * * Copyright 1989, David Gay. All Rights Reserved. * This software is freely redistrubatable. */ /* Main program */ #include #include #include #include #include #include "graph.h" #include "file.h" #include "object.h" #include "uio.h" #include "list.h" #include "grph.h" #include "user/eval.h" #include "tracker.h" #include #include /* Gadget ids for variables requester */ #define VARSLIST 2 #define VARSDEL 3 const char *eval_messages[] = { "No error", "Syntax error", "Out of memory", "Right bracket expected", "Obsolete", "Left bracket expected", "Function not differentiable", "Recursion detected", "Result not numeric" }; /* The various libraries used */ struct LayersBase *LayersBase; extern struct DiskfontBase *DiskfontBase; extern struct GfxBase *GfxBase; extern struct IntuitionBase *IntuitionBase; struct ArpBase *ArpBase; list graph_list; /* List of graphs */ context vars; /* List of variables */ static int vars_saved = TRUE; /* Used by variable requester */ static char var[VARLEN], val[EXPRLEN]; static struct Gadget *valg, *delg; static struct ListInfo *var_info; /* Inform objects that variable var has changed value. They may need to recalculate */ static void check_vars(char *var) { struct graph *scan; struct object *o; /* Variables are global ==> for all graphs, all objects */ for (scan = first(&graph_list); succ(scan); scan = succ(scan)) for (o = first(&scan->o_list); succ(o); o = succ(o)) o->var_change(o, var); } /* Redraw all graphs (eg after variables have changed) */ static void draw_all(void) { struct graph *g; for (g = first(&graph_list); succ(g); g = succ(g)) draw_graph(g, TRUE); } /* Load a graph from a file into a new graph. See file format. */ static void load_file(struct graph *from) { FILE *f; char file[FILELEN]; if (getfile(file, "Load graph")) if (f = fopen(file, "r")) { short tag; if (READ(f, tag) && tag == GRAPH_TAG) { struct graph *g = load_graph(from, f); if (g) add_head(&graph_list, g); } else message(from, "File is not a graph", (char *)NULL); fclose(f); } else message(from, "Couldn't open file", file, (char *)NULL); } /* Save current graph to a file. See file format. */ /* TBD: Provide some default file name */ static void save_file(struct graph *g) { FILE *f; char file[FILELEN]; if (getfile(file, "Save graph")) if (f = fopen(file, "w")) { save_graph(g, f); fclose(f); } else message(g, "Couldn't open file", file, (char *)NULL); } /* Read variables from a file. See file format. This adds variables to the current context, it doesn't eliminate the old ones. */ static void load_variables(struct graph *from) { FILE *f; char file[FILELEN]; if (getfile(file, "Load variables")) if (f = fopen(file, "r")) { short tag; int ok = FALSE; if (READ(f, tag) && tag == FILE_TAG) /* This is a variable file */ { int done = FALSE; ok = TRUE; do /* Read all the file */ { short tag; if (READ(f, tag)) { switch (tag) { case FILE_END: done = TRUE; break; case VAR_TAG: /* Another variable */ { char name[VARLEN], val[EXPRLEN]; if (READ(f, name) && READ(f, val) && READ(f, tag) && tag == VAR_END) { value vv = compile(val); if (!vv || !set_var_name(name, vv)) message(from, "Failed to set variab le(no memory ?)", (char *)NULL); check_vars(name); } else ok = FALSE; } break; default: ok = FALSE; break; } } else ok = FALSE; } while (!done && ok); } fclose(f); if (!ok) message(from, "File does not contain variables", (char *)NULL); else draw_all(); } else message(from, "Couldn't open file", file, (char *)NULL); } /* Save variables. See file format. */ static void save_variables(struct graph *g) { FILE *f; char file[FILELEN]; if (getfile(file, "Save variables")) if (f = fopen(file, "w")) { short tag = FILE_TAG; int ok = FALSE; if (WRITE(f, tag)) { tnode *var; ok = TRUE; /* For all variables */ for (var = first(&vars); ok && succ(var); var = succ(var)) { short end = VAR_END; char name[VARLEN], expr[EXPRLEN]; value vv = get_var_name(var->ln_Name); name[VARLEN - 1] = '\0'; strncpy(name, var->ln_Name, VARLEN - 1); if (vv) decompile(vv, expr, EXPRLEN); else expr[0] = '\0'; tag = VAR_TAG; ok = WRITE(f, tag) && WRITE(f, name) && WRITE(f, expr) && WRITE(f, end); } tag = FILE_END; if (ok) ok = WRITE(f, tag); } fclose(f); if (!ok) message(g, "Error writing file", (char *)NULL); else vars_saved = TRUE; } else message(g, "Couldn't open file", file, (char *)NULL); } /* Handle variable requester. Could it be cleaned up ??? */ int vars_handler(struct Gadget *gg, ULONG class, struct Requester *req, struct graph *g) { int id = gg->GadgetID; int change = FALSE; /* Has the list changed ? */ int actval = FALSE; /* Activate variable value gadget */ int actvar = FALSE; /* " " name " */ int refresh = FALSE; /* value & name gadgets have changed */ UWORD valpos, varpos; struct Gadget *varg = ListStr(var_info); /* The lists string gadget */ if (gg == valg) /* Value gadget message, class is always GADGETUP */ { valpos = RemoveGList(req->RWindow, valg, 1); varpos = RemoveGList(req->RWindow, varg, 1); refresh = TRUE; strip(var); /* remove blanks */ strlwr(var); if (*var) { value vv = compile(val), old; if (eval_error != 0) /* Invalid value, allow correction */ { DisplayBeep(req->RWindow->WScreen); actval = TRUE; } else /* Try & set variable */ { if (old = get_var_name(var)) free_expr(old); else /* list changes, there will be an extra var */ change = TRUE; check_vars(var); if (!set_var_name(var, vv)) { alert(g->io.win, "Couldn't create variable", NULL); change = FALSE; } val[0] = var[0] = '\0'; actvar = TRUE; } } else /* blank var name -- erase val */ { DisplayBeep(req->RWindow->WScreen); actvar = TRUE; } } else if (id == VARSDEL) /* Delete selected */ { value vv; valpos = RemoveGList(req->RWindow, valg, 1); varpos = RemoveGList(req->RWindow, varg, 1); refresh = TRUE; strip(var); strlwr(var); if (*var && (vv = get_var_name(var))) { free_expr(vv); free_var_name(var); change = TRUE; check_vars(var); } var[0] = val[0] = '\0'; actvar = TRUE; } else if (id == VARSLIST) /* Something in list selected */ { if (ModifyList(gg, req, req->RWindow, class == GADGETUP) != 0 && *var) { /* New value selected from list or typed, ie new name. Switch to va lue gadget */ value vv; valpos = RemoveGList(req->RWindow, valg, 1); varpos = RemoveGList(req->RWindow, varg, 1); refresh = TRUE; strip(var); if (!(vv = get_var_name(var)) || !decompile(vv, val, EXPRLEN)) val[ 0] = '\0'; actval = TRUE; } } else return std_ghandler(gg, class, req, g); /* Handle requester services */ if (refresh) { AddGList(req->RWindow, varg, varpos, 1, req); AddGList(req->RWindow, valg, valpos, 1, req); RefreshGList(valg, req->RWindow, req, 1); RefreshGList(varg, req->RWindow, req, 1); } if (change) /* Variable list changed */ if (!ChangeList(var_info, &vars, req, req->RWindow)) { EndRequest(req, req->RWindow); /* Not enough memory ... */ actval = actvar = FALSE; } if (actval) ActivateGadget(valg, req->RWindow, req); if (actvar) ActivateGadget(varg, req->RWindow, req); vars_saved = FALSE; return FALSE; } /* Setup variables requester */ void enter_vars(struct graph *g) { struct Requester *req; struct Memory *m; struct Gadget *gl = NULL; if ((m = NewMemory()) && (req = InitReq(50, 20, 285, 126, m)) && SetReqBorder(req, 1, m) && AddIntuiText(&req->ReqText, "Edit variables", 86, 6, m) && (var_info = AddList(&gl, VARSLIST, "Variables", &vars, var, VARLEN, 0, RELVERIFY, 20, 20, 160, 80, TRUE, m)) && (valg = AddText(&gl, VARSLIST, "Value", FALSE, val, EXPRLEN, TRUE, 0, R ELVERIFY, 64, 110, 116, 10, TRUE, m)) && AddBox(&gl, TRUE, "Ok", 0, RELVERIFY | ENDGADGET, 200, 23, 65, 15, FALS E, m) && AddBox(&gl, VARSDEL, "Delete", 0, RELVERIFY, 200, 86, 65, 15, FALSE, m) ) { var[0] = val[0] = '\0'; SetReqGadgets(req, gl); DoRequest(req, g, vars_handler); /* vars_handler called for every gadget event */ } Free(m); } /* Create a new graph */ static struct graph *add_graph(struct graph *from) { struct graph *g = new_graph(from); if (g) add_head(&graph_list, g); return g; } /* Delete a graph, checking for save */ static void remove_graph(struct graph *g) { if (!g->saved) save_file(g); remove(g); delete_graph(g); } /* Global initialisation */ static int init(void) { if (GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0)) if (DiskfontBase = (struct Library *)OpenLibrary("diskfont.library", 0) ) if (IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition. library", 33)) if (LayersBase = (struct LayersBase *)OpenLibrary("layers.libra ry", 33)) if (ArpBase = (struct ArpBase *)OpenLibrary("arp.library", 0)) { new_list(&graph_list); if (init_user() && init_grph() && add_graph(NULL)) if (init_expr()) { init_context(&vars); set_context(&vars); return TRUE; } else alert(NULL, "Expression init failed", "No memo ry ?"); } else alert(NULL, "arp.library required", NULL); else alert(NULL, "layers.library V1.2 required", NULL); else alert(NULL, "intuition.library V1.2 required", NULL); else alert(NULL, "diskfont.library required", NULL); else alert(NULL, "graphics.library required", NULL); return FALSE; } /* Free ALL resources ! */ static void clean_up(void) { struct graph *g, *next; cleanup_expr(); cleanup_grph(); cleanup_user(); for (g = first(&graph_list); next = succ(g); g = next) remove_graph(g); if (ArpBase) CloseLibrary((struct Library *)ArpBase); if (LayersBase) CloseLibrary((struct Library *)LayersBase); if (IntuitionBase) CloseLibrary((struct Library *)IntuitionBase); if (DiskfontBase) CloseLibrary((struct Library *)DiskfontBase); if (GfxBase) CloseLibrary((struct Library *)GfxBase); #ifdef DEBUG TrackerExitReport(); #endif } void main(int argc, char **argv) { struct cmd cmd; int done = FALSE; if (init()) do switch ((cmd = next_command()).command) { case close: done = succ(succ(first(&graph_list))) == NULL; /* one graph left ? */ if (done && !vars_saved) save_variables(cmd.g); remove_graph(cmd.g); break; case _new_graph: add_graph(cmd.g); break; case _load_graph: load_file(cmd.g); break; case _save_graph: save_file(cmd.g); break; case print_graph: prt_graph(cmd.g); break; case iff_graph: iff_todisk(cmd.g); break; case save_vars: save_variables(cmd.g); break; case load_vars: load_variables(cmd.g); break; case quit: if (!vars_saved) save_variables(cmd.g); done = TRUE; break; case add_function: select_object(cmd.g, add_object(cmd.g, new_function(cmd.g)) ); break; case add_label: add_object(cmd.g, (struct object *)new_label(cmd.g, cmd.dat a.pt.x, cmd.data.pt.y)); break; case del_object: remove_object(cmd.g, cmd.data.o); break; case limits: enter_limits(cmd.g); break; case axes: enter_axes(cmd.g); break; case zoom: zoom_in(cmd.g, cmd.data.zoom_in.x0, cmd.data.zoom_in.y0, cm d.data.zoom_in.x1, cmd.data.zoom_in.y1); break; case zoom_out: zoom_factor(cmd.g, cmd.data.zoom_out); break; case center: center_graph(cmd.g, cmd.data.pt.x, cmd.data.pt.y); break; case edit: { struct Region *ref; if (cmd.data.o->edit(cmd.data.o, &ref)) { cmd.g->saved = FALSE; refresh_graph(cmd.g, TRUE, ref); } } break; case improve: refresh_graph(cmd.g, TRUE, cmd.data.o->improve(cmd.data.o)) ; break; case edit_vars: enter_vars(cmd.g); draw_all(); break; } while (!done); clean_up(); }