1 /*
2  *  Copyright (C) 1995, 1996  Karl-Johan Johnsson.
3  */
4 
5 #include <X11/IntrinsicP.h>
6 #include <X11/StringDefs.h>
7 #include <stdio.h>
8 
9 #include "Compat.h"
10 
11 #include "MenuI.h"
12 #include "PullRightP.h"
13 
14 static XtResource resources[] = {
15 #define offset(field) XtOffsetOf(PullRightGadgetRec, pull_right.field)
16     {XtNmenuName, XtCMenuName, XtRString, sizeof(String),
17      offset(menu_name), XtRImmediate, (XtPointer)NULL},
18     {XtNarrowSize, XtCArrowSize, XtRDimension, sizeof(Dimension),
19      offset(arrow_size), XtRImmediate, (XtPointer)6},
20     {XtNarrowOffset, XtCArrowOffset, XtRDimension, sizeof(Dimension),
21      offset(arrow_offset), XtRImmediate, (XtPointer)8},
22     {XtNarrowShadowWidth, XtCShadowWidth, XtRDimension, sizeof(Dimension),
23      offset(arrow_shadow_width), XtRImmediate, (XtPointer)1},
24 #undef offset
25 };
26 
27 static void	Initialize(Widget, Widget, ArgList, Cardinal*);
28 static void	Destroy(Widget);
29 static Boolean	SetValues(Widget, Widget, Widget, ArgList, Cardinal*);
30 static void	Redisplay(Widget, XEvent*, Region);
31 static void	ChangeHl(MenuGadget);
32 static void	Popdown(MenuGadget);
33 static int	Notify(MenuGadget);
34 static int	PostNotify(MenuGadget);
35 static void	SetActive(MenuGadget, int);
36 
37 PullRightGadgetClassRec pullRightGadgetClassRec = {
38     { /* rectObj fields */
39         (WidgetClass) &stringGadgetClassRec, /* superclass              */
40         "PullRightGadget",              /* class_name                   */
41         sizeof(PullRightGadgetRec),     /* widget_size                  */
42         NULL,                           /* class_initialize             */
43         NULL,                           /* class_part_initialize        */
44         FALSE,                          /* class_inited                 */
45         Initialize,                     /* initialize                   */
46         NULL,                           /* initialize_hook              */
47         NULL,                           /* rect1                        */
48         NULL,                           /* rect2                        */
49         0,                              /* rect3                        */
50         resources,                      /* resources                    */
51         XtNumber(resources),            /* num_resources                */
52         NULLQUARK,                      /* xrm_class                    */
53         FALSE,                          /* rect4                        */
54         FALSE,                          /* rect5                        */
55         FALSE,                          /* rect6                        */
56         FALSE,                          /* rect7                        */
57         Destroy,    			/* destroy                      */
58         NULL,                           /* resize                       */
59 	Redisplay,                      /* expose                       */
60         SetValues,                      /* set_values                   */
61         NULL,                           /* set_values_hook              */
62         XtInheritSetValuesAlmost,       /* set_values_almost            */
63         NULL,                           /* get_values_hook              */
64         NULL,                           /* rect9                        */
65         XtVersion,                      /* version                      */
66         NULL,                           /* callback_private             */
67         NULL,                           /* rect10                       */
68         NULL,                           /* query_geometry               */
69         NULL,                           /* rect11                       */
70         NULL,                           /* extension                    */
71     },
72     { /* menu_g fields */
73         ChangeHl,			/* change_highlighted		*/
74 	Popdown,			/* popdown			*/
75 	Notify,				/* notify			*/
76 	PostNotify,			/* post_notify			*/
77 	SetActive,			/* set_active			*/
78 	True,				/* ignore_leave			*/
79 	NULL,				/* extension			*/
80     },
81     { /* menu_str_g fields */
82 	NULL,				/* extension			*/
83     },
84     { /* pull_right fields */
85 	NULL,				/* extension			*/
86     },
87 };
88 
89 WidgetClass pullRightGadgetClass = (WidgetClass)&pullRightGadgetClassRec;
90 
91 /*************************************************************************/
92 
draw_arrow(PullRightGadget g)93 static void draw_arrow(PullRightGadget g)
94 {
95     MenuWidget	parent = (MenuWidget)g->object.parent;
96     Display	*disp = XtDisplay(parent);
97     Window	win = XtWindow(parent);
98     int		s = g->pull_right.arrow_size;
99     int		x, y;
100 
101     if (s <= 0)
102 	return;
103 
104     x = g->rectangle.x + g->rectangle.width -
105 	g->pull_right.arrow_offset - parent->shadow.shadow_width;
106     y = g->rectangle.y + g->rectangle.height/2;
107 
108     if (parent->shadow.line_mode) {
109 	short	sw = parent->shadow.shadow_width;
110 	XPoint	point[4];
111 
112 	XClearArea(disp, win, x - s - sw, y - s/2 - sw,
113 		   s + 2 * sw, s + 2 * sw, False);
114 
115 	if (!g->menu_g.hl)
116 	    sw = 0;
117 	else {
118 	    sw /= 2;
119 	    s -= sw;
120 	}
121 
122 	point[0].x = point[3].x = x - sw/2;
123 	point[1].x = point[2].x = x - s;
124 
125 	point[0].y = point[3].y = y;
126 	point[1].y = y - s/2;
127 	point[2].y = y + s/2;
128 
129 	XDrawLines(disp, win,
130 		   !g->menu_g.hl ?
131 		   parent->shadow.light_gc : parent->shadow.dark_gc,
132 		   point, 4, CoordModeOrigin);
133     } else {
134 	XPoint	point[6];
135 	short	sw = g->pull_right.arrow_shadow_width;
136 	GC	light_gc, dark_gc;
137 
138 	if (g->menu_g.hl) {
139 	    light_gc = parent->shadow.dark_gc;
140 	    dark_gc = parent->shadow.light_gc;
141 	} else {
142 	    light_gc = parent->shadow.light_gc;
143 	    dark_gc = parent->shadow.dark_gc;
144 	}
145 
146 	if (sw == 0)
147 	    return;
148 	else if (sw == 1) {
149 	    point[0].x = point[3].x = x;
150 	    point[1].x = point[2].x = x - s;
151 
152 	    point[0].y = point[3].y = y;
153 	    point[1].y = y - s/2;
154 	    point[2].y = y + s/2;
155 
156 	    if (!g->menu_g.hl && parent->shadow.arm_gc != 0)
157 		XFillPolygon(disp, win, parent->shadow.arm_gc,
158 			     point, 3, Convex, CoordModeOrigin);
159 
160 	    XDrawLines(disp, win, dark_gc, point, 3, CoordModeOrigin);
161 	    XDrawLines(disp, win, light_gc, &point[2], 2, CoordModeOrigin);
162 	} else {
163 	    point[0].x = x;
164 	    point[1].x = point[2].x = x - s;
165 	    point[3].x = point[4].x = x - s + sw;
166 	    point[5].x = x - (9 * sw)/4;
167 
168 	    point[0].y = point[5].y = y;
169 	    point[1].y = y - s/2;
170 	    point[2].y = y + s/2;
171 	    point[3].y = point[2].y - (3 * sw)/2;
172 	    point[4].y = point[1].y + (3 * sw)/2;
173 
174 	    if (!g->menu_g.hl && parent->shadow.arm_gc != 0)
175 		XFillPolygon(disp, win, parent->shadow.arm_gc,
176 			     &point[3], 3, Convex, CoordModeOrigin);
177 
178 	    XFillPolygon(disp, win, dark_gc,
179 			 point, 6, Nonconvex, CoordModeOrigin);
180 	    point[1] = point[0];
181 	    point[4] = point[5];
182 	    XFillPolygon(disp, win, light_gc,
183 			 &point[1], 4, Convex, CoordModeOrigin);
184 	}
185     }
186 }
187 
find_menu(PullRightGadget g)188 static Widget find_menu(PullRightGadget g)
189 {
190     if (g->pull_right.menu_name) {
191 	Widget loop;
192 
193 	for (loop = g->object.parent ; loop ; loop = XtParent(loop)) {
194 	    Widget	temp = XtNameToWidget(loop, g->pull_right.menu_name);
195 
196 	    if (temp && XtIsShell(temp))
197 		return temp;
198 	}
199     }
200 
201     return NULL;
202 }
203 
204 /*************************************************************************/
205 
Initialize(Widget grequest,Widget gnew,ArgList args,Cardinal * no_args)206 static void Initialize(Widget grequest, Widget gnew,
207 		       ArgList args, Cardinal *no_args)
208 {
209     PullRightGadget	g = (PullRightGadget)gnew;
210 
211     if (!g->pull_right.menu_name)
212 	g->pull_right.menu = NULL;
213     else {
214 	g->pull_right.menu_name = XtNewString(g->pull_right.menu_name);
215 	g->pull_right.menu = find_menu(g);
216     }
217 }
218 
Destroy(Widget gw)219 static void Destroy(Widget gw)
220 {
221     PullRightGadget	w = (PullRightGadget)gw;
222 
223     XtFree(w->pull_right.menu_name);
224 }
225 
Redisplay(Widget gw,XEvent * event,Region region)226 static void Redisplay(Widget gw, XEvent *event, Region region)
227 {
228     PullRightGadget	w = (PullRightGadget)gw;
229 
230     stringGadgetClass->core_class.expose((Widget)w, event, region);
231 
232     draw_arrow(w);
233 }
234 
SetValues(Widget gcurrent,Widget grequest,Widget gnew,ArgList args,Cardinal * num_args)235 static Boolean SetValues(Widget gcurrent,
236 			 Widget grequest,
237 			 Widget gnew,
238 			 ArgList args,
239 			 Cardinal *num_args)
240 {
241     Boolean		redisplay = False;
242     PullRightGadget	new = (PullRightGadget)gnew;
243     PullRightGadget	current = (PullRightGadget)gcurrent;
244 
245     if (new->pull_right.menu_name != current->pull_right.menu_name) {
246 	XtFree(current->pull_right.menu_name);
247 	if (!new->pull_right.menu_name)
248 	    new->pull_right.menu = NULL;
249 	else {
250 	    new->pull_right.menu_name = XtNewString(new->pull_right.menu_name);
251 	    new->pull_right.menu = find_menu(new);
252 	}
253     }
254 
255     return redisplay;
256 }
257 
ChangeHl(MenuGadget m)258 static void ChangeHl(MenuGadget m)
259 {
260     PullRightGadget	g      = (PullRightGadget)m;
261     Widget		parent = g->object.parent;
262     Widget		menu   = g->pull_right.menu;
263     Screen		*screen;
264     Arg			args[2];
265     Position		x, y;
266 
267     Redisplay((Widget)g, NULL, NULL);
268 
269     if (!menu && !(menu = g->pull_right.menu = find_menu(g))) {
270 	fprintf(stderr, "PullRightGadget: Couldn't find menu: %s\n",
271 		g->pull_right.menu_name);
272 	return;
273     }
274 
275     /*
276      *  Unhighlight.
277      */
278     if (!g->menu_g.hl) {
279 	PopdownMenuShell(menu);
280 	return;
281     }
282 
283     screen = XtScreen(menu);
284     if (!XtIsRealized(menu))
285 	XtRealizeWidget(menu);
286     XtTranslateCoords(parent, parent->core.width, g->rectangle.y, &x, &y);
287     if (x < 0)
288 	x = 0;
289     else {
290 	int	tmp = WidthOfScreen(screen) - menu->core.width;
291 
292 	if (tmp >= 0 && x > tmp)
293 	    x = tmp;
294     }
295     if (y < 0)
296 	y = 0;
297     else {
298 	int	tmp = HeightOfScreen(screen) - menu->core.height;
299 
300 	if (tmp >= 0 && y > tmp)
301 	    y = tmp;
302     }
303     XtSetArg(args[0], XtNx, x);
304     XtSetArg(args[1], XtNy, y);
305     XtSetValues(menu, args, 2);
306     XtPopup(menu, XtGrabNone/*xclusive*/);
307     XtAddGrab(menu, False, False);
308 }
309 
Popdown(MenuGadget m)310 static void Popdown(MenuGadget m)
311 {
312     PullRightGadget	g = (PullRightGadget)m;
313 
314     if (g->pull_right.menu)
315 	PopdownMenuShell(g->pull_right.menu);
316 }
317 
Notify(MenuGadget m)318 static int Notify(MenuGadget m)
319 {
320     PullRightGadget	g = (PullRightGadget)m;
321 
322     if (!g->pull_right.menu || g->menu_g.inside) {
323 	XtCallCallbackList((Widget)g, g->menu_g.callback, NULL);
324 	return True;
325     }
326 
327     return NotifyMenuShell(g->pull_right.menu);
328 }
329 
PostNotify(MenuGadget m)330 static int PostNotify(MenuGadget m)
331 {
332     PullRightGadget	g = (PullRightGadget)m;
333 
334     if (!g->pull_right.menu || g->menu_g.inside) {
335 	XtCallCallbackList((Widget)g, g->menu_g.post_popdown_callback, NULL);
336 	return True;
337     }
338 
339     return PostNotifyMenuShell(g->pull_right.menu);
340 }
341 
SetActive(MenuGadget m,int active)342 static void SetActive(MenuGadget m, int active)
343 {
344     PullRightGadget	g = (PullRightGadget)m;
345 
346     g->menu_g.active = active;
347     if (g->pull_right.menu)
348 	SetActiveMenuShell(g->pull_right.menu, active);
349 }
350