(3)

NAME
XfwfPullDown

DESCRIPTION
A PullDown button looks like a normal button, but when it is clicked or pressed, a menu drops down. The menu can be specified in two ways: as a string or as a widget. If a string is given, it must be in the correct syntax for the XfwfTextMenu widget and a widget will be created automatically. If a widget is given instead, it must be a widget that is able to pop up: in other words: a Shell widget or a subclass of Shell. The most likely application of this widget is as a menu in a menu bar. A menu bar can be created, e.g., with a RowCol widget and a number of PullDown buttons. When the popup is passed in as a string in the menu resource, the widget will call the activate callback (see XfwfButton) when the user exits the menu with a valid selection. It will also call the changeSelection callback (see below) when the user moves from one menu item to the next. Neither of these callbacks will be called when the popup is passed in as a ready-made widget in the popup resource. Note however, that the call_data argument of the activate callback will contain the number of the selected item (0, 1, 2, etc.) and not the event, as is the case in an XfwfButton widget.

  Public variables
XfwfPullDown
Name | Class | Type | Default
XtNpopup | XtCPopup | Widget | NULL
XtNmenu | XtCMenu | String | NULL
XtNcascaded | XtCCascaded | Boolean | False
XtNhotkey | XtCHotkey | String | NULL
XtNmenuCursor | XtCMenuCursor | Cursor | "arrow"
XtNprepare | XtCPrepare | Callback | NULL
XtNchangeSelection | XtCChangeSelection | Callback | NULL

XtNpopup
    Two resources determine the menu. The popup resource can be pointed at the widget that should pop up when the button is pressed, but when the popup resource is NULL, the menu will be created from the menu resource. If the popup resource is set, it must be set to a widget that is (a subclass of) a Shell widget.

    Widget popup = NULL

XtNmenu
    When popup is NULL, the widget will be created from the textual description in menu. For the syntax of this string, see the XfwfTextMenu.

    String menu = NULL

XtNcascaded
    When the button is part of a dialog or menu, that has already caused a grab of the pointer, the resource cascaded should be True. If the button is inserted into an XfwfMenuBar, the resource is set to True automatically.

    Boolean cascaded = False

XtNhotkey
    The pull down button installs an event handler that catches all keyboard events for the whole application and checks for a special key combination. If that key is detected, the menu will be popped up. Thus, the menu can be used even if the keyboard focus is somewhere else. The hotkey resource is usually a string such as "Alt<Key>a", or "Ctrl<Key>f". When the hotkey resource is used, any accelerators will be ignored.

    String hotkey = NULL

XtNmenuCursor
    The cursor shape that is used in the menu can be set with the menuCursor resource. By default it is an arrow pointing to the upper right.

    Cursor menuCursor = <String>"arrow"

XtNprepare
    The prepare callback is invoked by the prepare action, normally just before the menu is popped up.

    <Callback> XtCallbackList prepare = NULL

XtNchangeSelection
    When the user moves from one item to another in the menu that was created from the menu resource, the changeSelection callback is called, with the number of the new selection as call_data. This callback is called even when the new selection is an inactive (grayed out) item. It is also called when the mouse leaves the menu, in which case the argument will be -1. This callback will notbe called when the menu is a ready-made widget, passed in via the popup resource.

    <Callback> XtCallbackList changeSelection = NULL

XtNframeWidth
    Since the most likely place for this type of button will be in a menubar, the default frame width is reset to zero.

     frameWidth = 0

XtNshrinkToFit
    The shrinkToFit resource is set to a new default of True.

     shrinkToFit = True
    XfwfButton
    Name | Class | Type | Default
    XtNactivate | XtCActivate | Callback | NULL
    XtNenter | XtCEnter | Callback | NULL
    XtNleave | XtCLeave | Callback | NULL
    XfwfLabel
    Name | Class | Type | Default
    XtNlabel | XtCLabel | String | NULL
    XtNtablist | XtCTablist | String | NULL
    XtNfont | XtCFont | FontStruct | XtDefaultFont
    XtNforeground | XtCForeground | Pixel | XtDefaultForeground
    XtNhlForeground | XtCHlForeground | Pixel | XtDefaultForeground
    XtNalignment | XtCAlignment | Alignment | 0
    XtNtopMargin | XtCTopMargin | Dimension | 2
    XtNbottomMargin | XtCBottomMargin | Dimension | 2
    XtNleftMargin | XtCLeftMargin | Dimension | 2
    XtNrightMargin | XtCRightMargin | Dimension | 2
    XtNshrinkToFit | XtCShrinkToFit | Boolean | False
    XtNrvStart | XtCRvStart | Int | 0
    XtNrvLength | XtCRvLength | Int | 0
    XtNhlStart | XtCHlStart | Int | 0
    XtNhlLength | XtCHlLength | Int | 0
    XfwfBoard
    Name | Class | Type | Default
    XtNabs_x | XtCAbs_x | Position | 0
    XtNrel_x | XtCRel_x | Float | "0.0"
    XtNabs_y | XtCAbs_y | Position | 0
    XtNrel_y | XtCRel_y | Float | "0.0"
    XtNabs_width | XtCAbs_width | Position | 0
    XtNrel_width | XtCRel_width | Float | "1.0"
    XtNabs_height | XtCAbs_height | Position | 0
    XtNrel_height | XtCRel_height | Float | "1.0"
    XtNhunit | XtCHunit | Float | "1.0"
    XtNvunit | XtCVunit | Float | "1.0"
    XtNlocation | XtCLocation | String | NULL
    XfwfFrame
    Name | Class | Type | Default
    XtNcursor | XtCCursor | Cursor | None
    XtNframeType | XtCFrameType | FrameType | XfwfRaised
    XtNframeWidth | XtCFrameWidth | Dimension | 0
    XtNouterOffset | XtCOuterOffset | Dimension | 0
    XtNinnerOffset | XtCInnerOffset | Dimension | 0
    XtNshadowScheme | XtCShadowScheme | ShadowScheme | XfwfAuto
    XtNtopShadowColor | XtCTopShadowColor | Pixel | compute_topcolor
    XtNbottomShadowColor | XtCBottomShadowColor | Pixel | compute_bottomcolor
    XtNtopShadowStipple | XtCTopShadowStipple | Bitmap | NULL
    XtNbottomShadowStipple | XtCBottomShadowStipple | Bitmap | NULL
    XfwfCommon
    Name | Class | Type | Default
    XtNtraversalOn | XtCTraversalOn | Boolean | True
    XtNhighlightThickness | XtCHighlightThickness | Dimension | 2
    XtNhighlightColor | XtCHighlightColor | Pixel | XtDefaultForeground
    XtNhighlightPixmap | XtCHighlightPixmap | Pixmap | None
    XtNnextTop | XtCNextTop | Callback | NULL
    XtNuserData | XtCUserData | Pointer | NULL
    Composite
    Name | Class | Type | Default
    XtNchildren | XtCChildren | WidgetList | NULL
    insertPosition | XtCInsertPosition | XTOrderProc | NULL
    numChildren | XtCNumChildren | Cardinal | 0
    Core
    Name | Class | Type | Default
    XtNx | XtCX | Position | 0
    XtNy | XtCY | Position | 0
    XtNwidth | XtCWidth | Dimension | 0
    XtNheight | XtCHeight | Dimension | 0
    borderWidth | XtCBorderWidth | Dimension | 0
    XtNcolormap | XtCColormap | Colormap | NULL
    XtNdepth | XtCDepth | Int | 0
    destroyCallback | XtCDestroyCallback | XTCallbackList | NULL
    XtNsensitive | XtCSensitive | Boolean | True
    XtNtm | XtCTm | XTTMRec | NULL
    ancestorSensitive | XtCAncestorSensitive | Boolean | False
    accelerators | XtCAccelerators | XTTranslations | NULL
    borderColor | XtCBorderColor | Pixel | 0
    borderPixmap | XtCBorderPixmap | Pixmap | NULL
    background | XtCBackground | Pixel | 0
    backgroundPixmap | XtCBackgroundPixmap | Pixmap | NULL
    mappedWhenManaged | XtCMappedWhenManaged | Boolean | True
    XtNscreen | XtCScreen | Screen * | NULL

  Translations
<Btn1Down>: prepare() open_menu()
Button1<Enter>: prepare() open_menu() enter()
<Key>Return: prepare() open_menu()
<Btn1Up>: popdown_maybe()
<EnterNotify>: enter()
<LeaveNotify>: leave()

  Actions

note
    note is for debugging only.
    void note($, XEvent* event, String* params, Cardinal* num_params)
    {
        static String Xeventname[] = {
    	"", "", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
    	"MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn",
    	"FocusOut", "KeymapNotify", "Expose", "GraphicsExpose",
    	"NoExpose", "VisisbilityNotify", "CreateNotify", "DestroyNotify",
    	"UnmapNotify", "MapNotify", "MapRequest", "ReparentNotify",
    	"ConfigureNotify", "ConfigureRequest", "GravityNotify",
    	"ResizeRequest", "CirculateNotify", "CirculateRequest",
    	"PropertyNotify", "SelectionClear", "SelectionRequest",
    	"SelectionNotify", "ColormapNotify", "ClientMessage",
    	"MappingNotify", };
        (void) fprintf(stderr, "%s received %s\n",
    		   XtName($), Xeventname[event->type]);
    }

prepare
    The prepare action just calls the prepare callback functions, passing the XEvent pointer in the call_data argument.
    void prepare($, XEvent* event, String* params, Cardinal* num_params)
    {
        XtCallCallbackList($, $prepare, event);
    }

open_menu
    The open_menu action computes the coordinates of the menu, such that it will appear directly below the button. A local grab is added, so that the button itself will continu to receive events, even after the popped up menu has grabbed the pointer and keyboard. The accelerators of the menu are installed in the pulldown button, which will continue to have the focus. It then pops up the menu.
    void open_menu($, XEvent* event, String* params, Cardinal* num_params)
    {
        Position x, y;
        if ($popup == NULL) return;
        /* fprintf(stderr, "open_menu \"%s\"\n", XtName($)); */
        XtTranslateCoords($, 0, $height, x, y);
        XtVaSetValues($popup, XtNx, x, XtNy, y, NULL);
        if (! XfwfCallProcessMenu(XtParent($), $popup, $menuCursor))
    	XtPopupSpringLoaded($popup);
    }

popdown_maybe
    The podown_maybe action only pops down the menu when the pointer is outside the button. When the pointer is on the button, the menu stays posted.
    void popdown_maybe($, XEvent* event, String* params, Cardinal* num_params)
    {
        Position x, y;
        switch (event->type) {
        case ButtonRelease:
        case ButtonPress:
    	x = event->xbutton.x;
    	y = event->xbutton.y;
    	break;
        case MotionNotify:
    	x = event->xmotion.x;
    	y = event->xmotion.y;
    	break;
        case EnterNotify:
        case LeaveNotify:
    	x = event->xcrossing.x;
    	y = event->xcrossing.y;
    	break;
        default:
    	XtAppWarning(XtWidgetToApplicationContext($),
    		   "Incorrect event for popdown_maybe()");
        }
        if (x < 0 || $width <= x || y < 0 || $height <= y) XtPopdown($popup);
    }

Importss
incl
 <Xfwf/TextMenu.h>
incl
 <Xfwf/MenuBar.h>
incl
 <stdio.h>

  Private variables
When the popup menu is created from the menu resource, the variable own_popup is set to True. In this way it is possible to destroy the popup again when it is no longer needed.

Boolean  own_popup

  Methods
class_initialize()
{
    XtRegisterGrabAction(open_menu, True, ButtonPressMask |
			 ButtonReleaseMask | EnterWindowMask |
			 LeaveWindowMask | PointerMotionMask,
			 GrabModeAsync, GrabModeAsync);
}
Both the initialize and set_values methods need to create a new menu widget, if the menu resource is set and the popup resource is not. A utility function is used to do the actual work. If both popup and menu are set, popup is used and menu is ignored.
initialize(Widget  request, $, ArgList  args, Cardinal * num_args)
{
    if ($menu) $menu = XtNewString($menu);
    if (! $popup  $menu) {
	$popup = XtVaCreatePopupShell
	    ("menu", xfwfTextMenuWidgetClass, $, XtNmenu, $menu, XtNcursor,
	     $menuCursor, NULL);
	$own_popup = True;
	XtAddCallback($popup, XtNactivate, activate_cb, $);
	XtAddCallback($popup, XtNchangeSelection, change_cb, $);
    } else
	$own_popup = False;
    if (! $popup  $hotkey) create_accelerator($);
}
set_values is similar. Changes in these resources never cause a redraw, so the method always returns False. A change in menu is only recognized if there is no change in popup at the same time.
Boolean  set_values(Widget  old, Widget  request, $, ArgList  args, Cardinal * num_args)
{
    if ($old$menu != $menu) {
	XtFree($old$menu);
	$menu = XtNewString($menu);
    }
    if ($old$popup != $popup) {
	if ($own_popup  $old$popup) XtDestroyWidget($old$popup);
	$own_popup = False;
    } else if ($old$menu != $menu) {
	$popup = XtVaCreatePopupShell
	    ("menu", xfwfTextMenuWidgetClass, $, XtNmenu, $menu, XtNcursor,
	     $menuCursor, NULL);
	$own_popup = True;
	XtAddCallback($popup, XtNactivate, activate_cb, $);
	XtAddCallback($popup, XtNchangeSelection, change_cb, $);
    }
    if ($old$menuCursor != $menuCursor)
	if ($popup) XtVaSetValues($popup, XtNcursor, $menuCursor, NULL);
    if ($old$hotkey != $hotkey)
	create_accelerator($);
    return False;
}

Utilities
create_accelerator takes the hotkey and expands it into a correct accelerators resource.
create_accelerator($)
{
    char s[100];
    XtAccelerators a;
    (void) strcpy(s, $hotkey);
    (void) strcat(s, ": prepare() open_menu()");
    a = XtParseAcceleratorTable(s);
    XtVaSetValues($, XtNaccelerators, a, NULL);
}
The activate_cb routine is attached to the activate callback of the menu, but only if the menu is created by the PullDown widget itself. It simply calls the PullDown widget's activate callback. The call_data argument contains the number of the selected menu item.
activate_cb(Widget  menu, XtPointer  client_data, XtPointer  call_data)
{
    Widget $ = (Widget) client_data;
    XtCallCallbackList($, $activate, call_data);
}
The change_cb routine is attached to the changeSelection callback of the menu, but only if the menu is created by the PullDown widget itself. It simply calls the PullDown widget's changeSelection callback. The call_data argument contains the number of the selected menu item.
change_cb(Widget  menu, XtPointer  client_data, XtPointer  call_data)
{
    Widget $ = (Widget) client_data;
    XtCallCallbackList($, $changeSelection, call_data);
}