gecko-dev/cmd/xfe/XfeWidgets/Xfe/Cascade.c

1320 строки
36 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
/*----------------------------------------------------------------------*/
/* */
/* Name: <Xfe/Cascade.c> */
/* Description: XfeCascade widget source. */
/* Author: Ramiro Estrugo <ramiro@netscape.com> */
/* */
/*----------------------------------------------------------------------*/
#include <Xfe/CascadeP.h>
#include <Xfe/ManagerP.h>
#include <Xm/RowColumnP.h>
/*----------------------------------------------------------------------*/
/* */
/* Warnings and messages */
/* */
/*----------------------------------------------------------------------*/
#define MESSAGE1 "Widget is not a XfeButton."
#define MESSAGE2 "XmNsubMenuId is a read-only resource."
#define MESSAGE3 "XmNpoppedUp is a read-only resource."
#define MESSAGE4 "XmNtorn is a read-only resource."
#define SUB_MENU_ID_NAME "PopupMenu"
/*
* In order to get popup menus working as if they were pulldown menus,
* we need to trick motif into ignoring the event up until the point when
* we know it is ok to pop it up. This will happen either right away,
* or after the mapping delay timeout expires (based on the value of
* XmNmappingDelay). We accomplish this by swapping these two values for
* the popup's XmNmenuPost resource. This scheme seems pretty robust. If
* a user happens to have a 7 button mouse and they hit Button7 on the
* widget, then a (harmless ?) passive grab will occur. We make sure the
* grab does not screw the user by installing a ButtonPress event handler
* on the widget and ungrabing the pointer just in case.
*/
#define POPUP_IGNORE "<Btn3Down>"
#define POPUP_ACCEPT "<Btn1Down>"
/* Tear off model for cascade */
#define TEAR_MODEL(cp) \
((cp)->allow_tear_off ? XmTEAR_OFF_ENABLED : XmTEAR_OFF_DISABLED)
/* Popup shell */
#define POPUP_SHELL(cp) \
(XtParent((cp)->sub_menu_id))
/*----------------------------------------------------------------------*/
/* */
/* Core class methods */
/* */
/*----------------------------------------------------------------------*/
static void ClassInitialize (void);
static void Initialize (Widget,Widget,ArgList,Cardinal *);
static void Destroy (Widget);
static Boolean SetValues (Widget,Widget,Widget,ArgList,Cardinal *);
/*----------------------------------------------------------------------*/
/* */
/* XfePrimitive class methods */
/* */
/*----------------------------------------------------------------------*/
static void PreferredGeometry (Widget,Dimension *,Dimension *);
static void DrawComponents (Widget,XEvent *,Region,XRectangle *);
static void LayoutComponents (Widget);
/*----------------------------------------------------------------------*/
/* */
/* XfeCascade action procedures */
/* */
/*----------------------------------------------------------------------*/
static void Arm (Widget,XEvent *,char **,Cardinal *);
static void Activate (Widget,XEvent *,char **,Cardinal *);
static void Disarm (Widget,XEvent *,char **,Cardinal *);
static void Post (Widget,XEvent *,char **,Cardinal *);
static void Unpost (Widget,XEvent *,char **,Cardinal *);
/*----------------------------------------------------------------------*/
/* */
/* Misc XfeCascade functions */
/* */
/*----------------------------------------------------------------------*/
static void DelayTimeout (XtPointer,XtIntervalId *);
static void DrawCascadeArrow (Widget,XEvent *,Region,XRectangle *);
static void LayoutCascadeArrow (Widget);
static void GetPostPosition (Widget,Position *,Position *);
static void InvokeTearCallback (Widget,XEvent *,Boolean);
/*----------------------------------------------------------------------*/
/* */
/* SubMenu event handlers and callbacks */
/* */
/*----------------------------------------------------------------------*/
static void SubMenuEH (Widget,XtPointer,XEvent *,Boolean *);
static void SubMenuTearCB (Widget,XtPointer,XtPointer);
static void UnGrabEH (Widget,XtPointer,XEvent *,Boolean *);
/*----------------------------------------------------------------------*/
/* */
/* Rep type registration functions */
/* */
/*----------------------------------------------------------------------*/
static void CascadeRegisterRepTypes(void);
/*----------------------------------------------------------------------*/
/* */
/* XfeCascade Resources */
/* */
/*----------------------------------------------------------------------*/
static XtResource resources[] =
{
/* Callback resources */
{
XmNcascadingCallback,
XmCCallback,
XmRCallback,
sizeof(XtCallbackList),
XtOffsetOf(XfeCascadeRec,xfe_cascade . cascading_callback),
XmRImmediate,
(XtPointer) NULL
},
{
XmNpopdownCallback,
XmCCallback,
XmRCallback,
sizeof(XtCallbackList),
XtOffsetOf(XfeCascadeRec,xfe_cascade . popdown_callback),
XmRImmediate,
(XtPointer) NULL
},
{
XmNpopupCallback,
XmCCallback,
XmRCallback,
sizeof(XtCallbackList),
XtOffsetOf(XfeCascadeRec,xfe_cascade . popup_callback),
XmRImmediate,
(XtPointer) NULL
},
{
XmNsubmenuTearCallback,
XmCCallback,
XmRCallback,
sizeof(XtCallbackList),
XtOffsetOf(XfeCascadeRec,xfe_cascade . submenu_tear_callback),
XmRImmediate,
(XtPointer) NULL
},
/* Sub menu resources */
{
XmNmappingDelay,
XmCMappingDelay,
XmRInt,
sizeof(int),
XtOffsetOf(XfeCascadeRec , xfe_cascade . mapping_delay),
XmRImmediate,
(XtPointer) 250
},
{
XmNsubMenuId,
XmCReadOnly,
XmRWidget,
sizeof(Widget),
XtOffsetOf(XfeCascadeRec , xfe_cascade . sub_menu_id),
XmRImmediate,
(XtPointer) NULL
},
{
XmNsubMenuAlignment,
XmCSubMenuAlignment,
XmRAlignment,
sizeof(unsigned char),
XtOffsetOf(XfeCascadeRec , xfe_cascade . sub_menu_alignment),
XmRImmediate,
(XtPointer) XmALIGNMENT_BEGINNING
},
{
XmNsubMenuLocation,
XmCSubMenuLocation,
XmRLocationType,
sizeof(unsigned char),
XtOffsetOf(XfeCascadeRec , xfe_cascade . sub_menu_location),
XmRImmediate,
(XtPointer) XmLOCATION_SOUTH
},
{
XmNpoppedUp,
XmCReadOnly,
XmRBoolean,
sizeof(Boolean),
XtOffsetOf(XfeCascadeRec , xfe_cascade . popped_up),
XmRImmediate,
(XtPointer) False
},
{
XmNmatchSubMenuWidth,
XmCMatchSubMenuWidth,
XmRBoolean,
sizeof(Boolean),
XtOffsetOf(XfeCascadeRec , xfe_cascade . match_sub_menu_width),
XmRImmediate,
(XtPointer) False
},
/* Tear resources */
{
XmNtorn,
XmCReadOnly,
XmRBoolean,
sizeof(Boolean),
XtOffsetOf(XfeCascadeRec , xfe_cascade . torn),
XmRImmediate,
(XtPointer) False
},
{
XmNallowTearOff,
XmCBoolean,
XmRBoolean,
sizeof(Boolean),
XtOffsetOf(XfeCascadeRec , xfe_cascade . allow_tear_off),
XmRImmediate,
(XtPointer) False
},
{
XmNtornShellTitle,
XmCTornShellTitle,
XmRString,
sizeof(String),
XtOffsetOf(XfeCascadeRec , xfe_cascade . torn_shell_title),
XmRImmediate,
(XtPointer) NULL
},
/* Cascade arrow resources */
{
XmNcascadeArrowDirection,
XmCCascadeArrowDirection,
XmRArrowDirection,
sizeof(unsigned char),
XtOffsetOf(XfeCascadeRec , xfe_cascade . cascade_arrow_direction),
XmRImmediate,
(XtPointer) XmARROW_DOWN
},
{
XmNcascadeArrowLocation,
XmCCascadeArrowLocation,
XmRLocationType,
sizeof(unsigned char),
XtOffsetOf(XfeCascadeRec , xfe_cascade . cascade_arrow_location),
XmRImmediate,
(XtPointer) XmLOCATION_SOUTH_WEST
},
{
XmNcascadeArrowHeight,
XmCCascadeArrowHeight,
XmRVerticalDimension,
sizeof(Dimension),
XtOffsetOf(XfeCascadeRec , xfe_cascade . cascade_arrow_rect . height),
XmRImmediate,
(XtPointer) 3
},
{
XmNcascadeArrowWidth,
XmCCascadeArrowWidth,
XmRHorizontalDimension,
sizeof(Dimension),
XtOffsetOf(XfeCascadeRec , xfe_cascade . cascade_arrow_rect . width),
XmRImmediate,
(XtPointer) 5
},
{
XmNdrawCascadeArrow,
XmCDrawCascadeArrow,
XmRBoolean,
sizeof(Boolean),
XtOffsetOf(XfeCascadeRec , xfe_cascade . draw_cascade_arrow),
XmRImmediate,
(XtPointer) False
},
/* Sub menu resources */
};
/*----------------------------------------------------------------------*/
/* */
/* XfeButton Synthetic Resources */
/* */
/*----------------------------------------------------------------------*/
static XmSyntheticResource syn_resources[] =
{
{
XmNcascadeArrowHeight,
sizeof(Dimension),
XtOffsetOf(XfeCascadeRec , xfe_cascade . cascade_arrow_rect . height),
_XmFromVerticalPixels,
_XmToVerticalPixels
},
{
XmNcascadeArrowWidth,
sizeof(Dimension),
XtOffsetOf(XfeCascadeRec , xfe_cascade . cascade_arrow_rect . width),
_XmFromHorizontalPixels,
_XmToHorizontalPixels
},
};
/*----------------------------------------------------------------------*/
/* */
/* XfeButton actions */
/* */
/*----------------------------------------------------------------------*/
static XtActionsRec actions[] =
{
{ "Arm", Arm },
{ "Activate", Activate },
{ "Disarm", Disarm },
};
/*----------------------------------------------------------------------*/
/* */
/* XfeCascade widget class record initialization */
/* */
/*----------------------------------------------------------------------*/
_XFE_WIDGET_CLASS_RECORD(cascade,Cascade) =
{
{
/* Core Part */
(WidgetClass) &xfeButtonClassRec, /* superclass */
"XfeCascade", /* class_name */
sizeof(XfeCascadeRec), /* widget_size */
ClassInitialize, /* class_initialize */
NULL, /* class_part_initialize*/
FALSE, /* class_inited */
Initialize, /* initialize */
NULL, /* initialize_hook */
XtInheritRealize, /* realize */
actions, /* actions */
XtNumber(actions), /* num_actions */
(XtResource *)resources, /* resources */
XtNumber(resources), /* num_resources */
NULLQUARK, /* xrm_class */
TRUE, /* compress_motion */
XtExposeCompressMaximal, /* compress_exposure */
TRUE, /* compress_enterleave */
FALSE, /* visible_interest */
Destroy, /* destroy */
XtInheritResize, /* resize */
XtInheritExpose, /* expose */
SetValues, /* set_values */
NULL, /* set_values_hook */
XtInheritSetValuesAlmost, /* set_values_almost */
NULL, /* get_values_hook */
NULL, /* accept_focus */
XtVersion, /* version */
NULL, /* callback_private */
XtInheritTranslations, /* tm_table */
XtInheritQueryGeometry, /* query_geometry */
XtInheritDisplayAccelerator, /* display accel */
NULL, /* extension */
},
/* XmPrimitive Part */
{
XmInheritBorderHighlight, /* border_highlight */
XmInheritBorderUnhighlight, /* border_unhighlight */
XtInheritTranslations, /* translations */
XmInheritArmAndActivate, /* arm_and_activate */
(XmSyntheticResource *)syn_resources, /* syn resources */
XtNumber(syn_resources), /* num syn_resources */
NULL, /* extension */
},
/* XfePrimitive Part */
{
XfeInheritBitGravity, /* bit_gravity */
PreferredGeometry, /* preferred_geometry */
XfeInheritUpdateBoundary, /* update_boundary */
NULL, /* prepare_components */
LayoutComponents, /* layout_components */
XfeInheritDrawBackground, /* draw_background */
XfeInheritDrawShadow, /* draw_shadow */
DrawComponents, /* draw_components */
NULL, /* extension */
},
/* XfeLabel Part */
{
XfeInheritLayoutString, /* layout_string */
XfeInheritDrawString, /* draw_string */
XfeInheritDrawSelection, /* draw_selection */
XfeInheritGetLabelGC, /* get_label_gc */
XfeInheritGetSelectionGC, /* get_selection_gc */
NULL, /* extension */
},
/* XfeButton Part */
{
XfeInheritLayoutPixmap, /* layout_pixmap */
XfeInheritDrawPixmap, /* draw_pixmap */
XfeInheritDrawAccentBorder, /* draw_accent_border */
XfeInheritDrawUnderline, /* draw_underline */
XfeInheritArmTimeout, /* arm_timeout */
NULL, /* extension */
},
/* XfeCascade Part */
{
NULL, /* extension */
},
};
/*----------------------------------------------------------------------*/
/* */
/* xfeCascadeWidgetClass declaration. */
/* */
/*----------------------------------------------------------------------*/
_XFE_WIDGET_CLASS(cascade,Cascade);
/*----------------------------------------------------------------------*/
/* */
/* Rep type registration functions */
/* */
/*----------------------------------------------------------------------*/
static void
CascadeRegisterRepTypes(void)
{
static String location_names[] =
{
"location_east",
"location_north",
"location_north_east",
"location_north_west",
"location_south",
"location_south_east",
"location_south_west",
"location_west",
NULL
};
XfeRepTypeRegister(XmRLocationType,location_names);
}
/*----------------------------------------------------------------------*/
/*----------------------------------------------------------------------*/
/* */
/* Core class methods */
/* */
/*----------------------------------------------------------------------*/
static void
ClassInitialize()
{
/* Register XfeCascade representation types */
CascadeRegisterRepTypes();
}
/*----------------------------------------------------------------------*/
static void
Initialize(Widget rw,Widget nw,ArgList args,Cardinal *nargs)
{
XfeCascadePart * cp = _XfeCascadePart(nw);
Arg xargs[3];
Cardinal n;
/* Make sure rep types are ok */
XfeRepTypeCheck(nw,XmRLocationType,&cp->cascade_arrow_location,
XmLOCATION_SOUTH_WEST);
XfeRepTypeCheck(nw,XmRArrowDirection,&cp->cascade_arrow_direction,
XmARROW_DOWN);
XfeRepTypeCheck(nw,XmRAlignment,&cp->sub_menu_alignment,
XmALIGNMENT_BEGINNING);
XfeRepTypeCheck(nw,XmRLocationType,&cp->sub_menu_location,
XmLOCATION_SOUTH);
/* Create popup using our probably non-standard visual/colormap/depth */
n = 0;
XtSetArg(xargs[n],XmNcolormap, XfeColormap(nw)); n++;
XtSetArg(xargs[n],XmNdepth, XfeDepth(nw)); n++;
XtSetArg(xargs[n],XmNvisual, XfeVisual(nw)); n++;
cp->sub_menu_id = XmCreatePopupMenu(nw,SUB_MENU_ID_NAME,xargs,n);
XfeSetValue(cp->sub_menu_id,XmNmenuPost,(XtArgVal) POPUP_IGNORE);
/* Add ungrab event handler to cascade button */
XtInsertEventHandler(nw,
ButtonPressMask,
True,
UnGrabEH,
(XtPointer) nw,
XtListTail);
/* Add map/unmap event handler to sub menu's parent shell */
XtAddEventHandler(POPUP_SHELL(cp),
StructureNotifyMask,
True,
SubMenuEH,
(XtPointer) nw);
/* Add tear callbacks top submenu */
XtAddCallback(cp->sub_menu_id,
XmNtearOffMenuActivateCallback,
SubMenuTearCB,
(XtPointer) nw);
XtAddCallback(cp->sub_menu_id,
XmNtearOffMenuDeactivateCallback,
SubMenuTearCB,
(XtPointer) nw);
/* Initialize private members */
cp->delay_timer_id = 0;
cp->default_menu_cursor = XmGetMenuCursor(XtDisplay(nw));
/* Update the tear model */
XfeSetValue(cp->sub_menu_id,XmNtearOffModel,TEAR_MODEL(cp));
/* Update the torn shell title */
if (cp->torn_shell_title)
{
cp->torn_shell_title = (String) XtNewString(cp->torn_shell_title);
}
else
{
cp->torn_shell_title = (String) XtNewString(XtName(nw));
}
XfeSetValue(POPUP_SHELL(cp),XmNtitle,(XtArgVal)cp->torn_shell_title);
/* Finish of initialization */
_XfePrimitiveChainInitialize(rw,nw,xfeCascadeWidgetClass);
}
/*----------------------------------------------------------------------*/
static void
Destroy(Widget w)
{
XfeCascadePart * cp = _XfeCascadePart(w);
/* Free the torn shell title if needed */
if (cp->torn_shell_title)
{
XtFree(cp->torn_shell_title);
}
/* Remove all CallBacks */
/* XtRemoveAllCallbacks(w,XmNpopdownCallback); */
/* XtRemoveAllCallbacks(w,XmNpopupCallback); */
/* XtRemoveAllCallbacks(w,XmNcascadingCallback); */
}
/*----------------------------------------------------------------------*/
static Boolean
SetValues(Widget ow,Widget rw,Widget nw,ArgList args,Cardinal *nargs)
{
XfeCascadePart * np = _XfeCascadePart(nw);
XfeCascadePart * op = _XfeCascadePart(ow);
/* sub_menu_id */
if (np->sub_menu_id != op->sub_menu_id)
{
np->sub_menu_id = op->sub_menu_id;
_XfeWarning(nw,MESSAGE2);
}
/* popped_up */
if (np->popped_up != op->popped_up)
{
np->popped_up = op->popped_up;
_XfeWarning(nw,MESSAGE3);
}
/* torn */
if (np->torn != op->torn)
{
np->torn = op->torn;
_XfeWarning(nw,MESSAGE4);
}
/* allow_tear_off */
if (np->allow_tear_off != op->allow_tear_off)
{
XfeSetValue(np->sub_menu_id,XmNtearOffModel,TEAR_MODEL(np));
}
/* torn_shell_title */
if (np->torn_shell_title != op->torn_shell_title)
{
if (op->torn_shell_title)
{
XtFree(op->torn_shell_title);
}
if (np->torn_shell_title)
{
np->torn_shell_title = (String) XtNewString(np->torn_shell_title);
}
else
{
np->torn_shell_title = (String) XtNewString(" ");
}
XfeSetValue(POPUP_SHELL(np),XmNtitle,(XtArgVal) np->torn_shell_title);
}
/* sub_menu_alignment */
if (np->sub_menu_alignment != op->sub_menu_alignment)
{
/* Make sure the new sub menu alignment is ok */
XfeRepTypeCheck(nw,XmRAlignment,&np->sub_menu_alignment,
XmALIGNMENT_BEGINNING);
_XfeConfigFlags(nw) |= XfeConfigExpose;
}
/* sub_menu_location */
if (np->sub_menu_location != op->sub_menu_location)
{
/* Make sure the new sub menu location is ok */
XfeRepTypeCheck(nw,XmRLocationType,&np->sub_menu_location,
XmLOCATION_SOUTH);
}
/* cascade_arrow_location */
if (np->cascade_arrow_location != op->cascade_arrow_location)
{
/* Make sure the new cascade arrow location is ok */
XfeRepTypeCheck(nw,XmRLocationType,&np->cascade_arrow_location,
XmLOCATION_SOUTH_WEST);
}
return _XfePrimitiveChainSetValues(ow,rw,nw,xfeCascadeWidgetClass);
}
/*----------------------------------------------------------------------*/
/*----------------------------------------------------------------------*/
/* */
/* XfePrimitive methods */
/* */
/*----------------------------------------------------------------------*/
static void
PreferredGeometry(Widget w,Dimension *width,Dimension *height)
{
XfeCascadePart * cp = _XfeCascadePart(w);
XfeButtonWidgetClass bwc = (XfeButtonWidgetClass) xfeButtonWidgetClass;
/* */
(*bwc->xfe_primitive_class.preferred_geometry)(w,width,height);
/* If the sub menu has children, use its width if needed */
if (_XfeIsAlive(cp->sub_menu_id) &&
_XfemNumChildren(cp->sub_menu_id) &&
cp->match_sub_menu_width)
{
*width = _XfeWidth(cp->sub_menu_id);
}
}
/*----------------------------------------------------------------------*/
static void
LayoutComponents(Widget w)
/*----------------------------------------------------------------------*/
{
/* Invoke layout_string method */
_XfeLabelLayoutString(w);
/* Invoke layout_pixmap method */
_XfeButtonLayoutPixmap(w);
/* Layout the cascade arrow */
LayoutCascadeArrow(w);
}
/*----------------------------------------------------------------------*/
static void
DrawComponents(Widget w,XEvent *event,Region region,XRectangle * clip_rect)
{
/* Invoke draw_selection method */
_XfeLabelDrawSelection(w,event,region,clip_rect);
/* Invoke draw_string method */
_XfeLabelDrawString(w,event,region,clip_rect);
/* Invoke draw_pixmap method */
_XfeButtonDrawPixmap(w,event,region,clip_rect);
/* Invoke draw_border method */
_XfeButtonDrawAccentBorder(w,event,region,clip_rect);
/* Invoke draw_underline method */
_XfeButtonDrawUnderline(w,event,region,clip_rect);
/* Draw Cascade Arrow */
DrawCascadeArrow(w,event,region,clip_rect);
}
/*----------------------------------------------------------------------*/
/*----------------------------------------------------------------------*/
/* */
/* Misc XfeCascade functions */
/* */
/*----------------------------------------------------------------------*/
static void
DelayTimeout(XtPointer client_data,XtIntervalId * id)
{
Widget w = (Widget) client_data;
XfeCascadePart * cp = _XfeCascadePart(w);
XfeButtonPart * bp = _XfeButtonPart(w);
/* If we are still armed and the pointer remains in the button */
if (bp->armed && _XfePointerInside(w))
{
Post(w,NULL,NULL,NULL);
}
/* Reset the timer */
cp->delay_timer_id = 0;
}
/*----------------------------------------------------------------------*/
static void
DrawCascadeArrow(Widget w,XEvent *event,Region region,XRectangle * clip_rect)
{
XfeCascadePart * cp = _XfeCascadePart(w);
XfeButtonPart * bp = _XfeButtonPart(w);
Dimension offset;
/* Make sure the cascade arrow is actually needed before drawing it */
if (!cp->draw_cascade_arrow ||
!cp->cascade_arrow_rect.height ||
!cp->cascade_arrow_rect.width)
{
return;
}
/* Compute the arming offset */
offset = bp->armed ? bp->arm_offset : 0;
/* Draw the arrow as an XmArrowButton would */
if (bp->emulate_motif)
{
XfeDrawMotifArrow(XtDisplay(w),
_XfePrimitiveDrawable(w),
_XfeTopShadowGC(w),
_XfeTopShadowGC(w),
_XfeBackgroundGC(w),
cp->cascade_arrow_rect.x + offset,
cp->cascade_arrow_rect.y + offset,
cp->cascade_arrow_rect.width,
cp->cascade_arrow_rect.height,
cp->cascade_arrow_direction,
2, /* hard coded to be compatible with Motif */
bp->armed);
}
/* Draw the arrow as an XfeButton would */
else
{
XfeDrawArrow(XtDisplay(w),
_XfePrimitiveDrawable(w),
_XfeLabelGetLabelGC(w),
cp->cascade_arrow_rect.x + offset,
cp->cascade_arrow_rect.y + offset,
cp->cascade_arrow_rect.width,
cp->cascade_arrow_rect.height,
cp->cascade_arrow_direction);
}
}
/*----------------------------------------------------------------------*/
static void
LayoutCascadeArrow(Widget w)
{
XfeCascadePart * cp = _XfeCascadePart(w);
XfeButtonPart * bp = _XfeButtonPart(w);
XRectangle bound;
/* Make sure the cascade arrow has some dimensions */
if (!cp->cascade_arrow_rect.height || !cp->cascade_arrow_rect.width)
{
return;
}
/* If the button layout has a pixmap, place the arrow there */
if ((bp->button_layout != XmBUTTON_LABEL_ONLY) &&
bp->pixmap_rect.width && bp->pixmap_rect.height)
{
XfeRectSet(&bound,
bp->pixmap_rect.x,
bp->pixmap_rect.y,
bp->pixmap_rect.width,
bp->pixmap_rect.height);
}
else
{
XfeRectSet(&bound,
_XfeBoundaryX(w),
_XfeBoundaryY(w),
_XfeBoundaryWidth(w),
_XfeBoundaryHeight(w));
}
/* Determine the horizontal layout of the arrow */
switch(cp->cascade_arrow_location)
{
case XmLOCATION_NORTH_EAST:
case XmLOCATION_SOUTH_EAST:
case XmLOCATION_EAST:
cp->cascade_arrow_rect.x = bound.x - cp->cascade_arrow_rect.width;
break;
case XmLOCATION_NORTH:
case XmLOCATION_SOUTH:
cp->cascade_arrow_rect.x =
(bound.width - cp->cascade_arrow_rect.width) / 2 +
bound.x;
break;
case XmLOCATION_SOUTH_WEST:
case XmLOCATION_WEST:
case XmLOCATION_NORTH_WEST:
cp->cascade_arrow_rect.x =
bound.width - cp->cascade_arrow_rect.width +
bound.x;
break;
}
/* Determine the vertical layout of the arrow */
switch(cp->cascade_arrow_location)
{
case XmLOCATION_NORTH:
case XmLOCATION_NORTH_EAST:
case XmLOCATION_NORTH_WEST:
cp->cascade_arrow_rect.y =
bound.y;
break;
case XmLOCATION_EAST:
case XmLOCATION_WEST:
cp->cascade_arrow_rect.y =
(bound.height - cp->cascade_arrow_rect.height) / 2 +
bound.y;
break;
case XmLOCATION_SOUTH:
case XmLOCATION_SOUTH_EAST:
case XmLOCATION_SOUTH_WEST:
cp->cascade_arrow_rect.y =
bound.height - cp->cascade_arrow_rect.height +
bound.y;
break;
}
}
/*----------------------------------------------------------------------*/
static void
GetPostPosition(Widget w,Position * x_out,Position * y_out)
{
XfeCascadePart * cp = _XfeCascadePart(w);
XfeButtonPart * bp = _XfeButtonPart(w);
Dimension deco_offset;
Position x;
Position y;
Position root_x = XfeRootX(w);
Position root_y = XfeRootY(w);
/* Position the menu below the button (like pulldown menus do) */
deco_offset = _XfeHighlightThickness(w);
if (bp->raise_on_enter)
{
deco_offset += bp->accent_border_thickness;
}
switch(cp->sub_menu_alignment)
{
case XmALIGNMENT_BEGINNING:
x = root_x + deco_offset;
break;
case XmALIGNMENT_CENTER:
x = root_x + (_XfeWidth(w) - _XfeWidth(cp->sub_menu_id)) / 2;
break;
case XmALIGNMENT_END:
x = root_x + _XfeWidth(w) - _XfeWidth(cp->sub_menu_id) - deco_offset;
break;
}
y = root_y + _XfeHeight(w) - deco_offset;
if (x_out)
{
*x_out = x;
}
if (y_out)
{
*y_out = y;
}
}
/*----------------------------------------------------------------------*/
static void
InvokeTearCallback(Widget w,XEvent * event,Boolean torn)
{
XfeCascadePart * cp = _XfeCascadePart(w);
/* Invoke the callbacks only if needed */
if (cp->submenu_tear_callback)
{
XfeSubmenuTearCallbackStruct cbs;
cbs.event = event;
cbs.reason = XmCR_SUBMENU_TEAR;
cbs.torn = torn;
/* Flush the display */
XFlush(XtDisplay(w));
/* Invoke the Callback List */
XtCallCallbackList(w,cp->submenu_tear_callback,&cbs);
}
}
/*----------------------------------------------------------------------*/
/*----------------------------------------------------------------------*/
/* */
/* XfeCascade action procedures */
/* */
/*----------------------------------------------------------------------*/
static void
Arm(Widget w,XEvent * event,char ** params,Cardinal * nparams)
{
XfeCascadePart * cp = _XfeCascadePart(w);
/* Make sure we are not insensitive (or pretending to be insensitive) */
if (!_XfeIsSensitive(w))
{
return;
}
/* Make sure the submenu is not already posted */
if (cp->popped_up)
{
return;
}
/* First, arm the button */
_XfeButtonArm(w,event,params,nparams);
/* Invoke the cascading callback so that client can do magic */
_XfeInvokeCallbacks(w,cp->cascading_callback,XmCR_CASCADING,event,True);
/* make sure the submenu has some children before posting */
if (!_XfemNumChildren(cp->sub_menu_id))
{
return;
}
/* Make sure the submenu is not torn */
if (cp->torn)
{
return;
}
/* Make sure no other timer is active. If there is, remove it */
if (cp->delay_timer_id)
{
XtRemoveTimeOut(cp->delay_timer_id);
cp->delay_timer_id = 0;
}
/* Install the mapping delay timer if needed */
if (cp->mapping_delay)
{
XfeSetValue(cp->sub_menu_id,XmNmenuPost,(XtArgVal) POPUP_ACCEPT);
cp->delay_timer_id = XtAppAddTimeOut(XtWidgetToApplicationContext(w),
cp->mapping_delay,
DelayTimeout,
(XtPointer) w);
}
/* If no mapping delay is given, post the submenu right away */
else
{
XfeSetValue(cp->sub_menu_id,XmNmenuPost,(XtArgVal) POPUP_ACCEPT);
Post(w,event,NULL,0);
}
}
/*----------------------------------------------------------------------*/
static void
Activate(Widget w,XEvent * event,char ** params,Cardinal * nparams)
{
XfeCascadePart * cp = _XfeCascadePart(w);
XfeSetValue(cp->sub_menu_id,XmNmenuPost,(XtArgVal) POPUP_IGNORE);
_XfeButtonActivate(w,event,params,nparams);
}
/*----------------------------------------------------------------------*/
static void
Disarm(Widget w,XEvent *event,char **params,Cardinal *nparams)
{
XfeCascadePart * cp = _XfeCascadePart(w);
/* Make sure we are not insensitive (or pretending to be insensitive) */
if (!_XfeIsSensitive(w))
{
return;
}
_XfeButtonDisarm(w,event,params,nparams);
XfeSetValue(cp->sub_menu_id,XmNmenuPost,(XtArgVal) POPUP_IGNORE);
}
/*----------------------------------------------------------------------*/
static void
Post(Widget w,XEvent * event,char ** params,Cardinal * nparams)
{
XfeCascadePart * cp = _XfeCascadePart(w);
if (_XfeIsAlive(POPUP_SHELL(cp)) && _XfeIsAlive(cp->sub_menu_id))
{
XEvent * the_event = event;
Position x;
Position y;
if (!the_event)
{
XButtonEvent be;
/*
* Hackery Hackery Hackery
*
* This crap occurs so that the menu can be posted programatically
* in any context and not only button press which the motif popup
* menus seem to expect. I dont think all the given fields are
* needed, but they are ther anyway.
*/
be.type = ButtonPress;
be.serial = 0;
be.send_event = 0;
be.display = XtDisplay(w);
be.window = _XfeWindow(w);
be.root = DefaultRootWindow(XtDisplay(w));
be.subwindow = None;
be.time = CurrentTime;
be.state = AnyModifier;
be.button = Button1;
be.same_screen = True;
the_event = (XEvent *) &be;
}
assert( the_event != NULL );
/*
* Force the modifiers to any. The motif row column - and thus
* popup menus, will not post correctly if either the ShiftMask
* or LockMask bit is set in the button event modifier state.
*/
if (the_event->type == ButtonPress)
{
the_event->xbutton.state = AnyModifier;
}
/* Find out where the sub menu is supposed to post */
GetPostPosition(w,&x,&y);
/* Position the sub menu */
XfeMenuPositionXY(cp->sub_menu_id,x,y);
/* Make sure the submenu is realized */
if (!XtIsRealized(cp->sub_menu_id))
{
XtRealizeWidget(cp->sub_menu_id);
}
/* Synchronize and flush the display before popping up the submenu */
XSync(XtDisplay(w),False);
XmUpdateDisplay(w);
/* Post the button */
_XmPostPopupMenu(cp->sub_menu_id,the_event);
}
}
/*----------------------------------------------------------------------*/
static void
Unpost(Widget w,XEvent *event,char **params,Cardinal *nparams)
{
XfeCascadePart * cp = _XfeCascadePart(w);
if (_XfeIsAlive(XtParent(cp->sub_menu_id)) && _XfeIsAlive(cp->sub_menu_id))
{
XtUnmanageChild(cp->sub_menu_id);
}
}
/*----------------------------------------------------------------------*/
/*----------------------------------------------------------------------*/
/* */
/* SubMenu event handlers and callbacks */
/* */
/*----------------------------------------------------------------------*/
static void
SubMenuEH(Widget shell,
XtPointer client_data,
XEvent * event,
Boolean * cont)
{
Widget w = (Widget) client_data;
XfeCascadePart * cp = _XfeCascadePart(w);
switch (event->type)
{
case MapNotify:
XfeSetValue(cp->sub_menu_id,XmNmenuPost,(XtArgVal) POPUP_IGNORE);
/* Submenu is now popped up */
cp->popped_up = True;
/* Invoke the popup callbacks */
_XfeInvokeCallbacks(w,cp->popup_callback,XmCR_POPUP,event,True);
break;
case UnmapNotify:
Disarm(w,NULL,NULL,0);
XfeSetValue(cp->sub_menu_id,XmNmenuPost,(XtArgVal) POPUP_IGNORE);
/* Submenu is now popped down */
cp->popped_up = False;
/* Invoke the popdown callbacks */
_XfeInvokeCallbacks(w,cp->popdown_callback,XmCR_POPDOWN,event,True);
break;
}
}
/*----------------------------------------------------------------------*/
static void
SubMenuTearCB(Widget menu,XtPointer client_data,XtPointer call_data)
{
XmRowColumnCallbackStruct * cbs = (XmRowColumnCallbackStruct *) call_data;
Widget w = (Widget) client_data;
XfeCascadePart * cp = _XfeCascadePart(w);
if (cbs->reason == XmCR_TEAR_OFF_ACTIVATE)
{
cp->torn = True;
}
else if (cbs->reason == XmCR_TEAR_OFF_DEACTIVATE)
{
cp->torn = False;
}
InvokeTearCallback(w,cbs->event,cp->torn);
}
/*----------------------------------------------------------------------*/
static void
UnGrabEH(Widget shell,
XtPointer client_data,
XEvent * event,
Boolean * cont)
{
Widget w = (Widget) client_data;
XfeCascadePart * cp = _XfeCascadePart(w);
if ((event->type == ButtonPress) && (event->xbutton.button >= Button2))
{
XtUngrabPointer(cp->sub_menu_id,CurrentTime);
}
}
/*----------------------------------------------------------------------*/
/*----------------------------------------------------------------------*/
/* */
/* XfeCascade Public Methods */
/* */
/*----------------------------------------------------------------------*/
Widget
XfeCreateCascade(Widget parent,char *name,Arg *args,Cardinal count)
{
return (XtCreateWidget(name,xfeCascadeWidgetClass,parent,args,count));
}
/*----------------------------------------------------------------------*/
/* extern */ void
XfeCascadeDestroyChildren(Widget w)
{
XfeCascadePart * cp = _XfeCascadePart(w);
assert( _XfeIsAlive(w) );
assert( XfeIsCascade(w) );
assert( _XfeIsAlive(cp->sub_menu_id) );
XfeChildrenDestroy(cp->sub_menu_id);
}
/*----------------------------------------------------------------------*/
/* extern */ Boolean
XfeCascadeArmAndPost(Widget w,XEvent * event)
{
XfeCascadePart * cp = _XfeCascadePart(w);
assert( _XfeIsAlive(w) );
assert( XfeIsCascade(w) );
assert( _XfeIsAlive(cp->sub_menu_id) );
/* Make sure the subment is not already popped up or torn */
if (!cp->popped_up &&
!cp->torn &&
_XfeIsAlive(POPUP_SHELL(cp)) &&
_XfeIsAlive(cp->sub_menu_id))
{
Arm(w,event,NULL,0);
Post(w,event,NULL,0);
return True;
}
return False;
}
/*----------------------------------------------------------------------*/
/* extern */ Boolean
XfeCascadeDisarmAndUnpost(Widget w,XEvent * event)
{
XfeCascadePart * cp = _XfeCascadePart(w);
assert( _XfeIsAlive(w) );
assert( XfeIsCascade(w) );
assert( _XfeIsAlive(cp->sub_menu_id) );
/* Make sure the subment is not already popped up or torn */
if (cp->popped_up)
{
Disarm(w,event,NULL,0);
Unpost(w,event,NULL,0);
return True;
}
return False;
}
/*----------------------------------------------------------------------*/
/* extern */ void
XfeCascadeGetChildren(Widget w,WidgetList * children,Cardinal * num_children)
{
XfeCascadePart * cp = _XfeCascadePart(w);
assert( _XfeIsAlive(w) );
assert( XfeIsCascade(w) );
assert( _XfeIsAlive(cp->sub_menu_id) );
assert( children != NULL || num_children != NULL );
if (_XfeIsAlive(w) && _XfeIsAlive(cp->sub_menu_id))
{
*children = _XfemChildren(cp->sub_menu_id);
*num_children = _XfemNumChildren(cp->sub_menu_id);
}
else
{
*children = NULL;
*num_children = 0;
}
}
/*----------------------------------------------------------------------*/