MenuButton.c revision 5ec34c4c
1/*
2Copyright 1989, 1994, 1998  The Open Group
3
4Permission to use, copy, modify, distribute, and sell this software and its
5documentation for any purpose is hereby granted without fee, provided that
6the above copyright notice appear in all copies and that both that
7copyright notice and this permission notice appear in supporting
8documentation.
9
10The above copyright notice and this permission notice shall be included in
11all copies or substantial portions of the Software.
12
13THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
16OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
17AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
20Except as contained in this notice, the name of The Open Group shall not be
21used in advertising or otherwise to promote the sale, use or other dealings
22in this Software without prior written authorization from The Open Group.
23 *
24 */
25
26/*
27 * MenuButton.c - Source code for MenuButton widget.
28 *
29 * This is the source code for the Athena MenuButton widget.
30 * It is intended to provide an easy method of activating pulldown menus.
31 *
32 * Date:    May 2, 1989
33 *
34 * By:      Chris D. Peterson
35 *          MIT X Consortium
36 *          kit@expo.lcs.mit.edu
37 */
38
39#ifdef HAVE_CONFIG_H
40#include <config.h>
41#endif
42#include <stdio.h>
43#include <X11/IntrinsicP.h>
44#include <X11/StringDefs.h>
45#include <X11/Xaw/MenuButtoP.h>
46#include <X11/Xaw/XawInit.h>
47#include "Private.h"
48
49/*
50 * Class Methods
51 */
52static void XawMenuButtonClassInitialize(void);
53static void XawMenuButtonInitialize(Widget, Widget, ArgList, Cardinal*);
54static void XawMenuButtonDestroy(Widget);
55static Boolean XawMenuButtonSetValues(Widget, Widget, Widget, ArgList, Cardinal*);
56
57/*
58 * Actions
59 */
60static void PopupMenu(Widget, XEvent*, String*, Cardinal*);
61
62/*
63 * Initialization
64 */
65#define superclass ((CommandWidgetClass)&commandClassRec)
66
67static char defaultTranslations[] =
68"<Enter>:"	"highlight()\n"
69"<Leave>:"	"reset()\n"
70"Any<BtnDown>:"	"reset() PopupMenu()\n";
71
72static char default_menu_name[] = "menu";
73
74#define offset(field) XtOffsetOf(MenuButtonRec, field)
75static XtResource resources[] = {
76  {
77    XtNmenuName,
78    XtCMenuName,
79    XtRString,
80    sizeof(String),
81    offset(menu_button.menu_name),
82    XtRString,
83    (XtPointer)default_menu_name
84  },
85};
86#undef offset
87
88static XtActionsRec actionsList[] =
89{
90  {"PopupMenu",	PopupMenu},
91};
92
93MenuButtonClassRec menuButtonClassRec = {
94  /* core */
95  {
96    (WidgetClass)superclass,		/* superclass		  */
97    "MenuButton",			/* class_name		  */
98    sizeof(MenuButtonRec),		/* size			  */
99    XawMenuButtonClassInitialize,	/* class_initialize	  */
100    NULL,				/* class_part_initialize  */
101    False,				/* class_inited		  */
102    XawMenuButtonInitialize,		/* initialize		  */
103    NULL,				/* initialize_hook	  */
104    XtInheritRealize,			/* realize		  */
105    actionsList,			/* actions		  */
106    XtNumber(actionsList),		/* num_actions		  */
107    resources,				/* resources		  */
108    XtNumber(resources),		/* num_resources	  */
109    NULLQUARK,				/* xrm_class		  */
110    False,				/* compress_motion	  */
111    True,				/* compress_exposure	  */
112    True,				/* compress_enterleave	  */
113    False,				/* visible_interest	  */
114    XawMenuButtonDestroy,		/* destroy		  */
115    XtInheritResize,			/* resize		  */
116    XtInheritExpose,			/* expose		  */
117    XawMenuButtonSetValues,		/* set_values		  */
118    NULL,				/* set_values_hook	  */
119    XtInheritSetValuesAlmost,		/* set_values_almost	  */
120    NULL,				/* get_values_hook	  */
121    NULL,				/* accept_focus		  */
122    XtVersion,				/* version		  */
123    NULL,				/* callback_private	  */
124    defaultTranslations,		/* tm_table		  */
125    XtInheritQueryGeometry,		/* query_geometry	  */
126    XtInheritDisplayAccelerator,	/* display_accelerator	  */
127    NULL,				/* extension */
128  },
129  /* simple */
130  {
131    XtInheritChangeSensitive		/* change_sensitive	  */
132  },
133  /* label */
134  {
135    NULL,				/* extension */
136  },
137  /* command */
138  {
139    NULL,				/* extension */
140  },
141  /* menu_button */
142  {
143    NULL,				/* extension */
144  },
145};
146
147WidgetClass menuButtonWidgetClass = (WidgetClass)&menuButtonClassRec;
148
149/*
150 * Implementation
151 */
152static void
153XawMenuButtonClassInitialize(void)
154{
155    XawInitializeWidgetSet();
156    XtRegisterGrabAction(PopupMenu, True,
157			 ButtonPressMask | ButtonReleaseMask,
158			 GrabModeAsync, GrabModeAsync);
159}
160
161/*ARGSUSED*/
162static void
163XawMenuButtonInitialize(Widget request _X_UNUSED, Widget cnew,
164			ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
165{
166    MenuButtonWidget mbw = (MenuButtonWidget)cnew;
167
168    if (mbw->menu_button.menu_name != default_menu_name)
169	mbw->menu_button.menu_name = XtNewString(mbw->menu_button.menu_name);
170}
171
172static void
173XawMenuButtonDestroy(Widget w)
174{
175    MenuButtonWidget mbw = (MenuButtonWidget)w;
176
177    if (mbw->menu_button.menu_name != default_menu_name)
178	XtFree(mbw->menu_button.menu_name);
179}
180
181/*ARGSUSED*/
182static Boolean
183XawMenuButtonSetValues(Widget current, Widget request _X_UNUSED, Widget cnew,
184		       ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
185{
186    MenuButtonWidget mbw_old = (MenuButtonWidget)current;
187    MenuButtonWidget mbw_new = (MenuButtonWidget)cnew;
188
189    if (mbw_old->menu_button.menu_name != mbw_new->menu_button.menu_name) {
190	if (mbw_old->menu_button.menu_name != default_menu_name)
191	    XtFree(mbw_old->menu_button.menu_name);
192	if (mbw_new->menu_button.menu_name != default_menu_name)
193	    mbw_new->menu_button.menu_name =
194		XtNewString(mbw_new->menu_button.menu_name);
195    }
196
197    return (False);
198}
199
200/*ARGSUSED*/
201static void
202PopupMenu(Widget w, XEvent *event _X_UNUSED, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED)
203{
204    MenuButtonWidget mbw = (MenuButtonWidget)w;
205    Widget menu = NULL, temp;
206    Arg arglist[2];
207    Cardinal num_args;
208    int menu_x, menu_y, menu_width, menu_height, button_height;
209    Position button_x, button_y;
210
211    temp = w;
212    while(temp != NULL) {
213	menu = XtNameToWidget(temp, mbw->menu_button.menu_name);
214	if (menu == NULL)
215	    temp = XtParent(temp);
216	else
217	    break;
218    }
219
220    if (menu == NULL) {
221	char error_buf[BUFSIZ];
222
223	snprintf(error_buf, sizeof(error_buf),
224		 "MenuButton:  Could not find menu widget named %s.",
225		 mbw->menu_button.menu_name);
226	XtAppWarning(XtWidgetToApplicationContext(w), error_buf);
227	return;
228    }
229
230    if (!XtIsRealized(menu))
231	XtRealizeWidget(menu);
232
233    menu_width = XtWidth(menu) + (XtBorderWidth(menu) << 1);
234    button_height = XtHeight(w) + (XtBorderWidth(w) << 1);
235    menu_height = XtHeight(menu) + (XtBorderWidth(menu) << 1);
236
237    XtTranslateCoords(w, 0, 0, &button_x, &button_y);
238    menu_x = button_x;
239    menu_y = button_y + button_height;
240
241    if (menu_y >= 0) {
242	int scr_height = HeightOfScreen(XtScreen(menu));
243
244	if (menu_y + menu_height > scr_height)
245	    menu_y = button_y - menu_height;
246	if (menu_y < 0) {
247	    menu_y = scr_height - menu_height;
248	    menu_x = button_x + XtWidth(w) + (XtBorderWidth(w) << 1);
249	    if (menu_x + menu_width > WidthOfScreen(XtScreen(menu)))
250		menu_x = button_x - menu_width;
251	}
252    }
253    if (menu_y < 0)
254	menu_y = 0;
255
256    if (menu_x >= 0) {
257	int scr_width = WidthOfScreen(XtScreen(menu));
258
259	if (menu_x + menu_width > scr_width)
260	    menu_x = scr_width - menu_width;
261    }
262    if (menu_x < 0)
263	menu_x = 0;
264
265    num_args = 0;
266    XtSetArg(arglist[num_args], XtNx, menu_x); num_args++;
267    XtSetArg(arglist[num_args], XtNy, menu_y); num_args++;
268    XtSetValues(menu, arglist, num_args);
269
270    XtPopupSpringLoaded(menu);
271}
272