/* * Copyright 1993 The University of Newcastle upon Tyne * * Permission to use, copy, modify and distribute this software and its * documentation for any purpose other than its commercial exploitation * is hereby granted without fee, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of The University of Newcastle upon Tyne not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. The University of * Newcastle upon Tyne makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * THE UNIVERSITY OF NEWCASTLE UPON TYNE DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF * NEWCASTLE UPON TYNE BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. * * Author: Jim Wight (j.k.wight@newcastle.ac.uk) * Department of Computing Science * University of Newcastle upon Tyne, UK */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern char *getenv(); extern void *malloc(), *realloc(); #include "Confirmer.h" #include "Hyper.h" #include "util.h" #include "axinfo.h" #include "version.h" #define INFOFILE "axe.info" #if __STDC__ #define MAKESTRING(name,string) \ name##String = XtNewString(string); \ name##Len = strlen(name##String); #else #define MAKESTRING(name,string) \ name/**/String = XtNewString(string); \ name/**/Len = strlen(name/**/String); #endif #define BCOPY(outof, into, upto) \ into = malloc((unsigned) (upto - outof + 1)); \ bcopy(outof, into, upto - outof); \ into[upto - outof] = 0; #define TABLECHUNK 20 typedef struct { String tag; int pos; } TagTableRec, *TagTablePtr; typedef struct { String version; Boolean tree; String node; String dir; } AppResRec, *AppResRecPtr; Widget top, hyper = 0, links, tree = 0, root, porthole, lastNode = 0; char soh = '\001', eoh = '\004'; XContext context; String infofile, basedir, currentfile = 0, buffer; int baselen, buflen; TagTablePtr tagTable; int maxTags = 0, numberTags = 0, currentTag = -1, lastTag = -1; TagTablePtr indirectTable = 0; int maxIndirects = 0, numberIndirects = 0; int dragX, dragY; String fileString, nodeString, prevString, nextString, upString; String menuString, entryString, xrefString, btagString, etagString; String itagString, tagiString; int fileLen, nodeLen, prevLen, nextLen, upLen; int menuLen, entryLen, xrefLen, btagLen, etagLen; int itagLen, tagiLen; Atom AxinfoUserAtom(display) Display *display; { char propName[1024], *user; strcpy(propName, "AXE_AXINFO"); if ( (user = getenv("USER")) ) { strcat(propName, "_"); strcat(propName, user); } return XInternAtom(display, propName, False); } Boolean IsTag(tag, tagId) char *tag; int *tagId; { int t; for (t = 0; t < numberTags; ++t) { if (strcmp(tag, tagTable[t].tag) == 0) { *tagId = t; return True; } } return False; } char* Find(buffer, magic) char *buffer, magic; { char *bp; for (bp = buffer; *bp && *bp != magic; ++bp) {} return *bp ? bp : 0; } char* Locate(buffer, search) char *buffer, *search; { char *bp; int i, len = strlen(search); for (bp = buffer; *bp && *bp != INFO_COOKIE; ++bp) { for (i = 0; i < len; ++i) { if (bp[i] != search[i]) { break; } } if (search[i] == 0) { return bp + len; } } return bp; } char* ReverseLocate(buffer, search) char *buffer, *search; { char *bp, *rsearch; int i, j, len = strlen(search); rsearch = malloc((unsigned) (len + 1)); for (i = 0, j = len - 1; i < len; ++i, --j) { rsearch[j] = search[i]; } rsearch[len] = 0; for (bp = buffer; *bp; --bp) { for (i = 0; i < len; ++i) { if (bp[-i] != rsearch[i]) { break; } } if (search[i] == 0) { free(rsearch); return bp + 1; } } free(rsearch); return bp; } int LookingAt(bp, nbp) char *bp, **nbp; { if (*bp == INFO_COOKIE) { ++bp; while (*bp == '\n' || *bp == INFO_FF) { ++bp; } } switch (*bp) { case 'F': if (strncmp(bp, fileString, fileLen) == 0) { *nbp = bp + fileLen; return IS_NODE; } break; case 'T': if (strncmp(bp, btagString, btagLen) == 0) { *nbp = bp + btagLen; return IS_TAG_TABLE; } break; case 'I': if (strncmp(bp, itagString, itagLen) == 0) { *nbp = bp + itagLen; return IS_INDIRECT; } break; case '(': if (strncmp(bp, tagiString, tagiLen) == 0) { *nbp = bp + tagiLen; return IS_INDIRECT_LABEL; } break; case '\n': if (strncmp(bp, menuString, menuLen) == 0) { *nbp = bp + menuLen; return IS_MENU; } else if (strncmp(bp, entryString, entryLen) == 0) { *nbp = bp + entryLen; return IS_ENTRY; } else if (bp[1] == '*' || bp[1] == '=' || bp[1] == '-' || bp[1] == '.') { *nbp = bp + 2; return IS_SECTIONING; } break; case 'N': if (strncmp(bp, nodeString, nodeLen) == 0) { *nbp = bp + nodeLen; return IS_TAG; } else if (strncmp(bp, nextString, nextLen) == 0) { *nbp = bp + nextLen; return IS_NEXT; } break; case 'P': if (strncmp(bp, prevString, prevLen) == 0) { *nbp = bp + prevLen; return IS_PREV; } break; case 'U': if (strncmp(bp, upString, upLen) == 0) { *nbp = bp + upLen; return IS_UP; } break; case '*': if (strncmp(bp, xrefString, xrefLen) == 0) { *nbp = bp + xrefLen; return IS_XREF; } break; default: return 0; } return 0; } /*ARGSUSED*/ void Exit(widget, client_data, call_data) Widget widget; XtPointer client_data, call_data; { exit(1); } void OpenInfofile(file) char *file; { FILE *inf; struct stat fstats; if (currentfile) { if (file != currentfile) { free(buffer); currentfile = 0; } else { return; } } inf = fopen(file, "r"); if (!inf) { Widget conf; char message[1024]; conf = XtVaCreateManagedWidget("confirmer", confirmerWidgetClass, top, XtNgrabOnPopup, True, NULL); XtAddCallback(conf, XtNpopdownCallback, Exit, (XtPointer) 0); sprintf(message, "Unable to open\n%s", file); ConfirmerRequestConfirmation(conf, message, "acknowledge", ConfirmerPopdown, "unused", NULL, (XtPointer) 0); XtAppMainLoop(XtWidgetToApplicationContext(conf)); }; if (fstat(fileno(inf), &fstats) == -1) { perror("fstat"); exit(1); } if (!(buffer = malloc((unsigned) (fstats.st_size + 1)))) { printf("Out of memory\n"); exit(1); } if (fread(buffer, (int) fstats.st_size, 1, inf) != 1) { printf("Read error\n"); exit(1); } buffer[fstats.st_size] = 0; buflen = fstats.st_size; currentfile = file; fclose(inf); } char* MakeHeaderBuffer(bp) char *bp; { char *sp = bp, *header; while (*bp != '\n') ++bp; /* +15 to be absoultely safe on length = "{} " for Next, Prev and Up */ header = XtMalloc((unsigned) (bp - sp + 15 + 32)); /* Assumes this takes up less space than "File: xyz.intro" */ sprintf(header, "Top: %cTop%c ", soh, eoh); return header; } void CompleteHeaderBuffer(header) char *header; { if (lastTag >= 0) { sprintf(header, "Last: %c%s%c", soh, tagTable[lastTag].tag, eoh); } } void HyperLink(type, bp, header, eol) int type; char *bp, **header, **eol; { char *sp, *head = *header; int len; switch (type) { case IS_NEXT: strcat(head, nextString); break; case IS_PREV: strcat(head, prevString); break; case IS_UP: strcat(head, upString); break; } sp = bp; while (*bp == ' ') { ++bp; } strncat(head, sp, bp - sp); sprintf(&head[strlen(head)], "%c", soh); sp = bp; while (*bp != ',' && *bp != '\n') { ++bp; } strncat(head, sp, bp - sp); sprintf(&head[strlen(head)], "%c ", eoh); if (*bp == '\n') { *eol = bp; /* Blank out back to comma terminating "Node: Tag," */ for (--bp; *bp != ','; --bp) { *bp = ' '; } *bp = ' '; } else { *bp =' '; /* overwrites ',' */ *eol = 0; } } void Entry(bp) char *bp; { char *sp; sp = bp; bp = Find(bp, ':'); if (bp[1] == ':') { bp[1] = eoh; for ( ; bp > sp; --bp) { bp[0] = bp[-1]; } sp[0] = soh; } else { ++bp; while (*bp == ' ') { ++bp; } bp[-1] = soh; while (*bp != '\t' && *bp != ',' && *bp != '.' && *bp != '\n') { ++bp; } bp[0] = eoh; } } void CrossRef(bp) char *bp; { char *sp; while (*bp == ' ' || *bp == '\n' || *bp == '\t') { ++bp; } sp = bp - 1; bp = Find(bp, ':'); if (bp[1] == ':') { /* * This is a "*Note node-name::" case. * Convert it into "*Note Node name", i.e. sacrifice * the two ':' characters to make way for the two markers. */ bp[1] = eoh; for ( ; bp > sp; --bp) { bp[0] = bp[-1]; } sp[1] = soh; } else { /* * "*Note cross-reference-name: node-name[,.]" or * "*Note cross-reference-name: (info-file)node-name[.,]" cases. * * Convert the former to * "*Note cross-reference-name:node-name", i.e. sacrifice * leading space and terminating period or comma, * * and the latter to * "*Note cross-reference-name:(info-file)node-name", i.e. * shift "(info-file)" down 1 to release space in front of node * name and sacrifice terminating period or comma. */ bp = bp + 1; while (*bp == ' ' || *bp == '\n') { ++bp; } sp = bp - 1; if (*bp == '(') { bp = Find(bp, ')'); for ( ; sp < bp; ++sp) { sp[0] = sp[1]; } sp = bp; } sp[0] = soh; while (*bp != '.' && *bp != ',') { ++bp; } *bp = eoh; } } char* ExtractNodeAndHyperize(buffer, header, body) char *buffer, **header, **body; { char c, *sp, *bp = buffer, *node, *headerBuffer, *eol; int type; while (*bp && *bp != INFO_COOKIE) { ++bp; } BCOPY(buffer, node, bp) bp = node; *header = 0; *body = 0; while (*bp) { sp = bp; switch ( (type = LookingAt(bp, &bp)) ) { case IS_NODE: headerBuffer = MakeHeaderBuffer(sp); break; case IS_NEXT: case IS_PREV: case IS_UP: if (!(*body)) /* i.e. finished with header, and hence allows */ { /* possibility of "Up:" in text, for instance */ HyperLink(type, bp, &headerBuffer, &eol); if (eol) { CompleteHeaderBuffer(&headerBuffer[strlen(headerBuffer)]); *header = headerBuffer; *body = node; } } break; case IS_ENTRY: Entry(bp); break; case IS_XREF: CrossRef(bp); break; default: ++bp; break; } } return node; } char* MakeNode(nodename, header, body) char *nodename, **header, **body; { char *bp, *sp, *tp, *node; int i, p; String f = infofile; if (!IsTag(nodename, &p)) { return 0; } if (indirectTable) { for (i = numberIndirects - 1; i >= 0; --i) { if (tagTable[p].pos >= indirectTable[i].pos) { f = indirectTable[i].tag; break; } } } OpenInfofile(f); if (indirectTable) { bp = &buffer[tagTable[p].pos - indirectTable[i].pos + indirectTable[0].pos]; } else { bp = &buffer[tagTable[p].pos]; } if (bp[-1] == INFO_COOKIE) /* The Tag Table number doesn't always plonk */ /* you on the INFO_COOKIE */ { ++bp; } else { while (*bp && *bp != INFO_COOKIE) { ++bp; } bp += 2; } sp = bp; if (LookingAt(bp, &bp) == IS_NODE) { for (i = 0; i < numberTags; ++i) { if (strcmp(nodename, tagTable[i].tag) == 0) { lastTag = currentTag; currentTag = i; break; } } return ExtractNodeAndHyperize(sp, header, body); } else { return 0; } } void InvertWidget(widget) Widget widget; { Pixel fg, bg; if (!widget) { return; } XtVaGetValues(widget, XtNforeground, &fg, XtNbackground, &bg, NULL); XtVaSetValues(widget, XtNforeground, bg, XtNbackground, fg, NULL); } void PositionNode(node, highlight) char *node; Boolean highlight; { String label; WidgetList children; Cardinal numChildren; int child; Position x, y, tx, ty; Dimension w, h, pw, ph, tw, th; XtVaGetValues(tree, XtNchildren, &children, XtNnumChildren, &numChildren, NULL); /* NB child = 1 excludes root widget */ for (child = 1; child < numChildren; ++child) { XtVaGetValues(children[child], XtNlabel, &label, NULL); if (strcmp(node, label) == 0) { if (highlight) { InvertWidget(children[child]); InvertWidget(lastNode); lastNode = children[child]; } break; } } XtVaGetValues(children[child], XtNx, &x, XtNy, &y, XtNwidth, &w, XtNheight, &h, NULL); XtVaGetValues(porthole, XtNwidth, &pw, XtNheight, &ph, NULL); XtVaGetValues(tree, XtNx, &tx, XtNy, &ty, NULL); /* if (y + h + ty < 0 || y + h + ty > ph || x + w + tx < 0 || x + w + tx > pw) { */ XtVaSetValues(tree, XtNx, -(x - pw / 2), XtNy, -(y - ph / 2), NULL); /* } */ } void RevealNode(tag) char *tag; { String node, header, body; int i; if (currentTag >= 0 && strcmp(tag, tagTable[currentTag].tag) == 0) { return; } if ( !(node = MakeNode(tag, &header, &body)) ) { return; } HyperSetText(hyper, (body ? body : node)); XtFree(node); if (header) { HyperSetText(links, header); } XtFree(header); if (XtIsSubclass(XtParent(hyper), viewportWidgetClass) && XtIsRealized(hyper)) { XawViewportSetLocation(XtParent(hyper), 0.0, 0.0); } #if defined(XtSpecificationRelease) && XtSpecificationRelease > 4 if (tree) { PositionNode(tag, True); } #endif } void ExposeNode(tag) char *tag; { Widget popup; void MakeHyperWindow(); if (hyper) { for (popup = XtParent(hyper); !XtIsShell(popup); popup = XtParent(popup)) {} XMapRaised(XtDisplay(hyper), XtWindow(popup)); RevealNode(tag); } else { MakeHyperWindow(tag); } } void DisplayNode(widget, client_data, call_data) Widget widget; XtPointer client_data, call_data; { char *tag, *node; XtVaGetValues(widget, XtNlabel, &tag, NULL); tag = XtNewString(tag); ExposeNode(tag); XtFree(tag); } Widget AddNode(name, parent) char *name; Widget parent; { Widget node = XtVaCreateManagedWidget(name, commandWidgetClass, tree, XtNtreeParent, parent, NULL); XtAddCallback(node, XtNcallback, DisplayNode, (XtPointer) 0); return node; } Widget FindNode(name) char *name; { WidgetList children; Cardinal numChildren; int i; XtVaGetValues(tree, XtNchildren, &children, XtNnumChildren, &numChildren, NULL); for (i = 0; i < numChildren; ++i) { if (strcmp(XtName(children[i]), name) == 0) { return children[i]; } } return AddNode(name, root); } char* ProcessMenuItem(buffer, parent) char *buffer; Widget parent; { Widget node; char *sp, *bp, *nodename, *help; sp = bp = buffer; bp = Find(bp, ':'); if (bp[1] != ':') { ++bp; while (*bp == ' ') { ++bp; } sp = bp; while (*bp != '\t' && *bp != ',' && *bp != '.' && *bp != '\n') { ++bp; } } BCOPY(sp, nodename, bp) node = FindNode(nodename); XtVaSetValues(node, XtNtreeParent, parent, NULL); free(nodename); return bp; } char* ProcessMenu(buffer, node) char *buffer; Widget node; { char *bp; for(bp = buffer; ; ) { bp = Locate(bp, INFO_MENU_ENTRY_LABEL); if (!*bp || *bp == INFO_COOKIE) { break; } else { bp = ProcessMenuItem(bp, node); } } return bp; } char* ProcessNode(buffer) char *buffer; { Widget node; char *bp, *sp, *s, c, *idx; /* It has got to exist */ bp = Locate(buffer, INFO_NODE_LABEL); sp = bp; bp = Find(bp, ','); BCOPY(sp, s, bp) sp = s; while (*sp == ' ') { ++sp; } if (strcmp(sp, "Top") == 0) { root = node = AddNode(sp, (Widget) 0); } else { /* * There's potentially too much in indexes, so ignore them. And, * furthermore, the repeated node names screw up the hierarchy. */ if ( (idx = rindex(sp, 'I')) && strcmp(idx, "Index") == 0) { free(s); return bp; } else { node = FindNode(sp); } } free(s); c = *(bp = Locate(bp, INFO_MENU_LABEL)); if (c == 0) { return 0; } else if (c != INFO_COOKIE) { bp = ProcessMenu(bp, node); } return bp; } Widget InfoOf(widget) Widget widget; { Widget popup; for (popup = XtParent(widget); !XtIsShell(popup); popup = XtParent(popup)) {} return XtNameToWidget(popup, "*info"); } /*ARGSUSED*/ void ShowInfo(widget, event, params, num_params) Widget widget; XEvent *event; String *params; Cardinal *num_params; { XPointer value; String nodename; if(XFindContext(XtDisplay(widget), (XID) XrmStringToQuark(XtName(widget)), context, &value) == 0) { XtVaSetValues(InfoOf(widget), XtNlabel, (String) value, NULL); } else { XtVaSetValues(InfoOf(widget), XtNlabel, "", NULL); } } /*ARGSUSED*/ void ClearInfo(widget, event, params, num_params) Widget widget; XEvent *event; String *params; Cardinal *num_params; { XtVaSetValues(InfoOf(widget), XtNlabel, "", NULL); } void BuildIndirectTable(buffer) char *buffer; { char *f, v[16], *sp, *bp = ReverseLocate(buffer, INDIRECT_TAGS_TABLE_LABEL); char *subinfo; int len; indirectTable = (TagTablePtr) malloc((unsigned) TABLECHUNK * sizeof(TagTableRec)); maxIndirects += TABLECHUNK; while (*bp != INFO_COOKIE) { sp = bp; bp = Find(bp, ':'); BCOPY(sp, f, bp) sp = ++bp; bp = Find(sp, '\n'); strncpy(v, sp, bp - sp); v[bp - sp] = 0; ++bp; if (numberIndirects == maxIndirects) { maxIndirects += TABLECHUNK; indirectTable = (TagTablePtr) realloc(indirectTable, (unsigned) maxIndirects * sizeof(TagTableRec)); } len = baselen + 1 + strlen(f) + 1; subinfo = XtMalloc((unsigned) len); strcpy(subinfo, basedir); strcat(subinfo, "/"); strcat(subinfo, f); indirectTable[numberIndirects].tag = subinfo; indirectTable[numberIndirects].pos = atoi(v); ++numberIndirects; } } void BuildTagTable() { char *bp, *sp, *s, v[16], *dummy; OpenInfofile(infofile); bp = ReverseLocate(buffer + buflen - 1, TAGS_TABLE_BEG_LABEL); if (LookingAt(bp, &dummy) == IS_INDIRECT_LABEL) { BuildIndirectTable(bp); while (*bp != '\n') { ++bp; } ++bp; } tagTable = (TagTablePtr) malloc((unsigned) TABLECHUNK * sizeof(TagTableRec)); maxTags += TABLECHUNK; while (LookingAt(bp, &bp) == IS_TAG) { while (*bp == ' ') { ++bp; } sp = bp; bp = Find(bp, INFO_TAGSEP); BCOPY(sp, s, bp) ++bp; sp = bp; bp = Find(bp, '\n'); strncpy(v, sp, bp - sp); v[bp - sp] = 0; ++bp; if (numberTags == maxTags) { maxTags += TABLECHUNK; tagTable = (TagTablePtr) realloc(tagTable, (unsigned) maxTags * sizeof(TagTableRec)); } tagTable[numberTags].tag = s; tagTable[numberTags].pos = atoi(v); ++numberTags; } } /*ARGSUSED*/ void Quit(widget, client_data, call_data) Widget widget; XtPointer client_data, call_data; { exit(0); } /*ARGSUSED*/ void DeleteTree(widget, event, params, num_params) Widget widget; XEvent *event; String *params; Cardinal *num_params; { if (hyper) { tree = 0; lastNode = 0; XtDestroyWidget(widget); } else { exit(0); } } /*ARGSUSED*/ void CloseTree(widget, client_data, call_data) Widget widget; XtPointer client_data, call_data; { Widget shell; for (shell = XtParent(widget); !XtIsShell(shell); shell = XtParent(shell)) {} XtCallActionProc(shell, "delete-tree", (XEvent *) 0, (String *) 0, (Cardinal) 0); } void MakeTreeWindow() { Display *display = XtDisplay(top); Atom wm_delete_window; Widget popup, pane, form, bpane, quit, close, info, shell; Dimension height; char *bp, *dummy; int i; if (tree) { for (popup = XtParent(tree); !XtIsShell(popup); popup = XtParent(popup)) {} XMapRaised(XtDisplay(tree), XtWindow(popup)); return; } popup = XtVaCreatePopupShell("treewin", topLevelShellWidgetClass, top, NULL); pane = XtVaCreateManagedWidget("pane", panedWidgetClass, popup, NULL); form = XtVaCreateManagedWidget("form", formWidgetClass, pane, NULL); porthole = XtVaCreateManagedWidget("porthole", portholeWidgetClass, form, NULL); tree = XtVaCreateManagedWidget("tree", treeWidgetClass, porthole, NULL); (void) XSaveContext(display, (XID) XrmStringToQuark("tree"), context, "Press and drag any button to scroll"); bpane = XtVaCreateManagedWidget("bpane", panedWidgetClass, pane, XtNorientation, XtorientHorizontal, XtNskipAdjust, True, NULL); quit = XtVaCreateManagedWidget("quit", commandWidgetClass, bpane, NULL); (void) XSaveContext(display, (XID) XrmStringToQuark("quit"), context, "Terminate the application"); XtAddCallback(quit, XtNcallback, Quit, (XtPointer) 0); close = XtVaCreateManagedWidget("close", commandWidgetClass, bpane, NULL); (void) XSaveContext(display, (XID) XrmStringToQuark("close"), context, "Delete this window"); XtAddCallback(close, XtNcallback, CloseTree, (XtPointer) 0); info = XtVaCreateManagedWidget("info", labelWidgetClass, bpane, NULL); (void) XSaveContext(display, (XID) XrmStringToQuark("info"), context, "aXe Information System Version 1.0"); for (i = 0; i < numberIndirects || numberIndirects == 0; ++i) { if (numberIndirects == 0) { OpenInfofile(infofile); } else { OpenInfofile(indirectTable[i].tag); } for (bp = buffer; bp; ) { if ( !(bp = Find(bp, INFO_COOKIE)) ) { break; } if (LookingAt(bp, &dummy) == IS_NODE) { bp = ProcessNode(bp + 1); } else { ++bp; } } if (numberIndirects == 0) { break; } } XtRealizeWidget(popup); if (hyper) { for (shell = XtParent(hyper); !XtIsShell(shell); shell = XtParent(shell)) {} XtVaGetValues(shell, XtNheight, &height, NULL); XtVaSetValues(popup, XtNheight, height, NULL); } if (currentTag >= 0) { PositionNode(tagTable[currentTag].tag, True); } else { PositionNode("Top", False); } wm_delete_window = XInternAtom (display, "WM_DELETE_WINDOW", False); (void) XSetWMProtocols(display, XtWindow(popup), &wm_delete_window, 1); XtPopup(popup, XtGrabNone); } void GatherFromNode(buffer) char *buffer; { char *bp, *sp, *fp, *nodename, ch, *help; bp = Locate(buffer, INFO_NODE_LABEL); while (*bp == ' ') { ++bp; } sp = bp; bp = Find(bp, ','); BCOPY(sp, nodename, bp) while (*bp && *bp != INFO_COOKIE) { sp = bp; if (LookingAt(bp, &bp) == IS_SECTIONING) { ch = sp[1]; while (*bp == ch) { ++bp; } if (*bp == '\n') { fp = sp - 1; while (*fp != '\n') { --fp; } ++fp; BCOPY(fp, help, sp) (void) XSaveContext(XtDisplay(top), (XID) XrmStringToQuark(nodename), context, (XtPointer) help); } break; } else { ++bp; } } free(nodename); return; } void GatherSectioning() { int tag, ind; char *node, *file; for (tag = 0; tag < numberTags; ++tag) { if (indirectTable) { for (ind = numberIndirects - 1; ind >= 0; --ind) { if (tagTable[tag].pos >= indirectTable[ind].pos) { file = indirectTable[ind].tag; break; } } } else { file = infofile; } OpenInfofile(file); if (indirectTable) { node = &buffer[tagTable[tag].pos - indirectTable[ind].pos + indirectTable[0].pos]; } else { node = &buffer[tagTable[tag].pos]; } GatherFromNode(node + 1); } } /*ARGSUSED*/ void ShowNode(widget, client_data, call_data) Widget widget; XtPointer client_data, call_data; { String tag = XtNewString(((hyperCallbackStruct *) call_data)->text); RevealNode(tag); XtFree(tag); } /*ARGSUSED*/ void ExplainNode(widget, client_data, call_data) Widget widget; XtPointer client_data, call_data; { hyperCallbackStruct *hcs = (hyperCallbackStruct *) call_data; if (hcs) { String tag = ((hyperCallbackStruct *) call_data)->text; XPointer value; if(XFindContext(XtDisplay(widget), (XID) XrmStringToQuark(tag), context, &value) == 0) { XtVaSetValues(InfoOf(widget), XtNlabel, (String) value, NULL); return; } } XtVaSetValues(InfoOf(widget), XtNlabel, "", NULL); } /*ARGSUSED*/ void Last(widget, client_data, call_data) Widget widget; XtPointer client_data, call_data; { if (lastTag >= 0) { RevealNode(tagTable[lastTag].tag); } } /*ARGSUSED*/ void Tree(widget, client_data, call_data) Widget widget; XtPointer client_data, call_data; { MakeTreeWindow(); } void SetHyperWMHints(shell) Widget shell; { Window window = XtWindow(shell); Display *display = XtDisplay(shell); WidgetList children; Widget viewport = XtNameToWidget(shell, "*view"); Dimension wHeight, vHeight; int margin; XFontStruct *font; XSizeHints hints; long supp; XtVaGetValues(shell, XtNheight, &wHeight, NULL); XtVaGetValues(viewport, XtNheight, &vHeight, NULL); XtVaGetValues(hyper, XtNnormalFont, &font, XtNmargin, &margin, NULL); /* * All this kind of assumes that the height of * highlightFont is the same as that of normalFont. */ if (!XGetWMNormalHints(display, window, &hints, &supp)) { hints.flags = 0; } hints.flags |= PResizeInc | PBaseSize; hints.height_inc = font->ascent + font->descent; hints.width_inc = font->max_bounds.width; /* 4 = vSpace in MakeHyperWindow */ hints.base_height = wHeight - vHeight + 4; hints.base_width = 2 * margin; XSetWMNormalHints(display, window, &hints); } typedef struct { int rows; int columns; } RowColRec, *RowColRecPtr; static XtResource rowsColsResource[] = { { "rows", "Rows", XtRInt, sizeof(int), XtOffset(RowColRecPtr, rows), XtRImmediate, (XtPointer) 24 }, { "columns", "Columns", XtRInt, sizeof(int), XtOffset(RowColRecPtr, columns), XtRImmediate, (XtPointer) 80 }, }; void MakeHyperWindow(nodename) char *nodename; { Widget popup, pane, view, hpane, quit, map, info; char *node; Display *display = XtDisplay(top); Atom wm_delete_window; RowColRec rowsCols; XFontStruct *hyperFont, *normalFont; Dimension width, height; int rows, columns, margin, vSpace = 4; popup = XtVaCreatePopupShell("hyperwin", topLevelShellWidgetClass, top, NULL); pane = XtVaCreateManagedWidget("pane", panedWidgetClass, popup, NULL); view = XtVaCreateManagedWidget("view", viewportWidgetClass, pane, NULL); hyper = XtVaCreateManagedWidget("hyper", hyperWidgetClass, view, NULL); XtAddCallback(hyper, XtNactivateCallback, ShowNode, (XtPointer) 0); XtVaGetValues(hyper, XtNnormalFont, &hyperFont, XtNmargin, &margin, NULL); XtVaSetValues(hyper, XtNstartHighlight, soh, XtNendHighlight, eoh, NULL); links = XtVaCreateManagedWidget("links", hyperWidgetClass, pane, XtNskipAdjust, True, NULL); XtAddCallback(links, XtNactivateCallback, ShowNode, (XtPointer) 0); XtAddCallback(links, XtNhyperCallback, ExplainNode, (XtPointer) 0); XtVaGetValues(links, XtNnormalFont, &normalFont, NULL); XtVaSetValues(links, XtNstartHighlight, soh, XtNendHighlight, eoh, NULL); hpane = XtVaCreateManagedWidget("hpane", panedWidgetClass, pane, XtNorientation, XtorientHorizontal, XtNskipAdjust, True, NULL); quit = XtVaCreateManagedWidget("quit", commandWidgetClass, hpane, NULL); (void) XSaveContext(display, (XID) XrmStringToQuark("quit"), context, "Terminate the application"); XtAddCallback(quit, XtNcallback, Quit, (XtPointer) 0); #if defined(XtSpecificationRelease) && XtSpecificationRelease > 4 map = XtVaCreateManagedWidget("map", commandWidgetClass, hpane, NULL); (void) XSaveContext(display, (XID) XrmStringToQuark("map"), context, "Display the hypertext tree"); XtAddCallback(map, XtNcallback, Tree, (XtPointer) top); #endif info = XtVaCreateManagedWidget("info", labelWidgetClass, hpane, NULL); (void) XSaveContext(display, (XID) XrmStringToQuark("info"), context, "aXe Information System Version 1.0"); RevealNode(nodename); XtVaGetApplicationResources(popup, (XtPointer) &rowsCols, rowsColsResource, XtNumber(rowsColsResource), NULL); XtVaSetValues(view, XtNheight, (Dimension) (rowsCols.rows * (hyperFont->ascent + hyperFont->descent) + vSpace), NULL); XtVaSetValues(hyper, XtNwidth, (Dimension) (rowsCols.columns * hyperFont->max_bounds.width + 2 * margin), NULL); XtVaSetValues(links, XtNheight, normalFont->ascent + normalFont->descent + vSpace, NULL); XtRealizeWidget(popup); wm_delete_window = XInternAtom (display, "WM_DELETE_WINDOW", False); (void) XSetWMProtocols(display, XtWindow(popup), &wm_delete_window, 1); SetHyperWMHints(popup); XtPopup(popup, XtGrabNone); } /*ARGSUSED*/ void StartDrag(widget, event, params, num_params) Widget widget; XEvent *event; String *params; Cardinal *num_params; { dragX = event->xbutton.x_root; dragY = event->xbutton.y_root; } /*ARGSUSED*/ void Drag(widget, event, params, num_params) Widget widget; XEvent *event; String *params; Cardinal *num_params; { Position x, y; XtVaGetValues(tree, XtNx, &x, XtNy, &y, NULL); XtVaSetValues(tree, XtNx, (Position) (x + (event->xbutton.x_root - dragX) * 10), XtNy, (Position) (y + (event->xbutton.y_root - dragY) * 10), NULL); dragX = event->xbutton.x_root; dragY = event->xbutton.y_root; } /*ARGSUSED*/ void DeleteHyper(widget, event, params, num_params) Widget widget; XEvent *event; String *params; Cardinal *num_params; { if (strcmp(XGetAtomName(XtDisplay(widget), event->xclient.data.l[0]), "WM_DELETE_WINDOW") != 0) { return; } if (tree) { hyper = 0; XtDestroyWidget(widget); } else { exit(0); } } /*ARGSUSED*/ void Notify(widget, event, params, num_params) Widget widget; XEvent *event; String *params; Cardinal *num_params; { Atom gotAtom; int gotFormat; unsigned long gotItems, moreBytes; unsigned char *propValue; if (XGetWindowProperty(XtDisplay(widget), XtWindow(widget), XInternAtom(XtDisplay(widget), "AXE_AXINFO", False), 0L, 64L, /* 64 is an overestimate */ False, XA_STRING, &gotAtom, &gotFormat, &gotItems, &moreBytes, &propValue) == Success) { if (gotAtom == XA_STRING) { ExposeNode((char *) propValue); XtFree((char *) propValue); } } else { /* Ought to do something here just in case */ } } XtActionsRec actions[] = { "delete-hyper", DeleteHyper, "delete-tree", DeleteTree, "show-info", ShowInfo, "clear-info", ClearInfo, "start-drag", StartDrag, "drag", Drag, "notify", Notify, }; static XrmOptionDescRec options[] = { {"-tree", ".tree", XrmoptionNoArg, (XPointer) "True"}, {"-node", ".node", XrmoptionSepArg, (XPointer) 0}, {"-rows", "*hyperwin.rows", XrmoptionSepArg, (XPointer) 0}, }; static XtResource appResources[] = { { "version", "Version", XtRString, sizeof(String), XtOffset(AppResRecPtr, version), XtRImmediate, (XtPointer) 0 }, { "tree", "Tree", XtRBoolean, sizeof(Boolean), XtOffset(AppResRecPtr, tree), XtRImmediate, (XtPointer) False }, { "node", "Node", XtRString, sizeof(String), XtOffset(AppResRecPtr, node), XtRImmediate, (XtPointer) 0, }, { "infoPath", "InfoPath", XtRString, sizeof(String), XtOffset(AppResRecPtr, dir), XtRImmediate, (XtPointer) 0 }, }; main(argc, argv) int argc; char *argv[]; { XtAppContext app; Display *display; AppResRec resources; String file; int len; void Listen(); context = XUniqueContext(); top = XtVaAppInitialize(&app, "Axinfo", options, XtNumber(options), &argc, argv, (String *) 0, NULL); display = XtDisplay(top); XtVaGetApplicationResources(top, (XtPointer) &resources, appResources, XtNumber(appResources), NULL); if (!resources.version || strcmp(resources.version, QVERSION) != 0) { char message[1024]; Widget noapps, msg, quit; strcpy(message, "This is "); strcat(message, QVERSION); strcat(message, ".\n\n"); strcat(message, "The application defaults file\n"); strcat(message, "for this release has not been\n"); strcat(message, "found."); if (resources.version) { strcat(message, "\n\nIt may help to know in solving\n"); strcat(message, "the problem that the defaults\n"); strcat(message, "file for "); strcat(message, resources.version); strcat(message, " was found."); } noapps = XtVaCreateManagedWidget("noapps", formWidgetClass, top, NULL); msg = XtVaCreateManagedWidget("msg", labelWidgetClass, noapps, XtNlabel, message, NULL); quit = XtVaCreateManagedWidget("Quit", commandWidgetClass, noapps, XtNfromVert, msg, NULL); XtAddCallback(quit, XtNcallback, Quit, (XtPointer) 0); XtRealizeWidget(top); XtAppMainLoop(app); } basedir = XtNewString(resources.dir); baselen = strlen(basedir); MAKESTRING(file,INFO_FILE_LABEL) MAKESTRING(node,INFO_NODE_LABEL) MAKESTRING(prev,INFO_PREV_LABEL) MAKESTRING(next,INFO_NEXT_LABEL) MAKESTRING(up,INFO_UP_LABEL) MAKESTRING(menu,INFO_MENU_LABEL) MAKESTRING(entry,INFO_MENU_ENTRY_LABEL) MAKESTRING(xref,INFO_XREF_LABEL) MAKESTRING(btag,TAGS_TABLE_BEG_LABEL) MAKESTRING(etag,TAGS_TABLE_END_LABEL) MAKESTRING(itag,INDIRECT_TAGS_TABLE_LABEL) MAKESTRING(tagi,TAGS_TABLE_IS_INDIRECT_LABEL) XtAppAddActions(app, actions, XtNumber(actions)); if (argc > 1) { file = XtNewString(argv[1]); } else { file = XtNewString(INFOFILE); } len = baselen + 1 + strlen(file) + 1; infofile = XtMalloc((unsigned) len); strcpy(infofile, basedir); strcat(infofile, "/"); strcat(infofile, file); XtFree(file); BuildTagTable(); GatherSectioning(); { int dummy; if (resources.node && IsTag(resources.node, &dummy)) { MakeHyperWindow(resources.node); } #if defined(XtSpecificationRelease) && XtSpecificationRelease > 4 else if (!resources.tree) { MakeHyperWindow("Top"); } if (resources.tree) { MakeTreeWindow(); } #else else { MakeHyperWindow("Top"); } #endif } Listen(); XtAppMainLoop(app); } void Listen() { Widget listen; Window window; Display *display; listen = XtVaCreatePopupShell("listen", topLevelShellWidgetClass, top, NULL); (void) XtVaCreateManagedWidget("core", coreWidgetClass, listen, XtNwidth, 1, XtNheight, 1, NULL); XtRealizeWidget(listen); display = XtDisplay(listen); window = XtWindow(listen); TrapErrors(display); /* If axeis is already running this one will take over listening duties */ XChangeProperty(display, XDefaultRootWindow(display), AxinfoUserAtom(display), XA_WINDOW, 32, PropModeReplace, (unsigned char *) &window, 1); /* * Stamping the listening window with the AXE_AXINFO_user property * enables a check to be made that the window id stored under the * AXE_AXINFO_user property on the root window is still valid when * aXe comes to attempt to notify axeis. If the window exists but it * doesn't have the property then axeis has been terminated and * the id reallocated, otherwise we should be OK. */ if (xerror == Success) { XChangeProperty(display, window, AxinfoUserAtom(display), XA_STRING, 8, PropModeReplace, NULL, 0); } DontTrapErrors(display); }