NHDT->ANH, nethack->anethack, nhdat->anhdat
[aNetHack.git] / win / X11 / winmenu.c
blob60787660bc7b93d2664173b0be458d4a6b331460
1 /* aNetHack 0.0.1 winmenu.c $ANH-Date: 1453448854 2016/01/22 07:47:34 $ $ANH-Branch: aNetHack-3.6.0 $:$ANH-Revision: 1.13 $ */
2 /* Copyright (c) Dean Luick, 1992 */
3 /* aNetHack 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"
42 static void FDECL(menu_select, (Widget, XtPointer, XtPointer));
43 static void FDECL(invert_line, (struct xwindow *, x11_menu_item *, int, long));
44 static void FDECL(menu_ok, (Widget, XtPointer, XtPointer));
45 static void FDECL(menu_cancel, (Widget, XtPointer, XtPointer));
46 static void FDECL(menu_all, (Widget, XtPointer, XtPointer));
47 static void FDECL(menu_none, (Widget, XtPointer, XtPointer));
48 static void FDECL(menu_invert, (Widget, XtPointer, XtPointer));
49 static void FDECL(menu_search, (Widget, XtPointer, XtPointer));
50 static void FDECL(select_all, (struct xwindow *));
51 static void FDECL(select_none, (struct xwindow *));
52 static void FDECL(select_match, (struct xwindow *, char *));
53 static void FDECL(invert_all, (struct xwindow *));
54 static void FDECL(invert_match, (struct xwindow *, char *));
55 static void FDECL(menu_popdown, (struct xwindow *));
56 #ifdef USE_FWF
57 static void FDECL(sync_selected, (struct menu_info_t *, int, int *));
58 #endif
60 static void FDECL(move_menu, (struct menu *, struct menu *));
61 static void FDECL(free_menu, (struct menu *));
62 static void FDECL(reset_menu_to_default, (struct menu *));
63 static void FDECL(clear_old_menu, (struct xwindow *));
64 static char *FDECL(copy_of, (const char *));
66 #define reset_menu_count(mi) ((mi)->counting = FALSE, (mi)->menu_count = 0L)
68 static const char menu_translations[] = "#override\n\
69 <Key>Left: scroll(4)\n\
70 <Key>Right: scroll(6)\n\
71 <Key>Up: scroll(8)\n\
72 <Key>Down: scroll(2)\n\
73 <Key>: menu_key()";
76 * Menu callback.
78 /* ARGSUSED */
79 static void
80 menu_select(w, client_data, call_data)
81 Widget w;
82 XtPointer client_data, call_data;
84 struct xwindow *wp;
85 struct menu_info_t *menu_info;
86 #ifdef USE_FWF
87 XfwfMultiListReturnStruct *lrs = (XfwfMultiListReturnStruct *) call_data;
88 #else
89 XawListReturnStruct *lrs = (XawListReturnStruct *) call_data;
90 int i;
91 x11_menu_item *curr;
92 #endif
93 long how_many;
95 nhUse(client_data);
97 wp = find_widget(w);
98 menu_info = wp->menu_information;
99 how_many = menu_info->counting ? menu_info->menu_count : -1L;
100 reset_menu_count(menu_info);
102 #ifdef USE_FWF
103 /* if we've reached here, we've found our selected item */
104 switch (lrs->action) {
105 case XfwfMultiListActionNothing:
106 pline("menu_select: nothing action?");
107 break;
108 case XfwfMultiListActionStatus:
109 pline("menu_select: status action?");
110 break;
111 case XfwfMultiListActionHighlight:
112 case XfwfMultiListActionUnhighlight:
113 sync_selected(menu_info, lrs->num_selected, lrs->selected_items);
114 break;
116 #else
117 for (i = 0, curr = menu_info->curr_menu.base; i < lrs->list_index; i++) {
118 if (!curr)
119 panic("menu_select: out of menu items!");
120 curr = curr->next;
122 XawListUnhighlight(w); /* unhilight item */
124 /* if the menu is not active or don't have an identifier, try again */
125 if (!menu_info->is_active || curr->identifier.a_void == 0) {
126 X11_nhbell();
127 return;
130 /* if we've reached here, we've found our selected item */
131 if (menu_info->how != PICK_ONE || !curr->preselected)
132 curr->selected = !curr->selected;
133 curr->preselected = FALSE;
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 /* invert selection unless explicitly choosing the preselected
182 entry of a PICK_ONE menu */
183 if (wp->menu_information->how != PICK_ONE || !curr->preselected)
184 curr->selected = !curr->selected;
185 curr->preselected = FALSE;
186 if (curr->selected) {
187 #ifdef USE_FWF
188 XfwfMultiListHighlightItem((XfwfMultiListWidget) wp->w, which);
189 #else
190 curr->str[2] = (how_many != -1) ? '#' : '+';
191 #endif
192 curr->pick_count = how_many;
193 } else {
194 #ifdef USE_FWF
195 XfwfMultiListUnhighlightItem((XfwfMultiListWidget) wp->w, which);
196 #else
197 curr->str[2] = '-';
198 #endif
199 curr->pick_count = -1L;
204 * Called when we get a key press event on a menu window.
206 /* ARGSUSED */
207 void
208 menu_key(w, event, params, num_params)
209 Widget w;
210 XEvent *event;
211 String *params;
212 Cardinal *num_params;
214 struct menu_info_t *menu_info;
215 x11_menu_item *curr;
216 struct xwindow *wp;
217 char ch;
218 int count;
219 boolean selected_something;
221 nhUse(params);
222 nhUse(num_params);
224 wp = find_widget(w);
225 menu_info = wp->menu_information;
227 ch = key_event_to_char((XKeyEvent *) event);
229 if (ch == '\0') { /* don't accept nul char/modifier event */
230 /* don't beep */
231 return;
234 if (menu_info->is_active) { /* waiting for input */
235 /* first check for an explicit selector match, so that it won't be
236 overridden if it happens to duplicate a mapped menu command (':'
237 to look inside a container vs ':' to select via search string) */
238 for (curr = menu_info->curr_menu.base; curr; curr = curr->next)
239 if (curr->identifier.a_void != 0 && curr->selector == ch)
240 goto make_selection;
242 ch = map_menu_cmd(ch);
243 if (ch == '\033') { /* quit */
244 if (menu_info->counting) {
245 /* when there's a count in progress, ESC discards it
246 rather than dismissing the whole menu */
247 reset_menu_count(menu_info);
248 return;
250 select_none(wp);
251 } else if (ch == '\n' || ch == '\r') {
252 ; /* accept */
253 } else if (digit(ch)) {
254 /* special case: '0' is also the default ball class */
255 if (ch == '0' && !menu_info->counting
256 && index(menu_info->curr_menu.gacc, ch))
257 goto group_accel;
258 menu_info->menu_count *= 10L;
259 menu_info->menu_count += (long) (ch - '0');
260 if (menu_info->menu_count != 0L) /* ignore leading zeros */
261 menu_info->counting = TRUE;
262 return;
263 } else if (ch == MENU_SEARCH) { /* search */
264 if (menu_info->how == PICK_ANY || menu_info->how == PICK_ONE) {
265 char buf[BUFSZ + 2], tmpbuf[BUFSZ];
267 X11_getlin("Search for:", tmpbuf);
268 if (!*tmpbuf || *tmpbuf == '\033')
269 return;
270 /* convert "string" into "*string*" for use with pmatch() */
271 Sprintf(buf, "*%s*", tmpbuf);
273 if (menu_info->how == PICK_ANY) {
274 invert_match(wp, buf);
275 return;
276 } else {
277 select_match(wp, buf);
279 } else {
280 X11_nhbell();
281 return;
283 } else if (ch == MENU_SELECT_ALL) { /* select all */
284 if (menu_info->how == PICK_ANY)
285 select_all(wp);
286 else
287 X11_nhbell();
288 return;
289 } else if (ch == MENU_UNSELECT_ALL) { /* unselect all */
290 if (menu_info->how == PICK_ANY)
291 select_none(wp);
292 else
293 X11_nhbell();
294 return;
295 } else if (ch == MENU_INVERT_ALL) { /* invert all */
296 if (menu_info->how == PICK_ANY)
297 invert_all(wp);
298 else
299 X11_nhbell();
300 return;
301 } else if (index(menu_info->curr_menu.gacc, ch)) {
302 group_accel:
303 /* matched a group accelerator */
304 if (menu_info->how == PICK_ANY || menu_info->how == PICK_ONE) {
305 for (count = 0, curr = menu_info->curr_menu.base; curr;
306 curr = curr->next, count++) {
307 if (curr->identifier.a_void != 0
308 && curr->gselector == ch) {
309 invert_line(wp, curr, count, -1L);
310 /* for PICK_ONE, a group accelerator will
311 only be included in gacc[] if it matches
312 exactly one entry, so this must be it... */
313 if (menu_info->how == PICK_ONE)
314 goto menu_done; /* pop down */
317 #ifndef USE_FWF
318 XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0,
319 True);
320 #endif
321 } else
322 X11_nhbell();
323 return;
324 } else {
325 make_selection:
326 selected_something = FALSE;
327 for (count = 0, curr = menu_info->curr_menu.base; curr;
328 curr = curr->next, count++)
329 if (curr->identifier.a_void != 0 && curr->selector == ch)
330 break;
332 if (curr) {
333 invert_line(wp, curr, count,
334 menu_info->counting ? menu_info->menu_count : -1L);
335 #ifndef USE_FWF
336 XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0,
337 True);
338 #endif
339 selected_something = curr->selected;
340 } else {
341 X11_nhbell(); /* no match */
343 if (!(selected_something && menu_info->how == PICK_ONE))
344 return; /* keep going */
346 /* pop down */
347 } else { /* permanent inventory window */
348 if (ch != '\033') {
349 X11_nhbell();
350 return;
352 /* pop down on ESC */
355 menu_done:
356 menu_popdown(wp);
359 /* ARGSUSED */
360 static void
361 menu_ok(w, client_data, call_data)
362 Widget w;
363 XtPointer client_data, call_data;
365 struct xwindow *wp = (struct xwindow *) client_data;
367 nhUse(w);
368 nhUse(call_data);
370 menu_popdown(wp);
373 /* ARGSUSED */
374 static void
375 menu_cancel(w, client_data, call_data)
376 Widget w; /* don't use - may be None */
377 XtPointer client_data, call_data;
379 struct xwindow *wp = (struct xwindow *) client_data;
381 nhUse(w);
382 nhUse(call_data);
384 if (wp->menu_information->is_active) {
385 select_none(wp);
386 wp->menu_information->cancelled = TRUE;
388 menu_popdown(wp);
391 /* ARGSUSED */
392 static void
393 menu_all(w, client_data, call_data)
394 Widget w;
395 XtPointer client_data, call_data;
397 nhUse(w);
398 nhUse(call_data);
400 select_all((struct xwindow *) client_data);
403 /* ARGSUSED */
404 static void
405 menu_none(w, client_data, call_data)
406 Widget w;
407 XtPointer client_data, call_data;
409 nhUse(w);
410 nhUse(call_data);
412 select_none((struct xwindow *) client_data);
415 /* ARGSUSED */
416 static void
417 menu_invert(w, client_data, call_data)
418 Widget w;
419 XtPointer client_data, call_data;
421 nhUse(w);
422 nhUse(call_data);
424 invert_all((struct xwindow *) client_data);
427 /* ARGSUSED */
428 static void
429 menu_search(w, client_data, call_data)
430 Widget w;
431 XtPointer client_data, call_data;
433 struct xwindow *wp = (struct xwindow *) client_data;
434 struct menu_info_t *menu_info = wp->menu_information;
435 char buf[BUFSZ + 2], tmpbuf[BUFSZ];
437 nhUse(w);
438 nhUse(call_data);
440 X11_getlin("Search for:", tmpbuf);
441 if (!*tmpbuf || *tmpbuf == '\033')
442 return;
443 /* convert "string" into "*string*" for use with pmatch() */
444 Sprintf(buf, "*%s*", tmpbuf);
446 if (menu_info->how == PICK_ANY)
447 invert_match(wp, buf);
448 else
449 select_match(wp, buf);
451 if (menu_info->how == PICK_ONE)
452 menu_popdown(wp);
455 static void
456 select_all(wp)
457 struct xwindow *wp;
459 x11_menu_item *curr;
460 int count;
461 boolean changed = FALSE;
463 reset_menu_count(wp->menu_information);
464 for (count = 0, curr = wp->menu_information->curr_menu.base; curr;
465 curr = curr->next, count++)
466 if (curr->identifier.a_void != 0)
467 if (!curr->selected) {
468 invert_line(wp, curr, count, -1L);
469 changed = TRUE;
472 #ifndef USE_FWF
473 if (changed)
474 XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer,
475 0, 0, True);
476 #endif
479 static void
480 select_none(wp)
481 struct xwindow *wp;
483 x11_menu_item *curr;
484 int count;
485 boolean changed = FALSE;
487 reset_menu_count(wp->menu_information);
488 for (count = 0, curr = wp->menu_information->curr_menu.base; curr;
489 curr = curr->next, count++)
490 if (curr->identifier.a_void != 0)
491 if (curr->selected) {
492 invert_line(wp, curr, count, -1L);
493 changed = TRUE;
496 #ifndef USE_FWF
497 if (changed)
498 XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer,
499 0, 0, True);
500 #endif
503 static void
504 invert_all(wp)
505 struct xwindow *wp;
507 x11_menu_item *curr;
508 int count;
510 reset_menu_count(wp->menu_information);
511 for (count = 0, curr = wp->menu_information->curr_menu.base; curr;
512 curr = curr->next, count++)
513 if (curr->identifier.a_void != 0)
514 invert_line(wp, curr, count, -1L);
516 #ifndef USE_FWF
517 XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer,
518 0, 0, True);
519 #endif
522 static void
523 invert_match(wp, match)
524 struct xwindow *wp;
525 char *match; /* wildcard pattern for pmatch() */
527 x11_menu_item *curr;
528 int count;
529 boolean changed = FALSE;
531 reset_menu_count(wp->menu_information);
532 for (count = 0, curr = wp->menu_information->curr_menu.base; curr;
533 curr = curr->next, count++)
534 if (curr->identifier.a_void != 0) {
535 if (pmatchi(match, curr->str)) {
536 invert_line(wp, curr, count, -1L);
537 changed = TRUE;
539 curr->preselected = FALSE;
542 #ifndef USE_FWF
543 if (changed)
544 XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer,
545 0, 0, True);
546 #endif
549 static void
550 select_match(wp, match)
551 struct xwindow *wp;
552 char *match; /* wildcard pattern for pmatch() */
554 x11_menu_item *curr, *found = 0;
555 int count;
557 reset_menu_count(wp->menu_information);
558 for (count = 0, curr = wp->menu_information->curr_menu.base; curr;
559 curr = curr->next, count++)
560 if (curr->identifier.a_void != 0) {
561 if (!found && pmatchi(match, curr->str))
562 found = curr;
563 curr->preselected = FALSE;
566 if (found) {
567 if (!found->selected) {
568 invert_line(wp, found, count, -1L);
569 #ifndef USE_FWF
570 XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer,
571 0, 0, True);
572 #endif
574 } else {
575 /* no match */
576 X11_nhbell();
580 static void
581 menu_popdown(wp)
582 struct xwindow *wp;
584 nh_XtPopdown(wp->popup); /* remove the event grab */
585 if (wp->menu_information->is_active)
586 exit_x_event = TRUE; /* exit our event handler */
587 wp->menu_information->is_up = FALSE; /* menu is down */
590 #ifdef USE_FWF
592 * Make sure our idea of selected matches the FWF Multilist's idea of what
593 * is currently selected. The MultiList's selected list can change without
594 * notifying us if one or more items are selected and then another is
595 * selected (not toggled). Then the items that were selected are deselected
596 * but we are not notified.
598 static void
599 sync_selected(menu_info, num_selected, items)
600 struct menu_info_t *menu_info;
601 int num_selected;
602 int *items;
604 int i, j, *ip;
605 x11_menu_item *curr;
606 Boolean found;
608 for (i = 0, curr = menu_info->curr_menu.base; curr;
609 i++, curr = curr->next) {
610 found = False;
611 for (j = 0, ip = items; j < num_selected; j++, ip++)
612 if (*ip == i) {
613 found = True;
614 break;
616 #if 0
617 if (curr->selected && !found)
618 printf("sync: deselecting %s\n", curr->str);
619 else if (!curr->selected && found)
620 printf("sync: selecting %s\n", curr->str);
621 #endif
622 curr->selected = found ? TRUE : FALSE;
623 /* once active selection takes place, preselection becomes history */
624 curr->preselected = FALSE;
627 #endif /* USE_FWF */
629 /* Global functions ========================================================
632 void
633 X11_start_menu(window)
634 winid window;
636 struct xwindow *wp;
637 check_winid(window);
639 wp = &window_list[window];
641 if (wp->menu_information->is_menu) {
642 /* make sure we'ere starting with a clean slate */
643 free_menu(&wp->menu_information->new_menu);
644 } else {
645 wp->menu_information->is_menu = TRUE;
649 /*ARGSUSED*/
650 void
651 X11_add_menu(window, glyph, identifier, ch, gch, attr, str, preselected)
652 winid window;
653 int glyph; /* unused (for now) */
654 const anything *identifier;
655 char ch;
656 char gch; /* group accelerator (0 = no group) */
657 int attr;
658 const char *str;
659 boolean preselected;
661 x11_menu_item *item;
662 struct menu_info_t *menu_info;
664 nhUse(glyph);
666 check_winid(window);
667 menu_info = window_list[window].menu_information;
668 if (!menu_info->is_menu) {
669 impossible("add_menu: called before start_menu");
670 return;
673 item = (x11_menu_item *) alloc((unsigned) sizeof(x11_menu_item));
674 item->next = (x11_menu_item *) 0;
675 item->identifier = *identifier;
676 item->attr = attr;
677 item->selected = item->preselected = preselected;
678 item->pick_count = -1L;
680 if (identifier->a_void) {
681 char buf[4 + BUFSZ];
682 int len = strlen(str);
684 if (!ch) {
685 /* Supply a keyboard accelerator. Only the first 52 get one. */
687 if (menu_info->new_menu.curr_selector) {
688 ch = menu_info->new_menu.curr_selector++;
689 if (ch == 'z')
690 menu_info->new_menu.curr_selector = 'A';
691 else if (ch == 'Z')
692 menu_info->new_menu.curr_selector = 0; /* out */
696 if (len >= BUFSZ) {
697 /* We *think* everything's coming in off at most BUFSZ bufs... */
698 impossible("Menu item too long (%d).", len);
699 len = BUFSZ - 1;
701 Sprintf(buf, "%c %c ", ch ? ch : ' ', preselected ? '+' : '-');
702 (void) strncpy(buf + 4, str, len);
703 buf[4 + len] = '\0';
704 item->str = copy_of(buf);
705 } else {
706 /* no keyboard accelerator */
707 item->str = copy_of(str);
708 ch = 0;
711 item->selector = ch;
712 item->gselector = gch;
714 if (menu_info->new_menu.last) {
715 menu_info->new_menu.last->next = item;
716 } else {
717 menu_info->new_menu.base = item;
719 menu_info->new_menu.last = item;
720 menu_info->new_menu.count++;
723 void
724 X11_end_menu(window, query)
725 winid window;
726 const char *query;
728 struct menu_info_t *menu_info;
730 check_winid(window);
731 menu_info = window_list[window].menu_information;
732 if (!menu_info->is_menu) {
733 impossible("end_menu: called before start_menu");
734 return;
736 menu_info->new_menu.query = copy_of(query);
740 X11_select_menu(window, how, menu_list)
741 winid window;
742 int how;
743 menu_item **menu_list;
745 x11_menu_item *curr;
746 struct xwindow *wp;
747 struct menu_info_t *menu_info;
748 Arg args[10];
749 Cardinal num_args;
750 String *ptr;
751 int retval;
752 Dimension v_pixel_width, v_pixel_height, lblwidth[6], maxlblwidth;
753 boolean labeled;
754 Widget viewport_widget, form, label,
755 ok, cancel, all, none, invert, search, lblwidget[6];
756 Boolean sens;
757 #ifdef USE_FWF
758 Boolean *boolp;
759 #endif
760 char gacc[QBUFSZ], *ap;
762 *menu_list = (menu_item *) 0;
763 check_winid(window);
764 wp = &window_list[window];
765 menu_info = wp->menu_information;
766 if (!menu_info->is_menu) {
767 impossible("select_menu: called before start_menu");
768 return 0;
771 menu_info->how = (short) how;
773 /* collect group accelerators; for PICK_NONE, they're ignored;
774 for PICK_ONE, only those which match exactly one entry will be
775 accepted; for PICK_ANY, those which match any entry are okay */
776 gacc[0] = '\0';
777 if (menu_info->how != PICK_NONE) {
778 int i, n, gcnt[128];
779 #define GSELIDX(c) ((c) &127) /* guard against `signed char' */
781 for (i = 0; i < SIZE(gcnt); i++)
782 gcnt[i] = 0;
783 for (n = 0, curr = menu_info->new_menu.base; curr; curr = curr->next)
784 if (curr->gselector && curr->gselector != curr->selector) {
785 ++n;
786 ++gcnt[GSELIDX(curr->gselector)];
789 if (n > 0) /* at least one group accelerator found */
790 for (ap = gacc, curr = menu_info->new_menu.base; curr;
791 curr = curr->next)
792 if (curr->gselector && !index(gacc, curr->gselector)
793 && (menu_info->how == PICK_ANY
794 || gcnt[GSELIDX(curr->gselector)] == 1)) {
795 *ap++ = curr->gselector;
796 *ap = '\0'; /* re-terminate for index() */
799 menu_info->new_menu.gacc = copy_of(gacc);
800 reset_menu_count(menu_info);
803 * Create a string and sensitive list for the new menu.
805 menu_info->new_menu.list_pointer = ptr = (String *) alloc(
806 (unsigned) (sizeof(String) * (menu_info->new_menu.count + 1)));
807 for (curr = menu_info->new_menu.base; curr; ptr++, curr = curr->next)
808 *ptr = (String) curr->str;
809 *ptr = 0; /* terminate list with null */
811 #ifdef USE_FWF
812 menu_info->new_menu.sensitive = boolp = (Boolean *) alloc(
813 (unsigned) (sizeof(Boolean) * (menu_info->new_menu.count)));
814 for (curr = menu_info->new_menu.base; curr; boolp++, curr = curr->next)
815 *boolp = (curr->identifier.a_void != 0);
816 #else
817 menu_info->new_menu.sensitive = (Boolean *) 0;
818 #endif
819 labeled = (menu_info->new_menu.query && *(menu_info->new_menu.query))
820 ? TRUE
821 : FALSE;
824 * Menus don't appear to size components correctly, except
825 * when first created. For 3.2.0 release, just recreate
826 * each time.
828 if (menu_info->valid_widgets
829 && (window != WIN_INVEN || !flags.perm_invent)) {
830 XtDestroyWidget(wp->popup);
831 menu_info->valid_widgets = FALSE;
832 menu_info->is_up = FALSE;
835 if (!menu_info->valid_widgets) {
836 Dimension row_spacing;
838 num_args = 0;
839 XtSetArg(args[num_args], XtNallowShellResize, True);
840 num_args++;
841 wp->popup = XtCreatePopupShell((window == WIN_INVEN)
842 ? "inventory" : "menu",
843 (how == PICK_NONE)
844 ? topLevelShellWidgetClass
845 : transientShellWidgetClass,
846 toplevel, args, num_args);
847 XtOverrideTranslations(wp->popup,
848 XtParseTranslationTable(
849 "<Message>WM_PROTOCOLS: menu_delete()"));
851 num_args = 0;
852 XtSetArg(args[num_args], XtNtranslations,
853 XtParseTranslationTable(menu_translations)); num_args++;
854 form = XtCreateManagedWidget("mform", formWidgetClass, wp->popup,
855 args, num_args);
857 num_args = 0;
858 XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;
859 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++;
860 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop); num_args++;
861 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
862 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++;
864 label = labeled ? XtCreateManagedWidget(menu_info->new_menu.query,
865 labelWidgetClass, form,
866 args, num_args)
867 : (Widget) 0;
870 * Create ok, cancel, all, none, invert, and search buttons..
872 maxlblwidth = 0;
873 num_args = 0;
874 XtSetArg(args[num_args], nhStr(XtNfromVert), label); num_args++;
875 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++;
876 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop); num_args++;
877 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
878 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++;
879 ok = XtCreateManagedWidget("OK", commandWidgetClass, form,
880 args, num_args);
881 XtAddCallback(ok, XtNcallback, menu_ok, (XtPointer) wp);
882 XtSetArg(args[0], XtNwidth, &lblwidth[0]);
883 XtGetValues(lblwidget[0] = ok, args, ONE);
884 if (lblwidth[0] > maxlblwidth)
885 maxlblwidth = lblwidth[0];
887 num_args = 0;
888 XtSetArg(args[num_args], nhStr(XtNfromVert), label); num_args++;
889 XtSetArg(args[num_args], nhStr(XtNfromHoriz), ok); num_args++;
890 XtSetArg(args[num_args], nhStr(XtNsensitive), how != PICK_NONE);
891 num_args++;
892 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++;
893 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop); num_args++;
894 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
895 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++;
896 cancel = XtCreateManagedWidget("cancel", commandWidgetClass, form,
897 args, num_args);
898 XtAddCallback(cancel, XtNcallback, menu_cancel, (XtPointer) wp);
899 XtSetArg(args[0], XtNwidth, &lblwidth[1]);
900 XtGetValues(lblwidget[1] = cancel, args, ONE);
901 if (lblwidth[1] > maxlblwidth)
902 maxlblwidth = lblwidth[1];
904 sens = (how == PICK_ANY);
905 num_args = 0;
906 XtSetArg(args[num_args], nhStr(XtNfromVert), label); num_args++;
907 XtSetArg(args[num_args], nhStr(XtNfromHoriz), cancel); num_args++;
908 XtSetArg(args[num_args], nhStr(XtNsensitive), sens); num_args++;
909 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++;
910 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop); num_args++;
911 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
912 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++;
913 all = XtCreateManagedWidget("all", commandWidgetClass, form,
914 args, num_args);
915 XtAddCallback(all, XtNcallback, menu_all, (XtPointer) wp);
916 XtSetArg(args[0], XtNwidth, &lblwidth[2]);
917 XtGetValues(lblwidget[2] = all, args, ONE);
918 if (lblwidth[2] > maxlblwidth)
919 maxlblwidth = lblwidth[2];
921 num_args = 0;
922 XtSetArg(args[num_args], nhStr(XtNfromVert), label); num_args++;
923 XtSetArg(args[num_args], nhStr(XtNfromHoriz), all); num_args++;
924 XtSetArg(args[num_args], nhStr(XtNsensitive), sens); num_args++;
925 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++;
926 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop); num_args++;
927 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
928 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++;
929 none = XtCreateManagedWidget("none", commandWidgetClass, form,
930 args, num_args);
931 XtAddCallback(none, XtNcallback, menu_none, (XtPointer) wp);
932 XtSetArg(args[0], XtNwidth, &lblwidth[3]);
933 XtGetValues(lblwidget[3] = none, args, ONE);
934 if (lblwidth[3] > maxlblwidth)
935 maxlblwidth = lblwidth[3];
937 num_args = 0;
938 XtSetArg(args[num_args], nhStr(XtNfromVert), label); num_args++;
939 XtSetArg(args[num_args], nhStr(XtNfromHoriz), none); num_args++;
940 XtSetArg(args[num_args], nhStr(XtNsensitive), sens); num_args++;
941 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++;
942 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop); num_args++;
943 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
944 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++;
945 invert = XtCreateManagedWidget("invert", commandWidgetClass, form,
946 args, num_args);
947 XtAddCallback(invert, XtNcallback, menu_invert, (XtPointer) wp);
948 XtSetArg(args[0], XtNwidth, &lblwidth[4]);
949 XtGetValues(lblwidget[4] = invert, args, ONE);
950 if (lblwidth[4] > maxlblwidth)
951 maxlblwidth = lblwidth[4];
953 num_args = 0;
954 XtSetArg(args[num_args], nhStr(XtNfromVert), label); num_args++;
955 XtSetArg(args[num_args], nhStr(XtNfromHoriz), invert); num_args++;
956 XtSetArg(args[num_args], nhStr(XtNsensitive), how != PICK_NONE);
957 num_args++;
958 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++;
959 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop); num_args++;
960 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
961 XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++;
962 search = XtCreateManagedWidget("search", commandWidgetClass, form,
963 args, num_args);
964 XtAddCallback(search, XtNcallback, menu_search, (XtPointer) wp);
965 XtSetArg(args[0], XtNwidth, &lblwidth[5]);
966 XtGetValues(lblwidget[5] = search, args, ONE);
967 if (lblwidth[5] > maxlblwidth)
968 maxlblwidth = lblwidth[5];
970 /* make all buttons be the same width */
972 int i;
974 XtSetArg(args[0], XtNwidth, maxlblwidth);
975 for (i = 0; i < 6; ++i)
976 if (lblwidth[i] < maxlblwidth)
977 XtSetValues(lblwidget[i], args, ONE);
980 num_args = 0;
981 XtSetArg(args[num_args], nhStr(XtNallowVert), True); num_args++;
982 XtSetArg(args[num_args], nhStr(XtNallowHoriz), False); num_args++;
983 XtSetArg(args[num_args], nhStr(XtNuseBottom), True); num_args++;
984 XtSetArg(args[num_args], nhStr(XtNuseRight), True); num_args++;
985 #if 0
986 XtSetArg(args[num_args], nhStr(XtNforceBars), True); num_args++;
987 #endif
988 XtSetArg(args[num_args], nhStr(XtNfromVert), all); num_args++;
989 XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++;
990 XtSetArg(args[num_args], nhStr(XtNbottom), XtChainBottom); num_args++;
991 XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
992 XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++;
993 viewport_widget = XtCreateManagedWidget(
994 "menu_viewport", /* name */
995 viewportWidgetClass, form, /* parent widget */
996 args, num_args); /* values, and number of values */
998 /* make new menu the current menu */
999 move_menu(&menu_info->new_menu, &menu_info->curr_menu);
1001 num_args = 0;
1002 XtSetArg(args[num_args], nhStr(XtNforceColumns), True); num_args++;
1003 XtSetArg(args[num_args], nhStr(XtNcolumnSpacing), 1); num_args++;
1004 XtSetArg(args[num_args], nhStr(XtNdefaultColumns), 1); num_args++;
1005 XtSetArg(args[num_args], nhStr(XtNlist),
1006 menu_info->curr_menu.list_pointer); num_args++;
1007 #ifdef USE_FWF
1008 XtSetArg(args[num_args], nhStr(XtNsensitiveArray),
1009 menu_info->curr_menu.sensitive); num_args++;
1010 XtSetArg(args[num_args], nhStr(XtNmaxSelectable),
1011 menu_info->curr_menu.count); num_args++;
1012 #endif
1013 wp->w = XtCreateManagedWidget("menu_list", /* name */
1014 #ifdef USE_FWF
1015 xfwfMultiListWidgetClass,
1016 #else
1017 listWidgetClass,
1018 #endif
1019 viewport_widget, /* parent widget */
1020 args, /* set some values */
1021 num_args); /* number of values to set */
1023 XtAddCallback(wp->w, XtNcallback, menu_select, (XtPointer) 0);
1025 /* Get the font and margin information. */
1026 num_args = 0;
1027 XtSetArg(args[num_args], XtNfont, &menu_info->fs); num_args++;
1028 XtSetArg(args[num_args], XtNinternalHeight,
1029 &menu_info->internal_height); num_args++;
1030 XtSetArg(args[num_args], XtNinternalWidth,
1031 &menu_info->internal_width); num_args++;
1032 XtSetArg(args[num_args], nhStr(XtNrowSpacing), &row_spacing);
1033 num_args++;
1034 XtGetValues(wp->w, args, num_args);
1036 /* font height is ascent + descent */
1037 menu_info->line_height = menu_info->fs->max_bounds.ascent
1038 + menu_info->fs->max_bounds.descent
1039 + row_spacing;
1041 menu_info->valid_widgets = TRUE;
1043 num_args = 0;
1044 XtSetArg(args[num_args], XtNwidth, &v_pixel_width); num_args++;
1045 XtSetArg(args[num_args], XtNheight, &v_pixel_height); num_args++;
1046 XtGetValues(wp->w, args, num_args);
1047 } else {
1048 Dimension len;
1050 viewport_widget = XtParent(wp->w);
1052 /* get the longest string on new menu */
1053 v_pixel_width = 0;
1054 for (ptr = menu_info->new_menu.list_pointer; *ptr; ptr++) {
1055 len = XTextWidth(menu_info->fs, *ptr, strlen(*ptr));
1056 if (len > v_pixel_width)
1057 v_pixel_width = len;
1060 /* add viewport internal border */
1061 v_pixel_width += 2 * menu_info->internal_width;
1062 v_pixel_height = (2 * menu_info->internal_height)
1063 + (menu_info->new_menu.count * menu_info->line_height);
1065 /* make new menu the current menu */
1066 move_menu(&menu_info->new_menu, &menu_info->curr_menu);
1067 #ifdef USE_FWF
1068 XfwfMultiListSetNewData((XfwfMultiListWidget) wp->w,
1069 menu_info->curr_menu.list_pointer, 0, 0, TRUE,
1070 menu_info->curr_menu.sensitive);
1071 #else
1072 XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0, TRUE);
1073 #endif
1076 /* if viewport will be bigger than the screen, limit its height */
1077 num_args = 0;
1078 XtSetArg(args[num_args], XtNwidth, &v_pixel_width); num_args++;
1079 XtSetArg(args[num_args], XtNheight, &v_pixel_height); num_args++;
1080 XtGetValues(wp->w, args, num_args);
1081 if ((Dimension) XtScreen(wp->w)->height * 5 / 6 < v_pixel_height) {
1082 /* scrollbar is 14 pixels wide. Widen the form to accommodate it. */
1083 v_pixel_width += 14;
1085 /* shrink to fit vertically */
1086 v_pixel_height = XtScreen(wp->w)->height * 5 / 6;
1088 num_args = 0;
1089 XtSetArg(args[num_args], XtNwidth, v_pixel_width); num_args++;
1090 XtSetArg(args[num_args], XtNheight, v_pixel_height); num_args++;
1091 XtSetValues(wp->w, args, num_args);
1093 XtRealizeWidget(wp->popup); /* need to realize before we position */
1095 /* if menu is not up, position it */
1096 if (!menu_info->is_up)
1097 positionpopup(wp->popup, FALSE);
1099 menu_info->is_up = TRUE;
1100 if (window == WIN_INVEN && how == PICK_NONE) {
1101 /* cant use nh_XtPopup() because it may try to grab the focus */
1102 XtPopup(wp->popup, (int) XtGrabNone);
1103 if (!updated_inventory)
1104 XMapRaised(XtDisplay(wp->popup), XtWindow(wp->popup));
1105 XSetWMProtocols(XtDisplay(wp->popup), XtWindow(wp->popup),
1106 &wm_delete_window, 1);
1107 retval = 0;
1108 } else {
1109 menu_info->is_active = TRUE; /* waiting for user response */
1110 menu_info->cancelled = FALSE;
1111 nh_XtPopup(wp->popup, (int) XtGrabExclusive, wp->w);
1112 (void) x_event(EXIT_ON_EXIT);
1113 menu_info->is_active = FALSE;
1114 if (menu_info->cancelled)
1115 return -1;
1117 retval = 0;
1118 for (curr = menu_info->curr_menu.base; curr; curr = curr->next)
1119 if (curr->selected)
1120 retval++;
1122 if (retval) {
1123 menu_item *mi;
1124 boolean toomany = (how == PICK_ONE && retval > 1);
1126 if (toomany)
1127 retval = 1;
1128 *menu_list = mi = (menu_item *) alloc(retval * sizeof(menu_item));
1129 for (curr = menu_info->curr_menu.base; curr; curr = curr->next)
1130 if (curr->selected) {
1131 mi->item = curr->identifier;
1132 mi->count = curr->pick_count;
1133 if (!toomany)
1134 mi++;
1135 if (how == PICK_ONE && !curr->preselected)
1136 break;
1139 } /* ?(WIN_INVEN && PICK_NONE) */
1141 return retval;
1144 /* End global functions ====================================================
1148 * Allocate a copy of the given string. If null, return a string of
1149 * zero length.
1151 static char *
1152 copy_of(s)
1153 const char *s;
1155 if (!s)
1156 s = "";
1157 return dupstr(s);
1160 static void
1161 move_menu(src_menu, dest_menu)
1162 struct menu *src_menu, *dest_menu;
1164 free_menu(dest_menu); /* toss old menu */
1165 *dest_menu = *src_menu; /* make new menu current */
1166 /* leave no dangling ptrs */
1167 reset_menu_to_default(src_menu);
1170 static void
1171 free_menu(mp)
1172 struct menu *mp;
1174 while (mp->base) {
1175 mp->last = mp->base;
1176 mp->base = mp->base->next;
1178 free((genericptr_t) mp->last->str);
1179 free((genericptr_t) mp->last);
1181 if (mp->query)
1182 free((genericptr_t) mp->query);
1183 if (mp->gacc)
1184 free((genericptr_t) mp->gacc);
1185 if (mp->list_pointer)
1186 free((genericptr_t) mp->list_pointer);
1187 if (mp->sensitive)
1188 free((genericptr_t) mp->sensitive);
1189 reset_menu_to_default(mp);
1192 static void
1193 reset_menu_to_default(mp)
1194 struct menu *mp;
1196 mp->base = mp->last = (x11_menu_item *) 0;
1197 mp->query = (const char *) 0;
1198 mp->gacc = (const char *) 0;
1199 mp->count = 0;
1200 mp->list_pointer = (String *) 0;
1201 mp->sensitive = (Boolean *) 0;
1202 mp->curr_selector = 'a'; /* first accelerator */
1205 static void
1206 clear_old_menu(wp)
1207 struct xwindow *wp;
1209 struct menu_info_t *menu_info = wp->menu_information;
1211 free_menu(&menu_info->curr_menu);
1212 free_menu(&menu_info->new_menu);
1214 if (menu_info->valid_widgets) {
1215 nh_XtPopdown(wp->popup);
1216 menu_info->is_up = FALSE;
1217 XtDestroyWidget(wp->popup);
1218 menu_info->valid_widgets = FALSE;
1219 wp->w = wp->popup = (Widget) 0;
1223 void
1224 create_menu_window(wp)
1225 struct xwindow *wp;
1227 wp->type = NHW_MENU;
1228 wp->menu_information =
1229 (struct menu_info_t *) alloc(sizeof(struct menu_info_t));
1230 (void) memset((genericptr_t) wp->menu_information, '\0',
1231 sizeof(struct menu_info_t));
1232 reset_menu_to_default(&wp->menu_information->curr_menu);
1233 reset_menu_to_default(&wp->menu_information->new_menu);
1234 reset_menu_count(wp->menu_information);
1235 wp->w = wp->popup = (Widget) 0;
1238 void
1239 destroy_menu_window(wp)
1240 struct xwindow *wp;
1242 clear_old_menu(wp); /* this will also destroy the widgets */
1243 free((genericptr_t) wp->menu_information);
1244 wp->menu_information = (struct menu_info_t *) 0;
1245 wp->type = NHW_NONE; /* allow re-use */
1248 /*winmenu.c*/