X11 menus
[aNetHack.git] / win / X11 / winmenu.c
blob8fe7baec3aff57e9dc890f874b8a230482ffc05b
1 /* NetHack 3.6 winmenu.c $NHDT-Date: 1453448854 2016/01/22 07:47:34 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.13 $ */
2 /* Copyright (c) Dean Luick, 1992 */
3 /* NetHack may be freely redistributed. See license for details. */
5 /*
6 * File for creating menus.
8 * + Global functions: start_menu, add_menu, end_menu, select_menu
9 */
10 /*#define USE_FWF*/ /* use FWF's list widget */
12 #ifndef SYSV
13 #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
14 #endif
16 #include <X11/Intrinsic.h>
17 #include <X11/StringDefs.h>
18 #include <X11/Shell.h>
19 #include <X11/Xatom.h>
20 #include <X11/Xaw/Label.h>
21 #include <X11/Xaw/Command.h>
22 #include <X11/Xaw/Viewport.h>
23 #include <X11/Xaw/Cardinals.h>
24 #include <X11/Xaw/Box.h>
25 #ifdef USE_FWF
26 #include <X11/Xfwf/MultiList.h>
27 #else
28 #include <X11/Xaw/List.h>
29 #endif
30 #include <X11/Xos.h>
32 #ifdef PRESERVE_NO_SYSV
33 #ifdef SYSV
34 #undef SYSV
35 #endif
36 #undef PRESERVE_NO_SYSV
37 #endif
39 #include "hack.h"
40 #include "winX.h"
41 #include <ctype.h>
43 static void FDECL(menu_select, (Widget, XtPointer, XtPointer));
44 static void FDECL(invert_line,
45 (struct xwindow *, x11_menu_item *, int, long));
46 static void FDECL(menu_ok, (Widget, XtPointer, XtPointer));
47 static void FDECL(menu_cancel, (Widget, XtPointer, XtPointer));
48 static void FDECL(menu_all, (Widget, XtPointer, XtPointer));
49 static void FDECL(menu_none, (Widget, XtPointer, XtPointer));
50 static void FDECL(menu_invert, (Widget, XtPointer, XtPointer));
51 static void FDECL(menu_search, (Widget, XtPointer, XtPointer));
52 static void FDECL(select_all, (struct xwindow *));
53 static void FDECL(select_none, (struct xwindow *));
54 static void FDECL(select_match, (struct xwindow *, char *));
55 static void FDECL(invert_all, (struct xwindow *));
56 static void FDECL(invert_match, (struct xwindow *, char *));
57 static void FDECL(menu_popdown, (struct xwindow *));
58 #ifdef USE_FWF
59 static void FDECL(sync_selected, (struct menu_info_t *, int, int *));
60 #endif
62 static void FDECL(move_menu, (struct menu *, struct menu *));
63 static void FDECL(free_menu, (struct menu *));
64 static void FDECL(reset_menu_to_default, (struct menu *));
65 static void FDECL(clear_old_menu, (struct xwindow *));
66 static char *FDECL(copy_of, (const char *));
68 #define reset_menu_count(mi) ((mi)->counting = FALSE, (mi)->menu_count = 0L)
70 static const char menu_translations[] = "#override\n\
71 <Key>Left: scroll(4)\n\
72 <Key>Right: scroll(6)\n\
73 <Key>Up: scroll(8)\n\
74 <Key>Down: scroll(2)\n\
75 <Key>: menu_key()";
78 * Menu callback.
80 /* ARGSUSED */
81 static void
82 menu_select(w, client_data, call_data)
83 Widget w;
84 XtPointer client_data, call_data;
86 struct xwindow *wp;
87 struct menu_info_t *menu_info;
88 #ifdef USE_FWF
89 XfwfMultiListReturnStruct *lrs = (XfwfMultiListReturnStruct *) call_data;
90 #else
91 XawListReturnStruct *lrs = (XawListReturnStruct *) call_data;
92 int i;
93 x11_menu_item *curr;
94 #endif
95 long how_many;
97 nhUse(client_data);
99 wp = find_widget(w);
100 menu_info = wp->menu_information;
101 how_many = menu_info->counting ? menu_info->menu_count : -1L;
102 reset_menu_count(menu_info);
104 #ifdef USE_FWF
105 /* if we've reached here, we've found our selected item */
106 switch (lrs->action) {
107 case XfwfMultiListActionNothing:
108 pline("menu_select: nothing action?");
109 break;
110 case XfwfMultiListActionStatus:
111 pline("menu_select: status action?");
112 break;
113 case XfwfMultiListActionHighlight:
114 case XfwfMultiListActionUnhighlight:
115 sync_selected(menu_info, lrs->num_selected, lrs->selected_items);
116 break;
118 #else
119 for (i = 0, curr = menu_info->curr_menu.base; i < lrs->list_index; i++) {
120 if (!curr)
121 panic("menu_select: out of menu items!");
122 curr = curr->next;
124 XawListUnhighlight(w); /* unhilight item */
126 /* if the menu is not active or don't have an identifier, try again */
127 if (!menu_info->is_active || curr->identifier.a_void == 0) {
128 X11_nhbell();
129 return;
132 /* if we've reached here, we've found our selected item */
133 curr->selected = !curr->selected;
134 if (curr->selected) {
135 curr->str[2] = (how_many != -1L) ? '#' : '+';
136 curr->pick_count = how_many;
137 } else {
138 curr->str[2] = '-';
139 curr->pick_count = -1L;
141 XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0, True);
142 #endif
144 if (menu_info->how == PICK_ONE)
145 menu_popdown(wp);
149 * Called when menu window is deleted.
151 /* ARGSUSED */
152 void
153 menu_delete(w, event, params, num_params)
154 Widget w;
155 XEvent *event;
156 String *params;
157 Cardinal *num_params;
159 nhUse(event);
160 nhUse(params);
161 nhUse(num_params);
163 menu_cancel((Widget) None, (XtPointer) find_widget(w), (XtPointer) 0);
167 * Invert the count'th line (curr) in the given window.
169 /*ARGSUSED*/
170 static void
171 invert_line(wp, curr, which, how_many)
172 struct xwindow *wp;
173 x11_menu_item *curr;
174 int which;
175 long how_many;
177 #ifndef USE_FWF
178 nhUse(which);
179 #endif
180 reset_menu_count(wp->menu_information);
181 curr->selected = !curr->selected;
182 if (curr->selected) {
183 #ifdef USE_FWF
184 XfwfMultiListHighlightItem((XfwfMultiListWidget) wp->w, which);
185 #else
186 curr->str[2] = (how_many != -1) ? '#' : '+';
187 #endif
188 curr->pick_count = how_many;
189 } else {
190 #ifdef USE_FWF
191 XfwfMultiListUnhighlightItem((XfwfMultiListWidget) wp->w, which);
192 #else
193 curr->str[2] = '-';
194 #endif
195 curr->pick_count = -1L;
200 * Called when we get a key press event on a menu window.
202 /* ARGSUSED */
203 void
204 menu_key(w, event, params, num_params)
205 Widget w;
206 XEvent *event;
207 String *params;
208 Cardinal *num_params;
210 struct menu_info_t *menu_info;
211 x11_menu_item *curr;
212 struct xwindow *wp;
213 char ch;
214 int count;
215 boolean selected_something;
217 nhUse(params);
218 nhUse(num_params);
220 wp = find_widget(w);
221 menu_info = wp->menu_information;
223 ch = key_event_to_char((XKeyEvent *) event);
225 if (ch == '\0') { /* don't accept nul char/modifier event */
226 /* don't beep */
227 return;
230 if (menu_info->is_active) { /* waiting for input */
231 /* first check for an explicit selector match, so that it won't be
232 overridden if it happens to duplicate a mapped menu command (':'
233 to look inside a container vs ':' to select via search string) */
234 for (curr = menu_info->curr_menu.base; curr; curr = curr->next)
235 if (curr->identifier.a_void != 0 && curr->selector == ch)
236 goto make_selection;
238 ch = map_menu_cmd(ch);
239 if (ch == '\033') { /* quit */
240 if (menu_info->counting) {
241 /* when there's a count in progress, ESC discards it
242 rather than dismissing the whole menu */
243 reset_menu_count(menu_info);
244 return;
246 select_none(wp);
247 } else if (ch == '\n' || ch == '\r') {
248 ; /* accept */
249 } else if (isdigit(ch)) {
250 /* special case: '0' is also the default ball class */
251 if (ch == '0' && !menu_info->counting
252 && index(menu_info->curr_menu.gacc, ch))
253 goto group_accel;
254 menu_info->menu_count *= 10L;
255 menu_info->menu_count += (long) (ch - '0');
256 if (menu_info->menu_count != 0L) /* ignore leading zeros */
257 menu_info->counting = TRUE;
258 return;
259 } else if (ch == MENU_SEARCH) { /* search */
260 if (menu_info->how == PICK_ANY || menu_info->how == PICK_ONE) {
261 char buf[BUFSZ + 2], tmpbuf[BUFSZ];
263 X11_getlin("Search for:", tmpbuf);
264 if (!*tmpbuf || *tmpbuf == '\033')
265 return;
266 /* convert "string" into "*string*" for use with pmatch() */
267 Sprintf(buf, "*%s*", tmpbuf);
269 if (menu_info->how == PICK_ANY) {
270 invert_match(wp, buf);
271 return;
272 } else {
273 select_match(wp, buf);
275 } else {
276 X11_nhbell();
277 return;
279 } else if (ch == MENU_SELECT_ALL) { /* select all */
280 if (menu_info->how == PICK_ANY)
281 select_all(wp);
282 else
283 X11_nhbell();
284 return;
285 } else if (ch == MENU_UNSELECT_ALL) { /* unselect all */
286 if (menu_info->how == PICK_ANY)
287 select_none(wp);
288 else
289 X11_nhbell();
290 return;
291 } else if (ch == MENU_INVERT_ALL) { /* invert all */
292 if (menu_info->how == PICK_ANY)
293 invert_all(wp);
294 else
295 X11_nhbell();
296 return;
297 } else if (index(menu_info->curr_menu.gacc, ch)) {
298 group_accel:
299 /* matched a group accelerator */
300 if (menu_info->how == PICK_ANY || menu_info->how == PICK_ONE) {
301 for (count = 0, curr = menu_info->curr_menu.base; curr;
302 curr = curr->next, count++) {
303 if (curr->identifier.a_void != 0
304 && curr->gselector == ch) {
305 invert_line(wp, curr, count, -1L);
306 /* for PICK_ONE, a group accelerator will
307 only be included in gacc[] if it matches
308 exactly one entry, so this must be it... */
309 if (menu_info->how == PICK_ONE)
310 goto menu_done; /* pop down */
313 #ifndef USE_FWF
314 XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0,
315 True);
316 #endif
317 } else
318 X11_nhbell();
319 return;
320 } else {
321 make_selection:
322 selected_something = FALSE;
323 for (count = 0, curr = menu_info->curr_menu.base; curr;
324 curr = curr->next, count++)
325 if (curr->identifier.a_void != 0 && curr->selector == ch)
326 break;
328 if (curr) {
329 invert_line(wp, curr, count, menu_info->counting
330 ? menu_info->menu_count
331 : -1L);
332 #ifndef USE_FWF
333 XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0,
334 True);
335 #endif
336 selected_something = curr->selected;
337 } else {
338 X11_nhbell(); /* no match */
340 if (!(selected_something && menu_info->how == PICK_ONE))
341 return; /* keep going */
343 /* pop down */
344 } else { /* permanent inventory window */
345 if (ch != '\033') {
346 X11_nhbell();
347 return;
349 /* pop down on ESC */
352 menu_done:
353 menu_popdown(wp);
356 /* ARGSUSED */
357 static void
358 menu_ok(w, client_data, call_data)
359 Widget w;
360 XtPointer client_data, call_data;
362 struct xwindow *wp = (struct xwindow *) client_data;
364 nhUse(w);
365 nhUse(call_data);
367 menu_popdown(wp);
370 /* ARGSUSED */
371 static void
372 menu_cancel(w, client_data, call_data)
373 Widget w; /* don't use - may be None */
374 XtPointer client_data, call_data;
376 struct xwindow *wp = (struct xwindow *) client_data;
378 nhUse(w);
379 nhUse(call_data);
381 if (wp->menu_information->is_active) {
382 select_none(wp);
383 wp->menu_information->cancelled = TRUE;
385 menu_popdown(wp);
388 /* ARGSUSED */
389 static void
390 menu_all(w, client_data, call_data)
391 Widget w;
392 XtPointer client_data, call_data;
394 nhUse(w);
395 nhUse(call_data);
397 select_all((struct xwindow *) client_data);
400 /* ARGSUSED */
401 static void
402 menu_none(w, client_data, call_data)
403 Widget w;
404 XtPointer client_data, call_data;
406 nhUse(w);
407 nhUse(call_data);
409 select_none((struct xwindow *) client_data);
412 /* ARGSUSED */
413 static void
414 menu_invert(w, client_data, call_data)
415 Widget w;
416 XtPointer client_data, call_data;
418 nhUse(w);
419 nhUse(call_data);
421 invert_all((struct xwindow *) client_data);
424 /* ARGSUSED */
425 static void
426 menu_search(w, client_data, call_data)
427 Widget w;
428 XtPointer client_data, call_data;
430 struct xwindow *wp = (struct xwindow *) client_data;
431 struct menu_info_t *menu_info = wp->menu_information;
432 char buf[BUFSZ + 2], tmpbuf[BUFSZ];
434 nhUse(w);
435 nhUse(call_data);
437 X11_getlin("Search for:", tmpbuf);
438 if (!*tmpbuf || *tmpbuf == '\033')
439 return;
440 /* convert "string" into "*string*" for use with pmatch() */
441 Sprintf(buf, "*%s*", tmpbuf);
443 if (menu_info->how == PICK_ANY)
444 invert_match(wp, buf);
445 else
446 select_match(wp, buf);
448 if (menu_info->how == PICK_ONE)
449 menu_popdown(wp);
452 static void
453 select_all(wp)
454 struct xwindow *wp;
456 x11_menu_item *curr;
457 int count;
458 boolean changed = FALSE;
460 reset_menu_count(wp->menu_information);
461 for (count = 0, curr = wp->menu_information->curr_menu.base; curr;
462 curr = curr->next, count++)
463 if (curr->identifier.a_void != 0)
464 if (!curr->selected) {
465 invert_line(wp, curr, count, -1L);
466 changed = TRUE;
469 #ifndef USE_FWF
470 if (changed)
471 XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer, 0,
472 0, True);
473 #endif
476 static void
477 select_none(wp)
478 struct xwindow *wp;
480 x11_menu_item *curr;
481 int count;
482 boolean changed = FALSE;
484 reset_menu_count(wp->menu_information);
485 for (count = 0, curr = wp->menu_information->curr_menu.base; curr;
486 curr = curr->next, count++)
487 if (curr->identifier.a_void != 0)
488 if (curr->selected) {
489 invert_line(wp, curr, count, -1L);
490 changed = TRUE;
493 #ifndef USE_FWF
494 if (changed)
495 XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer, 0,
496 0, True);
497 #endif
500 static void
501 invert_all(wp)
502 struct xwindow *wp;
504 x11_menu_item *curr;
505 int count;
507 reset_menu_count(wp->menu_information);
508 for (count = 0, curr = wp->menu_information->curr_menu.base; curr;
509 curr = curr->next, count++)
510 if (curr->identifier.a_void != 0)
511 invert_line(wp, curr, count, -1L);
513 #ifndef USE_FWF
514 XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer, 0, 0,
515 True);
516 #endif
519 static void
520 invert_match(wp, match)
521 struct xwindow *wp;
522 char *match; /* wildcard pattern for pmatch() */
524 x11_menu_item *curr;
525 int count;
526 boolean changed = FALSE;
528 reset_menu_count(wp->menu_information);
529 for (count = 0, curr = wp->menu_information->curr_menu.base; curr;
530 curr = curr->next, count++)
531 if (curr->identifier.a_void != 0 && pmatchi(match, curr->str)) {
532 invert_line(wp, curr, count, -1L);
533 changed = TRUE;
536 #ifndef USE_FWF
537 if (changed)
538 XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer, 0,
539 0, True);
540 #endif
543 static void
544 select_match(wp, match)
545 struct xwindow *wp;
546 char *match; /* wildcard pattern for pmatch() */
548 x11_menu_item *curr;
549 int count;
551 reset_menu_count(wp->menu_information);
552 for (count = 0, curr = wp->menu_information->curr_menu.base; curr;
553 curr = curr->next, count++)
554 if (curr->identifier.a_void != 0 && pmatchi(match, curr->str)) {
555 if (!curr->selected) {
556 invert_line(wp, curr, count, -1L);
557 #ifndef USE_FWF
558 XawListChange(wp->w,
559 wp->menu_information->curr_menu.list_pointer, 0,
560 0, True);
561 #endif
563 return;
566 /* no match */
567 X11_nhbell();
570 static void
571 menu_popdown(wp)
572 struct xwindow *wp;
574 nh_XtPopdown(wp->popup); /* remove the event grab */
575 if (wp->menu_information->is_active)
576 exit_x_event = TRUE; /* exit our event handler */
577 wp->menu_information->is_up = FALSE; /* menu is down */
580 #ifdef USE_FWF
582 * Make sure our idea of selected matches the FWF Multilist's idea of what
583 * is currently selected. The MultiList's selected list can change without
584 * notifying us if one or more items are selected and then another is
585 * selected (not toggled). Then the items that were selected are deselected
586 * but we are not notified.
588 static void
589 sync_selected(menu_info, num_selected, items)
590 struct menu_info_t *menu_info;
591 int num_selected;
592 int *items;
594 int i, j, *ip;
595 x11_menu_item *curr;
596 Boolean found;
598 for (i = 0, curr = menu_info->curr_menu.base; curr;
599 i++, curr = curr->next) {
600 found = False;
601 for (j = 0, ip = items; j < num_selected; j++, ip++)
602 if (*ip == i) {
603 found = True;
604 break;
606 #if 0
607 if (curr->selected && !found)
608 printf("sync: deselecting %s\n", curr->str);
609 else if (!curr->selected && found)
610 printf("sync: selecting %s\n", curr->str);
611 #endif
612 curr->selected = found ? TRUE : FALSE;
615 #endif /* USE_FWF */
617 /* Global functions ========================================================
620 void
621 X11_start_menu(window)
622 winid window;
624 struct xwindow *wp;
625 check_winid(window);
627 wp = &window_list[window];
629 if (wp->menu_information->is_menu) {
630 /* make sure we'ere starting with a clean slate */
631 free_menu(&wp->menu_information->new_menu);
632 } else {
633 wp->menu_information->is_menu = TRUE;
637 /*ARGSUSED*/
638 void
639 X11_add_menu(window, glyph, identifier, ch, gch, attr, str, preselected)
640 winid window;
641 int glyph; /* unused (for now) */
642 const anything *identifier;
643 char ch;
644 char gch; /* group accelerator (0 = no group) */
645 int attr;
646 const char *str;
647 boolean preselected;
649 x11_menu_item *item;
650 struct menu_info_t *menu_info;
652 nhUse(glyph);
653 nhUse(preselected); /*FIXME*/
655 check_winid(window);
656 menu_info = window_list[window].menu_information;
657 if (!menu_info->is_menu) {
658 impossible("add_menu: called before start_menu");
659 return;
662 item = (x11_menu_item *) alloc((unsigned) sizeof(x11_menu_item));
663 item->next = (x11_menu_item *) 0;
664 item->identifier = *identifier;
665 item->attr = attr;
666 /* item->selected = preselected; */
667 item->selected = FALSE;
668 item->pick_count = -1L;
670 if (identifier->a_void) {
671 char buf[4 + BUFSZ];
672 int len = strlen(str);
674 if (!ch) {
675 /* Supply a keyboard accelerator. Only the first 52 get one. */
677 if (menu_info->new_menu.curr_selector) {
678 ch = menu_info->new_menu.curr_selector++;
679 if (ch == 'z')
680 menu_info->new_menu.curr_selector = 'A';
681 else if (ch == 'Z')
682 menu_info->new_menu.curr_selector = 0; /* out */
686 if (len >= BUFSZ) {
687 /* We *think* everything's coming in off at most BUFSZ bufs... */
688 impossible("Menu item too long (%d).", len);
689 len = BUFSZ - 1;
691 Sprintf(buf, "%c - ", ch ? ch : ' ');
692 (void) strncpy(buf + 4, str, len);
693 buf[4 + len] = '\0';
694 item->str = copy_of(buf);
695 } else {
696 /* no keyboard accelerator */
697 item->str = copy_of(str);
698 ch = 0;
701 item->selector = ch;
702 item->gselector = gch;
704 if (menu_info->new_menu.last) {
705 menu_info->new_menu.last->next = item;
706 } else {
707 menu_info->new_menu.base = item;
709 menu_info->new_menu.last = item;
710 menu_info->new_menu.count++;
713 void
714 X11_end_menu(window, query)
715 winid window;
716 const char *query;
718 struct menu_info_t *menu_info;
720 check_winid(window);
721 menu_info = window_list[window].menu_information;
722 if (!menu_info->is_menu) {
723 impossible("end_menu: called before start_menu");
724 return;
726 menu_info->new_menu.query = copy_of(query);
730 X11_select_menu(window, how, menu_list)
731 winid window;
732 int how;
733 menu_item **menu_list;
735 x11_menu_item *curr;
736 struct xwindow *wp;
737 struct menu_info_t *menu_info;
738 Arg args[10];
739 Cardinal num_args;
740 String *ptr;
741 int retval;
742 Dimension v_pixel_width, v_pixel_height, lblwidth[6], maxlblwidth;
743 boolean labeled;
744 Widget viewport_widget, form, label,
745 ok, cancel, all, none, invert, search, lblwidget[6];
746 Boolean sens;
747 #ifdef USE_FWF
748 Boolean *boolp;
749 #endif
750 char gacc[QBUFSZ], *ap;
752 *menu_list = (menu_item *) 0;
753 check_winid(window);
754 wp = &window_list[window];
755 menu_info = wp->menu_information;
756 if (!menu_info->is_menu) {
757 impossible("select_menu: called before start_menu");
758 return 0;
761 menu_info->how = (short) how;
763 /* collect group accelerators; for PICK_NONE, they're ignored;
764 for PICK_ONE, only those which match exactly one entry will be
765 accepted; for PICK_ANY, those which match any entry are okay */
766 gacc[0] = '\0';
767 if (menu_info->how != PICK_NONE) {
768 int i, n, gcnt[128];
769 #define GSELIDX(c) ((c) &127) /* guard against `signed char' */
771 for (i = 0; i < SIZE(gcnt); i++)
772 gcnt[i] = 0;
773 for (n = 0, curr = menu_info->new_menu.base; curr; curr = curr->next)
774 if (curr->gselector && curr->gselector != curr->selector) {
775 ++n;
776 ++gcnt[GSELIDX(curr->gselector)];
779 if (n > 0) /* at least one group accelerator found */
780 for (ap = gacc, curr = menu_info->new_menu.base; curr;
781 curr = curr->next)
782 if (curr->gselector && !index(gacc, curr->gselector)
783 && (menu_info->how == PICK_ANY
784 || gcnt[GSELIDX(curr->gselector)] == 1)) {
785 *ap++ = curr->gselector;
786 *ap = '\0'; /* re-terminate for index() */
789 menu_info->new_menu.gacc = copy_of(gacc);
790 reset_menu_count(menu_info);
793 * Create a string and sensitive list for the new menu.
795 menu_info->new_menu.list_pointer = ptr = (String *) alloc(
796 (unsigned) (sizeof(String) * (menu_info->new_menu.count + 1)));
797 for (curr = menu_info->new_menu.base; curr; ptr++, curr = curr->next)
798 *ptr = (String) curr->str;
799 *ptr = 0; /* terminate list with null */
801 #ifdef USE_FWF
802 menu_info->new_menu.sensitive = boolp = (Boolean *) alloc(
803 (unsigned) (sizeof(Boolean) * (menu_info->new_menu.count)));
804 for (curr = menu_info->new_menu.base; curr; boolp++, curr = curr->next)
805 *boolp = (curr->identifier.a_void != 0);
806 #else
807 menu_info->new_menu.sensitive = (Boolean *) 0;
808 #endif
809 labeled = (menu_info->new_menu.query && *(menu_info->new_menu.query))
810 ? TRUE
811 : FALSE;
814 * Menus don't appear to size components correctly, except
815 * when first created. For 3.2.0 release, just recreate
816 * each time.
818 if (menu_info->valid_widgets
819 && (window != WIN_INVEN || !flags.perm_invent)) {
820 XtDestroyWidget(wp->popup);
821 menu_info->valid_widgets = FALSE;
822 menu_info->is_up = FALSE;
825 if (!menu_info->valid_widgets) {
826 Dimension row_spacing;
828 num_args = 0;
829 XtSetArg(args[num_args], XtNallowShellResize, True);
830 num_args++;
831 wp->popup =
832 XtCreatePopupShell(window == WIN_INVEN ? "inventory" : "menu",
833 how == PICK_NONE ? topLevelShellWidgetClass
834 : transientShellWidgetClass,
835 toplevel, args, num_args);
836 XtOverrideTranslations(
837 wp->popup,
838 XtParseTranslationTable("<Message>WM_PROTOCOLS: menu_delete()"));
840 num_args = 0;
841 XtSetArg(args[num_args], XtNtranslations,
842 XtParseTranslationTable(menu_translations));
843 num_args++;
844 form = XtCreateManagedWidget("mform", formWidgetClass, wp->popup,
845 args, num_args);
847 num_args = 0;
848 XtSetArg(args[num_args], XtNborderWidth, 0);
849 num_args++;
850 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
851 num_args++;
852 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop);
853 num_args++;
854 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
855 num_args++;
856 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft);
857 num_args++;
859 if (labeled)
860 label =
861 XtCreateManagedWidget(menu_info->new_menu.query,
862 labelWidgetClass, form, args, num_args);
863 else
864 label = NULL;
867 * Create ok, cancel, all, none, invert, and search buttons..
869 maxlblwidth = 0;
870 num_args = 0;
871 XtSetArg(args[num_args], nhStr(XtNfromVert), label);
872 num_args++;
873 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
874 num_args++;
875 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop);
876 num_args++;
877 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
878 num_args++;
879 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft);
880 num_args++;
881 ok = XtCreateManagedWidget("OK", commandWidgetClass, form, args,
882 num_args);
883 XtAddCallback(ok, XtNcallback, menu_ok, (XtPointer) wp);
884 XtSetArg(args[0], XtNwidth, &lblwidth[0]);
885 XtGetValues(lblwidget[0] = ok, args, ONE);
886 if (lblwidth[0] > maxlblwidth)
887 maxlblwidth = lblwidth[0];
889 num_args = 0;
890 XtSetArg(args[num_args], nhStr(XtNfromVert), label);
891 num_args++;
892 XtSetArg(args[num_args], nhStr(XtNfromHoriz), ok);
893 num_args++;
894 XtSetArg(args[num_args], nhStr(XtNsensitive), how != PICK_NONE);
895 num_args++;
896 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
897 num_args++;
898 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop);
899 num_args++;
900 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
901 num_args++;
902 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft);
903 num_args++;
904 cancel = XtCreateManagedWidget("cancel", commandWidgetClass, form,
905 args, num_args);
906 XtAddCallback(cancel, XtNcallback, menu_cancel, (XtPointer) wp);
907 XtSetArg(args[0], XtNwidth, &lblwidth[1]);
908 XtGetValues(lblwidget[1] = cancel, args, ONE);
909 if (lblwidth[1] > maxlblwidth)
910 maxlblwidth = lblwidth[1];
912 sens = (how == PICK_ANY);
913 num_args = 0;
914 XtSetArg(args[num_args], nhStr(XtNfromVert), label);
915 num_args++;
916 XtSetArg(args[num_args], nhStr(XtNfromHoriz), cancel);
917 num_args++;
918 XtSetArg(args[num_args], nhStr(XtNsensitive), sens);
919 num_args++;
920 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
921 num_args++;
922 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop);
923 num_args++;
924 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
925 num_args++;
926 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft);
927 num_args++;
928 all = XtCreateManagedWidget("all", commandWidgetClass, form, args,
929 num_args);
930 XtAddCallback(all, XtNcallback, menu_all, (XtPointer) wp);
931 XtSetArg(args[0], XtNwidth, &lblwidth[2]);
932 XtGetValues(lblwidget[2] = all, args, ONE);
933 if (lblwidth[2] > maxlblwidth)
934 maxlblwidth = lblwidth[2];
936 num_args = 0;
937 XtSetArg(args[num_args], nhStr(XtNfromVert), label);
938 num_args++;
939 XtSetArg(args[num_args], nhStr(XtNfromHoriz), all);
940 num_args++;
941 XtSetArg(args[num_args], nhStr(XtNsensitive), sens);
942 num_args++;
943 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
944 num_args++;
945 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop);
946 num_args++;
947 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
948 num_args++;
949 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft);
950 num_args++;
951 none = XtCreateManagedWidget("none", commandWidgetClass, form, args,
952 num_args);
953 XtAddCallback(none, XtNcallback, menu_none, (XtPointer) wp);
954 XtSetArg(args[0], XtNwidth, &lblwidth[3]);
955 XtGetValues(lblwidget[3] = none, args, ONE);
956 if (lblwidth[3] > maxlblwidth)
957 maxlblwidth = lblwidth[3];
959 num_args = 0;
960 XtSetArg(args[num_args], nhStr(XtNfromVert), label);
961 num_args++;
962 XtSetArg(args[num_args], nhStr(XtNfromHoriz), none);
963 num_args++;
964 XtSetArg(args[num_args], nhStr(XtNsensitive), sens);
965 num_args++;
966 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
967 num_args++;
968 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop);
969 num_args++;
970 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
971 num_args++;
972 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft);
973 num_args++;
974 invert = XtCreateManagedWidget("invert", commandWidgetClass, form,
975 args, num_args);
976 XtAddCallback(invert, XtNcallback, menu_invert, (XtPointer) wp);
977 XtSetArg(args[0], XtNwidth, &lblwidth[4]);
978 XtGetValues(lblwidget[4] = invert, args, ONE);
979 if (lblwidth[4] > maxlblwidth)
980 maxlblwidth = lblwidth[4];
982 num_args = 0;
983 XtSetArg(args[num_args], nhStr(XtNfromVert), label);
984 num_args++;
985 XtSetArg(args[num_args], nhStr(XtNfromHoriz), invert);
986 num_args++;
987 XtSetArg(args[num_args], nhStr(XtNsensitive), how != PICK_NONE);
988 num_args++;
989 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
990 num_args++;
991 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop);
992 num_args++;
993 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
994 num_args++;
995 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft);
996 num_args++;
997 search = XtCreateManagedWidget("search", commandWidgetClass, form,
998 args, num_args);
999 XtAddCallback(search, XtNcallback, menu_search, (XtPointer) wp);
1000 XtSetArg(args[0], XtNwidth, &lblwidth[5]);
1001 XtGetValues(lblwidget[5] = search, args, ONE);
1002 if (lblwidth[5] > maxlblwidth)
1003 maxlblwidth = lblwidth[5];
1005 /* make all buttons be the same width */
1007 int i;
1009 XtSetArg(args[0], XtNwidth, maxlblwidth);
1010 for (i = 0; i < 6; ++i)
1011 if (lblwidth[i] < maxlblwidth)
1012 XtSetValues(lblwidget[i], args, ONE);
1015 num_args = 0;
1016 XtSetArg(args[num_args], nhStr(XtNallowVert), True);
1017 num_args++;
1018 XtSetArg(args[num_args], nhStr(XtNallowHoriz), False);
1019 num_args++;
1020 XtSetArg(args[num_args], nhStr(XtNuseBottom), True);
1021 num_args++;
1022 XtSetArg(args[num_args], nhStr(XtNuseRight), True);
1023 num_args++;
1024 #if 0
1025 XtSetArg(args[num_args], nhStr(XtNforceBars), True); num_args++;
1026 #endif
1027 XtSetArg(args[num_args], nhStr(XtNfromVert), all);
1028 num_args++;
1029 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop);
1030 num_args++;
1031 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainBottom);
1032 num_args++;
1033 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft);
1034 num_args++;
1035 XtSetArg(args[num_args], nhStr(XtNright), XtChainRight);
1036 num_args++;
1037 viewport_widget = XtCreateManagedWidget(
1038 "menu_viewport", /* name */
1039 viewportWidgetClass, form, /* parent widget */
1040 args, num_args); /* values, and number of values */
1042 /* make new menu the current menu */
1043 move_menu(&menu_info->new_menu, &menu_info->curr_menu);
1045 num_args = 0;
1046 XtSetArg(args[num_args], nhStr(XtNforceColumns), True);
1047 num_args++;
1048 XtSetArg(args[num_args], nhStr(XtNcolumnSpacing), 1);
1049 num_args++;
1050 XtSetArg(args[num_args], nhStr(XtNdefaultColumns), 1);
1051 num_args++;
1052 XtSetArg(args[num_args], nhStr(XtNlist),
1053 menu_info->curr_menu.list_pointer);
1054 num_args++;
1055 #ifdef USE_FWF
1056 XtSetArg(args[num_args], nhStr(XtNsensitiveArray),
1057 menu_info->curr_menu.sensitive);
1058 num_args++;
1059 XtSetArg(args[num_args], nhStr(XtNmaxSelectable),
1060 menu_info->curr_menu.count);
1061 num_args++;
1062 #endif
1063 wp->w = XtCreateManagedWidget("menu_list", /* name */
1064 #ifdef USE_FWF
1065 xfwfMultiListWidgetClass,
1066 #else
1067 listWidgetClass,
1068 #endif
1069 viewport_widget, /* parent widget */
1070 args, /* set some values */
1071 num_args); /* number of values to set */
1073 XtAddCallback(wp->w, XtNcallback, menu_select, (XtPointer) 0);
1075 /* Get the font and margin information. */
1076 num_args = 0;
1077 XtSetArg(args[num_args], XtNfont, &menu_info->fs);
1078 num_args++;
1079 XtSetArg(args[num_args], XtNinternalHeight,
1080 &menu_info->internal_height);
1081 num_args++;
1082 XtSetArg(args[num_args], XtNinternalWidth,
1083 &menu_info->internal_width);
1084 num_args++;
1085 XtSetArg(args[num_args], nhStr(XtNrowSpacing), &row_spacing);
1086 num_args++;
1087 XtGetValues(wp->w, args, num_args);
1089 /* font height is ascent + descent */
1090 menu_info->line_height = menu_info->fs->max_bounds.ascent
1091 + menu_info->fs->max_bounds.descent
1092 + row_spacing;
1094 menu_info->valid_widgets = TRUE;
1096 num_args = 0;
1097 XtSetArg(args[num_args], XtNwidth, &v_pixel_width);
1098 num_args++;
1099 XtSetArg(args[num_args], XtNheight, &v_pixel_height);
1100 num_args++;
1101 XtGetValues(wp->w, args, num_args);
1102 } else {
1103 Dimension len;
1105 viewport_widget = XtParent(wp->w);
1107 /* get the longest string on new menu */
1108 v_pixel_width = 0;
1109 for (ptr = menu_info->new_menu.list_pointer; *ptr; ptr++) {
1110 len = XTextWidth(menu_info->fs, *ptr, strlen(*ptr));
1111 if (len > v_pixel_width)
1112 v_pixel_width = len;
1115 /* add viewport internal border */
1116 v_pixel_width += 2 * menu_info->internal_width;
1117 v_pixel_height =
1118 (2 * menu_info->internal_height)
1119 + (menu_info->new_menu.count * menu_info->line_height);
1121 /* make new menu the current menu */
1122 move_menu(&menu_info->new_menu, &menu_info->curr_menu);
1123 #ifdef USE_FWF
1124 XfwfMultiListSetNewData((XfwfMultiListWidget) wp->w,
1125 menu_info->curr_menu.list_pointer, 0, 0, TRUE,
1126 menu_info->curr_menu.sensitive);
1127 #else
1128 XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0, TRUE);
1129 #endif
1132 /* if viewport will be bigger than the screen, limit its height */
1133 num_args = 0;
1134 XtSetArg(args[num_args], XtNwidth, &v_pixel_width);
1135 num_args++;
1136 XtSetArg(args[num_args], XtNheight, &v_pixel_height);
1137 num_args++;
1138 XtGetValues(wp->w, args, num_args);
1139 if ((Dimension) XtScreen(wp->w)->height * 5 / 6 < v_pixel_height) {
1140 /* scrollbar is 14 pixels wide. Widen the form to accommodate it. */
1141 v_pixel_width += 14;
1143 /* shrink to fit vertically */
1144 v_pixel_height = XtScreen(wp->w)->height * 5 / 6;
1146 num_args = 0;
1147 XtSetArg(args[num_args], XtNwidth, v_pixel_width);
1148 num_args++;
1149 XtSetArg(args[num_args], XtNheight, v_pixel_height);
1150 num_args++;
1151 XtSetValues(wp->w, args, num_args);
1153 XtRealizeWidget(wp->popup); /* need to realize before we position */
1155 /* if menu is not up, position it */
1156 if (!menu_info->is_up)
1157 positionpopup(wp->popup, FALSE);
1159 menu_info->is_up = TRUE;
1160 if (window == WIN_INVEN && how == PICK_NONE) {
1161 /* cant use nh_XtPopup() because it may try to grab the focus */
1162 XtPopup(wp->popup, (int) XtGrabNone);
1163 if (!updated_inventory)
1164 XMapRaised(XtDisplay(wp->popup), XtWindow(wp->popup));
1165 XSetWMProtocols(XtDisplay(wp->popup), XtWindow(wp->popup),
1166 &wm_delete_window, 1);
1167 retval = 0;
1168 } else {
1169 menu_info->is_active = TRUE; /* waiting for user response */
1170 menu_info->cancelled = FALSE;
1171 nh_XtPopup(wp->popup, (int) XtGrabExclusive, wp->w);
1172 (void) x_event(EXIT_ON_EXIT);
1173 menu_info->is_active = FALSE;
1174 if (menu_info->cancelled)
1175 return -1;
1177 retval = 0;
1178 for (curr = menu_info->curr_menu.base; curr; curr = curr->next)
1179 if (curr->selected)
1180 retval++;
1182 if (retval) {
1183 menu_item *mi;
1185 *menu_list = mi = (menu_item *) alloc(retval * sizeof(menu_item));
1186 for (curr = menu_info->curr_menu.base; curr; curr = curr->next)
1187 if (curr->selected) {
1188 mi->item = curr->identifier;
1189 mi->count = curr->pick_count;
1190 mi++;
1195 return retval;
1198 /* End global functions ====================================================
1202 * Allocate a copy of the given string. If null, return a string of
1203 * zero length.
1205 * This is an exact duplicate of copy_of() in tty/wintty.c.
1207 static char *
1208 copy_of(s)
1209 const char *s;
1211 if (!s)
1212 s = "";
1213 return strcpy((char *) alloc((unsigned) (strlen(s) + 1)), s);
1216 static void
1217 move_menu(src_menu, dest_menu)
1218 struct menu *src_menu, *dest_menu;
1220 free_menu(dest_menu); /* toss old menu */
1221 *dest_menu = *src_menu; /* make new menu current */
1222 /* leave no dangling ptrs */
1223 reset_menu_to_default(src_menu);
1226 static void
1227 free_menu(mp)
1228 struct menu *mp;
1230 while (mp->base) {
1231 mp->last = mp->base;
1232 mp->base = mp->base->next;
1234 free((genericptr_t) mp->last->str);
1235 free((genericptr_t) mp->last);
1237 if (mp->query)
1238 free((genericptr_t) mp->query);
1239 if (mp->gacc)
1240 free((genericptr_t) mp->gacc);
1241 if (mp->list_pointer)
1242 free((genericptr_t) mp->list_pointer);
1243 if (mp->sensitive)
1244 free((genericptr_t) mp->sensitive);
1245 reset_menu_to_default(mp);
1248 static void
1249 reset_menu_to_default(mp)
1250 struct menu *mp;
1252 mp->base = mp->last = (x11_menu_item *) 0;
1253 mp->query = (const char *) 0;
1254 mp->gacc = (const char *) 0;
1255 mp->count = 0;
1256 mp->list_pointer = (String *) 0;
1257 mp->sensitive = (Boolean *) 0;
1258 mp->curr_selector = 'a'; /* first accelerator */
1261 static void
1262 clear_old_menu(wp)
1263 struct xwindow *wp;
1265 struct menu_info_t *menu_info = wp->menu_information;
1267 free_menu(&menu_info->curr_menu);
1268 free_menu(&menu_info->new_menu);
1270 if (menu_info->valid_widgets) {
1271 nh_XtPopdown(wp->popup);
1272 menu_info->is_up = FALSE;
1273 XtDestroyWidget(wp->popup);
1274 menu_info->valid_widgets = FALSE;
1275 wp->w = wp->popup = (Widget) 0;
1279 void
1280 create_menu_window(wp)
1281 struct xwindow *wp;
1283 wp->type = NHW_MENU;
1284 wp->menu_information =
1285 (struct menu_info_t *) alloc(sizeof(struct menu_info_t));
1286 (void) memset((genericptr_t) wp->menu_information, '\0',
1287 sizeof(struct menu_info_t));
1288 reset_menu_to_default(&wp->menu_information->curr_menu);
1289 reset_menu_to_default(&wp->menu_information->new_menu);
1290 reset_menu_count(wp->menu_information);
1291 wp->w = wp->popup = (Widget) 0;
1294 void
1295 destroy_menu_window(wp)
1296 struct xwindow *wp;
1298 clear_old_menu(wp); /* this will also destroy the widgets */
1299 free((genericptr_t) wp->menu_information);
1300 wp->menu_information = (struct menu_info_t *) 0;
1301 wp->type = NHW_NONE; /* allow re-use */
1304 /*winmenu.c*/