#include "univ.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SCROLLSIZE 13 /* If more than this # of entries, scroll it */ #define CONFIRMBIT 0x80000000 /* This menu entry needs confirmation */ Widget ShellofWidget(); Widget ScrollShellWMCreate(); Index SubMenuofMenu(); Widget MakeWinButton(); void WindowMenuCB(w, user_data, call_data) Widget w; XtPointer user_data; XtPointer call_data; { long data; PadObj *po; char *label; po = (PadObj *)user_data; XtVaGetValues(w, XtNuserData, &data, NULL); if (data & CONFIRMBIT) { XtVaGetValues(w, XtNlabel, &label, NULL); Confirm(w, label, P_ACTION, data & ~CONFIRMBIT, po, po); } else ToHost(P_ACTION, data, po, po); } WindowChangeMenu(p) Pad *p; { int i; int entries, submenus, s; Index ix; PadDeleteMenus(p); if (!p->po.carte) return; MenuCountEntries(p->po.carte, &entries, &submenus); if (entries) WindowMenuAdd(p, p->po.carte, entries); for(i = 0; i < submenus; i++) { ix = SubMenuofMenu(p->po.carte, i); if (ix == (Index)0) continue; MenuCountEntries(ix, &entries, &s); WindowMenuAdd(p, ix, entries); } } WindowMenuAdd(p, ix, entries) Pad *p; Index ix; int entries; { Carte *c; Widget w, shell; char *text, *text2; char buffer[256]; if (entries == 0) return; c = IndexToCarte(ix); if (entries == 1) { /* Button */ w = MakeWinButton(p, c); } else if (entries <= SCROLLSIZE) { /* Menu */ text = IndexToStr(c->bin[0]); if (!text || !strcmp(text, "")) text = "ops"; w = XtVaCreateManagedWidget(text, menuButtonWidgetClass, p->control, XtNpushpin, OL_OUT, NULL); MakeWinButtonMenu(p, w, ix, 0); } else { /* Scrolling List */ text = IndexToStr(c->bin[0]); if (text && strcmp(text, "")) { sprintf(buffer, "%s...", text); text2 = buffer; } else { text = "ops"; text2 = "ops..."; } w = XtVaCreateManagedWidget(text2, oblongButtonWidgetClass, p->control, NULL); shell = ScrollShellWMCreate(w, text); MakeWinScrollMenu(p, shell, ix, 0); } PadAddMenu(p, w); } /* * Create a button */ Widget MakeWinButton(p, c) Pad *p; Carte *c; { int i, last; int entries, submenus; long data; Widget w; Index nix; Carte *nest; char buffer[128], *cp, *text; for (i = 1, last = c->size; i <= last; i++) { nix = c->bin[i]; if (nix & CARTE) { nest = IndexToCarte(nix); if (!nest->bin[0]) { MenuCountEntries(nix, &entries, &submenus); if (entries) return MakeWinButton(p, nest); last -= nest->size - 1; } } else { data = c->bin[i]; text = IndexToStr(c->bin[i]); cp = buffer; do *cp++ = *text & 0x7F; while (*text++); if (cp[-2] == '?') { data |= CONFIRMBIT; cp[-2] = 0; } w = XtVaCreateManagedWidget(buffer, oblongButtonWidgetClass, p->control, XtNuserData, (XtPointer)data, XtNlabelJustify, OL_CENTER, NULL); XtAddCallback(w, XtNselect, WindowMenuCB, (XtPointer)&p->po); return w; } } return (Widget)NULL; } /* * Create a menu for a button (or a menushell, since it has the same interface) */ int MakeWinButtonMenu(p, w, index, width) Pad *p; Widget w; Index index; int width; { register int i; int cnt; int last; long data; Index nix; register Carte *nest, *c; Widget pane, entry, submenu, shell; char *text; char buffer[128], *cp; int iswinmenu; c = IndexToCarte(index); XtVaGetValues(w, XtNmenuPane, &pane, NULL); if (!width) { PadAddPopup(p, pane); width = c->width; } if (c->attrib & NUMERIC) { fprintf(stderr, "MakeWinButtonMenu: Not implemented\n"); return 0; } for (i = 1, last = c->size; i <= last; i++) { nix = c->bin[i]; if (nix & CARTE) { nest = IndexToCarte(nix); if (!nest->bin[0]) last -= MakeWinButtonMenu(p, w, nix, width) - 1; } else { data = c->bin[i]; text = IndexToStr(c->bin[i]); cp = buffer; do { if (*text & 0x80) { cnt = width - strlen(text+1) - (cp-buffer); while(cnt--) *cp++ = *text & 0x7F; } else *cp++ = *text; } while (*text++); if (cp[-2] == '?') { data |= CONFIRMBIT; cp[-2] = 0; } entry = XtVaCreateManagedWidget(buffer, oblongButtonWidgetClass, pane, XtNuserData, (XtPointer)data, XtNlabelJustify, OL_CENTER, NULL); XtAddCallback(entry, XtNselect, WindowMenuCB, (XtPointer)&p->po); } } return c->size; } /* * The OpenLook scrolling list does not make a copy of the strings * for the entries, so we have to explicitly take care of allocation * and freeing of the strings. This structure is also used to store * the object arguments for the call to ToHost. */ struct scrollwininfo { PadObj *obj; int last; OlListToken tok[1]; }; static void scrollmenudelCB(w, info, call_data) Widget w; struct scrollwininfo *info; XtPointer call_data; { int i; OlListItem *ip; for(i = 0; i < info->last; i++) { ip = OlListItemPointer(info->tok[i]); XtFree(ip->label); } XtFree(info); } static void scrollmenuselCB(w, info, token) Widget w; struct scrollwininfo *info; OlListToken token; { long data; OlListItem *ip; ip = OlListItemPointer(token); data = (long)ip->user_data; ToHost(P_ACTION, data, info->obj, info->obj); } int MakeWinScrollMenu(p, w, index, width) Pad *p; Widget w; Index index; int width; { register int i; int cnt; int last; long data; Index nix; register Carte *nest, *c; Widget pane, entry, submenu; char *text; char buffer[128], *cp; OlListToken (*additem)(); OlListToken token; OlListItem item; struct scrollwininfo *info; c = IndexToCarte(index); if (!width) { width = c->width; XtVaGetValues(w, XtNupperControlArea, &pane, NULL); info = (struct scrollwininfo *) XtMalloc(sizeof(struct scrollwininfo) + sizeof(OlListToken) * c->size); PadAddPopup(p, w); info->obj = &p->po; info->last = 0; w = XtVaCreateManagedWidget("list", scrollingListWidgetClass, pane, XtNviewHeight, 10, XtNuserData, (XtPointer)info, NULL); XtAddCallback(w,XtNuserMakeCurrent, scrollmenuselCB, info); XtAddCallback(w,XtNdestroyCallback, scrollmenudelCB, info); } XtVaGetValues(w, XtNuserData, (XtArgVal)&info, XtNapplAddItem, (XtArgVal)&additem, NULL); item.label_type = OL_STRING; item.attr = 0; item.mnemonic = 0; if (c->attrib & NUMERIC) { fprintf(stderr, "MakeWinScrollMenu: Not implemented\n"); return 0; } for (i = 1, last = c->size; i <= last; i++) { nix = c->bin[i]; if (nix & CARTE) { nest = IndexToCarte(nix); if (!nest->bin[0]) last -= MakeWinScrollMenu(p, w, nix, width) - 1; } else { data = c->bin[i]; text = IndexToStr(c->bin[i]); cp = buffer; do { if (*text & 0x80) { cnt = width - strlen(text+1) - (cp-buffer); while(cnt--) *cp++ = *text & 0x7F; } else *cp++ = *text; } while (*text++); item.label = (XtPointer)XtNewString(buffer); item.user_data = (XtPointer)data; token = additem(w, NULL, NULL, item); info->tok[info->last++] = token; } } return c->size; } void PopUpScrollWMCB(w, shell, call_data) Widget w; Widget shell; XtPointer call_data; { int x, y, j; unsigned u; Window wj; if (((ShellWidget)shell)->shell.popped_up == TRUE) XRaiseWindow(XtDisplay(shell), XtWindow(shell)); else { XQueryPointer(XtDisplay(w), XtWindow(w), &wj, &wj, &x, &y, &j, &j, &u); PopUpScrollMenu(shell, x, y, FALSE); } } Widget ScrollShellWMCreate(but, s) Widget but; char *s; { Widget shell; shell = XtVaCreatePopupShell(s, popupWindowShellWidgetClass, but, XtNpushpin, OL_IN, XtNmappedWhenManaged, FALSE, NULL); XtAddCallback(but, XtNselect, PopUpScrollWMCB, (XtPointer)shell); return shell; } PadAddMenu(p, w) Pad *p; Widget w; { if (p->nomenus == MAXPOPUPS) { fprintf(stderr, "PadAddMenu: Too many window subments\n"); return; } p->menus[p->nomenus++] = w; } PadDeleteMenus(p) Pad *p; { Widget *c, *e; e = &p->menus[p->nomenus]; for(c = p->menus; c < e; c++) XtDestroyWidget(*c); p->nopopups = 0; p->nomenus = 0; } Widget ShellofWidget(w) Widget w; { while (w != (Widget)NULL && !XtIsShell(w)) w = XtParent(w); return(w); } PadAddPopup(p, w) Pad *p; Widget w; { w = ShellofWidget(w); if (!w || p->nopopups == MAXPOPUPS) return; p->popups[p->nopopups++] = w; } PadUnmapPopups(p) Pad *p; { Widget w; int i; ShellWidget shell; for(i = 0; i < p->nopopups; i++) { w = p->popups[i]; shell = (ShellWidget)w; if (shell->shell.popped_up == TRUE) { XtUnmapWidget(w); p->popstate |= 1 << i; } } } PadMapPopups(p) Pad *p; { int i; Widget w; XSizeHints hints; MenuShellWidget m; for(i = 0; i < p->nopopups; i++) { if (p->popstate & (1 << i)) { w = p->popups[i]; /* We have to treat MenuShellWidget specially, since * otherwise when we remap it, it doesn't pop back to * the same position it was last in. */ if (XtIsSubclass(w, menuShellWidgetClass)) { m = (MenuShellWidget)w; *((struct _OldXSizeHints *)&hints) = m->wm.size_hints; hints.x = (int)m->core.x; hints.y = (int)m->core.y; hints.base_width = m->wm.base_width; hints.base_height = m->wm.base_height; hints.win_gravity = m->wm.win_gravity; hints.flags &= ~PPosition; hints.flags |= USPosition; XSetNormalHints(XtDisplay(w), XtWindow(w), &hints); } XtMapWidget(w); } } p->popstate = 0; } /* * Return the number of non-submenu and submenu entries, given an index */ MenuCountEntries(ix, entries, submenus) Index ix; int *entries; int *submenus; { Index nix; Carte *nest, *c; int e, s; int en, sn; int i, last; e = s = 0; c = IndexToCarte(ix); if (c->attrib & NUMERIC) e = c->size; else { for (i = 1, last = c->size; i <= last; i++) { nix = c->bin[i]; if (nix & CARTE) { nest = IndexToCarte(nix); if (nest->bin[0]) s++; else { MenuCountEntries(nix, &en, &sn); e += en; s += sn; last -= nest->size - 1; } } else e++; } } *entries = e; *submenus = s; } /* * Return the index of the n-th submenu of the menu referenced by index. */ Index SubMenuofMenu(ix, sub) Index ix; int sub; { Index nix; Carte *nest, *c; int en, sn; int i, last; c = IndexToCarte(ix); if (c->attrib & NUMERIC) return (Index)0; for (i = 1, last = c->size; i <= last; i++) { nix = c->bin[i]; if (nix & CARTE) { nest = IndexToCarte(nix); if (nest->bin[0]) { if (sub-- == 0) return nix; } else { MenuCountEntries(nix, &en, &sn); if (sub < sn) return SubMenuofMenu(nix, sub); sub -= sn; last -= nest->size - 1; } } } return (Index)0; }