1 /************************************************************************/
2 /*  File        : menu.c                                                */
3 /*  Description : Implement a Motif MenuBar into Mxvile                 */
4 /*                Parse a File (environment variable XVILE_MENU)        */
5 /*                and create it at the top of the window                */
6 /*  Auteur      : Philippe CHASSANY                                     */
7 /*  Date        : 20/02/97                                              */
8 /************************************************************************/
9 
10 /*
11  * $Id: x11menu.c,v 1.23 2021/12/08 21:24:48 tom Exp $
12  */
13 
14 #define NEED_X_INCLUDES 1
15 
16 /* Vile includes */
17 #include "estruct.h"
18 #include "edef.h"
19 
20 #if DISP_X11
21 
22 #if ATHENA_WIDGETS
23 #if defined(HAVE_LIB_XAW3DXFT)
24 #include	<X11/Xaw3dxft/Form.h>
25 #include	<X11/Xaw3dxft/SimpleMenu.h>
26 #include	<X11/Xaw3dxft/MenuButton.h>
27 #include	<X11/Xaw3dxft/SmeLine.h>
28 #include	<X11/Xaw3dxft/SmeBSB.h>
29 #elif defined(HAVE_LIB_XAW3D)
30 #include	<X11/Xaw3d/Form.h>
31 #include	<X11/Xaw3d/SimpleMenu.h>
32 #include	<X11/Xaw3d/MenuButton.h>
33 #include	<X11/Xaw3d/SmeLine.h>
34 #include	<X11/Xaw3d/SmeBSB.h>
35 #elif defined(HAVE_LIB_XAWPLUS)
36 #include	<X11/XawPlus/Form.h>
37 #include	<X11/XawPlus/SimpleMenu.h>
38 #include	<X11/XawPlus/MenuButton.h>
39 #include	<X11/XawPlus/SmeLine.h>
40 #include	<X11/XawPlus/SmeBSB.h>
41 #elif defined(HAVE_LIB_NEXTAW)
42 #include	<X11/neXtaw/Form.h>
43 #include	<X11/neXtaw/SimpleMenu.h>
44 #include	<X11/neXtaw/MenuButton.h>
45 #include	<X11/neXtaw/SmeLine.h>
46 #include	<X11/neXtaw/SmeBSB.h>
47 #elif defined(HAVE_LIB_XAW)
48 #include	<X11/Xaw/Form.h>
49 #include	<X11/Xaw/SimpleMenu.h>
50 #include	<X11/Xaw/MenuButton.h>
51 #include	<X11/Xaw/SmeLine.h>
52 #include	<X11/Xaw/SmeBSB.h>
53 #endif
54 #elif MOTIF_WIDGETS
55 #include	<Xm/MainW.h>
56 #include	<Xm/CascadeB.h>
57 #include	<Xm/SeparatoG.h>
58 #include	<Xm/RowColumn.h>
59 #include	<Xm/PushBG.h>
60 #include	<Xm/ToggleB.h>
61 #endif
62 
63 #define Nval(name,value) name, (XtArgVal)(value)
64 #define Sval(name,value) name, (value)
65 
66 #define MY_MENUS struct MyMenus
67 MY_MENUS {
68     MY_MENUS *next;
69     Widget parent;
70     Widget child;
71 };
72 
73 /* Locals */
74 static Widget cascade;
75 static MY_MENUS *my_menus;
76 
77 /************************************************************************/
78 /* Motif function to make a cascade button into a menubar               */
79 /************************************************************************/
80 void *
gui_make_menu(void * menubar,const char * nom,int the_class GCC_UNUSED)81 gui_make_menu(void *menubar, const char *nom, int the_class GCC_UNUSED)
82 {
83     MY_MENUS *remember;
84     Widget pm;
85     Widget menub = (Widget) menubar;
86 #if MOTIF_WIDGETS
87     XmString xms;
88     char str[50];
89 
90     sprintf(str, "%sMenu", nom);
91     pm = (Widget) XmCreatePulldownMenu(menub, str, NULL, 0);
92 #if OPT_MENUS_COLORED
93     XtVaSetValues(pm,
94 		  Nval(XtNforeground, x_menu_foreground()),
95 		  Nval(XtNbackground, x_menu_background()),
96 		  NULL);
97     XtVaSetValues(menub,
98 		  Nval(XtNforeground, x_menu_foreground()),
99 		  Nval(XtNbackground, x_menu_background()),
100 		  NULL);
101 #endif /* OPT_MENUS_COLORED */
102 
103     xms = XmStringCreateSimple(nom);
104 
105     cascade = XtVaCreateManagedWidget("menuHeader",
106 				      xmCascadeButtonWidgetClass, menub,
107 				      Nval(XmNlabelString, xms),
108 				      Nval(XmNsubMenuId, pm),
109 #if OPT_MENUS_COLORED
110 				      Nval(XtNforeground, x_menu_foreground()),
111 				      Nval(XtNbackground, x_menu_background()),
112 #endif /* OPT_MENUS_COLORED */
113 				      NULL);
114     XmStringFree(xms);
115 
116     if (the_class == 'H') {
117 	XtVaSetValues(menub,
118 		      Nval(XmNmenuHelpWidget, cascade),
119 		      NULL);
120     }
121 #elif ATHENA_WIDGETS
122     static Widget last;
123     char *str = XtMalloc((Cardinal) strlen(nom) + 10);
124 
125     sprintf(str, "%sMenu", nom);
126     pm = XtVaCreatePopupShell(str,
127 			      simpleMenuWidgetClass,
128 			      menub,
129 			      Nval(XtNgeometry, NULL),
130 			      NULL);
131 
132     cascade = XtVaCreateManagedWidget("menuHeader",
133 				      menuButtonWidgetClass,
134 				      menub,
135 				      Nval(XtNheight, x_menu_height()),
136 				      Nval(XtNlabel, nom),
137 				      Nval(XtNfromHoriz, last),
138 				      Nval(XtNmenuName, str),
139 				      NULL);
140 #if OPT_MENUS_COLORED
141     XtVaSetValues(pm,
142 		  Nval(XtNforeground, x_menu_foreground()),
143 		  Nval(XtNbackground, x_menu_background()),
144 		  NULL);
145     XtVaSetValues(menub,
146 		  Nval(XtNforeground, x_menu_foreground()),
147 		  Nval(XtNbackground, x_menu_background()),
148 		  NULL);
149     XtVaSetValues(cascade,
150 		  Nval(XtNforeground, x_menu_foreground()),
151 		  Nval(XtNbackground, x_menu_background()),
152 		  NULL);
153 #endif /* OPT_MENUS_COLORED */
154     last = cascade;
155 
156 #endif /* ATHENA_WIDGETS */
157 
158     /* remember the widgets we created, so we can destroy them */
159     if ((remember = typecalloc(MY_MENUS)) != 0) {
160 	remember->parent = pm;
161 	remember->child = cascade;
162 	remember->next = my_menus;
163 	my_menus = remember;
164     }
165 
166     return (void *) pm;
167 }
168 
169 /************************************************************************/
170 /* Motif function to make a button into a cascade (with accelarator)    */
171 /************************************************************************/
172 void *
gui_add_menu_item(void * pm,const char * nom,char * accel GCC_UNUSED,int the_class)173 gui_add_menu_item(void *pm, const char *nom, char *accel GCC_UNUSED, int the_class)
174 {
175     Widget w;
176 #if MOTIF_WIDGETS
177     XmString xms_accl;
178     XmString xms_name;
179     WidgetClass wc;
180 
181     switch (the_class) {
182     case 'S':
183 	wc = xmSeparatorGadgetClass;
184 	break;
185     default:
186 	/* FALLTHRU */
187     case 'B':
188 	wc = xmPushButtonGadgetClass;
189 	break;
190     }
191 
192     if (accel != NULL)
193 	xms_accl = XmStringCreateSimple(accel);
194     else
195 	xms_accl = XmStringCreateSimple("");
196 
197     xms_name = XmStringCreateSimple(nom);
198     w = XtVaCreateManagedWidget("menuEntry",
199 				wc,
200 				(Widget) pm,
201 				Nval(XmNacceleratorText, xms_accl),
202 				Nval(XmNlabelString, xms_name),
203 				NULL);
204 
205     XmStringFree(xms_accl);
206     XmStringFree(xms_name);
207 
208 #elif ATHENA_WIDGETS
209     w = XtVaCreateManagedWidget("menuEntry",
210 				((the_class == 'B')
211 				 ? smeBSBObjectClass
212 				 : smeLineObjectClass),
213 				(Widget) pm,
214 				Nval(XtNlabel, nom),
215 				NULL);
216 
217 #endif /* ATHENA_WIDGETS */
218     return (void *) w;
219 }
220 
221 /************************************************************************/
222 /* Function called when a buffer button is clicked into the menu        */
223 /* A buffer button takes part from the list tokens (L)                  */
224 /************************************************************************/
225 static void
list_proc_back(Widget w GCC_UNUSED,XtPointer bname GCC_UNUSED,XtPointer call GCC_UNUSED)226 list_proc_back(Widget w GCC_UNUSED,
227 	       XtPointer bname GCC_UNUSED,
228 	       XtPointer call GCC_UNUSED)
229 {
230     int num_buff;
231     int oldflag = im_waiting(-1);
232 
233 #if MOTIF_WIDGETS
234     char *accel;
235     XmString xms;
236     XtVaGetValues(w, XmNacceleratorText, &xms, NULL);
237 #ifndef XmFONTLIST_DEFAULT_TAG
238 #define XmFONTLIST_DEFAULT_TAG "XmFONTLIST_DEFAULT_TAG_STRING"
239 #endif
240     XmStringGetLtoR(xms, XmFONTLIST_DEFAULT_TAG, &accel);
241     num_buff = atoi(&accel[1]);
242 #elif ATHENA_WIDGETS
243     num_buff = (int) (long) bname;
244 #endif
245 
246     if (vile_is_busy)
247 	return;
248 
249     (void) histbuff(TRUE, num_buff);
250     (void) im_waiting(oldflag);
251     (void) update(TRUE);
252 }
253 
254 /************************************************************************/
255 /* Function called when a button is clicked into the menu               */
256 /* Bind and Action are tested                                           */
257 /************************************************************************/
258 static void
proc_back(Widget w GCC_UNUSED,XtPointer arg,XtPointer call GCC_UNUSED)259 proc_back(Widget w GCC_UNUSED, XtPointer arg, XtPointer call GCC_UNUSED)
260 {
261     char *macro_action = (char *) arg;
262     ActionFunc fact;
263     int oldflag = im_waiting(-1);
264     int exec_flag = TRUE;
265     char *s;
266 
267     TRACE(("Macro/Action=%s\n", macro_action));
268 
269 #if OPT_WORKING
270     if (vile_is_busy)
271 	return;
272 #endif
273 
274     if ((s = vlmenu_is_cmd(macro_action)) != 0) {
275 	macro_action = s;
276 	exec_flag = FALSE;
277     }
278 
279     if (vlmenu_is_bind(macro_action)) {
280 	docmd(macro_action, exec_flag, FALSE, 1);
281     } else {
282 	/* Action predefined */
283 	fact = vlmenu_action_func(macro_action);
284 	if (fact != NULL)
285 	    fact(macro_action);
286     }
287 
288     (void) im_waiting(oldflag);
289     (void) update(TRUE);
290 }
291 
292 void
gui_add_func_callback(void * w,void * closure)293 gui_add_func_callback(void *w, void *closure)
294 {
295 #if MOTIF_WIDGETS
296     XtAddCallback((Widget) w, XmNactivateCallback, proc_back, closure);
297 #elif ATHENA_WIDGETS
298     XtAddCallback((Widget) w, XtNcallback, proc_back, closure);
299 #endif /* ATHENA_WIDGETS */
300 }
301 
302 /************************************************************************/
303 /* Function called when the cascade containing the List (buffers)       */
304 /* is clicked => Post the PullDown associated                           */
305 /* It's dirty ...                                                       */
306 /************************************************************************/
307 static void
post_buffer_list(Widget w GCC_UNUSED,XtPointer client GCC_UNUSED,XEvent * ev GCC_UNUSED,Boolean * ok GCC_UNUSED)308 post_buffer_list(Widget w GCC_UNUSED,
309 		 XtPointer client GCC_UNUSED,
310 		 XEvent * ev GCC_UNUSED,
311 		 Boolean *ok GCC_UNUSED)
312 {
313     static unsigned in_item_menu_list = 0;	/* number allocated */
314     static unsigned nb_item_menu_list = 0;	/* number in use */
315     static Widget *pm_buffer;
316 
317     unsigned i, n = nb_item_menu_list;
318     BUFFER *bp;
319     char string[NBUFN + 2 + NFILEN], temp[1 + NFILEN], *p;
320     Widget pm = (Widget) client;
321 
322     TRACE(("post_buffer_list\n"));
323     nb_item_menu_list = 0;
324 
325     for_each_buffer(bp) {
326 	if (b_is_temporary(bp))	/* cf: hist_show() */
327 	    continue;
328 
329 	p = shorten_path(vl_strncpy(temp, bp->b_fname, sizeof(temp)), FALSE);
330 	sprintf(string, "%-*.*s %s", NBUFN - 1, NBUFN - 1,
331 		bp->b_bname, p);
332 
333 	sprintf(temp, "_%d", nb_item_menu_list);
334 	TRACE(("ACCEL(%s) = %s\n", temp, string));
335 
336 	if (nb_item_menu_list + 2 >= in_item_menu_list) {
337 	    unsigned m = in_item_menu_list;
338 
339 	    in_item_menu_list = (in_item_menu_list + 3) * 2;
340 
341 	    if (pm_buffer != 0)
342 		pm_buffer = typereallocn(Widget, pm_buffer, in_item_menu_list);
343 	    else
344 		pm_buffer = typeallocn(Widget, in_item_menu_list);
345 
346 	    if (pm_buffer == 0) {
347 		no_memory("post_buffer_list");
348 		return;
349 	    }
350 
351 	    while (m < in_item_menu_list)
352 		pm_buffer[m++] = 0;
353 	}
354 
355 	if (pm_buffer[nb_item_menu_list] == 0) {
356 	    pm_buffer[nb_item_menu_list] = (Widget) gui_add_menu_item(pm, string,
357 								      temp, 'B');
358 	} else {
359 #if MOTIF_WIDGETS
360 	    XmString xms = XmStringCreateSimple(string);
361 	    XtVaSetValues(pm_buffer[nb_item_menu_list],
362 			  Nval(XmNlabelString, xms),
363 			  NULL);
364 	    XmStringFree(xms);
365 	    XtRemoveCallback(pm_buffer[nb_item_menu_list],
366 			     XmNactivateCallback, list_proc_back, NULL);
367 #elif ATHENA_WIDGETS
368 	    XtVaSetValues(pm_buffer[nb_item_menu_list],
369 			  Nval(XtNlabel, string),
370 			  NULL);
371 	    XtRemoveAllCallbacks(pm_buffer[nb_item_menu_list],
372 				 XtNcallback);
373 #endif
374 	}
375 
376 #if MOTIF_WIDGETS
377 	XtAddCallback(pm_buffer[nb_item_menu_list],
378 		      XmNactivateCallback,
379 		      list_proc_back,
380 		      NULL);
381 #elif ATHENA_WIDGETS
382 	XtAddCallback(pm_buffer[nb_item_menu_list],
383 		      XtNcallback,
384 		      list_proc_back,
385 		      (XtPointer) (long) nb_item_menu_list);
386 #endif
387 	nb_item_menu_list++;
388     }
389     for (i = nb_item_menu_list; i < n; i++) {
390 	XtDestroyWidget(pm_buffer[i]);
391 	pm_buffer[i] = NULL;
392     }
393 
394 #if MOTIF_WIDGETS
395     XmUpdateDisplay(pm);
396 #endif
397 }
398 
399 void
gui_add_list_callback(void * pm)400 gui_add_list_callback(void *pm)
401 {
402     if (cascade != 0)
403 	XtAddEventHandler(cascade,
404 			  ButtonPressMask,
405 			  False,
406 			  post_buffer_list,
407 			  (Widget) pm);
408 }
409 
410 /*
411  * Hide/show menus, useful for avoiding thrashing while regenerating menus
412  */
413 int
gui_hide_menus(int f GCC_UNUSED,int n GCC_UNUSED)414 gui_hide_menus(int f GCC_UNUSED, int n GCC_UNUSED)
415 {
416     int rc = FALSE;
417     Widget w = x_menu_widget();
418 
419     if (w != 0) {
420 	XtUnmapWidget(w);
421 	rc = TRUE;
422     }
423     TRACE(("gui_hide_menus %d\n", rc));
424     return rc;
425 }
426 
427 int
gui_show_menus(int f GCC_UNUSED,int n GCC_UNUSED)428 gui_show_menus(int f GCC_UNUSED, int n GCC_UNUSED)
429 {
430     int rc = FALSE;
431     Widget w = x_menu_widget();
432 
433     if (w != 0) {
434 	XtMapWidget(w);
435 	rc = TRUE;
436     }
437     TRACE(("gui_show_menus %d\n", rc));
438     return rc;
439 }
440 
441 /*
442  * Remove children of the current menu widget, so we can regenerate them.
443  */
444 int
gui_remove_menus(int f GCC_UNUSED,int n GCC_UNUSED)445 gui_remove_menus(int f GCC_UNUSED, int n GCC_UNUSED)
446 {
447     int rc = TRUE;
448     int count = 0;
449 
450     gui_hide_menus(f, n);
451     while (my_menus != 0) {
452 	MY_MENUS *next = my_menus->next;
453 
454 	XtUnmanageChild(my_menus->parent);
455 	XtDestroyWidget(my_menus->parent);
456 	XtDestroyWidget(my_menus->child);
457 
458 	free(my_menus);
459 	my_menus = next;
460 	++count;
461     }
462     TRACE(("gui_remove_menus %d\n", count));
463     return rc;
464 }
465 
466 /*
467  * (Re)create the menus.
468  */
469 int
gui_create_menus(void)470 gui_create_menus(void)
471 {
472     int rc = do_menu(x_menu_widget());
473     TRACE(("gui_create_menus %d\n", rc));
474     return rc;
475 }
476 
477 #endif /* DISP_X11 */
478