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. */
6 * File for creating menus.
8 * + Global functions: start_menu, add_menu, end_menu, select_menu
10 /*#define USE_FWF*/ /* use FWF's list widget */
13 #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
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>
26 #include <X11/Xfwf/MultiList.h>
28 #include <X11/Xaw/List.h>
32 #ifdef PRESERVE_NO_SYSV
36 #undef PRESERVE_NO_SYSV
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
*));
57 static void FDECL(sync_selected
, (struct menu_info_t
*, int, int *));
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\
72 <Key>Down: scroll(2)\n\
80 menu_select(w
, client_data
, call_data
)
82 XtPointer client_data
, call_data
;
85 struct menu_info_t
*menu_info
;
87 XfwfMultiListReturnStruct
*lrs
= (XfwfMultiListReturnStruct
*) call_data
;
89 XawListReturnStruct
*lrs
= (XawListReturnStruct
*) call_data
;
98 menu_info
= wp
->menu_information
;
99 how_many
= menu_info
->counting
? menu_info
->menu_count
: -1L;
100 reset_menu_count(menu_info
);
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?");
108 case XfwfMultiListActionStatus
:
109 pline("menu_select: status action?");
111 case XfwfMultiListActionHighlight
:
112 case XfwfMultiListActionUnhighlight
:
113 sync_selected(menu_info
, lrs
->num_selected
, lrs
->selected_items
);
117 for (i
= 0, curr
= menu_info
->curr_menu
.base
; i
< lrs
->list_index
; i
++) {
119 panic("menu_select: out of menu items!");
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) {
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
;
139 curr
->pick_count
= -1L;
141 XawListChange(wp
->w
, menu_info
->curr_menu
.list_pointer
, 0, 0, True
);
144 if (menu_info
->how
== PICK_ONE
)
149 * Called when menu window is deleted.
153 menu_delete(w
, event
, params
, num_params
)
157 Cardinal
*num_params
;
163 menu_cancel((Widget
) None
, (XtPointer
) find_widget(w
), (XtPointer
) 0);
167 * Invert the count'th line (curr) in the given window.
171 invert_line(wp
, curr
, which
, how_many
)
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
) {
188 XfwfMultiListHighlightItem((XfwfMultiListWidget
) wp
->w
, which
);
190 curr
->str
[2] = (how_many
!= -1) ? '#' : '+';
192 curr
->pick_count
= how_many
;
195 XfwfMultiListUnhighlightItem((XfwfMultiListWidget
) wp
->w
, which
);
199 curr
->pick_count
= -1L;
204 * Called when we get a key press event on a menu window.
208 menu_key(w
, event
, params
, num_params
)
212 Cardinal
*num_params
;
214 struct menu_info_t
*menu_info
;
219 boolean selected_something
;
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 */
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
)
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
);
251 } else if (ch
== '\n' || ch
== '\r') {
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
))
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
;
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')
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
);
277 select_match(wp
, buf
);
283 } else if (ch
== MENU_SELECT_ALL
) { /* select all */
284 if (menu_info
->how
== PICK_ANY
)
289 } else if (ch
== MENU_UNSELECT_ALL
) { /* unselect all */
290 if (menu_info
->how
== PICK_ANY
)
295 } else if (ch
== MENU_INVERT_ALL
) { /* invert all */
296 if (menu_info
->how
== PICK_ANY
)
301 } else if (index(menu_info
->curr_menu
.gacc
, ch
)) {
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 */
318 XawListChange(wp
->w
, menu_info
->curr_menu
.list_pointer
, 0, 0,
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
)
333 invert_line(wp
, curr
, count
,
334 menu_info
->counting
? menu_info
->menu_count
: -1L);
336 XawListChange(wp
->w
, menu_info
->curr_menu
.list_pointer
, 0, 0,
339 selected_something
= curr
->selected
;
341 X11_nhbell(); /* no match */
343 if (!(selected_something
&& menu_info
->how
== PICK_ONE
))
344 return; /* keep going */
347 } else { /* permanent inventory window */
352 /* pop down on ESC */
361 menu_ok(w
, client_data
, call_data
)
363 XtPointer client_data
, call_data
;
365 struct xwindow
*wp
= (struct xwindow
*) client_data
;
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
;
384 if (wp
->menu_information
->is_active
) {
386 wp
->menu_information
->cancelled
= TRUE
;
393 menu_all(w
, client_data
, call_data
)
395 XtPointer client_data
, call_data
;
400 select_all((struct xwindow
*) client_data
);
405 menu_none(w
, client_data
, call_data
)
407 XtPointer client_data
, call_data
;
412 select_none((struct xwindow
*) client_data
);
417 menu_invert(w
, client_data
, call_data
)
419 XtPointer client_data
, call_data
;
424 invert_all((struct xwindow
*) client_data
);
429 menu_search(w
, client_data
, call_data
)
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
];
440 X11_getlin("Search for:", tmpbuf
);
441 if (!*tmpbuf
|| *tmpbuf
== '\033')
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
);
449 select_match(wp
, buf
);
451 if (menu_info
->how
== PICK_ONE
)
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);
474 XawListChange(wp
->w
, wp
->menu_information
->curr_menu
.list_pointer
,
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);
498 XawListChange(wp
->w
, wp
->menu_information
->curr_menu
.list_pointer
,
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);
517 XawListChange(wp
->w
, wp
->menu_information
->curr_menu
.list_pointer
,
523 invert_match(wp
, match
)
525 char *match
; /* wildcard pattern for pmatch() */
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);
539 curr
->preselected
= FALSE
;
544 XawListChange(wp
->w
, wp
->menu_information
->curr_menu
.list_pointer
,
550 select_match(wp
, match
)
552 char *match
; /* wildcard pattern for pmatch() */
554 x11_menu_item
*curr
, *found
= 0;
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
))
563 curr
->preselected
= FALSE
;
567 if (!found
->selected
) {
568 invert_line(wp
, found
, count
, -1L);
570 XawListChange(wp
->w
, wp
->menu_information
->curr_menu
.list_pointer
,
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 */
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.
599 sync_selected(menu_info
, num_selected
, items
)
600 struct menu_info_t
*menu_info
;
608 for (i
= 0, curr
= menu_info
->curr_menu
.base
; curr
;
609 i
++, curr
= curr
->next
) {
611 for (j
= 0, ip
= items
; j
< num_selected
; j
++, ip
++)
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
);
622 curr
->selected
= found
? TRUE
: FALSE
;
623 /* once active selection takes place, preselection becomes history */
624 curr
->preselected
= FALSE
;
629 /* Global functions ========================================================
633 X11_start_menu(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
);
645 wp
->menu_information
->is_menu
= TRUE
;
651 X11_add_menu(window
, glyph
, identifier
, ch
, gch
, attr
, str
, preselected
)
653 int glyph
; /* unused (for now) */
654 const anything
*identifier
;
656 char gch
; /* group accelerator (0 = no group) */
662 struct menu_info_t
*menu_info
;
667 menu_info
= window_list
[window
].menu_information
;
668 if (!menu_info
->is_menu
) {
669 impossible("add_menu: called before start_menu");
673 item
= (x11_menu_item
*) alloc((unsigned) sizeof(x11_menu_item
));
674 item
->next
= (x11_menu_item
*) 0;
675 item
->identifier
= *identifier
;
677 item
->selected
= item
->preselected
= preselected
;
678 item
->pick_count
= -1L;
680 if (identifier
->a_void
) {
682 int len
= strlen(str
);
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
++;
690 menu_info
->new_menu
.curr_selector
= 'A';
692 menu_info
->new_menu
.curr_selector
= 0; /* out */
697 /* We *think* everything's coming in off at most BUFSZ bufs... */
698 impossible("Menu item too long (%d).", len
);
701 Sprintf(buf
, "%c %c ", ch
? ch
: ' ', preselected
? '+' : '-');
702 (void) strncpy(buf
+ 4, str
, len
);
704 item
->str
= copy_of(buf
);
706 /* no keyboard accelerator */
707 item
->str
= copy_of(str
);
712 item
->gselector
= gch
;
714 if (menu_info
->new_menu
.last
) {
715 menu_info
->new_menu
.last
->next
= item
;
717 menu_info
->new_menu
.base
= item
;
719 menu_info
->new_menu
.last
= item
;
720 menu_info
->new_menu
.count
++;
724 X11_end_menu(window
, query
)
728 struct menu_info_t
*menu_info
;
731 menu_info
= window_list
[window
].menu_information
;
732 if (!menu_info
->is_menu
) {
733 impossible("end_menu: called before start_menu");
736 menu_info
->new_menu
.query
= copy_of(query
);
740 X11_select_menu(window
, how
, menu_list
)
743 menu_item
**menu_list
;
747 struct menu_info_t
*menu_info
;
752 Dimension v_pixel_width
, v_pixel_height
, lblwidth
[6], maxlblwidth
;
754 Widget viewport_widget
, form
, label
,
755 ok
, cancel
, all
, none
, invert
, search
, lblwidget
[6];
760 char gacc
[QBUFSZ
], *ap
;
762 *menu_list
= (menu_item
*) 0;
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");
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 */
777 if (menu_info
->how
!= PICK_NONE
) {
779 #define GSELIDX(c) ((c) &127) /* guard against `signed char' */
781 for (i
= 0; i
< SIZE(gcnt
); i
++)
783 for (n
= 0, curr
= menu_info
->new_menu
.base
; curr
; curr
= curr
->next
)
784 if (curr
->gselector
&& curr
->gselector
!= curr
->selector
) {
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
;
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 */
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);
817 menu_info
->new_menu
.sensitive
= (Boolean
*) 0;
819 labeled
= (menu_info
->new_menu
.query
&& *(menu_info
->new_menu
.query
))
824 * Menus don't appear to size components correctly, except
825 * when first created. For 3.2.0 release, just recreate
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
;
839 XtSetArg(args
[num_args
], XtNallowShellResize
, True
);
841 wp
->popup
= XtCreatePopupShell((window
== WIN_INVEN
)
842 ? "inventory" : "menu",
844 ? topLevelShellWidgetClass
845 : transientShellWidgetClass
,
846 toplevel
, args
, num_args
);
847 XtOverrideTranslations(wp
->popup
,
848 XtParseTranslationTable(
849 "<Message>WM_PROTOCOLS: menu_delete()"));
852 XtSetArg(args
[num_args
], XtNtranslations
,
853 XtParseTranslationTable(menu_translations
)); num_args
++;
854 form
= XtCreateManagedWidget("mform", formWidgetClass
, wp
->popup
,
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
,
870 * Create ok, cancel, all, none, invert, and search buttons..
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
,
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];
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
);
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
,
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
);
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
,
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];
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
,
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];
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
,
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];
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
);
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
,
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 */
974 XtSetArg(args
[0], XtNwidth
, maxlblwidth
);
975 for (i
= 0; i
< 6; ++i
)
976 if (lblwidth
[i
] < maxlblwidth
)
977 XtSetValues(lblwidget
[i
], args
, ONE
);
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
++;
986 XtSetArg(args
[num_args
], nhStr(XtNforceBars
), True
); num_args
++;
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
);
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
++;
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
++;
1013 wp
->w
= XtCreateManagedWidget("menu_list", /* name */
1015 xfwfMultiListWidgetClass
,
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. */
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
);
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
1041 menu_info
->valid_widgets
= TRUE
;
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
);
1050 viewport_widget
= XtParent(wp
->w
);
1052 /* get the longest string on new menu */
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
);
1068 XfwfMultiListSetNewData((XfwfMultiListWidget
) wp
->w
,
1069 menu_info
->curr_menu
.list_pointer
, 0, 0, TRUE
,
1070 menu_info
->curr_menu
.sensitive
);
1072 XawListChange(wp
->w
, menu_info
->curr_menu
.list_pointer
, 0, 0, TRUE
);
1076 /* if viewport will be bigger than the screen, limit its height */
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;
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);
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
)
1118 for (curr
= menu_info
->curr_menu
.base
; curr
; curr
= curr
->next
)
1124 boolean toomany
= (how
== PICK_ONE
&& 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
;
1135 if (how
== PICK_ONE
&& !curr
->preselected
)
1139 } /* ?(WIN_INVEN && PICK_NONE) */
1144 /* End global functions ====================================================
1148 * Allocate a copy of the given string. If null, return a string of
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
);
1175 mp
->last
= mp
->base
;
1176 mp
->base
= mp
->base
->next
;
1178 free((genericptr_t
) mp
->last
->str
);
1179 free((genericptr_t
) mp
->last
);
1182 free((genericptr_t
) mp
->query
);
1184 free((genericptr_t
) mp
->gacc
);
1185 if (mp
->list_pointer
)
1186 free((genericptr_t
) mp
->list_pointer
);
1188 free((genericptr_t
) mp
->sensitive
);
1189 reset_menu_to_default(mp
);
1193 reset_menu_to_default(mp
)
1196 mp
->base
= mp
->last
= (x11_menu_item
*) 0;
1197 mp
->query
= (const char *) 0;
1198 mp
->gacc
= (const char *) 0;
1200 mp
->list_pointer
= (String
*) 0;
1201 mp
->sensitive
= (Boolean
*) 0;
1202 mp
->curr_selector
= 'a'; /* first accelerator */
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;
1224 create_menu_window(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;
1239 destroy_menu_window(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 */