1 /*
2 Copyright 1989, 1994, 1998  The Open Group
3 
4 Permission to use, copy, modify, distribute, and sell this software and its
5 documentation for any purpose is hereby granted without fee, provided that
6 the above copyright notice appear in all copies and that both that
7 copyright notice and this permission notice appear in supporting
8 documentation.
9 
10 The above copyright notice and this permission notice shall be included in
11 all copies or substantial portions of the Software.
12 
13 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
16 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
17 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 
20 Except as contained in this notice, the name of The Open Group shall not be
21 used in advertising or otherwise to promote the sale, use or other dealings
22 in 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  */
52 static void XawMenuButtonClassInitialize(void);
53 static void XawMenuButtonInitialize(Widget, Widget, ArgList, Cardinal*);
54 static void XawMenuButtonDestroy(Widget);
55 static Boolean XawMenuButtonSetValues(Widget, Widget, Widget, ArgList, Cardinal*);
56 
57 /*
58  * Actions
59  */
60 static void PopupMenu(Widget, XEvent*, String*, Cardinal*);
61 
62 /*
63  * Initialization
64  */
65 #define superclass ((CommandWidgetClass)&commandClassRec)
66 
67 static char defaultTranslations[] =
68 "<Enter>:"	"highlight()\n"
69 "<Leave>:"	"reset()\n"
70 "Any<BtnDown>:"	"reset() PopupMenu()\n";
71 
72 static char default_menu_name[] = "menu";
73 
74 #define offset(field) XtOffsetOf(MenuButtonRec, field)
75 static 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 
88 static XtActionsRec actionsList[] =
89 {
90   {"PopupMenu",	PopupMenu},
91 };
92 
93 MenuButtonClassRec 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 
147 WidgetClass menuButtonWidgetClass = (WidgetClass)&menuButtonClassRec;
148 
149 /*
150  * Implementation
151  */
152 static void
XawMenuButtonClassInitialize(void)153 XawMenuButtonClassInitialize(void)
154 {
155     XawInitializeWidgetSet();
156     XtRegisterGrabAction(PopupMenu, True,
157 			 ButtonPressMask | ButtonReleaseMask,
158 			 GrabModeAsync, GrabModeAsync);
159 }
160 
161 /*ARGSUSED*/
162 static void
XawMenuButtonInitialize(Widget request _X_UNUSED,Widget cnew,ArgList args _X_UNUSED,Cardinal * num_args _X_UNUSED)163 XawMenuButtonInitialize(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 
172 static void
XawMenuButtonDestroy(Widget w)173 XawMenuButtonDestroy(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*/
182 static Boolean
XawMenuButtonSetValues(Widget current,Widget request _X_UNUSED,Widget cnew,ArgList args _X_UNUSED,Cardinal * num_args _X_UNUSED)183 XawMenuButtonSetValues(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*/
201 static void
PopupMenu(Widget w,XEvent * event _X_UNUSED,String * params _X_UNUSED,Cardinal * num_params _X_UNUSED)202 PopupMenu(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