[comp.windows.x.motif] Troubles using popup menus from PushButtonWidgets

drk@Rational.COM (David Kaelbling) (02/06/91)

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
the menu is up would be nice, but isn't crucial.  I'm using Motif 1.1.1
and the standard fully patched MIT X.V11R4 libraries on a Sun-SLC.

The problem: Setting XmNwhichButton (I know -- it's obsolete, but it works)
allows the menu to actually appear.  All works well *once*.  The next
time the cursor enters the button window, it arms itself!  My pet
theory was that because the menu grabbed the mouse button, normal
cleanup (disarming) didn't happen.

Failed solutions: I've tried almost every permutation of actions in
the translation table.  Replacing Btn1Down so it explicitly invokes
Disarm(), Leave(), BtnDown(), etc.  Using "<Btn1Down>:Leave()" so Arm()
is never called doesn't work either -- the next time the mouse enters,
the button still thinks it is armed.

Does anybody know the right incantation?

	Thanks,
	David
-- 
David Kaelbling                                       (408) 496-3600
c/o Rational; 3320 Scott Boulevard; Santa Clara, CA       95054-3197
Email: DKaelbling@Rational.COM, or uucp {uunet,ubvax,aeras}!igor!drk

nazgul@alfalfa.com (Kee Hinckley) (02/07/91)

> 
> 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 <Xm/XmP.h>


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;
}