/* * blk - Automatic Requester formatter and generator. * * Reads a requester description and formats graphically then generates the * appropriate C-code to specify the requester. See the examples and the * documentaion for using the program. * * Process flow: Box structure read by recursive-descent. Boxes formatted * and layed-out, also recursively. Borders generated and optimized into a * minimum set of Border structs. Working preview displayed for fiddling * with. Output declarations written to file. * * Problems: In a nutshell -- error recovery. It doesn't do too well if * it runs out of memory, especially in the Border optimization code. * Also, the user error messages on parse errors could be much better -- * like giving line number or a sample of the code at that point -- something * that would give a clue as to what went wrong. Other than that, it's * pretty good. * * Differences from version 1: Version 1 was distributed on a Fish disk * and was a real hack. This version supports a 'C'-like preprocessor * with #include's and #define's and macros with arguments. This makes * the Requester source files look much nicer since the underlying * grammar is so unreadable. * * Disclaimer: This is a tool I hacked up for my own use in creating requesters * for Modeler 3D. It works for me, but I make no claim as to the robustness * or other quality of the code. It's not mouse-driven, but it's such a * useful tool that it's worth learning anyway. Besides, you can put it in * your makefile's and have it work just like any other compiler. * * I'm making this available as a service to Amiga developers. You are * encouraged to enhance or modify as you need to make it more useful for * your own purposes. If you make any changes that make this a better * general-purpose tool, let me know about them. * * Stuart Ferguson 1/89 * (shf@well.UUCP) */ #include #include #include #include /* STD.H is my collection of useful macros. */ #include "std.h" /* LEX.H is the lexical analysis defines. */ #include "lex.h" #define abs(x) ((x)<0 ? -(x) : (x)) /* Size of font for text boxes (fixed width assumed). */ #define FONTWIDTH 8 #define FONTHEIGHT 8 /* Box types */ #define HBOX 1 #define VBOX 2 #define FILL 3 #define BLOK 4 #define TEXT 5 #define HRULE 6 #define VRULE 7 /* * Extended Gadget structure to hold an optional special gadget ID name * and the parent box for this gadget. */ struct SuperGadget { struct Gadget g; struct Box *box; char *gnam; }; /* * A requester is constructed from a tree of Boxes. Boxes are arranged in a * binary tree, one subtree is what is inside the Box (sub), the other is the * remaining Boxes at the same level (next). */ typedef struct Box { struct SuperGadget *gad; /* gadget - if any */ struct Box *next, *sub; /* binary tree links */ short type, /* box type - possibilities above */ col; /* color - for borders and text */ short xs, ys, /* box size (x,y) */ x, y, /* box position (x,y) */ nfil; /* number of filers inside this box */ char *val; /* string for TEXT boxes */ }; /* GLOBAL */ int infoLevel = 1, /* degree of verbosity (0=quiet, * 1=normal, 2=verbose) */ printBoxes = 0, /* printout flag (false normally) */ showPreview; /* preview flag (depends on whether there's * an output file and on the '-d' flag. */ char *globStr = "static "; /* structures normally static */ FILE *file; /* output file */ char *base; /* base name */ short def_bcol = 1, /* default border and */ def_tcol = 1; /* text colors */ /* String pointer ID's returned from the lexer. */ char *fill_pstr, *tbox_pstr, *hbox_pstr, *vbox_pstr, *blok_pstr, *s_pstr, *p_pstr, *pv_pstr, *ph_pstr; /* The Requester structures, the lists of parts and the guy itself. */ struct Border *blst = NULL; /* list header for border structs */ struct IntuiText *itlst = NULL; /* list header for text structs */ struct Gadget *glst = NULL; /* list header for gadgets */ #define LATER 0 struct Requester mreq = { NULL,5,5,LATER,LATER,0,0,LATER,LATER,LATER, 0,0,NULL,{NULL},NULL,NULL,{NULL} }; /* * Generic templates for creating Intuition structures. */ struct Border generic_border = {0, 0, LATER, 0, JAM1, LATER, LATER, LATER}; struct Gadget generic_gadget = { LATER, LATER, LATER, LATER, LATER, GADGHCOMP,RELVERIFY,BOOLGADGET|REQGADGET, LATER, NULL, LATER, 0, LATER, LATER, NULL }; struct IntuiText generic_itext = {LATER, 0, JAM2, LATER, LATER, LATER, LATER, LATER}; struct StringInfo generic_sinfo = {LATER, NULL, 0, LATER, 0}; struct PropInfo generic_pinfo = {AUTOKNOB|PROPBORDERLESS,0x8000,0x8000,0x8000,0x8000}; /* Two macros to extract GadgetType info from a Gadget structure. */ #define GADGETTYPEBITS (~GADGETTYPE) #define GTYPE(g) (((g)->GadgetType)&GADGETTYPEBITS) /* * The preview window. */ struct NewWindow nwin = { 0, 0, 300, 150, -1, -1, CLOSEWINDOW | REQCLEAR | MOUSEMOVE | GADGETDOWN | GADGETUP | VANILLAKEY, WINDOWDEPTH | WINDOWDRAG, NULL, NULL, (UBYTE *) "Preview", NULL, NULL, 0, 0, 0, 0, WBENCHSCREEN }; struct IntuitionBase *IntuitionBase; struct Box * ReadBoxList (); short Layin (); char * IMClass (); /* Lexer interface functions. */ char * FindString (); short NextToken (); /* * Returns a new, initialized Box struct. */ struct Box * NewBox (type) short type; { struct Box *b; if (!(b = NEW (struct Box))) { MemError (); return NULL; } b->type = type; b->nfil = 0; b->gad = NULL; b->val = NULL; b->next = b->sub = NULL; return b; } /* * Recursively frees Box tree. */ FreeBox (box) struct Box *box; { register struct Gadget *g; register struct StringInfo *si; if (!box) return; FreeBox (box->sub); FreeBox (box->next); if (g = (struct Gadget *) box->gad) { if (GTYPE(g) == STRGADGET) { si = (struct StringInfo *) g->SpecialInfo; FREE_X (si, struct StringInfo, si->MaxChars); } else if (GTYPE(g) == PROPGADGET) { FREE (g->SpecialInfo, struct PropInfo); FREE (g->GadgetRender, struct Image); } FREE (g, struct SuperGadget); } FREI (box); } /* * Recursively examine all nodes of Box tree and allocate Border structs for * all the HRULE and VRULE boxes. Adds new Borders to the main list. * Returns 0 for failure, 1 for sucess. */ int CreateBorder (box) register struct Box *box; { register struct Border *bd; if (!box) return 1; if (box->type == HRULE || box->type == VRULE) { if (!(bd = NEW (struct Border))) { MemError (); FreeBorder (); return 0; } *bd = generic_border; bd->FrontPen = box->col; bd->Count = 2; if (!(bd->XY = NEW_N (SHORT, 4))) { MemError (); FREI (bd); FreeBorder (); return 0; } bd->XY[0] = bd->XY[2] = box->x; bd->XY[1] = bd->XY[3] = box->y; if (box->type == HRULE) bd->XY[2] += box->xs - 1; else bd->XY[3] += box->ys - 1; bd->NextBorder = blst; blst = bd; } if (!CreateBorder (box->sub)) return 0; return (CreateBorder (box->next)); } /* * Frees all Border structs in main Border list. */ FreeBorder () { register struct Border *b, *nxt; for (b = blst; b; b = nxt) { nxt = b->NextBorder; FREE_N (b->XY, SHORT, b->Count * 2); FREI (b); } blst = NULL; } /* * Recursively examine all nodes of Box tree and allocate IntuiText structs * for TEXT boxes that are not string gadgets. Adds new Borders to the main * list. Returns 1 for sucess, 0 for failure. */ int CreateIText (box) register struct Box *box; { struct IntuiText *it; if (!box) return 1; /* * "box->val" may have been zero-ed by a string gadget grabbing * that text. If so, this is not an IntuiText. */ if (box->type == TEXT && box->val) { if (!(it = NEW(struct IntuiText))) { MemError (); FreeIText (); return 0; } *it = generic_itext; it->IText = (UBYTE *) box->val; it->LeftEdge = box->x; it->TopEdge = box->y; it->FrontPen = box->col; it->NextText = itlst; itlst = it; } if (!CreateIText (box->sub)) return 0; return (CreateIText (box->next)); } /* * Frees all IntuiText structs in the main list. (No need to free the * text itself since that is managed by the lexer.) */ FreeIText () { register struct IntuiText *it, *nxt; for (it = itlst; it; it = nxt) { nxt = it->NextText; FREI (it); } itlst = NULL; } /* * First pass at merging redundant Borders: Examines all the Borders in * the list for adjacency. Any borders that could use the same set of * polyline commands are merged into a single struct. */ MergeBorders () { register struct Border *a, *b; short i0, i1, x, y, *xy, j; register short i, ac, bc, merge; do { merge = -1; /* * Examine all pairs of borders, "a" and "b", that * are drawn with the same color, seaching for a pair * that can be merged. When loop exits with merge=-1, * all pairs have been merged. */ for (a = blst; a; a = a->NextBorder) { for (b = a->NextBorder; b; b = b->NextBorder) { if (a->FrontPen != b->FrontPen) continue; /* * Examine the 4 pairs of endpoints of each * polyline to see if any are adjacent to * each other. If any are found, the pairs * located are encoded into "merge" and * the search loop exits. */ ac = a->Count; bc = b->Count; for (i0 = 0; i0 < 2; i0++) for (i1 = 0; i1 < 2; i1++) { x = a->XY[i0*2 * (ac - 1)] - b->XY[i1*2 * (bc - 1)]; y = a->XY[i0*2 * (ac - 1) + 1] - b->XY[i1*2 * (bc - 1) + 1]; if (abs (x) + abs (y) == 1) merge = (i0 << 1) + i1; } if (merge != -1) break; } if (merge != -1) break; } if (merge == -1) continue; /* * Merging: Create a new polyline data array and move * the two parent polylines into the new one, possibly * reversing one or both in the process. * -- HELP ME: Is there a nice way out if this * allocation fails...? */ xy = NEW_N (SHORT, (bc + ac) * 2); x = ((merge & 2) == 0); /* x = reverse "a" */ y = ((merge & 1) == 1); /* y = reverse "b" */ j = 0; for (i = 0; i < ac; i++) { i0 = (x ? ac - 1 - i : i) * 2; xy[j++] = a->XY[i0]; xy[j++] = a->XY[i0 + 1]; } for (i = 0; i < bc; i++) { i0 = (y ? bc - 1 - i : i) * 2; xy[j++] = b->XY[i0]; xy[j++] = b->XY[i0 + 1]; } /* * Set "a" to have the new polyline data array. */ a->Count = j / 2; FREE_N (a->XY, SHORT, ac * 2); a->XY = xy; /* * Find "b's" predecessor and remove "b" from list. */ for (a = blst; a && a->NextBorder != b; a = a->NextBorder); a->NextBorder = b->NextBorder; FREE_N (b->XY, SHORT, bc * 2); FREE (b, struct Border); } while (merge != -1); } /* * Second pass of Border merging: Eliminates linear segments from all * Borders XY lists. The first pass will create lots of redundant points * along linear line segments. This pass will compress those out. */ MergeLinear () { register struct Border *b; register short i0, i1, i2, k, *xy; /* * Examine all borders with more than 1 line segment. */ for (b = blst; b; b = b->NextBorder) { if (b->Count < 3) continue; /* * Scan along the polyline list and compress out linear * segments by skiping over them. */ xy = b->XY; i0 = 0; i1 = 1; i2 = 2; k = 2; while (i2 < b->Count) { /* * Skip past linear segments. (I.e. find the bend.) */ while (i2 < b->Count && (xy[i0 * 2] == xy[i1 * 2] && xy[i1 * 2] == xy[i2 * 2] || xy[i0 * 2 + 1] == xy[i1 * 2 + 1] && xy[i1 * 2 + 1] == xy[i2 * 2 + 1])) { i1++; i2++; } if (i2 >= b->Count) continue; /* * Move polyline data to itself after skipping. */ xy[k++] = xy[i1 * 2]; xy[k++] = xy[i1 * 2 + 1]; i0 = i1; i1 = i2; i2 = i1 + 1; } xy[k++] = xy[i1 * 2]; xy[k++] = xy[i1 * 2 + 1]; k /= 2; if (k == b->Count) continue; /* * If this border has gotten shorter, allocate a new * array and transfer the new polyline data. */ xy = NEW_N (SHORT, k * 2); for (i0 = 0; i0 < k * 2; i0++) xy[i0] = b->XY[i0]; FREE_N (b->XY, SHORT, b->Count * 2); b->XY = xy; b->Count = k; } } /* * Set the XSize and YSize fields for this box and all below. */ Format (box) struct Box *box; { struct Box *b; short mx, my, sx, sy, nf; ASSERT (box); /* * Deal with the basis (leaf) cases. */ switch (box->type) { /* Blok and text nodes have fixed, already computed size. */ case BLOK: case TEXT: return; /* Fill node has no intrinsic size. */ case FILL: box->xs = box->ys = 0; box->nfil = 1; return; /* H and VRULES have no intrinsic X or Y size, respectively. */ case HRULE: box->xs = 0; return; case VRULE: box->ys = 0; return; } /* * H and VBOXes are the recursive case. First format each * internal box. */ for (b = box->sub; b; b = b->next) Format (b); /* * Compute total and max sizes in each direction. Total (sx,sy) is sum * of all sub-boxes, max (mx,my) is max of sub-boxes. Also inherit * filler count. */ my = mx = sx = sy = nf = 0; for (b = box->sub; b; b = b->next) { sx += b->xs; sy += b->ys; if (b->type == box->type || b->type == FILL) nf += b->nfil; if (b->xs > mx) mx = b->xs; if (b->ys > my) my = b->ys; } box->nfil = nf; /* * For horizontal boxes, bounding box is sum in x and max in y; for * vertical, bouding box is max in x and sum in y. This is the * minimum size of the containing box for the given subboxes. It * may still expand due to fillers. */ if (box->type == HBOX) { box->xs = sx; box->ys = my; } else if (box->type == VBOX) { box->xs = mx; box->ys = sy; } } /* * Compute the position of the boxes internal to this box given that this * box has correct location. The box size computed by Format() is a minimum * size, "mx" and "my" are the max that the box can be expanded by filler. */ Layout (box, mx, my) struct Box *box; short mx, my; { struct Box *b; short ish, z, nfil; long gap, ifil; ASSERT (box); /* * Rules fill out to their max possible size. */ if (box->type == HRULE) box->xs = mx; else if (box->type == VRULE) box->ys = my; /* * Process only HBOX and VBOX cases recursively. Any other case (a * basis case) has its position set correctly (see assumptions at * head of function). */ if (box->type != HBOX && box->type != VBOX) return; /* Get important values. Set the "is-hbox" (ish) flag. Get the * current X size for HBOXes or the Y size for VBOXes as "z". * "gap" is the differece between the max size and minimum size * given by the Format(), and is how much fillers can expand. */ ish = (box->type == HBOX); z = (ish ? box->x : box->y); gap = (ish ? mx - box->xs : my - box->ys); /* * Set positions by setting filler sizes. */ ifil = 0; Layin (box, &ifil, ish, z, box->nfil, gap); /* Update box size. If it had fillers, it got as big as * it could. */ if (box->nfil) { if (ish) box->xs = mx; else box->ys = my; } } /* * Layout internal boxes. Having this as a recursive function deals with * the case of VBOXes within VBOXes and HBOXes within HBOXes. * * NOTE: I'd comment this function, but I can't figure it out. It seems to * figure out the horizonal position of each box and update it as it goes * along. It also calls itself when there are nested same-class boxes. * Oh well. There's probably a better way to do it anyway. */ short Layin (box, ifil, ish, z, nfil, gap) struct Box *box; short *ifil, ish, z, nfil; long gap; { struct Box *b; short t; for (b = box->sub; b; b = b->next) { if (ish) { b->x = z; b->y = box->y; } else { b->x = box->x; b->y = z; } if (b->type == FILL) { t = (gap * (*ifil + 1)) / nfil - (gap ** ifil) / nfil; (*ifil)++; if (ish) b->xs = t; else b->ys = t; } else if ((ish && b->type == HBOX) || (!ish && b->type == VBOX)) { if (ish) b->ys = box->ys; else b->xs = box->xs; t = Layin (b, ifil, ish, z, nfil, gap) - z; if (ish) b->xs = t; else b->ys = t; } else Layout (b, box->xs, box->ys); z += (ish ? b->xs : b->ys); } return z; } /* * Use the computed position of the boxes to set the position of * the associated gadgets. */ PositionGadgets () { struct Box *b; struct Gadget *g; for (g = glst; g; g = g->NextGadget) { b = ((struct SuperGadget *) g)->box; g->LeftEdge = b->x; g->TopEdge = b->y; g->Width = b->xs; g->Height = b->ys; } } /* * Returns pointer to string containing box type name for printout. */ char * BoxType (typ) short typ; { switch (typ) { case HBOX: return ("HBOX"); case VBOX: return ("VBOX"); case BLOK: return ("BLOK"); case TEXT: return ("TEXT"); case FILL: return ("FILL"); case HRULE: return ("HRULE"); case VRULE: return ("VRULE"); } } /* * Recursively prints this box and all its contents. */ PrintBox (box, lev) struct Box *box; short lev; { int i; if (!box) return; for (i = 0; i < lev; i++) printf (" "); printf ("%s (%d,%d) %dx%d", BoxType (box->type), box->x, box->y, box->xs, box->ys); if (box->type == TEXT) printf (" <%s>", box->val); if (box->gad) printf (" [gadget]"); printf ("\n"); PrintBox (box->sub, lev + 1); PrintBox (box->next, lev); } /* * ==== INPUT SECTION ==== * * File input uses the "lex" front-end for macro processing. Main entry * points for this package are the NextToken() and Backspace() functions. * NextToken() returns the code for the next lexical item in the input * stream and sets a buffer pointer to point to its value. Backspace() * resets the lex package to re-read the last token read, so that the * file is effectively backspaced one token. FindString() is also used * to get the unique identifer pointer for a string from the hash table. */ /* * Read a number if there is one. Otherwise return false and don't * change n's value. */ BOOL Qnum (n, radix) short *n, radix; { short i = 0, tok; char *buf; tok = NextToken (&buf); if (tok != RT_NUM) { Backspace (); return 0; } for (; *buf >= '0' && *buf <= '9'; buf++) { i = i * radix + (*buf - '0'); } *n = i; return 1; } /* * Reads a double-quoted string like * "stuff" * from the file. Returns pointer to the string contents. */ char * ReadString () { short tok; char *buf; tok = NextToken (&buf); if (tok != RT_STR) { fprintf (stderr, "String not found.\n"); Backspace (); return NULL; } return buf; } /* * Read gadget ID of the form * :number * if there is one. Read as hex. If there is one, create a new * SuperGadget structure and add to the main gadget list. */ struct SuperGadget * ReadOptGadget (box) struct Box *box; { struct SuperGadget *sg; short tok, id; char *buf; tok = NextToken (&buf); if (tok != RT_CHR || *buf != ':') { Backspace (); return NULL; } if (!Qnum (&id, 16)) { fprintf (stderr, "Error reading gadget ID number\n"); return NULL; } if (!(sg = NEW (struct SuperGadget))) { MemError (); return NULL; } sg->g = generic_gadget; sg->gnam = NULL; sg->box = box; sg->g.GadgetID = id; sg->g.NextGadget = glst; glst = (struct Gadget *) sg; return sg; } /* * Get a box from the open file. Boxes are either single tokens ("f" * for FILL box, "-" and "|" for ordinary rules) or is a * composite of the form "("type data")". Type can be "h" for HBOX, * "v" for VBOX, "b" for BLOK, "t" for TEXT, or "-" and "|" again for * special rules. * * If there isn't a box here, ReadBox() returns NULL with the lexical * stream positioned back to read whatever was really there. */ struct Box * ReadBox () { short tok, i; char *buf, c; struct Box *b; tok = NextToken (&buf); if (tok == RT_ID && buf == fill_pstr) return NewBox (FILL); if (tok != RT_CHR) { Backspace (); return NULL; } c = *buf; if (c == '-') { if (!(b = NewBox (HRULE))) return NULL; b->ys = 1; b->col = def_bcol; return b; } if (c == '|') { if (!(b = NewBox (VRULE))) return NULL; b->xs = 1; b->col = def_bcol; return b; } if (c != '(') { Backspace (); return NULL; } /* * Decode the value inside the '('. */ tok = NextToken (&buf); c = *buf; if (tok == RT_ID) if (buf == hbox_pstr) { if (!(b = NewBox (HBOX))) return NULL; b->sub = ReadBoxList (); } else if (buf == vbox_pstr) { if (!(b = NewBox (VBOX))) return NULL; b->sub = ReadBoxList (); } else if (buf == tbox_pstr) { if (!(b = NewBox (TEXT))) return NULL; b->col = def_tcol; Qnum (&b->col, 10); if (!(b->val = ReadString ())) { FreeBox (b); return NULL; } b->xs = strlen (b->val) * FONTWIDTH; b->ys = FONTHEIGHT; } else if (buf == blok_pstr) { if (!(b = NewBox (BLOK))) return NULL; i = Qnum (&b->xs, 10); i &= Qnum (&b->ys, 10); if (!i) { fprintf (stderr, "Block needs X and Y sizes\n"); return NULL; } } else { fprintf (stderr, "Unrecognized box type <%s>\n", buf); return NULL; } else if (tok == RT_CHR) switch (c) { case '-': if (!(b = NewBox (HRULE))) return NULL; if (!Qnum (&b->ys, 10)) { fprintf (stderr, "Bad hrule structure.\n"); return NULL; } b->col = def_bcol; Qnum (&b->col, 10); break; case '|': if (!(b = NewBox (VRULE))) return NULL; if (!Qnum (&b->xs, 10)) { fprintf (stderr, "Bad vrule structure\n"); return NULL; } b->col = def_bcol; Qnum (&b->col, 10); break; default: fprintf (stderr, "Unrecognized box type <%c>\n", c); return NULL; } else { fprintf (stderr, "Unrecognized box type <%s>\n", buf); return NULL; } /* * Pick up the closing ')'. */ tok = NextToken (&buf); if (tok != RT_CHR || *buf != ')') { fprintf (stderr, "Parse error - expected ')' !\n"); FreeBox (b); return NULL; } /* * Read the optional Gadget for this box (as ":id"). */ b->gad = ReadOptGadget (b); return b; } /* * Read a list of boxes from the file stream. Recursive: read a box, * then read a list. */ struct Box * ReadBoxList () { struct Box *b; b = ReadBox (); if (!b) return NULL; b->next = ReadBoxList (); return b; } /* * Create a new StringInfo struct and initialize to point to the * given string buffer. Allocates space for the buffer along with * the info struct itself (NEW_X). Removes trailing spaces. */ APTR NewStrInfo (buf) char *buf; { struct StringInfo *si; short i; char *str; i = strlen (buf) + 1; if (!(si = NEW_X (struct StringInfo, i))) { MemError (); return NULL; } *si = generic_sinfo; si->Buffer = (UBYTE *) (str = (char *) (si+1)); si->MaxChars = i; strcpy (str, buf); for (i -= 2; i>=0 && str[i] == ' '; i--) str[i] = 0; return (APTR) si; } /* Create new PropInfo struct. Set the free motion flag based on the * id for this gadget "pv" = vert prop, "ph" = horiz prop, "p" = h+v prop. */ APTR NewPropInfo (id) char *id; { register struct PropInfo *pi; if (!(pi = NEW (struct PropInfo))) { MemError (); return NULL; } *pi = generic_pinfo; if (id == p_pstr || id == pv_pstr) pi->Flags |= FREEVERT; if (id == p_pstr || id == ph_pstr) pi->Flags |= FREEHORIZ; return (APTR) pi; } /* * Reads the list of gadget info from the end of the file. Reads as much * as there is. Format is: * number {s|p|pv|ph} {:string} string * stuff in {}'s is optional. Each entry gives extra info for the numbered * gadget. {s|p} is string or prop flag. {:string} is the optional named * value rather than just the nubmer. The last string is the gadget flags. * Each set of info gets added to the corresponding gadget structure in * the main list. */ ReadGadInfo () { struct Gadget *g; struct Box *box; short tok; char *buf, c, *actf; short i; USHORT flag; while (Qnum (&i, 16)) { /* * Locate the gadget in question and it's associated box. */ for (g = glst; g; g = g->NextGadget) if (g->GadgetID == i) break; if (!g) { fprintf (stderr, "Unknown gadget ID: %x\n", i); continue; } box = ((struct SuperGadget *) g)->box; /* Get the optional string or prop flag. */ tok = NextToken (&buf); if (tok == RT_ID) { if (buf == s_pstr) { g->GadgetType &= ~GADGETTYPEBITS; g->GadgetType |= STRGADGET; if (!(g->SpecialInfo = NewStrInfo (box->val))) return; box->val = NULL; } else if (buf == p_pstr || buf == ph_pstr || buf == pv_pstr) { g->GadgetType &= ~GADGETTYPEBITS; g->GadgetType |= PROPGADGET; if (!(g->SpecialInfo = NewPropInfo (buf))) return; if (!(g->GadgetRender = (APTR) NEW (struct Image))) { MemError (); FREE (g->SpecialInfo, struct PropInfo); return; } } else { fprintf (stderr, "Expected \"s\" or \"p\": <%s>\n", buf); break; } tok = NextToken (&buf); } /* Get optional gadget ID name string. */ if (tok == RT_CHR && *buf == ':') { ((struct SuperGadget *) g)->gnam = ReadString (); tok = NextToken (&buf); } Backspace (); /* Get and process required activation flags string. */ actf = ReadString (); g->Activation &= ~RELVERIFY; for (; *actf; actf++) { switch (*actf) { case 'B': g->Flags &= ~GADGHIGHBITS; g->Flags |= GADGHBOX; flag = 0; break; case 't': flag = TOGGLESELECT; break; case 'v': flag = RELVERIFY; break; case 'e': flag = ENDGADGET; break; case 'i': flag = GADGIMMEDIATE; break; case 'c': flag = STRINGCENTER; break; case 'f': flag = FOLLOWMOUSE; break; } g->Activation |= flag; } } } /* * Get values for the identifier strings from the lexical analyzer. * The lexer will return the same pointer for any identifier which * matches. */ AssignStrings () { fill_pstr = FindString ("f"); hbox_pstr = FindString ("h"); vbox_pstr = FindString ("v"); tbox_pstr = FindString ("t"); blok_pstr = FindString ("b"); s_pstr = FindString ("s"); p_pstr = FindString ("p"); ph_pstr = FindString ("ph"); pv_pstr = FindString ("pv"); } /* * To read file: open, read base name, read optional default border and text * colors, read a box (a BIG box), read gadget info blocks, close. */ struct Box * ReadFile () { struct Box *box; short i, tok; char *buf; AssignStrings (); tok = NextToken (&base); if (tok != RT_ID) { fprintf (stderr, "Cannot find base name\n"); return NULL; } Qnum (&def_bcol, 10); Qnum (&def_tcol, 10); if (infoLevel > 1) printf ("base name: \"%s\"\ndefault border color:" " %d\ndefault text color: %d\n", base, def_bcol, def_tcol); box = ReadBox (); ReadGadInfo (); /* * Make sure we're at the end of the file to make the * lexer happy. Print up to 10 error messages unless there * is no box from the previous call in which case there's something * wrong anyway. (Unless in verbose mode, then show 'em all.) */ i = ((box || infoLevel > 1) ? 10 : 0); while (NextToken (&buf) != RT_EOF) { if (i) { fprintf (stderr, "Token found after end of data: <%s>\n", buf); if (!--i) fprintf (stderr, "... etc.\n"); } } return box; } /* * ==== OUTPUT SECTION ==== * * Dumps structures created during the input and resolution phases of * the processing. Just takes a pointer to a Requester in WriteRequester() * and dumps the related structures as well. */ /* * Write string info and buffer declarations from string gadgets * (if any). */ WriteStrGad (glist) struct Gadget *glist; { struct Gadget *g; struct StringInfo *si; int i, n; /* Count number of string gadgets. */ for (n = 0, g = glist; g; g = g->NextGadget) if (GTYPE(g) == STRGADGET) n++; if (!n) return; /* Write the necessary buffers for the string infos. */ fprintf (file, "\n%sUBYTE %s_nbuf[%d][NUMCHR] = {\n\t", globStr, base, n); i = n; for (g = glist; g; g = g->NextGadget) { if (GTYPE(g) != STRGADGET) continue; si = (struct StringInfo *) g->SpecialInfo; fprintf (file, " \"%s\"", si->Buffer); if (--i) fprintf (file, ","); } fprintf (file, "\n};\n\n%sstruct StringInfo %s_sinfo[] = {\n", globStr, base); i = 0; for (g = glist; g; g = g->NextGadget) { if (GTYPE(g) != STRGADGET) continue; si = (struct StringInfo *) g->SpecialInfo; fprintf (file, "\t{&%s_nbuf[%d][0],undo,0,NUMCHR,0}", base, i++); if (--n) fprintf (file, ","); fprintf (file, "\n"); } fprintf (file, "};\n"); if (infoLevel > 1) printf ("wrote %d StringInfo structs\n", i); } /* * Write prop info and image declarations for prop gadgets (if any). */ WritePropGad (glist) struct Gadget *glist; { struct Gadget *g; struct PropInfo *pi; int i, n; /* Count number of prop gadgets. */ for (n = 0, g = glist; g; g = g->NextGadget) if (GTYPE(g) == PROPGADGET) n++; if (!n) return; /* Write the necessary images for the autoknobs. */ fprintf (file, "\n%sstruct Image %s_pimg[%d];\n", globStr, base, n); /* Write the PropInfo structures themselves. */ fprintf (file, "\n%sstruct PropInfo %s_pinfo[] = {\n", globStr, base); i = n; for (g = glist; g; g = g->NextGadget) { if (GTYPE(g) != PROPGADGET) continue; pi = (struct PropInfo *) g->SpecialInfo; fprintf (file, "\t{%u,%u,%u,%u,%u}", pi->Flags, pi->HorizPot, pi->VertPot, pi->HorizBody, pi->VertBody); if (--i) fprintf (file, ","); fprintf (file, "\n"); } fprintf (file, "};\n"); if (infoLevel > 1) printf ("wrote %d PropInfo structs\n", n); } /* * Write the gadgets from the main gadget list. Returns number of * gadgets written. */ int WriteGadgets (glist) struct Gadget *glist; { struct Gadget *g; int k = 1, nimg=0, nprp=0, nstr=0; char *nam; if (!glist) return 0; WriteStrGad (glist); WritePropGad (glist); fprintf (file, "\n%sstruct Gadget %s_gad[] = {\n", globStr, base); for (g = glist; g; g = g->NextGadget) { if (g->NextGadget) fprintf (file, "\t{&%s_gad[%d]", base, k++); else fprintf (file, "\t{NULL"); fprintf (file, ",%d,%d,%d,%d,%u,%u,%u,", g->LeftEdge, g->TopEdge, g->Width, g->Height, g->Flags, g->Activation, g->GadgetType); if (GTYPE(g) == PROPGADGET) fprintf (file, "(APTR)&%s_pimg[%d]", base, nimg++); else fprintf (file, "NULL"); fprintf (file, ",\n\t NULL,NULL,0,(APTR)"); if (GTYPE(g) == PROPGADGET) fprintf (file, "&%s_pinfo[%d]", base, nprp++); else if (GTYPE(g) == STRGADGET) fprintf (file, "&%s_sinfo[%d]", base, nstr++); else fprintf (file, "NULL"); if (nam = ((struct SuperGadget *) g)->gnam) fprintf (file, ",%s", nam); else fprintf (file, ",0x%x", g->GadgetID); if (g->NextGadget) fprintf (file, "},\n"); else fprintf (file, "}\n"); } fprintf (file, "};\n"); if (infoLevel > 1) printf ("wrote %d Gadget structs\n", k); return k; } /* * Write out list of IntuiText structs for main list. Returns number * of structures written. */ int WriteText (tlist) struct IntuiText *tlist; { struct IntuiText *it; int k = 1; if (!tlist) return 0; fprintf (file, "\n%sstruct IntuiText %s_txt[] = {\n", globStr, base); for (it = tlist; it; it = it->NextText) { fprintf (file, "\t{%d,%d,%d,%d,%d,&ta,(UBYTE*)\"%s\",", it->FrontPen, it->BackPen, it->DrawMode, it->LeftEdge, it->TopEdge, it->IText); if (it->NextText) fprintf (file, "&%s_txt[%d]},\n", base, k++); else fprintf (file, "NULL},\n"); } fprintf (file, "};\n"); if (infoLevel > 1) printf ("wrote %d IntuiText structs\n", k); return k; } /* * Write out list of XY arrays from Border struct main list */ WriteBorderXY (lst) struct Border *lst; { register struct Border *b; register short i; fprintf (file, "\n%sshort %s_brd_XY[] = {\n", globStr, base); for (b = lst; b; b = b->NextBorder) { fprintf (file, "\t"); for (i = 0; i < b->Count; i++) { fprintf (file, "%d,%d", b->XY[i * 2], b->XY[i * 2 + 1]); if (i != b->Count - 1 || b->NextBorder) fprintf (file, ", "); } fprintf (file, "\n"); } fprintf (file, "};\n"); } /* * Write out list of Border structs from main list. Returns nubmer of * structures written. */ int WriteBorder (lst) struct Border *lst; { register struct Border *b; register short i = 0, k = 1; if (!lst) return 0; WriteBorderXY (lst); fprintf (file, "\n%sstruct Border %s_brd[] = {\n", globStr, base); for (b = lst; b; b = b->NextBorder) { fprintf (file, "\t{0,0,%d,0,JAM1,%d,&%s_brd_XY[%d],", b->FrontPen, b->Count, base, i); i += b->Count * 2; if (b->NextBorder) fprintf (file, "&%s_brd[%d]},\n", base, k++); else fprintf (file, "NULL}\n"); } fprintf (file, "};\n"); if (infoLevel > 1) printf ("wrote %d Border structs\n", k); return k; } /* * Reverse the gadget list so it will make more sense to the client. * This way they will appear in the arrays in the order that they * appear in the description file. */ struct Gadget * ReverseGadList (head) struct Gadget *head; { struct Gadget *newhead = NULL, *nxt; for (; head; head = nxt) { nxt = head->NextGadget; head->NextGadget = newhead; newhead = head; } return newhead; } /* * The main output function. */ WriteRequester (name, req) char *name; struct Requester *req; { short i, ng, nt, nb; if (!(file = fopen (name, "w"))) { fprintf (stderr, "Can't open output file\n"); return; } req->ReqGadget = ReverseGadList (req->ReqGadget); ng = WriteGadgets (req->ReqGadget); nt = WriteText (req->ReqText); nb = WriteBorder (req->ReqBorder); /* * The requester itself. */ fprintf (file, "\n%sstruct Requester %s_req = {\n\ \tNULL,0,0,%d,%d,0,0,", globStr, base, req->Width, req->Height); if (ng) fprintf (file, "%s_gad,", base); else fprintf (file, "NULL,"); if (nb) fprintf (file, "%s_brd,", base); else fprintf (file, "NULL,"); if (nt) fprintf (file, "%s_txt,", base); else fprintf (file, "NULL,"); fprintf (file, "0,0,\n\tNULL,{NULL},NULL,NULL,{NULL}\n};\n"); fclose (file); } MemError () { fprintf (stderr, "Out of memory.\n"); } /* Main entry point. Decode args and call body function. Args are: * * -p : Print box description * -q : Run silent, run deep * -v : Verbose (not much different than normal, really) * -d : Display preview (is default unless output requested) * -s : Send Requester declarations to stdout */ main (argc, argv) int argc; char *argv[]; { int i, junk = 0, prev = 0, tostdout = 0; char *infile = NULL, *outfile = NULL; /* * Decode arguments. */ for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch (argv[i][1]) { case 'p': printBoxes = 1; break; case 'q': infoLevel = 0; break; case 'v': infoLevel = 2; break; case 'd': prev = 1; break; case 's': tostdout = 1; break; case 'g': globStr = ""; break; default: junk = 1; } } else { if (!infile) infile = argv[i]; else if (!outfile) outfile = argv[i]; else junk = 1; } } if (junk || !infile) { printf ("Usage: %s [-p|q|v|d|s|g] []\n", argv[0]); exit (1); } if (tostdout) { outfile = "*"; infoLevel = 0; } showPreview = (!outfile || prev); if (IntuitionBase = (struct IntuitionBase *) OpenLibrary ("intuition.library", 0L)) { Body (infile, outfile); CloseLibrary (IntuitionBase); } } Body (infile, outfile) char *infile, *outfile; { struct Window *win; struct Box *b; short h, w; if (infoLevel > 0) printf ( "Requester generator v2 Jan 1989 Stuart Ferguson\n"); if (!OpenLexFile (infile)) { fprintf (stderr, "Cannot open %s\n", infile); return; } if (b = ReadFile ()) { Format (b); b->x = b->y = 0; Layout (b, b->xs, b->ys); if (printBoxes) PrintBox (b, 0); if (CreateIText (b)) { if (CreateBorder (b)) { MergeBorders (); MergeLinear (); PositionGadgets (); mreq.Width = b->xs; mreq.Height = b->ys; mreq.ReqGadget = glst; mreq.ReqText = itlst; mreq.ReqBorder = blst; if (showPreview) PreviewRequester (&mreq); if (outfile) WriteRequester (outfile, &mreq); FreeBorder (); } FreeIText (); } FreeBox (b); } else fprintf (stderr, "Error reading box description.\n"); LexCleanup (); } /* * Open a window to preview the requester layout. */ PreviewRequester (req) struct Requester *req; { struct Window *win; short h, w; w = req->Width + 12; h = req->Height + 16; if (w > 640 || h > 200) { fprintf (stderr, "Requester too large for preview.\n"); return; } if (w < 150) w = 150; if (h < 60) h = 60; nwin.Width = w; nwin.Height = h; if (!(win = OpenWindow (&nwin))) { fprintf (stderr, "Unable to open preview window.\n"); return; } req->LeftEdge = (w - win->BorderRight + win->BorderLeft - req->Width) / 2; req->TopEdge = (h - win->BorderBottom + win->BorderTop - req->Height) / 2; RequesterLoop (win, req); CloseWindow (win); } RequesterLoop (win, req) struct Window *win; struct Requester *req; { struct Gadget *g; struct IntuiMessage *im; ULONG class, oldflags; USHORT code; int gend = 0, looping; /* * Determine if this requester can be terminated with a gadget. * If not, provide an alternate exit facility. */ for (g = req->ReqGadget; g; g = g->NextGadget) if (g->Activation & ENDGADGET) { gend = 1; break; } oldflags = req->Flags; if (!gend) { if (infoLevel > 0) printf ( "No Endgadget -- Press ESC to exit Requester.\n"); req->Flags |= NOISYREQ; } if (!Request (req, win)) { fprintf (stderr, "Unable to post Requester.\n"); req->Flags = oldflags; return; } looping = 1; while (looping) { im = (struct IntuiMessage *) GetMsg(win->UserPort); if (!im) { WaitPort (win->UserPort); continue; } class = im->Class; code = im->Code; ReplyMsg (im); if (class == VANILLAKEY && code == 27) break; if (infoLevel > 0) printf ("Message : %s\n", IMClass (class)); if (class == REQCLEAR) looping = 0; } if (looping) EndRequest (req, win); req->Flags = oldflags; } /* * Returns name of message class. Lots more classes here than are * possible, but what the hell. */ char * IMClass (class) ULONG class; { switch (class) { case SIZEVERIFY: return ("SIZEVERIFY"); case NEWSIZE: return ("NEWSIZE"); case REFRESHWINDOW: return ("REFRESHWINDOW"); case MOUSEBUTTONS: return ("MOUSEBUTTONS"); case MOUSEMOVE: return ("MOUSEMOVE"); case GADGETDOWN: return ("GADGETDOWN"); case GADGETUP: return ("GADGETUP"); case REQSET: return ("REQSET"); case MENUPICK: return ("MENUPICK"); case CLOSEWINDOW: return ("CLOSEWINDOW"); case RAWKEY: return ("RAWKEY"); case REQVERIFY: return ("REQVERIFY"); case REQCLEAR: return ("REQCLEAR"); case MENUVERIFY: return ("MENUVERIFY"); case NEWPREFS: return ("NEWPREFS"); case DISKINSERTED: return ("DISKINSERTED"); case DISKREMOVED: return ("DISKREMOVED"); case WBENCHMESSAGE: return ("WBENCHMESSAGE"); case ACTIVEWINDOW: return ("ACTIVEWINDOW"); case INACTIVEWINDOW: return ("INACTIVEWINDOW"); case DELTAMOVE: return ("DELTAMOVE"); case VANILLAKEY: return ("VANILLAKEY"); case INTUITICKS: return ("INTUITICKS"); } } #define DEBUG /* * Debug routines. */ #ifdef DEBUG PrintBorder () { struct Border *b; short i; printf ("Borders:\n"); for (b=blst; b; b=b->NextBorder) { printf ("%d %d %d %d\n:: ", b->LeftEdge, b->TopEdge, b->FrontPen, b->Count); for (i=0; iCount; i++) printf ("%d,%d ", b->XY[i*2],b->XY[i*2+1]); printf ("\n"); } } PrintGadget () { USHORT typ; struct Gadget *g; printf ("Gadgets:\n"); for (g=glst; g; g=g->NextGadget) { printf ("%d,%d %d,%d ", g->LeftEdge, g->TopEdge, g->Width, g->Height); typ = GTYPE(g); if (typ == PROPGADGET) printf ("PROP"); if (typ == STRGADGET) printf ("STRING"); if (typ == BOOLGADGET) printf ("BOOL"); printf ("\n"); } } #endif