[comp.windows.x] How do I create popup menus with Xt?

solomon@speedy.cs.wisc.edu (Marvin Solomon) (07/19/88)

A little while back somebody asked how to create popup menus and got a humorous
reply that the X Consortium decided that menus are a bad idea.  Well I decided
I really wanted to provide popup menus with my application (a troff previewer),
so I spend a week wrestling with the X Toolkit trying to figure out how to
do it.  I've had some limited success, but what I did was by no means
straightforward, so I thought I'd share my experiences with this newsgroup
in order that
	1.  Others can benfit from my experience
	2.  Somebody can tell me how I really SHOULD have done it
	3.  Perhaps the next version of the intrinsics will may things a
		bit easier.

First, I looked at all the examples of applications I could find to see
how others did it, and discovered that although the Toolkit intrinsics
have all kinds of stuff that seems to be designed for the purpose, ABSOLUTELY
NOBODY USES IT!  Among other places, I looked in xterm, twm, and awm (which
uses a modified version of the rtl menu package), xman, and xmh.  The stuff
in contrib/menus seems to use some of it, but that whole directory seems not
to have been updated to V11R2.  I can't get close to getting it to compile.
All the working applications use raw Xlib functions to draw and
manage their menus (and they each do it differently).  All that stuff in
Chapter 7 (Popup Widgets) seems to be virtually unused.  Moreoever, I've
come to the conclusion that at least some of it is unusable.  But more on that
later.

For concreteness sake, suppose I want a Macintosh-like interface:  There's
a menu bar with a bunch of buttons.  A mouse press in one of these buttons
pops up a menu.  Dragging the cursor through the menu without releasing the
mouse button highlights menu items.  Releasing the button while one of the menu
items is highlighted invokes a corresponding action and makes the popup menu
go away.  Dragging the cursor outside the popup menu without releasing the
button makes the menu go away without causing any action.

For the menu, bar I create a Box widget with some Command widgets inside it.
At initialization time, I create the menu itself as a popup shell containing
a Box containing a set of Buttons.  (By the way, the documentation gives
no advice on what widget_class to use for the popup-shell.  I found
that I had to make it overrideShellWidgetClass to prevent the window manager
from getting into the act when the window pops up).

The first problem is how to get the menu to pop up.
According to the manual, there are three ways to get the menu to pop up:
By calling XtPopup() explicitly (from inside a callback), by using
XtCallbackExclusive() (or one of its friends) as a callback procedure, or by
using MenuPopup() in a translation.  The first two approaches create the
popup only when the Command widget does its callbacks--by default, when the 
mouse button is released.  I tried using XtOverrideTranslations to override
the default
	"<Btn1Down>: set()\n <Btn1Up>: notify() unset()"
(NB:  this is from the code; the manual has it wrong) with the actions
	"<Btn1Down>: set() notify()\n <Btn1Up>: unset()"
but somehow, this doesn't work quite right (more on this later).
The third approach worked better.  I use
	"<Btn1Down>: set() MenuPopup(menu)\n"
where "menu" is the name of the popup shell.

The second problem is how to get the menu to pop up in a reasonable place--
i.e., near the cursor.  If the shell widget is not of class
overrideShellWidgetClass, the window manager kindly askes me to size and
locate the menu, which is clearly ridiculous.  Actions set up using the
translation manager are passed an event structure, which contains the
right information, but unfortunatly the buildin action for MenuPopup()
throws this information away.  What I settled on was to add an
XtNpopupCallback procedure to set the XtNx and XtNy attributes of the popup
shell.  I pass a pointer to the Command widget that caused the popup as
the 'closure' of the callback.  It queries the (x,y) position of that widget
and makes the menu pop up slightly offset from there.  On reflection, I
might have been better off to define my own popup action and ignore the
"handy" MenuPopup().

The third problem is to make the popup menu behave properly.  That was
relatively easy.  I overrode its transations with
		"<EnterWindow>: highlight() set()"

The fourth and hardest problem was to make the menu go away.  Once again,
there are three methods documented, none of which seems to work quite right:
call XtPopdown() from a callback, use XtCallbackPopdown() as a callback, or
use MenuPopdown() as an action.  The latter would seem to be the best, but
I couldn't figure out how to use it.  As a matter of fact, I've convinced
myself that MenuPopdown() is totally unusable.  The only reasonable action
to bind it to would be <Btn1Up>.  MenuPopdown() complains if the target
widget is not a shell widget. Since MenuPopdown() has no arguments,
its target widget is always the widget to which the translation is bound.
But if I set a translation for <Btn1UP> in the popup shell, the Command
buttons in the window never see the button up events, so I can't select
anything with them.  I can't modify them to activate on button down, since
the butten is already down, and activating them on EnterWidow is obviously
wrong too.
The solution I finally came up with is to add an XtCallbackPopdown() action
to EACH AND EVERY button in the menu, passing an XtPopdownID as the closure.

One final problem is how to make the popup menu go way if the user doesn't
choose any of its options.  I still haven't got that to work, but I have
one more trick I might try:  Define a translation for LeaveWindow on the
Box widget containing the menu.  Right now, if I pop up the menu with a button
press and then release the button somewhere other than one of the menu items,
the menu stays popped up.  If I go back and click in one of the menu items,
the corresponding action is taken and the menu goes away.  Curiously enough,
there doesn't seem to be any grab active:  I can still activate other buttons
in the menu bar.  All this despite that fact that the builtin action for
MenuPopup() sets "spring-loaded" and (so far as I can make out) does an
exclusive grab.  The same thing happend if I use XtCallbackExclusive()
instead of MenuPopup() to raise the menu.  Only in this case, the menu does
not "feel" the mouse until I release the button for the first time.

I intended to include the whole test program at the end of this message,
but as it's already too long, I'll send the test program under separate cover.
	Marvin Solomon
	Computer Sciences Department
	University of Wisconsin, Madison WI
	solomon@cs.wisc.edu or seismo!uwvax!solomon

solomon@speedy.cs.wisc.edu (Marvin Solomon) (07/19/88)

Here is my best effort at popup menus using Xt and Xaw.  See my previous
message for discussion.  (If you haven't seen the "previous message", just
wait a bit).
-------------------------------
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Cardinals.h>
#include <X11/Command.h>
#include <X11/Box.h>
#include <X11/Label.h>
#include <X11/Shell.h>
#include <stdio.h>

/* Command line options table.  Only resources are entered here...there is a
   pass over the remaining options after XtParseCommand is let loose. */

static XrmOptionDescRec options[] = {
{"-label",	"*label",	XrmoptionSepArg,	NULL}
};


/*
 * Report the syntax for calling xlabel
 */
Syntax(call)
    char *call;
{
    fprintf( stderr, "Usage: %s\n", call );
}

/* ARGSUSED */
void callback(w, str, dummy)
Widget w;
char *str;
int dummy;
{
	printf("%s button\n", str);
	fflush(stdout);
}

/* ARGSUSED */
void done(w, str, dummy)
Widget w;
char *str;
int dummy;
{
	exit(0);
}

/* ARGSUSED */
void set_menu_position(shell_widget, enable_widget, dummy)
Widget enable_widget, shell_widget;
int dummy;
{
	Position x, y;
	Arg args[2];

	XtTranslateCoords(enable_widget, (Position)5, (Position)5, &x, &y);
	XtSetArg(args[0], XtNx, x);
	XtSetArg(args[1], XtNy, y);
	XtSetValues(shell_widget, args, TWO);
}


void main(argc, argv)
    unsigned int argc;
    char **argv;
{
	Widget toplevel, menu_bar, button, popupshell, menu, popupbutton;
	XtPopdownIDRec popdown_pair;
	XtTranslations button_actions, popup_actions;
	Arg arg;

    toplevel = XtInitialize( "popup_test", "XPopupTest",
			     options, XtNumber(options),
			     &argc, argv );

    if (argc != 1) Syntax(argv[0]);

	button_actions = XtParseTranslationTable(
		"<EnterWindow>: highlight() set()\n");
	popup_actions = XtParseTranslationTable(
		"<Btn1Down>: set() MenuPopup(menu)\n");

 	/**** menu bar ****/
    menu_bar = XtCreateManagedWidget( "menu_bar", boxWidgetClass, toplevel,
		(Arg *)0, ZERO);

	/**** menu bar items ****/
    button = XtCreateManagedWidget( "yes", commandWidgetClass, menu_bar,
		(Arg *)0, ZERO );
	XtAddCallback(button, XtNcallback, callback, (caddr_t)"yes");

    popupbutton =
		XtCreateManagedWidget( "ShowMenu", commandWidgetClass, menu_bar,
			(Arg *)0, ZERO );
	XtOverrideTranslations(popupbutton, popup_actions);

	/**** popup menu ****/
	popupshell = XtCreatePopupShell("menu", overrideShellWidgetClass, toplevel,
		(Arg *)0, ZERO);
    menu = XtCreateManagedWidget( "PopupMenu", boxWidgetClass, popupshell,
		(Arg *)0, ZERO);
	XtAddCallback(popupshell,
		XtNpopupCallback, set_menu_position, (caddr_t)popupbutton);
	/*
	XtAddCallback(popupbutton,
		XtNcallback, XtCallbackExclusive, (caddr_t)popupshell);
	*/

	popdown_pair.enable_widget = popupbutton;
	popdown_pair.shell_widget = popupshell;

	/**** popup menu items ****/
	/* menu option 1 */
	button = XtCreateManagedWidget( "choice1", commandWidgetClass, menu,
		(Arg *)0, ZERO );
	XtAddCallback(button, XtNcallback, callback, (caddr_t)"button1");
	XtAddCallback(button, XtNcallback,
		XtCallbackPopdown, (caddr_t)&popdown_pair);
	XtOverrideTranslations(button, button_actions);

	/* menu option 2 */
	button = XtCreateManagedWidget( "choice2", commandWidgetClass, menu,
		(Arg *)0, ZERO );
	XtAddCallback(button, XtNcallback, callback, (caddr_t)"button2");
	XtAddCallback(button, XtNcallback,
		XtCallbackPopdown, (caddr_t)&popdown_pair);
	XtOverrideTranslations(button, button_actions);

	/* menu option 3 */
	button = XtCreateManagedWidget( "quit", commandWidgetClass, menu,
		(Arg *)0, ZERO );
	XtAddCallback(button, XtNcallback, done, (caddr_t)"done");
	XtOverrideTranslations(button, button_actions);
    
    XtRealizeWidget(toplevel);
    XtMainLoop();
}
	Marvin Solomon
	Computer Sciences Department
	University of Wisconsin, Madison WI
	solomon@cs.wisc.edu or seismo!uwvax!solomon

swick@ATHENA.MIT.EDU (Ralph R. Swick) (07/20/88)

> First, I looked at all the examples of applications I could find to see
> how others did it, and discovered that although the Toolkit intrinsics
> have all kinds of stuff that seems to be designed for the purpose, ABSOLUTELY
> NOBODY USES IT!

You have here only another instance of resource allocation and scheduling
conflicts.  The clients you cite were all built in pre-Xt days and therefore
had to (independently) do everything the hard way.  The task of fixing
things that work has been very low on our priority list.

The popup support in Xt does work.  There are some kinks that will be
smoothed out in R3, as well as widgets which have more mechanism
(and less policy :-) for using the popup support in Xt.

It sounds like you are basically on the right track in your adopted role
as widget writer trying to figure out how to use the mechanisms.  Your
frustration in being forced to play that role is understandable.  We
will be happy to deal with specific bugs in Xt and Xaw as they are
brought to our attention.

blumen@parcvax.Xerox.COM (Robert Blumen) (07/20/88)

In article <6005@spool.cs.wisc.edu> solomon@speedy.cs.wisc.edu (Marvin Solomon) writes:
>A little while back somebody asked how to create popup menus and got a humorous
>reply that the X Consortium decided that menus are a bad idea.  Well I decided
>I really wanted to provide popup menus with my application (a troff previewer),
>so I spend a week wrestling with the X Toolkit trying to figure out how to
>do it.  I've had some limited success, but what I did was by no means
>straightforward, so I thought I'd share my experiences with this newsgroup
>in order that
>	2.  Somebody can tell me how I really SHOULD have done it

ok...

>For concreteness sake, suppose I want a Macintosh-like interface:  There's
>a menu bar with a bunch of buttons.  A mouse press in one of these buttons
>pops up a menu.  Dragging the cursor through the menu without releasing the
>mouse button highlights menu items.  Releasing the button while one of the menu
>items is highlighted invokes a corresponding action and makes the popup menu
>go away.  Dragging the cursor outside the popup menu without releasing the
>button makes the menu go away without causing any action.
>

I used the same, except for the last sentence.
>
>The second problem is how to get the menu to pop up in a reasonable place--
>i.e., near the cursor.What I settled on was to add an
>XtNpopupCallback procedure to set the XtNx and XtNy attributes of the popup
>shell.  

What I don't like about this is that XtNpopupCallback should be left to the
application programmer,  and I think that menus should be provided at a lower
level.  What I did is not much better...

>
>The fourth and hardest problem was to make the menu go away.  Once again,
>there are three methods documented, none of which seems to work quite right:
>call XtPopdown() from a callback, use XtCallbackPopdown() as a callback, or
>use MenuPopdown() as an action.  The latter would seem to be the best, but
>I couldn't figure out how to use it.  

Use MenuPopdown as a translation for the Unmap action on the override shell.

>	Marvin Solomon

My solution is the following: 


-------
I created three new widget classes: menuShell, menuBox, and
menuButton.  

-------
MenuShell is a subclass of overrideShell with the following changes:

1. MenuShellClassPart has one field that does nothing.  It has a
MenuShellPart as follows :

typedef struct _MenuShell_Part { 
	Boolean putback_cursor; /* whether to put cursor back after popdown */
	Boolean menu_under_cursor; /* whether to pup menu up under the cursor*/

/* private */
	Position cursor_x, cursor_y;
} MenuShellPart;



2. It has the DEFAULT translations

static char  defaultTranslations[] = 
       	"<BtnUp>: MenuPopdown() select()\n\
	<Map>: setup()";

Where Select is a function that does a depth-first search of the menuShell's
widget tree until it finds one that is a menuButton Widget that has its
command.set field == TRUE.  It then calls XtCallCallbacks for this widget.

Setup is a function that calls XQueryPointer to find the pointer
location, and sets the cursor_x and cursor_y to the cursor position.


3. The Initialize routine sets the shell.create_popup_child_proc to the
function "GetLocation".   The create_popup_child_proc is called everytime
that the popup is mapped.  GetLocation uses XQueryPointer to find the pointer
location, then calls XtMakeGeometryRequest with request_mode = CWX | CWY and
the x and y fields set to positions near the cursor.  This positions the menu
properly, near the cursor.

-------

MenuBox is a subclass of box with the following changes:

1. The MenuBoxClassRec has one field that does nothing.  The menuBoxPart has
the same.

2. MenuBox has its own change_managed procedure which makes sure that all of
the buttons in the menu have the same width.  The way it does this is that it
calls XtQueryGeometry on all of its  managed children to find out their
preferred width, and keeps track of the maximum width of any child.

Then, for any children that are smaller than the max, it calls XtResizeWidget
on that child.  Then, it calls its superclass's change_managed to do the
layout of the box.

-------

MenuButton is a subclass of Command with the following changes:

1. MenuButtonClassPart has one field that does nothing MenuButtonPart is the
same. 

2.   MenuButton has new default translations:

static char defaultTranslations[] =
     "<EnterWindow>:	set() highlight()\n\
     <LeaveWindow>:	unset(NoRedisplay) unhighlight()";


So when the cursor enters the menu button, it is both highlighted and
inverted.  

-------

How to use them:

Create a menu shell with XtCreatePopupShell.

Create a menu box as a managed child of the menu shell with
XtCreateManagedWidget.  I know that the manual says not to use
XtCreateManagedWidget for a popup.  It doesn't seem to work their way.

Create a labelWidget as a managed child of the menu box, if desired.  This will
be the title of the menu.  Create as many menu buttons as managed children  of
the menu box.  For each button, add a callback which will be called when that
button is selected.  

To get the menu to pop up, create a Command widget, and add the
following translation with XtOverrrideTranslations:

"<BtnDown>: MenuPopup( menu_name_goes_here )"; 

-------

What's so great about this?  Now that the widgets are incorporated into widgets,
it is very easy to create menus without understanding how they work or worrying
about callbacks, translations, etc.  They have the desired behavior, and they
fit right into the widget hierarchy, simplifying future programming tasks.

Robert Blumen
blumen.pa@xerox.com

hds@io.UUCP (H.David Scarbro) (07/20/88)

The following is from Marvin Solomon's recent netnews message:

>One final problem is how to make the popup menu go way if the user doesn't
>choose any of its options.  I still haven't got that to work, but I have
>one more trick I might try:  Define a translation for LeaveWindow on the
>Box widget containing the menu.  Right now, if I pop up the menu with a button
>press and then release the button somewhere other than one of the menu items,
>the menu stays popped up.  If I go back and click in one of the menu items,
>the corresponding action is taken and the menu goes away.  Curiously enough,
>there doesn't seem to be any grab active:  I can still activate other buttons
>in the menu bar.  All this despite that fact that the builtin action for
>MenuPopup() sets "spring-loaded" and (so far as I can make out) does an
>exclusive grab.  The same thing happend if I use XtCallbackExclusive()
>instead of MenuPopup() to raise the menu.  Only in this case, the menu does
>not "feel" the mouse until I release the button for the first time.

The following is from Ralph Swick's response:

> The popup support in Xt does work.  There are some kinks that will be
> smoothed out in R3, as well as widgets which have more mechanism
> (and less policy :-) for using the popup support in Xt.

Using Marvin's example program, I found one of the "kinks" with MenuPopup().
The line in XtConvertStringToBoolean() in TMstate.c which is currently:

    *bP = (Boolean) *(int *)toVal.addr;

should read:

    *bP = *(Boolean*)toVal.addr;

By ironing out this "kink" and adding an override to call MenuPopdown() on
<Btn1Up> I made Marvin's example program work correctly.

This change doesn't fix the "kinks" in XtCallbackExclusive.  From looking at
this code, it appears there might be some state information that the translation
manager is changing as a part of the MenuPopup() that needs to be changed when
XtCallbackExclusive() is called.

I've already sent the MenuPopup() fix to xbugs.

----
David Scarbro                          UUCP: ..!{sun!sunne,mit-eddie}!ileaf!hds
Interleaf, Inc.                        Internet: hds@ileaf.com
Ten Canal Park                         Phone: (617)577-9800 x6608
Cambridge, MA 02141

solomon@speedy.cs.wisc.edu (Marvin Solomon) (07/21/88)

In article <8807191717.AA00552@LYRE.MIT.EDU> swick@ATHENA.MIT.EDU (Ralph R. Swick) writes:
>> First, I looked at all the examples of applications I could find to see
>> how others did it, and discovered that although the Toolkit intrinsics
>> have all kinds of stuff that seems to be designed for the purpose, ABSOLUTELY
>> NOBODY USES IT!
>
>You have here only another instance of resource allocation and scheduling
>conflicts.  The clients you cite were all built in pre-Xt days and therefore
>had to (independently) do everything the hard way.  The task of fixing
>things that work has been very low on our priority list.
>
>The popup support in Xt does work.  There are some kinks that will be
>smoothed out in R3, as well as widgets which have more mechanism
>(and less policy :-) for using the popup support in Xt.
>
>It sounds like you are basically on the right track in your adopted role
>as widget writer trying to figure out how to use the mechanisms.  Your
>frustration in being forced to play that role is understandable.  We
>will be happy to deal with specific bugs in Xt and Xaw as they are
>brought to our attention.

David Scarbro of InterLeaf suggested an enhancement for my sample program.
It now works exactly as I want it.  It involves fixing a specific bug in Xt.
More on this below.

As the above message (as well as one by Robert Blumen of Xerox PARC)
suggests, my problem is that I'm trying to take on the role of widget
creater when I'd much rather be widget user.
The main problem is that right now, the set of Xt-conforming widgets (i.e.,
lib/Xaw) is quite small.  Hopefully, that will change in the near future.
In the mean time, we've got lots of mechanism and precious little policy.
Otherwise stated:  The Intrinsics make everything possible, and nothing
easy :-).

A philosophical question:  Where is the Toolkit (with a capital "T") headed?
It seems to me that the "object-oriented" design dictates that there be
no such thing as "application programs".  Rather, each piece of application
functionality is realized as a new widget class.  This is the way xterm
is (more or less) designed, and it is the design I finally came up with for
my troff previewer:  I defined a new Proof widget class.

The justification for this approach goes as follows:  If a widget class is only
desiged to be instantiated (as opposed to inherited), it is likely to be
either too inflexible, or loaded down with a zillion options, which make
it complicated to understand and use.  The object-oriented approach says that
instead of giving a widget an option (resource) for every conceivable way
that it might be customized, it is better to create custom versions by
inheritence.  For example, the Box widget class could be extended with a
XtNlayout resource with values of "vertical", "horizontal", or "unconstrained"
(the current behavior), which would make it easier to use as a popup menu.
But a much better approach is to define a MenuBox subclass, as Robert
Blumen suggests, which is not only vertical, but also resizes the members
so they're all the same width.

There are two drawbacks to this approach.  First, it is an unfamiliar style
of programming.  However, once people get used to it, I think they'll like
it.  A little discussion in the Widgets document (including at least
one realistic example) can go a long way to ease the path.
The second problem is more serious:  The Toolkit (both intrinsics and
existing Athena widgets) seem to make the assumption that applications
programmers are simpletons that need to be lead by the hand, whereas
widget programmers are gurus initiated into the archane rites of toolkitism.
The object-oriented approach erases the distinction.  Existing widgets
are admirably easy to use if they do what you want by default, and moderately
easy to use if they can be customized by XtSetAttributes().
Creating a new widget class is mildly painful if you can find an existing
class definition that is "close enough" to hack on (my Proof widget is a
hacked Mailbox widget, which is itself a hack of the Clock widget, according
to a comment in Mailbox.h).  Creating a new widget class from scratch is
VERY confusing to the uninitiated.   It occurs to me that a lot of pain
is caused by trying to do object-oriented programming in a language that
doesn't support it.  Is there likely to be a C++ version of the Toolkit?
Yes I know about InterViews, but it seems to be totally incompatible with
the Intrisics, and it is not clear to me how to mix widgets from the two
worlds.  Another approach might be a pre-processor that takes a specification,
looking something like SmallTalk, and translates into FooP.h, Foo.h, and
Foo.c.

But I'm getting too far afield.  Several people have indicated privately
that they appreciate my sample popup program.  Fixes mailed to me by 
David Scarbro make it much better.  He found a bug in the Intrinsics that
seems to be the root of all my problems with MenuPopdown().  With that
bug fixed, adding MenuPopdown() to my program is straightforward.
I quote both fixes below (without permission--I hope he doesn't mind).
I hope he reported the bug fix to bugs@mit.  If not, perhaps somebody at
MIT reading this will take it as a bug report.

> I found your description of the perils of trying to use Xt to create popups
> very helpful.  I been playing with the Xt and had just started to try building
> a popup out of widgets.  Your message saved me a lot of time.  Thanks.
> 
> I think I solved the problem that you were having with MenuPopup().  By using
> a debugger, I discovered that _XtMenuPopup() was calling _XtPopup as follows:
>  _XtPopup(popup_shell, XtGrabNonexclusive, FALSE).  So your hunch was correct,
> no grab was active.  I chased this problem to a bug in
> XtConvertStringToBoolean().  To fix this bug, I changed:
>     *bP = (Boolean) *(int*)toVal.addr;
> to
>     *bP = *(Boolean*)toVal.addr;
> Now _XtPopup appears to be getting called correctly.
> 
> With one additional change to your program, popups seem work.  I had to add
> another translation mapping.
> 
> ----
> H. David Scarbro                       UUCP: ..!{sun!sunne,mit-eddie}!ileaf!hds
> Interleaf, Inc.                        Internet: hds@ileaf.com
> Ten Canal Park                         Phone: (617)577-9800 x6608
> Cambridge, MA 02141

The changes to my test program follow.  I'm still not happy with the kludge
I use for placement of the popup.  I may take a hint from Robert Blumen and
use XQueryPointer and requests to a geometry manager rather than
XtTranslateCoords and XtSetValues for XtNx, and XtNy.
========================= cut here, use patch ==============
RCS file: popup.c,v
retrieving revision 1.2
diff -b -c -r1.2 popup.c
*** /tmp/,RCSt1016586	Wed Jul 20 12:04:02 1988
--- popup.c	Wed Jul 20 11:11:50 1988
***************
*** 64,70
  {
	Widget toplevel, menu_bar, button, popupshell, menu, popupbutton;
	XtPopdownIDRec popdown_pair;
! 	XtTranslations button_actions, popup_actions;
	Arg arg;
  
	  toplevel = XtInitialize( "popup_test", "XPopupTest",

--- 64,70 -----
  {
	Widget toplevel, menu_bar, button, popupshell, menu, popupbutton;
	XtPopdownIDRec popdown_pair;
! 	XtTranslations button_actions, popup_actions, popdown_actions;
	Arg arg;
  
	  toplevel = XtInitialize( "popup_test", "XPopupTest",
***************
*** 77,82
		"<EnterWindow>: highlight() set()\n");
	popup_actions = XtParseTranslationTable(
		"<Btn1Down>: set() MenuPopup(menu)\n");
  
	/**** menu bar ****/
	  menu_bar = XtCreateManagedWidget( "menu_bar", boxWidgetClass, toplevel,

--- 77,84 -----
		"<EnterWindow>: highlight() set()\n");
	popup_actions = XtParseTranslationTable(
		"<Btn1Down>: set() MenuPopup(menu)\n");
+ 	popdown_actions = XtParseTranslationTable(
+ 		"<Btn1Up>: MenuPopdown()\n");
  
	/**** menu bar ****/
	  menu_bar = XtCreateManagedWidget( "menu_bar", boxWidgetClass, toplevel,
***************
*** 95,100
	/**** popup menu ****/
	popupshell = XtCreatePopupShell("menu", overrideShellWidgetClass, toplevel,
		(Arg *)0, ZERO);
	  menu = XtCreateManagedWidget( "PopupMenu", boxWidgetClass, popupshell,
		(Arg *)0, ZERO);
	XtAddCallback(popupshell,

--- 97,103 -----
	/**** popup menu ****/
	popupshell = XtCreatePopupShell("menu", overrideShellWidgetClass, toplevel,
		(Arg *)0, ZERO);
+ 	XtOverrideTranslations(popupshell, popdown_actions);
	  menu = XtCreateManagedWidget( "PopupMenu", boxWidgetClass, popupshell,
		(Arg *)0, ZERO);
	XtAddCallback(popupshell,
========== end of patch =============
	Marvin Solomon
	Computer Sciences Department
	University of Wisconsin, Madison WI
	solomon@cs.wisc.edu or seismo!uwvax!solomon

v40f00@exunido.uucp (Franz-Josef Stewing) (07/26/88)

Hello,

Let us add some more on the guessing about how to create popup menus
with Xt:

With the help of the notes by Marvin Solomon, H.David Scarbro
and Robert Blumen we found following solution:

=====================================================================

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Cardinals.h>
#include <X11/Command.h>
#include <X11/Box.h>
#include <X11/Label.h>
#include <X11/Shell.h>
#include <stdio.h>

/* Command line options table.  Only resources are entered here...there is a
   pass over the remaining options after XtParseCommand is let loose. */

static XrmOptionDescRec options[] = {
{"-label",	"*label",	XrmoptionSepArg,	NULL}
};

Widget toplevel, menu_bar, button, popupshell, menu, popupbutton;


/*
 * Report the syntax for calling xlabel
 */
Syntax(call)
    char *call;
{
    fprintf( stderr, "Usage: %s\n", call );
}

/* ARGSUSED */
void callback(w, str, dummy)
Widget w;
char *str;
int dummy;
{
	printf("%s button\n", str);
	fflush(stdout);
}

/* ARGSUSED */
void done(w, str, dummy)
Widget w;
char *str;
int dummy;
{
	exit(0);
}

/* ARGSUSED */
void set_pos(w, event, params, num_params)
    Widget  w;
    XEvent *event;
    String params;
    Cardinal *num_params;
{
	Position x, y;
	Arg args[2];
	Window win;

        XTranslateCoordinates(XtDisplay(w),
                              XtWindow(w),
                              XDefaultRootWindow(XtDisplay(w)),
                              event->xbutton.x,
                              event->xbutton.y,
                              &x, &y, &win);

	XtMoveWidget(popupshell, x, y);


}


void main(argc, argv)
    unsigned int argc;
    char **argv;
{
	XtTranslations button_actions, popup_actions, popdown_actions;
	Arg arg;

        static XtActionsRec action_table[] = {
               {"set_pos", (XtActionProc)set_pos}  
        };
 

    XtAddActions(action_table, ONE);

    toplevel = XtInitialize( "popup_test", "XPopupTest",
			     options, XtNumber(options),
			     &argc, argv );

    if (argc != 1) Syntax(argv[0]);


	button_actions = XtParseTranslationTable(
		"<EnterWindow>: highlight() set()\n");
	popup_actions = XtParseTranslationTable(
		"<Btn1Down>: set() set_pos() MenuPopup(menu)\n");
	popdown_actions = XtParseTranslationTable(
		"<Btn1Up>: MenuPopdown()\n");

 	/**** menu bar ****/
    menu_bar = XtCreateManagedWidget( "menu_bar", boxWidgetClass, toplevel,
		(Arg *)0, ZERO);

	/**** menu bar items ****/
    button = XtCreateManagedWidget( "yes", commandWidgetClass, menu_bar,
		(Arg *)0, ZERO );
	XtAddCallback(button, XtNcallback, callback, (caddr_t)"yes");

    popupbutton =
		XtCreateManagedWidget( "ShowMenu", commandWidgetClass, menu_bar,
			(Arg *)0, ZERO );

	XtOverrideTranslations(popupbutton, popup_actions);


	/**** popup menu ****/
    popupshell = XtCreatePopupShell("menu", overrideShellWidgetClass, toplevel,
		(Arg *)0, ZERO);

	XtOverrideTranslations(popupshell, popdown_actions);

    menu = XtCreateManagedWidget( "PopupMenu", boxWidgetClass, popupshell,
		(Arg *)0, ZERO);


	/**** popup menu items ****/
	/* menu option 1 */
	button = XtCreateManagedWidget( "choice1", commandWidgetClass, menu,
		(Arg *)0, ZERO );
	XtAddCallback(button, XtNcallback, callback, (caddr_t)"button1");
	XtOverrideTranslations(button, button_actions);

	/* menu option 2 */
	button = XtCreateManagedWidget( "choice2", commandWidgetClass, menu,
		(Arg *)0, ZERO );
	XtAddCallback(button, XtNcallback, callback, (caddr_t)"button2");
	XtOverrideTranslations(button, button_actions);

	/* menu option 3 */
	button = XtCreateManagedWidget( "quit", commandWidgetClass, menu,
		(Arg *)0, ZERO );
	XtAddCallback(button, XtNcallback, done, (caddr_t)"done");
	XtOverrideTranslations(button, button_actions);
    
  
    
    XtRealizeWidget(toplevel);
    XtMainLoop();
}

==========================================================================

The main changes to Marvin's second program were
- changing the Callback function "set_menu_position" to	the function
  "set_pos" which was added to a translation table and
- using XtMoveWidget to let the menu popup at an appropiate place.

This programm should work properly, i.e. let menus disappear when 
releasing the button _and_ appear where you expect them to. (Of
course only with the change to the Toolkit function 
"XtConvertStringToBoolean" which David Scarbro suggested)

Since we are rather new at the Toolkit  (and didn't understand Chapter 7)
we expect our solution a bit clumsy and 
we hope for some suggestions how to improve it.

Lutz Wiggershaus
University of Dortmund,
Informatics IV.

email: wiggersh@balduin.ls4.informatik.uni-dortmund.de