Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!sdd.hp.com!wuarchive!csus.edu!ucdavis!ucbvax!alfalfa.com!nazgul From: nazgul@alfalfa.com (Kee Hinckley) Newsgroups: comp.windows.x.motif Subject: Re: Troubles using popup menus from PushButtonWidgets Message-ID: <910206114208.1219@alphalpha> Date: 6 Feb 91 16:42:08 GMT References: <842@igor.Rational.COM> Sender: daemon@ucbvax.BERKELEY.EDU Distribution: inet Organization: The Internet Lines: 159 > > The goal: Make a push button that when selected (button 1 pressed, > *not* button 3) popups up a menu. Having the button arm while the I considered doing this but decided not to since the visuals are wrong. I don't expect things to pop down when I press a button. On the other hand, the visuals for an optionmenu are just right, except for that pesky little label. Enter the following grotesque hacks. You'll have to bear with the C++, but the concepts should stand. The goal here is to have a cascade button. That thing with the cute little box on the right. However the label shouldn't be there and the text of the button should be constant. // // OmXCascadeMenu Class // // clean up the string on delete OmXCascadeMenu::~OmXCascadeMenu() { if (labelString) XmStringFree(labelString); } // This gets called everytime a child button is activated, since we // need to reset the label and menuHistory. There's some other garbage // here to, you can try it without it, but this is the result of a lot // of "damn it doesn't work, lets try this", so I'm not going to try and // explain it all, I don't even know myself. void OmXCascadeMenu::resetCascade(Widget, XtPointer ud, XtPointer) { OmXCascadeMenu *cw = (OmXCascadeMenu *) ud; Widget cascade; Arg args[4]; cascade = XmOptionButtonGadget(cw->widget); if (cw->buttons.size()) XtSetArg(args[0], XmNmenuHistory, (*(cw->buttons.at(0)))->widget); else XtSetArg(args[0], XmNmenuHistory, cascade); XtSetValues(cw->widget, args, 1); XtSetValues(cw->pulldown, args, 1); XtSetArg(args[0], XmNlabelString, cw->labelString); XtSetArg(args[1], XmNmarginWidth, 2); XtSetArg(args[2], XmNmarginHeight, 2); XtSetValues(cascade, args, 3); } // In case we want to change the string void OmXCascadeMenu::setLabel(XmString str) { Boolean wasManaged = True; Widget cascade; if (XtIsManaged(widget)) XtUnmanageChild(widget); else wasManaged = False; if (labelString) XmStringFree(labelString); labelString = XmStringCopy(str); if (widget) resetCascade(NULL, this, NULL); if (wasManaged) { cascade = XmOptionButtonGadget(widget); XtUnmanageChild(cascade); XtManageChild(widget); XtManageChild(cascade); } } // Create the silly thing. Widget OmXCascadeMenu::create(Widget parent, String name, Arg *args, Cardinal argc) { Widget label, cascade; XmString str; OmXOptionMenu::create(parent, name, args, argc); arglist.add(XmNnumColumns, 1, XmNpacking, XmPACK_NONE, NULL); XtSetValues(widget, arglist.args, arglist.clear()); label = XmOptionLabelGadget(widget); arglist.add(XmNlabelString, &labelString, NULL); XtGetValues(label, arglist.args, arglist.clear()); str = XmStringCreateSimple(""); arglist.add(XmNlabelString, str, XmNwidth, 1, XmNheight, 1, NULL); XtSetValues(label, arglist.args, arglist.clear()); XmStringFree(str); cascade = XmOptionButtonGadget(widget); XtUnmanageChild(cascade); arglist.add(XmNmarginWidth, 2, XmNmarginHeight, 2, NULL); XtSetValues(cascade, arglist.args, arglist.clear()); XtManageChild(cascade); addCallback(XmNmapCallback, OmXCascadeMenu::resetCascade, this); return widget; } // How to manage it void OmXCascadeMenu::manage() { Widget cascade = XmOptionButtonGadget(widget); resetCascade(NULL, this, NULL); OmXOptionMenu::manage(); } // These are the routines to add child buttons. Since there can be // two kinds of buttons, and the stupid things have different callbacks, // you have to figure out which callback to use (to add resetCascade). // I should note that this is the same logic used inside of Motif. Ugh. // OmXMenuItem *OmXCascadeMenu::addMenuItem(OmXMenuItem *menuItem, Arg *args, Cardinal argc) { OmXMenuItem *mi; extern char *WhichCallback(Widget w); char *which; mi = OmXOptionMenu::addMenuItem(menuItem, args, argc); if (which = WhichCallback(mi->widget)) { mi->addCallback(which, OmXCascadeMenu::resetCascade, this); } return mi; } OmXMenuItem *OmXCascadeMenu::addMenuItem(long itemId, Arg *args, Cardinal argc) { OmXMenuItem *mi; extern char *WhichCallback(Widget w); char *which; mi = OmXOptionMenu::addMenuItem(itemId, args, argc); if (which = WhichCallback(mi->widget)) { mi->addCallback(which, OmXCascadeMenu::resetCascade, this); } return mi; } /* Here's the C file to figure out which button callback to use */ #include char *whichCallback( #if NeedFunctionPrototypes Widget w) #else w) Widget w; #endif { if (XmIsPushButtonGadget(w) || XmIsPushButton(w) || XmIsCascadeButton(w) || XmIsCascadeButtonGadget(w) || XmIsDrawnButton(w)) return (XmNactivateCallback); if (XmIsToggleButtonGadget(w) || XmIsToggleButton(w)) return (XmNvalueChangedCallback); return NULL; }