#include "univ.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Don't lower SCROLLSIZE without modifying pi, because the largest line * menu with cascades is currently 13 - the menu for a pointer to a struct * with 2 members. Otherwhise it will generate the message: * MakeLineScrollMenu: not implemented * If it is decreased this code will have to implemented. */ #define SCROLLSIZE 13 /* If more than this # of entries, scroll it */ #define NUMERIC_FLAG 0x80000000 Widget ScrollShellLMCreate(); static PadObj *LineObject; Widget LineShell; void LineMenuDelete(l) PadObj *l; { if (LineObject == l) XtPopdown(LineShell); } void LineMenuCB(w, user_data, call_data) Widget w; XtPointer user_data; XtPointer call_data; { long data; PadObj *po; po = (PadObj *)user_data; XtVaGetValues(w, XtNuserData, &data, NULL); if (data & NUMERIC_FLAG) ToHost(P_NUMERIC, (long)(short)data, po, LineObject); else ToHost(P_ACTION, data, po, LineObject); } /* * Create a menu for a button (or a menushell, since it has the same interface) */ int MakeLineButtonMenu(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; c = IndexToCarte(index); XtVaGetValues(w, XtNmenuPane, &pane, NULL); if (!width) width = c->width; if (c->attrib & NUMERIC) { i = c->bin[1]; last = i + c->size; for ( ; i < last; i++) { data = (i & 0xFFFF) | NUMERIC_FLAG; entry = XtVaCreateManagedWidget(itoa(i), oblongButtonWidgetClass, pane, XtNuserData, (XtPointer)data, NULL); XtAddCallback(entry, XtNselect, LineMenuCB, (XtPointer)&p->po); } return c->size; } for (i = 1, last = c->size; i <= last; i++) { nix = c->bin[i]; if (nix & CARTE) { nest = IndexToCarte(nix); if (nest->bin[0]) { text = IndexToStr(nest->bin[0]); if (nest->size > SCROLLSIZE) { sprintf(buffer, "%s...", text); submenu = XtVaCreateManagedWidget(buffer, oblongButtonWidgetClass, pane, XtNlabelJustify, OL_CENTER, NULL); shell = ScrollShellLMCreate(submenu, p->textw); MakeLineScrollMenu(p, shell, nix, 0); } else { submenu = XtVaCreateManagedWidget(text, menuButtonWidgetClass, pane, XtNlabelJustify, OL_CENTER, NULL); MakeLineButtonMenu(p, submenu, nix, 0); } } else last -= MakeLineButtonMenu(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++); entry = XtVaCreateManagedWidget(buffer, oblongButtonWidgetClass, pane, XtNuserData, (XtPointer)data, XtNlabelJustify, OL_CENTER, NULL); XtAddCallback(entry, XtNselect, LineMenuCB, (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 scrolllineinfo { PadObj *obj; PadObj *lobj; int last; Widget shell; OlListToken tok[1]; }; static void scrollmenudelCB(w, info, call_data) Widget w; struct scrolllineinfo *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 scrolllineinfo *info; OlListToken token; { long data; OlListItem *ip; ip = OlListItemPointer(token); data = (long)ip->user_data; if (data & NUMERIC_FLAG) ToHost(P_NUMERIC, (long)(short)data, info->obj, info->lobj); else ToHost(P_ACTION, data, info->obj, info->lobj); } int MakeLineScrollMenu(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 scrolllineinfo *info; c = IndexToCarte(index); if (!width) { width = c->width; XtVaGetValues(w, XtNupperControlArea, &pane, NULL); info = (struct scrolllineinfo *) XtMalloc(sizeof(struct scrolllineinfo) + sizeof(OlListToken) * c->size); info->shell = w; info->obj = &p->po; info->lobj = LineObject; 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) { i = c->bin[1]; last = i + c->size; for ( ; i < last; i++) { data = (i & 0xFFFF) | NUMERIC_FLAG; text = itoa(i); item.label = (XtPointer)XtNewString(text); item.user_data = (XtPointer)data; token = additem(w, NULL, NULL, item); info->tok[info->last++] = token; } return c->size; } for (i = 1, last = c->size; i <= last; i++) { nix = c->bin[i]; if (nix & CARTE) { nest = IndexToCarte(nix); if (nest->bin[0]) { /* Fix this when needed */ fprintf(stderr,"MakeLineScrollMenu: not implemented\n"); } else last -= MakeLineScrollMenu(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 LineMenuPopDownCB(w, client_data, call_data) Widget w; XtPointer client_data; XtPointer call_data; { XtDestroyWidget(w); } void LineMenuDestroyCB(w, client_data, call_data) Widget w; XtPointer client_data; XtPointer call_data; { if (LineShell == w) LineShell = (Widget)NULL; } void PopUpScrollLMCB(w, shell, call_data) Widget w; Widget shell; XtPointer call_data; { int x, y, j; unsigned u; Window wj; XQueryPointer(XtDisplay(w), XtWindow(w), &wj, &wj, &x, &y, &j, &j, &u); PopUpScrollMenu(shell, x, y, TRUE); } Widget ScrollShellLMCreate(but, parent) Widget but; Widget parent; { Widget shell; shell = XtVaCreatePopupShell("lineops", popupWindowShellWidgetClass, parent, XtNpushpin, OL_IN, XtNmappedWhenManaged, FALSE, NULL); XtAddCallback(shell, XtNpopdownCallback, LineMenuPopDownCB, NULL); if (but != (Widget)NULL) XtAddCallback(but, XtNselect, PopUpScrollLMCB,(XtPointer)shell); return shell; } LineMenu(p, line, x, y) Pad *p; int line; int x, y; { Widget shell; Widget control; PadLine *l; Carte *c; if (LineShell != (Widget)NULL) XtDestroyWidget(LineShell); l = LineiToPadLine(p, line); if (!l || !l->po.carte) return; LineObject = &l->po; c = IndexToCarte(l->po.carte); if (c->size > SCROLLSIZE) { shell = ScrollShellLMCreate((Widget)NULL, p->textw); MakeLineScrollMenu(p, shell, l->po.carte, 0); PopUpScrollMenu(shell, x, y, TRUE); } else { shell = XtVaCreatePopupShell("lineops", menuShellWidgetClass, p->textw, XtNmenuAugment, FALSE, NULL); XtAddCallback(shell, XtNpopdownCallback, LineMenuPopDownCB, NULL); MakeLineButtonMenu(p, shell, l->po.carte, 0); OlMenuPost(shell); } } /* * Pop up the menu, adjusting the position to insure it is completely visible. * If has already been popped up, use the old position. */ #define EDGE_BORDER 10 PopUpScrollMenu(shell, x, y, linemenu) Widget shell; int x, y; int linemenu; { Dimension width, height; int rwidth, rheight; int wasrealized = FALSE; if (XtIsRealized(shell) == FALSE) { wasrealized = TRUE; XtRealizeWidget(shell); XtVaGetValues(shell, XtNheight, &height, XtNwidth, &width, NULL); rwidth = WidthOfScreen(XtScreen(shell)); rheight = HeightOfScreen(XtScreen(shell)); y -= EDGE_BORDER; /* Crank it up a little */ if (x < EDGE_BORDER) x = EDGE_BORDER; else if ((x + (int)width) > (rwidth - EDGE_BORDER)) x = rwidth - (int)width - EDGE_BORDER; if (y < EDGE_BORDER) y = EDGE_BORDER; else if ((y + (int)height) > (rheight - EDGE_BORDER)) y = rheight - (int)height - EDGE_BORDER; XtVaSetValues(shell, XtNx, (Position)x, XtNy, (Position)y, NULL); #ifdef i386 { TransientShellWidget tshell; XSizeHints hints; tshell = (TransientShellWidget)shell; *((struct _OldXSizeHints *)&hints) = tshell->wm.size_hints; hints.flags &= ~(PPosition|PSize); hints.flags |= USPosition|USSize; hints.x = x; hints.y = y; hints.width = (int)width; hints.height = (int)height; hints.base_width = tshell->wm.base_width; hints.base_height = tshell->wm.base_height; hints.win_gravity = tshell->wm.win_gravity; XSetNormalHints(XtDisplay(shell), XtWindow(shell), &hints); } #endif } if (linemenu == TRUE) { LineShell = shell; XtAddCallback(shell, XtNdestroyCallback, LineMenuDestroyCB, NULL); } XtPopup(shell, XtGrabNone); if (wasrealized == TRUE) XtVaSetValues(shell, XtNmappedWhenManaged, TRUE, NULL); }