/* * GRAPH, Version 1.00 - 4 August 1989 * * Copyright 1989, David Gay. All Rights Reserved. * This software is freely redistrubatable. */ /* User interface routines */ #include #include #include #include #include #include #include #define INTUITIONPRIVATE #include #include #include #include #include "libraries/arpbase.h" #include #include #include #include #include #include #include #include "uio.h" #include "graph.h" #include "grph.h" #include "list.h" #include "object.h" #include "user/gadgets.h" #include "tracker.h" #include #include #include #include #define NODOS #include "proto/arp.h" #define DEFAVAILSIZE 2048 /* Default space reserved for AvailFonts */ #define ARROW 42 /* Identifier of arrow gadget */ #define CROSS 666 /* Identifer of cross gadget */ struct fnode /* font node */ { tnode node; char name[FONTLEN]; }; tlist flist; extern struct IntuitionBase *IntuitionBase; /* Default graph window */ static struct NewWindow graph_win = { 0, 0, 640, 200, -1, -1, RAWKEY | REFRESHWINDOW | CLOSEWINDOW | MENUPICK | GADGETDOWN | GADGETUP | R EQCLEAR | REQSET | MOUSEBUTTONS | MOUSEMOVE, WINDOWSIZING | WINDOWDRAG | WINDOWDEPTH | WINDOWCLOSE | SIMPLE_REFRESH | AC TIVATE, NULL, NULL, "Graph", NULL, NULL, 85, 60, -1, -1, WBENCHSCREEN }; /* The two icon images */ static UWORD chip arrow_data[] = { 0x7fff, 0x7fff, 0x601f, 0x603f, 0x607f, 0x607f, 0x603f, 0x661f, 0x6f0f, 0x7 f87, 0x7fc3, 0x7fe3, 0x7fff, 0x0000, 0x0000, 0x1fe0, 0x1fc0, 0x1f80, 0x1f80, 0x1fc0, 0x19e0, 0x10f0, 0x0 078, 0x003c, 0x001c, 0x0000 }; static struct Image arrow_image = { -1, 0, 16, 13, 2, arrow_data, 3, 0 }; static struct Gadget arrow = { NULL, -18, 18, 15, 13, GADGHCOMP | GADGIMAGE | GRELRIGHT, RELVERIFY | RIGHTBORDER | TOGGLESELECT, BOOLGADGET, (APTR)&arrow_image, NULL, NULL, 2, NULL, ARROW }; static UWORD chip cross_data[] = { 0x7fff, 0x7f7f, 0x7f7f, 0x7f7f, 0x7f7f, 0x7e3f, 0x4081, 0x7e3f, 0x7f7f, 0x7 f7f, 0x7f7f, 0x7f7f, 0x7fff, 0x0000, 0x0080, 0x0080, 0x0080, 0x0080, 0x01c0, 0x3f7e, 0x01c0, 0x0080, 0x0 080, 0x0080, 0x0080, 0x0000 }; static struct Image cross_image = { -1, 0, 16, 13, 2, cross_data, 3, 0 }; static struct Gadget cross = { NULL, -18, 36, 15, 13, GADGHCOMP | GADGIMAGE | GRELRIGHT | SELECTED, RELVERIFY | RIGHTBORDER | TOGGLESELECT, BOOLGADGET, (APTR)&cross_image, NULL, NULL, 1, NULL, CROSS }; static struct Memory *abort_mem; /* for abort requester */ static struct TextAttr alert_attr = { "topaz.font", 8 }; static struct TextFont *alert_font; static struct Task *me; static struct MsgPort *inputDevPort; /* for input device */ static struct IOStdReq *inputRequestBlock; static struct Interrupt handlerStuff; static int inputOpen; static long window_sigs; /* Combined sigs of all windows */ static struct TextAttr font = { "topaz.font", 8 }; /* font for menus, requester s, etc */ static struct Requester *req; /* Current requester */ static int ok; /* Value of last GadgetID */ static short reqdone; /* Req must go away. Don't activate next str g adget */ static gadgevent *gadgethandler; /* Handler for gadget events */ /* Add .font extension to fname (assumed of size FONTLEN) */ char *addfont(char *fname) { return strncat(fname, ".font", FONTLEN - 1 - strlen(fname)); } /* Removes .font extension if present */ char *remfont(char *fname) { int l = strlen(fname); if (l >= 5 && strcmp(&fname[l - 5], ".font") == 0) fname[l - 5] = '\0'; return fname; } /* Make a list of font names, one entry per font. Sizes, etc ignored. */ int make_font_list(void) { int ok = FALSE; char *abuf; new_list(&flist); if (abuf = AllocMem(DEFAVAILSIZE, 0L)) /* Alloc default avail buffer */ { int needs = AvailFonts(abuf, DEFAVAILSIZE, AFF_MEMORY | AFF_DISK); ok = TRUE; if (needs != 0) /* Need a bigger buffer */ { FreeMem(abuf, DEFAVAILSIZE); if (abuf = AllocMem(DEFAVAILSIZE + needs, 0L)) if (AvailFonts(abuf, DEFAVAILSIZE + needs, AFF_MEMORY | AFF_DIS K)) ok = FALSE; /* Definite failure */ } if (ok) /* Construct font list */ { struct AvailFontsHeader *hdr = (struct AvailFontsHeader *)abuf; struct AvailFonts *fl = (struct AvailFonts *)(hdr + 1); int i; /* Add font entries to sorted list, by name. Duplicate entries remo ved */ for (i = hdr->afh_NumEntries; i > 0; i--, fl++) { struct fnode *scan; char *name = fl->af_Attr.ta_Name; int cmp = -1; remfont(name); /* remove extension */ /* Find insertion position */ for (scan = first(&flist); succ(scan) && (cmp = strcmp(name, sc an->name)) > 0; scan = succ(scan)) ; if (cmp != 0) /* Not already present, add to list */ { struct fnode *n = alloc_node(sizeof(struct fnode)); if (!n) { ok = FALSE; break; } n->node.ln_Name = n->name; n->name[FONTLEN - 1] = '\0'; strncpy(n->name, name, FONTLEN - 1); /* Add at correct position */ insert(&flist, n, scan->node.ln_Pred); } } if (!ok) { free_list((list *)&flist, sizeof(struct fnode)); new_list(&flist); } } FreeMem(abuf, DEFAVAILSIZE + needs); } if (!ok) nomem(NULL); return ok; } /* Implement mutual exclude seeing Intuition is lazy */ /* (for border gadgets) */ /* ------------------------------------------------- */ static void MutualExclude(struct Gadget *us, struct Gadget *gadg, struct Window *win) { register int i; register struct Gadget *gp; register LONG mutex = us->MutualExclude; UWORD pos; /* scan gadget list */ for (i = 1, gp = gadg; gp && i != 0; i <<= 1, gp = gp->NextGadget) if (i & mutex) { pos = RemoveGadget(win, gp); gp->Flags &= ~SELECTED; /* unselect */ AddGadget(win, gp, pos); } pos = RemoveGadget(win, us); us->Flags |= SELECTED; AddGadget(win, us, pos); RefreshWindowFrame(win); /* This works for border gadgets */ } /* Implement mutual exclude seeing Intuition is lazy */ /* (for requester gadgets) */ static void MutEx(struct Gadget *us, struct Requester *req) { register int nb = 0, doneus = FALSE; register struct Gadget *gp, *first = NULL; register LONG mutex = us->MutualExclude; UWORD pos; /* scan gadget list */ for (gp = req->ReqGadget; gp && (mutex != 0 || !doneus); mutex >>= 1, gp = gp->NextGadget) { if ((mutex & 1) || (doneus = gp == us)) { if (!first) first = gp; pos = RemoveGList(req->RWindow, gp, 1); if (gp == us) gp->Flags |= SELECTED; /* select */ else gp->Flags &= ~SELECTED; /* unselect */ AddGList(req->RWindow, gp, pos, 1, req); } if (first) nb++; } if (first) RefreshGList(first, req->RWindow, req, nb); } /* Display requester, & handle everything until it goes away */ int DoRequest(struct Requester *r, struct graph *g, gadgevent *handle) { r->Flags |= NOISYREQ; /* We want keystrokes */ ok = FALSE; if (Request(r, g->io.win)) { /* setup vital info */ gadgethandler = handle; req = r; reqdone = FALSE; while (next_command().command != reqgone) ; /* Wait till it leaves */ req = NULL; /* No req. present */ gadgethandler = NULL; } return ok; } /* Find first string gadget */ static struct Gadget *NextText(struct Gadget *look) { while (look && (look->GadgetType & ~GADGETTYPE) != STRGADGET) look = look-> NextGadget; return look; } /* Default gadget handler, activates string gadgets in sequence & handles mutual exclude. Will normally be called by custom handlers if they don't have anything special to do. Returens the new value of ok */ int std_ghandler(struct Gadget *gg, ULONG class, struct Requester *req, struct graph *g) { if ((gg->GadgetType & ~GADGETTYPE) == STRGADGET && !reqdone) /* Activate ne xt one */ { struct Gadget *ng = NextText(gg->NextGadget); if (!ng) ng = NextText(req->ReqGadget); if (ng) ActivateGadget(ng, req->RWindow, req); } else if (gg->MutualExclude != 0) MutEx(gg, req); return gg->GadgetID != 0; } /* Insert ins in front of into, checking for string overflow ( sizeof(into)=maxlen ) */ static char *strinsert(char *into, char *ins, int maxlen) { int delta = strlen(ins); int start = strlen(into); int i; if (start + delta >= maxlen) start = maxlen - delta - 1; for (i = start - 1; i >= 0; i--) into[i + delta] = into[i]; into[start + delta] = '\0'; memcpy(into, ins, delta); return into; } /* Convert a lock to a path, store in to (maxlen chars long) */ static char *pathstr(char *to, long l, int maxlen) { long tl; int notfirst = FALSE; struct FileInfoBlock *fib = (struct FileInfoBlock *)AllocMem(sizeof(struct FileInfoBlock), 0); if (!fib) return(NULL); to[0] = '\0'; do { if (!Examine(l, fib)) { to = NULL; goto error; } if (fib->fib_DirEntryType > 0) strinsert(to, "/", maxlen); /* Is this still necessary ? */ if (fib->fib_FileName[0] == '\0') strinsert(to, "RAM", maxlen); else strinsert(to, fib->fib_FileName, maxlen); tl = l; l = ParentDir(l); if (notfirst) UnLock(tl); /* Release allocated locks */ notfirst = TRUE; } while (l); *(strchr(to, '/')) = ':'; /* First name is disk name */ error: FreeMem((char *)fib, sizeof(struct FileInfoBlock)); return(to); } /* Request a file from the user (save in file), return TRUE if OK, FALSE if cancelled or failed. Currently uses arp file requester. */ int getfile(char *file, char *msg) { static char directory[DSIZE + 1]; static struct FileRequester FR; char filename[FCHARS + 1]; filename[0] = '\0'; FR.fr_Hail = msg; FR.fr_File = filename; FR.fr_Dir = directory; if (FileRequest(&FR)) { long lock = Lock(directory, SHARED_LOCK); if (lock) { if (!pathstr(file, lock, FILELEN)) /* get dir path */ { UnLock(lock); return FALSE; } strncat(file, filename, FILELEN - 1 - strlen(file)); UnLock(lock); return TRUE; } else alert(NULL, "Failed to lock directory", directory); } return FALSE; } /* Setup an "abort" requester, in graph g with text msg (must be as long as max message) */ struct Requester *abort_request(struct graph *g, char *msg) { int len = strlen(msg); struct Requester *req; struct Gadget *gl = NULL; int height, width; static struct Gadget text = { NULL, 10, 10, 1, 1, GADGHNONE, 0L, BOOLGADGET | REQGADGET }; /* Construct requester */ height = 8 * 1 + 10 + 12 + 25; width = 8 * len + 2 * 10; if (width < 85) width = 85; text.GadgetText = NULL; if ((abort_mem = NewMemory()) && (req = InitReq(50, 15, width, height, abort_mem)) && SetReqBorder(req, 1, abort_mem) && AddBox(&gl, TRUE, "Stop!", 0, RELVERIFY, (width - 65) / 2, height - 25, 65, 15, FALSE, abort_mem) && AddIntuiText(&text.GadgetText, msg, 0, 0, abort_mem)) { SetReqGadgets(req, gl); text.NextGadget = req->ReqGadget; req->ReqGadget = &text; if (!Request(req, g->io.win)) req = NULL; /* display req */ } else req = NULL; if (!req) Free(abort_mem); return req; } /* Change abort requester message. msg must not be longer than the first msg */ void set_abort_msg(struct Requester *req, char *msg) { req->ReqGadget->GadgetText->IText = msg; RefreshGList(req->ReqGadget, req->RWindow, req, 1); } /* Clear abort requester */ void end_abort_request(struct Requester *req) { EndRequest(req, req->RWindow); Free(abort_mem); } /* Has the user asked for an abort ? */ int aborted(struct Requester *req) { int abort = FALSE; struct IntuiMessage *msg; while (msg = (struct IntuiMessage *)GetMsg(req->RWindow->UserPort)) { ULONG class = msg->Class; ReplyMsg((struct Message *)msg); if (class == REFRESHWINDOW) /* Ignore refreshes at this time */ { BeginRefresh(req->RWindow); EndRefresh(req->RWindow, TRUE); } else if (class == GADGETUP) abort = TRUE; } return abort; } /* Display a message in graph g. You pass as many string as you want, followed by (char *)NULL. This will try very hard to actually display it, calling alert with the first two strings if it fails. */ void message(struct graph *g, ...) { int nb, len; va_list msgs; char *scan; struct Memory *m; struct Requester *req; struct Gadget *gl = NULL; int height, width; int ok = FALSE; /* Find number of lines and maxmimum length */ nb = 0; len = 0; va_start(msgs, g); while (scan = va_arg(msgs, char *)) { int nl = strlen(scan); nb++; if (nl > len) len = nl; } va_end(msgs); /* Construct requester */ height = 8 * nb + 10 + 12 + 25; width = 8 * len + 2 * 10; if (width < 85) width = 85; if ((m = NewMemory()) && (req = InitReq(50, 15, width, height, m)) && SetReqBorder(req, 1, m) && AddBox(&gl, TRUE, "Ok", 0, RELVERIFY | ENDGADGET, (width - 65) / 2, hei ght - 25, 65, 15, FALSE, m)) { int y = 10 - 8; ok = TRUE; SetReqGadgets(req, gl); /* Add message strings */ va_start(msgs, g); while (ok && (scan = va_arg(msgs, char *))) ok = ok && AddIntuiText(&req->ReqText, scan, 10, (y += 8), m); va_end(msgs); /* You'll have a surprise if you press Amiga-B ... */ if (ok && g) ok = DoRequest(req, g, std_ghandler); } Free(m); if (!ok || !g) /* call alert */ { va_start(msgs, g); if (nb == 1) alert(g ? g->io.win : NULL, va_arg(msgs, char *), NULL); else if (nb >= 2) { char *m1 = va_arg(msgs, char *); char *m2 = va_arg(msgs, char *); alert(g ? g->io.win : NULL, m1, m2); } } } /* Display a two line auto request. Doesn't alloc any resources */ void alert(struct Window *win, char *msg1, char *msg2) { struct IntuiText text1, text2, negative; const static struct IntuiText template = { 0, 1, JAM1, 8, 0, &alert_attr }; int width, height; int ysize = alert_font ? alert_font->tf_YSize : 8; text1 = text2 = negative = template; text1.TopEdge = 8; text1.IText = msg1; width = IntuiTextLength(&text1) + 20; height = 37 + 2 * ysize; if (msg2 != NULL) { int w; text1.NextText = &text2; text2.TopEdge = text1.TopEdge + ysize; text2.IText = msg2; height += ysize; w = IntuiTextLength(&text2) + 20; if (w > width) width = w; } negative.LeftEdge = 6; negative.TopEdge = 4; negative.IText = "Ok"; AutoRequest(win, &text1, NULL, &negative, 0L, 0L, width, height); } /* Easy no mem requester */ void nomem(struct Window *win) { alert(win, "No memory !", NULL); } /* Return next menu selection that is a command (in graph g). *choice is the item number of the next menu selection */ static struct cmd process(UWORD *choice, struct graph *g) { struct MenuItem *item; UWORD itemnb, subnb; struct cmd cmd; struct pos *rect = (struct pos *)g->s.current; /* Prepare command */ cmd.command = none; cmd.g = g; /* Try & find a command */ while (cmd.command == none && *choice != MENUNULL) { item = ItemAddress(g->io.menu, *choice); itemnb = ITEMNUM(*choice); subnb = SUBNUM(*choice); /* Rem: illegal choices are disabled ==> no checking here */ switch (MENUNUM(*choice)) { case 0 : switch (itemnb) { /* Project */ case 0 : /* New Graph */ cmd.command = _new_graph; break; case 1 : /* Delete Graph */ cmd.command = close; break; case 2 : /* Load Graph */ cmd.command = _load_graph; break; case 3 : /* Save Graph */ cmd.command = _save_graph; break; case 4 : /* Output Graph */ switch (subnb) { case 0 : /* To Printer */ cmd.command = print_graph; break; case 1 : /* To Disk */ cmd.command = iff_graph; break; } break; case 6 : /* Load Variables */ cmd.command = load_vars; break; case 7 : /* Save Variables */ cmd.command = save_vars; break; case 9 : /* Quit */ cmd.command = quit; break; } break; case 1 : switch (itemnb) { /* Graph */ case 0 : /* Scale */ deselect(g); cmd.command = limits; break; case 1 : /* Axes */ deselect(g); cmd.command = axes; break; case 2 : /* Zoom */ cmd.command = zoom; cmd.data.zoom_in.x0 = rect->x0; cmd.data.zoom_in.y0 = rect->y0; cmd.data.zoom_in.x1 = rect->x1; cmd.data.zoom_in.y1 = rect->y1; deselect(g); break; case 3 : /* Zoom Out */ cmd.command = zoom_out; cmd.data.zoom_out = 2.0; break; case 4 : /* Center */ cmd.command = center; cmd.data.pt.x = rect->x0; cmd.data.pt.y = rect->y0; deselect(g); break; } break; case 2 : switch (itemnb) { /* Add */ case 0 : /* Function */ deselect(g); cmd.command = add_function; break; case 1 : /* Label */ cmd.command = add_label; cmd.data.pt.x = rect->x0; cmd.data.pt.y = rect->y0; deselect(g); break; } break; case 3 : switch (itemnb) { /* Edit */ case 0 : /* Variables */ cmd.command = edit_vars; break; case 2 : /* Select function */ { /* Processed locally */ struct object *o; if (o = choose_object(g, "Select")) { deselect(g); select_object(g, o); } } break; case 3 : /* Deselect */ /* Processed locally */ deselect(g); break; case 5 : /* Edit */ cmd.data.o = g->s.current; cmd.command = edit; break; case 6 : /* Improve */ cmd.data.f = g->s.current; cmd.command = improve; break; case 7 : /* Delete */ cmd.data.o = g->s.current; cmd.command = del_object; break; } break; } *choice = item->NextSelect; *choice = MENUNULL; } return cmd; } /* Return next command (in any graph) */ struct cmd next_command(void) { struct cmd cmd; static int gone = FALSE; cmd.command = none; while (cmd.command == none) /* Wait for one */ { struct graph *g; /* Scan all graphs, checking for commands */ for (g = first(&graph_list); cmd.command == none && succ(g); g = succ(g )) { int moved = FALSE; struct IntuiMessage *msg; WORD sx, sy; cmd.g = g; /* Any pending menu selections ? */ if (!req && g->io.nextmenu != MENUNULL) cmd = process(&g->io.nextme nu, g); /* Check for messages on window */ while (cmd.command == none && (msg = (struct IntuiMessage *)GetMsg( g->io.win->UserPort))) { /* Save interesting info */ ULONG class = msg->Class; UWORD code = msg->Code; UWORD qualifier = msg->Qualifier; struct Gadget *gg = (struct Gadget *)msg->IAddress, *gadg; sx = msg->MouseX; sy = msg->MouseY; ReplyMsg((struct Message *)msg); if (class == MOUSEMOVE) /* Accumulate moves */ moved = TRUE; else { if (moved) mouse_move(g, sx, sy); /* process any mouse move ment */ moved = FALSE; /* Most messages imply that the mouse button is released. M ake sure that the program thinks so */ if (class != MOUSEBUTTONS && class != REFRESHWINDOW) mouse_ up(g, sx, sy); switch (class) { /* Note that most messages are ignored while a requester is up in *any* window */ case RAWKEY: /* Check for Amiga-V/B (Ok, Cancel shortcut) */ if (req && (qualifier & AMIGALEFT) && (code == KEYC ODE_V || code == KEYCODE_B)) { ok = code == KEYCODE_V; /* Never let this happen while a string gadget is active ... */ EndRequest(req, req->RWindow); } break; case CLOSEWINDOW: if (!req) cmd.command = close; break; case MENUPICK: if (!req) { g->io.nextmenu = code; cmd = process(&g->io.nextmenu, g); } break; case REFRESHWINDOW: { int changed; /* Refresh partially only if size hasn't change d */ BeginRefresh(g->io.win); changed = g->io.win->Width != g->io.oldwidth || g->io.win->Height != g->io.oldheight; if (!changed) draw_graph(g, FALSE); /* No messa ges during refresh !!! */ EndRefresh(g->io.win, TRUE); if (changed) { set_scale(g); draw_graph(g, TRUE); } /* Wait for refresh after requester's disappear ance before signaling it */ if (gone) { gone = FALSE; cmd.command = reqgone; } } break; case REQSET: /* Activate first string gadget */ gadg = NextText(req->ReqGadget); if (gadg && !reqdone) ActivateGadget(gadg, g->io.wi n, req); break; case REQCLEAR: /* Allow 1 refresh before signaling this. There is probably a better way ! */ gone = TRUE; break; case GADGETUP: case GADGETDOWN: /* Ack! Hack to avoid bug in string gadget activati on (V1.2-1.3) */ if (class == GADGETUP && (gg->GadgetType & STRGADGE T) != 0 && IntuitionBase->LibNode.lib_Version <= 34) IntuitionBase->ActiveGadget = NULL; /* Handle requester hgadgets */ if (gadgethandler) ok = gadgethandler(gg, class, re q, g); else if (gg->GadgetID == ARROW || gg->GadgetID == C ROSS) { /* Select new mode */ MutualExclude(gg, g->io.gadgets, g->io.win); set_mode(g, gg->GadgetID == ARROW); } break; case MOUSEBUTTONS: /* Pass on to graph */ if (code == SELECTDOWN) mouse_down(g, sx, sy); else mouse_up(g, sx, sy); break; } } } /* Handle any pending moves */ if (moved) mouse_move(g, sx, sy); } /* Wait for something to happen */ if (cmd.command == none) Wait(window_sigs); } return cmd; } /* Enable/Disable various menus */ void disable_rect_menus(struct graph *g) { struct Window *win = g->io.win; OffMenu(win, SHIFTMENU(1) | SHIFTITEM(2) | SHIFTSUB(NOSUB)); /* Zoom */ OffMenu(win, SHIFTMENU(1) | SHIFTITEM(4) | SHIFTSUB(NOSUB)); /* Center */ OffMenu(win, SHIFTMENU(2) | SHIFTITEM(1) | SHIFTSUB(NOSUB)); /* Label */ OffMenu(win, SHIFTMENU(3) | SHIFTITEM(3) | SHIFTSUB(NOSUB)); /* Deselect */ } void enable_rect_menus(struct graph *g) { struct Window *win = g->io.win; OnMenu(win, SHIFTMENU(1) | SHIFTITEM(2) | SHIFTSUB(NOSUB)); /* Zoom */ OnMenu(win, SHIFTMENU(1) | SHIFTITEM(4) | SHIFTSUB(NOSUB)); /* Center */ OnMenu(win, SHIFTMENU(2) | SHIFTITEM(1) | SHIFTSUB(NOSUB)); /* Label */ OnMenu(win, SHIFTMENU(3) | SHIFTITEM(3) | SHIFTSUB(NOSUB)); /* Deselect */ } void disable_object_menus(struct graph *g) { struct Window *win = g->io.win; OffMenu(win, SHIFTMENU(3) | SHIFTITEM(3) | SHIFTSUB(NOSUB)); /* Deselect */ OffMenu(win, SHIFTMENU(3) | SHIFTITEM(5) | SHIFTSUB(NOSUB)); /* Edit */ OffMenu(win, SHIFTMENU(3) | SHIFTITEM(6) | SHIFTSUB(NOSUB)); /* Improve */ OffMenu(win, SHIFTMENU(3) | SHIFTITEM(7) | SHIFTSUB(NOSUB)); /* Delete */ } void enable_object_menus(struct graph *g) { struct Window *win = g->io.win; OnMenu(win, SHIFTMENU(3) | SHIFTITEM(3) | SHIFTSUB(NOSUB)); /* Deselect */ OnMenu(win, SHIFTMENU(3) | SHIFTITEM(5) | SHIFTSUB(NOSUB)); /* Edit */ OnMenu(win, SHIFTMENU(3) | SHIFTITEM(6) | SHIFTSUB(NOSUB)); /* Improve */ OnMenu(win, SHIFTMENU(3) | SHIFTITEM(7) | SHIFTSUB(NOSUB)); /* Delete */ } /* Convert double to nice string (must be of size NBLEN) */ char *double2str(char *to, double x) { int l; if (x == NOVAL) to[0] = '\0'; else sprintf(to, "%-5.3g", x); l = strlen(to); while (l > 0 && to[--l] == ' ') to[l] = '\0'; return to; } /* Convert string to double */ double str2double(char *from) { double x; if (sscanf(from, "%lf", &x) != 1) x = NOVAL; return x; } /* convert x to a string (must be of size INTLEN) */ char *int2str(char *to, int x) { if (x == INOVAL) to[0] = '\0'; else sprintf(to, "%d", x); return to; } /* convert string to integer */ int str2int(char *from) { int x; if (sscanf(from, "%d", &x) != 1) x = INOVAL; return x; } /* Removes leading & trailing blanks fron name. Returns name */ char *strip(char *name) { char *scan = name; while (isspace(*scan)) scan++; if (*scan) { char *copy = name; while (*scan) *(copy++) = *(scan++); while (isspace(*--copy)) ; *(copy + 1) = '\0'; } else *name = '\0'; return name; } /* Hack to allow you to press Amiga-V/B even while entering a string in a string requester */ static long __saveds __asm handle_ok_cancel(register __a0 struct InputEvent *ev , register __a1 void *dummy) { struct InputEvent *ep, *laste; static struct InputEvent retkey; if (req) /* run down the list of events to see if they pressed the magic key */ for (ep = ev, laste = NULL; ep != NULL; ep = ep->ie_NextEvent) { if (ep->ie_Class == IECLASS_RAWKEY && (ep->ie_Qualifier & AMIGALEFT ) && (ep->ie_Code == KEYCODE_V || ep->ie_Code == KEYCODE_B) && IntuitionBase->Ac tiveWindow == req->RWindow) { reqdone = TRUE; /* The requester is going away */ /* Add an extra "return key" event */ retkey.ie_Class = IECLASS_RAWKEY; retkey.ie_SubClass = 0; retkey.ie_Code = 0x44; /* return key */ retkey.ie_Qualifier = 0; retkey.ie_position = ep->ie_position; retkey.ie_TimeStamp = ep->ie_TimeStamp; retkey.ie_NextEvent = ep; /* we can handle this event so take it off the chain */ if (laste == NULL) ev = &retkey; else laste->ie_NextEvent = &retkey; break; } else laste = ep; } /* pass on the pointer to the event */ return (long)ev; } /* Create window, menus for a new graph */ int init_uio(struct graph *g) { /* Create mode select gadgets */ struct Gadget *a = AllocMem(sizeof(struct Gadget), 0L); struct Gadget *c = AllocMem(sizeof(struct Gadget), 0L); ModSys(0, 1, JAM2, &font); if (a && c) { struct Menu *ml = NULL, *project, *edit, *add, *graph; struct MenuItem *print; *a = arrow; *c = cross; a->NextGadget = c; g->io.gadgets = graph_win.FirstGadget = a; g->io.mem = NULL; /* Create window & menus */ if (g->io.win = OpenWindow(&graph_win)) if ((g->io.mem = NewMemory()) && (project = AddMenu(&ml, NULL, "Project", MENUENABLED, g->io.mem )) && AddItem(project, "New Graph", ITEMENABLED | HIGHCOMP, 0, 'N ', FALSE, g->io.mem) && AddItem(project, "Delete Graph", ITEMENABLED | HIGHCOMP, 0, 0, FALSE, g->io.mem) && AddItem(project, "Load Graph...", ITEMENABLED | HIGHCOMP, 0 , 'O', FALSE, g->io.mem) && AddItem(project, "Save Graph...", ITEMENABLED | HIGHCOMP, 0 , 'S', FALSE, g->io.mem) && (print = AddItem(project, "Output Graph", ITEMENABLED | HIG HCOMP, 0, 0, TRUE, g->io.mem)) && AddSub(print, "To Printer", ITEMENABLED | HIGHCOMP, 0, 'P', g->io.mem) && AddSub(print, "To Disk...", ITEMENABLED | HIGHCOMP, 0, 0, g->io.mem) && AddRule(project, g->io.mem) && AddItem(project, "Load Variables...", ITEMENABLED | HIGHCOM P, 0, 0, FALSE, g->io.mem) && AddItem(project, "Save Variables...", ITEMENABLED | HIGHCOM P, 0, 0, FALSE, g->io.mem) && AddRule(project, g->io.mem) && AddItem(project, "Quit", ITEMENABLED | HIGHCOMP, 0, 'Q', FA LSE, g->io.mem) && (graph = AddMenu(&ml, NULL, "Graph", MENUENABLED, g->io.mem)) & & AddItem(graph, "Scale...", ITEMENABLED | HIGHCOMP, 0, 'L', FALSE, g->io.mem) && AddItem(graph, "Axes...", ITEMENABLED | HIGHCOMP, 0, 'X', F ALSE, g->io.mem) && AddItem(graph, "Zoom", ITEMENABLED | HIGHCOMP, 0, 'Z', FALS E, g->io.mem) && AddItem(graph, "Zoom out", ITEMENABLED | HIGHCOMP, 0, 0, FA LSE, g->io.mem) && AddItem(graph, "Centre", ITEMENABLED | HIGHCOMP, 0, 'C', FA LSE, g->io.mem) && (add = AddMenu(&ml, NULL, "Add", MENUENABLED, g->io.mem)) && AddItem(add, "Function...", ITEMENABLED | HIGHCOMP, 0, 'F', FALSE, g->io.mem) && AddItem(add, "Label...", ITEMENABLED | HIGHCOMP, 0, 0, FALS E, g->io.mem) && (edit = AddMenu(&ml, NULL, "Edit", MENUENABLED, g->io.mem)) && AddItem(edit, "Variables...", ITEMENABLED | HIGHCOMP, 0, 'V ', FALSE, g->io.mem) && AddRule(edit, g->io.mem) && AddItem(edit, "Select function...", ITEMENABLED | HIGHCOMP, 0, 0, FALSE, g->io.mem) && AddItem(edit, "Deselect", ITEMENABLED | HIGHCOMP, 0, 0, FAL SE, g->io.mem) && AddRule(edit, g->io.mem) && AddItem(edit, "Edit...", ITEMENABLED | HIGHCOMP, 0, 'E', FA LSE, g->io.mem) && AddItem(edit, "Improve", ITEMENABLED | HIGHCOMP, 0, 'I', FA LSE, g->io.mem) && AddItem(edit, "Delete", ITEMENABLED | HIGHCOMP, 0, 0, FALSE , g->io.mem)) { SetMenuStrip(g->io.win, ml); g->io.menu = ml; disable_rect_menus(g); disable_object_menus(g); /* Add signal bit */ window_sigs |= 1 << g->io.win->UserPort->mp_SigBit; return TRUE; /* all done ok */ } else { Free(g->io.mem); CloseWindow(g->io.win); alert(NULL, "No memory !", NULL); } else alert(NULL, "Couldn't open window", NULL); } else alert(NULL, "No memory !", NULL); if (a) FreeMem(a, sizeof(struct Gadget)); if (c) FreeMem(c, sizeof(struct Gadget)); return FALSE; } /* Close window, etc */ void cleanup_uio(struct graph *g) { struct Gadget *gg, *next; ClearMenuStrip(g->io.win); CloseWindow(g->io.win); Free(g->io.mem); for (gg = g->io.gadgets; gg; gg = next) { next = gg->NextGadget; FreeMem(gg, sizeof(struct Gadget)); } /* Construct new signal list */ window_sigs = 0; for (g = first(&graph_list); succ(g); g = succ(g)) window_sigs |= 1 << g->io.win->UserPort->mp_SigBit; } /* Global initialisation */ int init_user() { if (!make_font_list()) return FALSE; /* Open alert font */ alert_font = OpenFont(&alert_attr); /* Install input handler */ me = FindTask(0); inputDevPort = CreatePort(0,0); if (inputDevPort) { if (inputRequestBlock = CreateStdIO(inputDevPort)) { handlerStuff.is_Data = NULL; handlerStuff.is_Code = (void *)handle_ok_cancel; handlerStuff.is_Node.ln_Pri = 51; if (OpenDevice("input.device", 0, inputRequestBlock, 0) == 0) { inputOpen = TRUE; inputRequestBlock->io_Command = IND_ADDHANDLER; inputRequestBlock->io_Data = (APTR)&handlerStuff; DoIO(inputRequestBlock); return TRUE; } } } alert(NULL, "Couldn't install input handler", NULL); return FALSE; } /* Global cleanup */ void cleanup_user() { if (inputOpen) { inputRequestBlock->io_Command = IND_REMHANDLER; inputRequestBlock->io_Data = (APTR)&handlerStuff; DoIO(inputRequestBlock); CloseDevice(inputRequestBlock); } if (inputRequestBlock) DeleteStdIO(inputRequestBlock); if (inputDevPort) DeletePort(inputDevPort); free_list((list *)&flist, sizeof(struct fnode)); }