/* * 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, also recursively * Borders generated and resolved into a minimum set of Border structs * File written, sometimes linearly, sometimes recursively ** * Problems: * The file reading isn't the most elegant in the world, relying on global * variables and backspacing to look ahead. A more general lexical approach * would be nice so that macros could be included (macros are sorely * needed). Also, the file formatted front-end is not the most * user-friendly in the world and something more mouse-driven would be * neat. The source-code generation is not terribily elegant either. A * better approach would be to build the actual structures used in the * requester dynamically so the requester in the preview window could * actually be played with, and generating source-code would be just a * matter of dumping the structures to a file. Borders do this already, and * it's somewhat more understandable than the other structures. ** * 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. Personally I shudder at the thought of * re-writing this into a real general-purpose tool just because bulk of * the code is so poorly written. It does give a nice example of * recursive-decent parsing, and the box layout stuff is kind of neat and * can be put to good use in other places. ** * Please inform my of any changes or improvements. Enjoy. */ #include #include #include #include /* Useful defines */ #define NEW(typ) (typ*)AllocMem((LONG)sizeof(typ),0L) #define FREE(p,typ) FreeMem(p,(LONG)sizeof(typ)) #define NEW_N(typ,n) (typ*)AllocMem((LONG)((n)*sizeof(typ)),0L) #define FREE_N(p,typ,n) FreeMem(p,(LONG)((n)*sizeof(typ))) #define BACKSPACE if (gc != -1) ungetc(gc,file) #define abs(x) ((x)<0 ? -(x) : (x)) /* Box types */ #define HBOX 1 #define VBOX 2 #define FILL 3 #define BLOK 4 #define TEXT 5 #define HRULE 6 #define VRULE 7 /* Dimension limits */ #define MAX_GAD 50 #define BUF_SIZ 500 /* 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 { short type, /* box type - possibilities defined above */ gid, /* Gadget id code (0 if none) */ 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 */ struct Box *next,*sub; /* binary tree links */ } BOX; /* Information on each gadget, zero-th is default */ struct GadInfo { short gid, /* gadget id code */ str, /* string gadget */ prp; /* prop gadget */ char *actf, /* flag string */ *gnam; /* gadget name (GadgetID field) */ } ginfo[MAX_GAD] = { -1,-1,-1,"v",NULL }; /* GLOBAL */ FILE *file; /* input and output files */ int gc; /* last character read */ char buf[BUF_SIZ]; /* big buffer for holding strings */ short bufpos=0; /* position of free space in buf */ char base[10]; /* base name */ short ngad=0,igad=0,ntxt=0,itxt=0, /* counters for gadgets, ituitexts, */ nstr=0,istr=0,nprp=0,iprp=0; /* string and prop gadgets */ short lgad=1; /* counter for gadinfo array (0 already defined) */ short def_bcol=1,def_tcol=1; /* default border and text colors */ struct Border *blst = NULL; /* list header for border structs */ /* generic border and intuitext structs */ struct Border brd = {0,0,1,0,JAM1,2,NULL,NULL}; struct IntuiText it = {1,0,JAM2,0,0,NULL,NULL,NULL}; /* the preview window */ struct NewWindow nwin = { 0,0,300,150, -1,-1,0,WINDOWDEPTH|WINDOWDRAG|SMART_REFRESH, NULL,NULL,(UBYTE*)"Blocks",NULL, NULL,0,0,0,0,WBENCHSCREEN }; struct IntuitionBase *IntuitionBase; /* Returns pointer to string containing box type name */ 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"); } } /* Returns a new, initialized Box struct */ BOX *NewBox (type) short type; { BOX *b; b = NEW(BOX); b->type = type; b->nfil = b->gid = 0; b->val = NULL; b->next = b->sub = NULL; } /* Recursively frees Box tree */ FreeBox (box) BOX *box; { if (!box) return; FreeBox (box->sub); FreeBox (box->next); FREE(box,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. */ CreateBorder (box) register BOX *box; { register struct Border *bd; if (!box) return; if (box->type == HRULE || box->type == VRULE) { bd = NEW(struct Border); *bd = brd; bd->FrontPen = box->col; bd->XY = NEW_N(SHORT,4); 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; } CreateBorder (box->sub); CreateBorder (box->next); } /* debug routine PrintBorder () { struct Border *b; short i; 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"); } } */ /* 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); FREE(b,struct Border); } } /* 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; for (a=blst; a; a=a->NextBorder) { for (b=a->NextBorder; b; b=b->NextBorder) { if (a->FrontPen != b->FrontPen) continue; 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) { xy = NEW_N(SHORT,(bc+ac)*2); x = (merge&2) == 0; /* reverse a */ y = (merge&1) == 1; /* reverse b */ j = 0; for (i=0; iXY[i0]; xy[j++] = a->XY[i0+1]; } for (i=0; iXY[i0]; xy[j++] = b->XY[i0+1]; } a->Count = j/2; FREE_N(a->XY,SHORT,ac*2); a->XY = xy; /* free b and remove 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 part of Border merging: Eliminates linear segments from all * Borders XY lists. */ MergeLinear () { register struct Border *b; register short i0,i1,i2,k,*xy; for (b=blst; b; b=b->NextBorder) { if (b->Count < 3) continue; xy = b->XY; i0 = 0; i1 = 1; i2 = 2; k = 2; while (i2 < b->Count) { while (i2Count && (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 (i2Count) { 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; xy = NEW_N(SHORT,k*2); for (i0=0; i0XY[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) BOX *box; { BOX *b; short mx,my,sx,sy,nf; if (!box) { printf ("Probably an error\nImproper leaf types\n"); return; } /* deal with the basis (leaf) cases */ switch (box->type) { case BLOK: case TEXT: return; /* size already set */ case FILL: /* fill node has no size */ box->xs = box->ys = 0; box->nfil = 1; return; /* H and VRULES have their Y and X sizes already set */ case HRULE: box->xs = 0; return; case VRULE: box->ys = 0; return; } /* only H and VBOXes left * 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 */ if (box->type == HBOX) { box->xs = sx; box->ys = my; } else if (box->type == VBOX) { box->xs = mx; box->ys = sy; } else printf ("Problem city --\nImpossible case\n"); } short Layin(); /* Compute the layout 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) BOX *box; short mx,my; { BOX *b; short ish,z,nfil; long gap,ifil; /* handle basis cases */ if (!box) { printf ("Bad leaf nodes\n"); return; } /* rules fill out to their max possible size */ if (box->type == HRULE) { box->xs = mx; } else if (box->type == VRULE) { box->ys = my; } if (box->type != HBOX && box->type != VBOX) return; /* process only HBOX and VBOX cases recursively * any onther case (a basis case) has its position set correctly * (see assumptions at head of function) */ ish = (box->type == HBOX); z = (ish ? box->x : box->y); gap = (ish ? mx-box->xs : my-box->ys); /* set positions setting filler sizes */ ifil = 0; Layin (box,&ifil,ish,z,box->nfil,gap); } /* Layout internal boxes * Having this as a recursive function deals with * the case of VBOXes within VBOXes */ short Layin (box,ifil,ish,z,nfil,gap) BOX *box; short *ifil,ish,z,nfil; long gap; { 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; } /* Recursively prints this box and all its contents */ PrintBox (box,lev) BOX *box; short lev; { int i; if (!box) return; for (i=0; itype), box->x,box->y, box->xs,box->ys); if (box->type == TEXT) printf (" <%s>", box->val); if (box->gid>0) printf (" [%d]", box->gid); printf ("\n"); PrintBox (box->sub,lev+1); PrintBox (box->next,lev); } /* Renders the text boxes in their correct locations in the window. * Recursive. */ DisplayBox (win,box,biasx,biasy) struct Window *win; struct Box *box; short biasx,biasy; { if (!box) return; if (box->type == TEXT) { it.IText = (UBYTE*)box->val; it.LeftEdge = box->x; it.TopEdge = box->y; it.FrontPen = box->col; PrintIText (win->RPort,&it,(LONG)biasx,(LONG)biasy); } DisplayBox (win,box->sub,biasx,biasy); DisplayBox (win,box->next,biasx,biasy); } /* INPUT SECTION */ /* Returns true for "c" a blank */ BOOL Blank(c) char c; { return (c==' ' || c=='\n' || c=='\t'); } /* Returns next non-blank char */ char NextChar() { char c; while (Blank(c=(char)(gc=getc(file)))); return c; } /* read a number if there is one * otherwise return false and don't change n's value */ BOOL Qnum(n,radix) short *n,radix; { char c; short i=0; BOOL isnum=FALSE; c = NextChar(); while (c >= '0' && c <= '9') { isnum = TRUE; i = i*radix + (c-'0'); c = (char)(gc=getc(file)); } BACKSPACE; if (isnum) *n = i; return isnum; } /* Reads a double-quoted string like * "stuff" * from the file into a place in the string buffer. Returns * pointer to the string constant. */ char *ReadString() { short oldpos; char c,*cp; oldpos = bufpos; cp = &buf[bufpos]; c = NextChar(); if (c != '\"') { printf ("String not found\n"); return 0; } while ((c=(char)(gc=getc(file))) != '\"') buf[bufpos++] = c; buf[bufpos++] = '\0'; return cp; } /* Read ID of the form * :number * if there is one. If not, return 0. * Number is read as hex. */ short ReadID() { short id; char c; c = NextChar(); if (c == ':') { fscanf (file, "%x", &id); ngad++; return id; } else { BACKSPACE; return 0; } } BOX *ReadBoxList (); /* Get a box from the open file */ BOX *ReadBox () { char c; BOX *b; c = NextChar(); if (c == 'f') return NewBox (FILL); if (c == '-') { b = NewBox (HRULE); b->ys = 1; b->col = def_bcol; return b; } if (c == '|') { b = NewBox (VRULE); b->xs = 1; b->col = def_bcol; return b; } if (c == '(') { c = NextChar(); switch (c) { case 'h': b = NewBox (HBOX); b->sub = ReadBoxList (); break; case 'v': b = NewBox (VBOX); b->sub = ReadBoxList (); break; case 't': ntxt++; b = NewBox (TEXT); b->col = def_tcol; Qnum (&b->col,10); b->val = ReadString(); if (!b->val) { FreeBox (b); return 0; } b->xs = strlen(b->val)*8; b->ys = 8; break; case 'b': b = NewBox (BLOK); fscanf (file, "%d%d", &b->xs, &b->ys); break; case '-': b = NewBox (HRULE); fscanf (file, "%d", &b->ys); b->col = def_bcol; Qnum (&b->col,10); break; case '|': b = NewBox (VRULE); fscanf (file, "%d", &b->xs); b->col = def_bcol; Qnum (&b->col,10); break; default: printf ("Unknown Key Char\n"); return 0; } c = NextChar(); if (c != ')') { printf ("parse problem - expected ')'\n"); FreeBox (b); return 0; } b->gid = ReadID(); return b; } else { BACKSPACE; return 0; } } /* Read a list of boxes from the file stream * Recursive - read a box, then read a list. */ BOX *ReadBoxList () { BOX *b; b = ReadBox (); if (!b) return 0; b->next = ReadBoxList(); return b; } /* Reads the list of gadget info from the end of the file * Reads as much as there is * format is: * number {s|p} {:string} string * stuff in {}'s is optional */ ReadGadInfo() { char c; short i; i = 0; while (Qnum(&i,16)) { ginfo[lgad].gid = i; ginfo[lgad].gnam = NULL; ginfo[lgad].str = -1; ginfo[lgad].prp = -1; c = NextChar(); if (c == 's') { ginfo[lgad].str = nstr++; c = NextChar(); } else if (c == 'p') { ginfo[lgad].prp = nprp++; c = NextChar(); } if (c==':') { ginfo[lgad].gnam = ReadString(); } else { BACKSPACE; } ginfo[lgad++].actf = ReadString(); } /* print out list of gadget info structs for pure delight */ for (i=0; i", i, ginfo[i].gid, ginfo[i].actf); if (ginfo[i].gnam) printf (" [%s]", ginfo[i].gnam); if (ginfo[i].str != -1) printf (" (string)"); if (ginfo[i].prp != -1) printf (" (prop)"); printf ("\n"); } } /* 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 */ BOX *ReadFile (nam) char *nam; { BOX *box; short i; printf ("opening file <%s>\n", nam); file = fopen (nam,"r"); if (!file) { printf ("Cannot open %s\n", nam); exit(0); } fscanf (file, "%s", base); Qnum (&def_bcol,10); Qnum (&def_tcol,10); box = ReadBox (); ReadGadInfo(); fclose (file); return box; } /* OUTPUT SECTION (this whole thing is crufty, sorry) * (except perhaps the Border stuff) */ /* Recursively locate and print the string for a given gadget id * Truncate trailing spaces */ WriteBufEntry (id,box) short id; BOX *box; { int i; if (!box) return; if (box->gid == id) { for (i=strlen(box->val)-1; i>0; i--) { if (box->val[i] == ' ') box->val[i] = '\0'; else break; } fprintf (file, "\"%s\"", box->val); if (++istr != nstr) fprintf (file, ", "); box->val = NULL; ntxt--; } else { WriteBufEntry (id,box->sub); WriteBufEntry (id,box->next); } } /* Output the StringInfo structs for all the string gadgets */ WriteStrInfo () { int i; for (i=0; igid) { /* find the Gadinfo entry for this gadget. search backwards so that a * failure to locate lands us on Gadinfo 0, the default */ for (i=lgad-1; i>=0; i--) if (ginfo[i].gid == box->gid || i == 0) break; if (++igad==ngad) { fprintf (file, " {NULL"); } else { fprintf (file, " {&%s_gad[%d]", base,igad); } fprintf (file, ",%d,%d,%d,%d,\n ", box->x,box->y,box->xs,box->ys); if (index(ginfo[i].actf, 'B')) fprintf (file, "GADGHBOX,"); else fprintf (file, "GADGHCOMP,"); t = 0; if (l = strlen(ginfo[i].actf)) for (j=0; jgid); if (igad==ngad) fprintf (file, "\n"); else fprintf (file, ",\n"); } WriteGad (box->sub); WriteGad (box->next); } /* Recursively locate and output IntuiText structs for all TEXT boxes */ WriteText (box) BOX *box; { if (!box) return; if (box->type == TEXT && box->val) { fprintf (file, " {%d,0,JAM2,%d,%d,&ta,(UBYTE*)\"%s\",", box->col,box->x,box->y,box->val); if (++itxt==ntxt) { fprintf (file, "NULL}\n"); } else { fprintf (file, "&%s_txt[%d]},\n", base,itxt); } } WriteText (box->sub); WriteText (box->next); } /* Write out list of Border structs from main list */ WriteBorder () { register struct Border *b; register short i=0,j=0; fprintf (file, "struct Border %s_brd[] = {\n", base); for (b=blst; b; b=b->NextBorder) { fprintf (file, " {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,++j); } else { fprintf (file, "NULL}\n"); } } fprintf (file, "};\n"); } /* Write out list of XY arrays from Border struct main list */ WriteBorderXY () { register struct Border *b; register short i; fprintf (file, "short %s_brd_XY[] = {\n", base); for (b=blst; b; b=b->NextBorder) { fprintf (file, " "); for (i=0; iCount; 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"); } /* The main output function */ WriteFile (name,box) char *name; BOX *box; { short i; if (!(file = fopen (name, "w"))) { printf ("Can't open output file\n"); return; } /* write optional string gadget structs */ if (nstr) { fprintf (file,"UBYTE %s_nbuf[%d][NUMCHR] = {\n ", base,nstr); for (i=0; ixs,box->ys,base,base,base); fclose (file); } main(argc,argv) int argc; char *argv[]; { if (argc < 2 || argc > 4) { printf ("Usage: blk [ | %%]\n"); exit(0); } IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",0L); if (IntuitionBase) { Body (argc,argv); CloseLibrary (IntuitionBase); } } Body(argc,argv) int argc; char *argv[]; { struct Window *win; BOX *b; short h,w; b = ReadFile (argv[1]); Format (b); b->x = b->y = 0; Layout (b,b->xs,b->ys); if (argc == 3 && argv[2][0] == '%') PrintBox (b,0); CreateBorder (b); MergeBorders (); MergeLinear (); /* open a window to preview the requester layout */ w = b->xs+20; h = b->ys+20; if (w>640 || h>200) printf ("requester too large\n"); else { nwin.Width = w; nwin.Height = h; win = OpenWindow (&nwin); if (win) { DrawBorder (win->RPort,blst,(LONG)win->BorderLeft+5,(LONG)win->BorderTop+5); DisplayBox (win,b,win->BorderLeft+5,win->BorderTop+5); /* if output specified, write it out, otherwise wait */ if (argc == 3 && argv[2][0] != '%') WriteFile(argv[2],b); else Delay (300L); CloseWindow (win); } else printf ("Unable to open Window\n"); } FreeBorder (); FreeBox (b); }