1 /* SCCS Id: @(#)winmisc.c 3.4 2000/05/21 */
2 /* Copyright (c) Dean Luick, 1992 */
3 /* NetHack may be freely redistributed. See license for details. */
4
5 /*
6 * Misc. popup windows: player selection and extended commands.
7 *
8 * + Global functions: player_selection() and get_ext_cmd().
9 */
10
11 #ifndef SYSV
12 #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
13 #endif
14
15 #include <X11/Intrinsic.h>
16 #include <X11/StringDefs.h>
17 #include <X11/Shell.h>
18 #include <X11/Xaw/Command.h>
19 #include <X11/Xaw/Form.h>
20 #include <X11/Xaw/Label.h>
21 #include <X11/Xaw/Cardinals.h>
22 #include <X11/Xos.h> /* for index() */
23 #include <X11/Xatom.h>
24
25 #ifdef PRESERVE_NO_SYSV
26 # ifdef SYSV
27 # undef SYSV
28 # endif
29 # undef PRESERVE_NO_SYSV
30 #endif
31
32 #include "hack.h"
33 #include "func_tab.h"
34 #include "winX.h"
35
36
37 static Widget extended_command_popup = 0;
38 static Widget extended_command_form;
39 static Widget *extended_commands = 0;
40 static int extended_command_selected; /* index of the selected command; */
41 static int ps_selected; /* index of selected role */
42 #define PS_RANDOM (-50)
43 #define PS_QUIT (-75)
44 static const char ps_randchars[] = "*@";
45 static const char ps_quitchars[] = "\033qQ";
46
47 #define EC_NCHARS 32
48 static boolean ec_active = FALSE;
49 static int ec_nchars = 0;
50 static char ec_chars[EC_NCHARS];
51 static Time ec_time;
52
53 static const char extended_command_translations[] =
54 "#override\n\
55 <Key>: ec_key()";
56
57 static const char player_select_translations[] =
58 "#override\n\
59 <Key>: ps_key()";
60 static const char race_select_translations[] =
61 "#override\n\
62 <Key>: race_key()";
63 static const char gend_select_translations[] =
64 "#override\n\
65 <Key>: gend_key()";
66 static const char algn_select_translations[] =
67 "#override\n\
68 <Key>: algn_key()";
69
70 static void FDECL(popup_delete, (Widget, XEvent*, String*, Cardinal*));
71 static void NDECL(ec_dismiss);
72 static Widget FDECL(make_menu, (const char *,const char *,const char *,
73 const char *,XtCallbackProc,
74 const char *,XtCallbackProc,
75 int,const char **, Widget **,
76 XtCallbackProc,Widget *));
77 static void NDECL(init_extended_commands_popup);
78 static void FDECL(ps_quit, (Widget,XtPointer,XtPointer));
79 static void FDECL(ps_random, (Widget,XtPointer,XtPointer));
80 static void FDECL(ps_select, (Widget,XtPointer,XtPointer));
81
82
83 /* Player Selection -------------------------------------------------------- */
84 /* ARGSUSED */
85 static void
ps_quit(w,client_data,call_data)86 ps_quit(w, client_data, call_data)
87 Widget w;
88 XtPointer client_data, call_data;
89 {
90 ps_selected = PS_QUIT;
91 exit_x_event = TRUE; /* leave event loop */
92 }
93
94 /* ARGSUSED */
95 static void
ps_random(w,client_data,call_data)96 ps_random(w, client_data, call_data)
97 Widget w;
98 XtPointer client_data, call_data;
99 {
100 ps_selected = PS_RANDOM;
101 exit_x_event = TRUE; /* leave event loop */
102 }
103
104 /* ARGSUSED */
105 static void
ps_select(w,client_data,call_data)106 ps_select(w, client_data, call_data)
107 Widget w;
108 XtPointer client_data, call_data;
109 {
110 ps_selected = (int) client_data;
111 exit_x_event = TRUE; /* leave event loop */
112 }
113
114 /* ARGSUSED */
115 void
ps_key(w,event,params,num_params)116 ps_key(w, event, params, num_params)
117 Widget w;
118 XEvent *event;
119 String *params;
120 Cardinal *num_params;
121 {
122 char ch, *mark;
123 char rolechars[QBUFSZ];
124 int i;
125
126 (void)memset(rolechars, '\0', sizeof rolechars); /* for index() */
127 for (i = 0; roles[i].name.m; ++i) {
128 ch = lowc(*roles[i].name.m);
129 /* if (flags.female && roles[i].name.f) ch = lowc(*roles[i].name.f); */
130 /* this supports at most two roles with the same first letter */
131 if (index(rolechars, ch)) ch = highc(ch);
132 rolechars[i] = ch;
133 }
134 ch = key_event_to_char((XKeyEvent *) event);
135 if (ch == '\0') { /* don't accept nul char/modifier event */
136 /* don't beep */
137 return;
138 }
139 mark = index(rolechars, ch);
140 if (!mark) mark = index(rolechars, lowc(ch));
141 if (!mark) mark = index(rolechars, highc(ch));
142 if (!mark) {
143 if (index(ps_randchars, ch))
144 ps_selected = PS_RANDOM;
145 else if (index(ps_quitchars, ch))
146 ps_selected = PS_QUIT;
147 else {
148 X11_nhbell(); /* no such class */
149 return;
150 }
151 } else
152 ps_selected = (int)(mark - rolechars);
153 exit_x_event = TRUE;
154 }
155
156 /* ARGSUSED */
157 void
race_key(w,event,params,num_params)158 race_key(w, event, params, num_params)
159 Widget w;
160 XEvent *event;
161 String *params;
162 Cardinal *num_params;
163 {
164 char ch, *mark;
165 char racechars[QBUFSZ];
166 int i;
167
168 (void)memset(racechars, '\0', sizeof racechars); /* for index() */
169 for (i = 0; races[i].noun; ++i) {
170 ch = lowc(*races[i].noun);
171 /* this supports at most two races with the same first letter */
172 if (index(racechars, ch)) ch = highc(ch);
173 racechars[i] = ch;
174 }
175 ch = key_event_to_char((XKeyEvent *) event);
176 if (ch == '\0') { /* don't accept nul char/modifier event */
177 /* don't beep */
178 return;
179 }
180 mark = index(racechars, ch);
181 if (!mark) mark = index(racechars, lowc(ch));
182 if (!mark) mark = index(racechars, highc(ch));
183 if (!mark) {
184 if (index(ps_randchars, ch))
185 ps_selected = PS_RANDOM;
186 else if (index(ps_quitchars, ch))
187 ps_selected = PS_QUIT;
188 else {
189 X11_nhbell(); /* no such race */
190 return;
191 }
192 } else
193 ps_selected = (int)(mark - racechars);
194 exit_x_event = TRUE;
195 }
196
197 /* ARGSUSED */
198 void
gend_key(w,event,params,num_params)199 gend_key(w, event, params, num_params)
200 Widget w;
201 XEvent *event;
202 String *params;
203 Cardinal *num_params;
204 {
205 char ch, *mark;
206 static char gendchars[] = "mf";
207
208 ch = key_event_to_char((XKeyEvent *) event);
209 if (ch == '\0') { /* don't accept nul char/modifier event */
210 /* don't beep */
211 return;
212 }
213 mark = index(gendchars, ch);
214 if (!mark) mark = index(gendchars, lowc(ch));
215 if (!mark) {
216 if (index(ps_randchars, ch))
217 ps_selected = PS_RANDOM;
218 else if (index(ps_quitchars, ch))
219 ps_selected = PS_QUIT;
220 else {
221 X11_nhbell(); /* no such gender */
222 return;
223 }
224 } else
225 ps_selected = (int)(mark - gendchars);
226 exit_x_event = TRUE;
227 }
228
229 /* ARGSUSED */
230 void
algn_key(w,event,params,num_params)231 algn_key(w, event, params, num_params)
232 Widget w;
233 XEvent *event;
234 String *params;
235 Cardinal *num_params;
236 {
237 char ch, *mark;
238 static char algnchars[] = "LNC";
239
240 ch = key_event_to_char((XKeyEvent *) event);
241 if (ch == '\0') { /* don't accept nul char/modifier event */
242 /* don't beep */
243 return;
244 }
245 mark = index(algnchars, ch);
246 if (!mark) mark = index(algnchars, highc(ch));
247 if (!mark) {
248 if (index(ps_randchars, ch))
249 ps_selected = PS_RANDOM;
250 else if (index(ps_quitchars, ch))
251 ps_selected = PS_QUIT;
252 else {
253 X11_nhbell(); /* no such alignment */
254 return;
255 }
256 } else
257 ps_selected = (int)(mark - algnchars);
258 exit_x_event = TRUE;
259 }
260
261 /* Global functions ========================================================= */
262 void
X11_player_selection()263 X11_player_selection()
264 {
265 int num_roles, num_races, num_gends, num_algns,
266 i, availcount, availindex;
267 Widget popup, player_form;
268 const char **choices;
269 char qbuf[QBUFSZ], plbuf[QBUFSZ];
270
271 /* avoid unnecessary prompts further down */
272 rigid_role_checks();
273
274 (void) root_plselection_prompt(plbuf, QBUFSZ - 1,
275 flags.initrole, flags.initrace, flags.initgend, flags.initalign);
276
277 while (flags.initrole < 0) {
278 if (flags.initrole == ROLE_RANDOM || flags.randomall) {
279 flags.initrole = pick_role(flags.initrace,
280 flags.initgend, flags.initalign, PICK_RANDOM);
281 break;
282 }
283
284 /* select a role */
285 for (num_roles = 0; roles[num_roles].name.m; ++num_roles) continue;
286 choices = (const char **)alloc(sizeof(char *) * num_roles);
287 for (;;) {
288 availcount = 0;
289 for (i = 0; i < num_roles; i++) {
290 choices[i] = 0;
291 if (ok_role(i, flags.initrace,
292 flags.initgend, flags.initalign)) {
293 choices[i] = roles[i].name.m;
294 if (flags.initgend >= 0 && flags.female && roles[i].name.f)
295 choices[i] = roles[i].name.f;
296 ++availcount;
297 }
298 }
299 if (availcount > 0) break;
300 else if (flags.initalign >= 0) flags.initalign = -1; /* reset */
301 else if (flags.initgend >= 0) flags.initgend = -1;
302 else if (flags.initrace >= 0) flags.initrace = -1;
303 else panic("no available ROLE+race+gender+alignment combinations");
304 }
305 Sprintf(qbuf, "Choose your %s Role", s_suffix(plbuf));
306 popup = make_menu("player_selection", qbuf,
307 player_select_translations,
308 "quit", ps_quit,
309 "random", ps_random,
310 num_roles, choices, (Widget **)0, ps_select, &player_form);
311
312 ps_selected = -1;
313 positionpopup(popup, FALSE);
314 nh_XtPopup(popup, (int)XtGrabExclusive, player_form);
315
316 /* The callbacks will enable the event loop exit. */
317 (void) x_event(EXIT_ON_EXIT);
318
319 nh_XtPopdown(popup);
320 XtDestroyWidget(popup);
321 free((genericptr_t)choices), choices = 0;
322
323 if (ps_selected == PS_QUIT) {
324 clearlocks();
325 X11_exit_nhwindows((char *)0);
326 terminate(0);
327 } else if (ps_selected == PS_RANDOM) {
328 flags.initrole = ROLE_RANDOM;
329 } else if (ps_selected < 0 || ps_selected >= num_roles) {
330 panic("player_selection: bad role select value %d", ps_selected);
331 } else {
332 flags.initrole = ps_selected;
333 }
334 }
335
336 (void) root_plselection_prompt(plbuf, QBUFSZ - 1,
337 flags.initrole, flags.initrace, flags.initgend, flags.initalign);
338
339 while (!validrace(flags.initrole, flags.initrace)) {
340 if (flags.initrace == ROLE_RANDOM || flags.randomall) {
341 flags.initrace = pick_race(flags.initrole,
342 flags.initgend, flags.initalign, PICK_RANDOM);
343 break;
344 }
345 /* select a race */
346 for (num_races = 0; races[num_races].noun; ++num_races) continue;
347 choices = (const char **)alloc(sizeof(char *) * num_races);
348 for (;;) {
349 availcount = availindex = 0;
350 for (i = 0; i < num_races; i++) {
351 choices[i] = 0;
352 if (ok_race(flags.initrole, i,
353 flags.initgend, flags.initalign)) {
354 choices[i] = races[i].noun;
355 ++availcount;
356 availindex = i; /* used iff only one */
357 }
358 }
359 if (availcount > 0) break;
360 else if (flags.initalign >= 0) flags.initalign = -1; /* reset */
361 else if (flags.initgend >= 0) flags.initgend = -1;
362 else panic("no available role+RACE+gender+alignment combinations");
363 }
364
365 if (availcount == 1) {
366 flags.initrace = availindex;
367 free((genericptr_t)choices), choices = 0;
368 } else {
369 Sprintf(qbuf, "Pick your %s race", s_suffix(plbuf));
370 popup = make_menu("race_selection", qbuf,
371 race_select_translations,
372 "quit", ps_quit,
373 "random", ps_random,
374 num_races, choices, (Widget **)0,
375 ps_select, &player_form);
376
377 ps_selected = -1;
378 positionpopup(popup, FALSE);
379 nh_XtPopup(popup, (int)XtGrabExclusive, player_form);
380
381 /* The callbacks will enable the event loop exit. */
382 (void) x_event(EXIT_ON_EXIT);
383
384 nh_XtPopdown(popup);
385 XtDestroyWidget(popup);
386 free((genericptr_t)choices), choices = 0;
387
388 if (ps_selected == PS_QUIT) {
389 clearlocks();
390 X11_exit_nhwindows((char *)0);
391 terminate(0);
392 } else if (ps_selected == PS_RANDOM) {
393 flags.initrace = ROLE_RANDOM;
394 } else if (ps_selected < 0 || ps_selected >= num_races) {
395 panic("player_selection: bad race select value %d", ps_selected);
396 } else {
397 flags.initrace = ps_selected;
398 }
399 } /* more than one race choice available */
400 }
401
402 (void) root_plselection_prompt(plbuf, QBUFSZ - 1,
403 flags.initrole, flags.initrace, flags.initgend, flags.initalign);
404
405 while (!validgend(flags.initrole, flags.initrace, flags.initgend)) {
406 if (flags.initgend == ROLE_RANDOM || flags.randomall) {
407 flags.initgend = pick_gend(flags.initrole, flags.initrace,
408 flags.initalign, PICK_RANDOM);
409 break;
410 }
411 /* select a gender */
412 num_gends = 2; /* genders[2] isn't allowed */
413 choices = (const char **)alloc(sizeof(char *) * num_gends);
414 for (;;) {
415 availcount = availindex = 0;
416 for (i = 0; i < num_gends; i++) {
417 choices[i] = 0;
418 if (ok_gend(flags.initrole, flags.initrace,
419 i, flags.initalign)) {
420 choices[i] = genders[i].adj;
421 ++availcount;
422 availindex = i; /* used iff only one */
423 }
424 }
425 if (availcount > 0) break;
426 else if (flags.initalign >= 0) flags.initalign = -1; /* reset */
427 else panic("no available role+race+GENDER+alignment combinations");
428 }
429
430 if (availcount == 1) {
431 flags.initgend = availindex;
432 free((genericptr_t)choices), choices = 0;
433 } else {
434 Sprintf(qbuf, "Your %s gender?", s_suffix(plbuf));
435 popup = make_menu("gender_selection", qbuf,
436 gend_select_translations,
437 "quit", ps_quit,
438 "random", ps_random,
439 num_gends, choices, (Widget **)0,
440 ps_select, &player_form);
441
442 ps_selected = -1;
443 positionpopup(popup, FALSE);
444 nh_XtPopup(popup, (int)XtGrabExclusive, player_form);
445
446 /* The callbacks will enable the event loop exit. */
447 (void) x_event(EXIT_ON_EXIT);
448
449 nh_XtPopdown(popup);
450 XtDestroyWidget(popup);
451 free((genericptr_t)choices), choices = 0;
452
453 if (ps_selected == PS_QUIT) {
454 clearlocks();
455 X11_exit_nhwindows((char *)0);
456 terminate(0);
457 } else if (ps_selected == PS_RANDOM) {
458 flags.initgend = ROLE_RANDOM;
459 } else if (ps_selected < 0 || ps_selected >= num_gends) {
460 panic("player_selection: bad gender select value %d", ps_selected);
461 } else {
462 flags.initgend = ps_selected;
463 }
464 } /* more than one gender choice available */
465 }
466
467 (void) root_plselection_prompt(plbuf, QBUFSZ - 1,
468 flags.initrole, flags.initrace, flags.initgend, flags.initalign);
469
470 while (!validalign(flags.initrole, flags.initrace, flags.initalign)) {
471 if (flags.initalign == ROLE_RANDOM || flags.randomall) {
472 flags.initalign = pick_align(flags.initrole, flags.initrace,
473 flags.initgend, PICK_RANDOM);
474 break;
475 }
476 /* select an alignment */
477 num_algns = 3; /* aligns[3] isn't allowed */
478 choices = (const char **)alloc(sizeof(char *) * num_algns);
479 for (;;) {
480 availcount = availindex = 0;
481 for (i = 0; i < num_algns; i++) {
482 choices[i] = 0;
483 if (ok_align(flags.initrole, flags.initrace,
484 flags.initgend, i)) {
485 choices[i] = aligns[i].adj;
486 ++availcount;
487 availindex = i; /* used iff only one */
488 }
489 }
490 if (availcount > 0) break;
491 else panic("no available role+race+gender+ALIGNMENT combinations");
492 }
493
494 if (availcount == 1) {
495 flags.initalign = availindex;
496 free((genericptr_t)choices), choices = 0;
497 } else {
498 Sprintf(qbuf, "Your %s alignment?", s_suffix(plbuf));
499 popup = make_menu("alignment_selection", qbuf,
500 algn_select_translations,
501 "quit", ps_quit,
502 "random", ps_random,
503 num_algns, choices, (Widget **)0,
504 ps_select, &player_form);
505
506 ps_selected = -1;
507 positionpopup(popup, FALSE);
508 nh_XtPopup(popup, (int)XtGrabExclusive, player_form);
509
510 /* The callbacks will enable the event loop exit. */
511 (void) x_event(EXIT_ON_EXIT);
512
513 nh_XtPopdown(popup);
514 XtDestroyWidget(popup);
515 free((genericptr_t)choices), choices = 0;
516
517 if (ps_selected == PS_QUIT) {
518 clearlocks();
519 X11_exit_nhwindows((char *)0);
520 terminate(0);
521 } else if (ps_selected == PS_RANDOM) {
522 flags.initalign = ROLE_RANDOM;
523 } else if (ps_selected < 0 || ps_selected >= num_algns) {
524 panic("player_selection: bad alignment select value %d", ps_selected);
525 } else {
526 flags.initalign = ps_selected;
527 }
528 } /* more than one alignment choice available */
529 }
530 }
531
532
533 int
X11_get_ext_cmd()534 X11_get_ext_cmd()
535 {
536 static Boolean initialized = False;
537
538 if (!initialized) {
539 init_extended_commands_popup();
540 initialized = True;
541 }
542
543 extended_command_selected = -1; /* reset selected value */
544
545 positionpopup(extended_command_popup, FALSE); /* center on cursor */
546 nh_XtPopup(extended_command_popup, (int)XtGrabExclusive,
547 extended_command_form);
548
549 /* The callbacks will enable the event loop exit. */
550 (void) x_event(EXIT_ON_EXIT);
551
552 return extended_command_selected;
553 }
554
555 /* End global functions ===================================================== */
556
557 /* Extended Command -------------------------------------------------------- */
558 /* ARGSUSED */
559 static void
extend_select(w,client_data,call_data)560 extend_select(w, client_data, call_data)
561 Widget w;
562 XtPointer client_data, call_data;
563 {
564 int selected = (int) client_data;
565
566 if (extended_command_selected != selected) {
567 /* visibly deselect old one */
568 if (extended_command_selected >= 0)
569 swap_fg_bg(extended_commands[extended_command_selected]);
570
571 /* select new one */
572 swap_fg_bg(extended_commands[selected]);
573 extended_command_selected = selected;
574 }
575
576 nh_XtPopdown(extended_command_popup);
577 /* reset colors while popped down */
578 swap_fg_bg(extended_commands[extended_command_selected]);
579 ec_active = FALSE;
580 exit_x_event = TRUE; /* leave event loop */
581 }
582
583 /* ARGSUSED */
584 static void
extend_dismiss(w,client_data,call_data)585 extend_dismiss(w, client_data, call_data)
586 Widget w;
587 XtPointer client_data, call_data;
588 {
589 ec_dismiss();
590 }
591
592 /* ARGSUSED */
593 static void
extend_help(w,client_data,call_data)594 extend_help(w, client_data, call_data)
595 Widget w;
596 XtPointer client_data, call_data;
597 {
598 /* We might need to make it known that we already have one listed. */
599 (void) doextlist();
600 }
601
602 /* ARGSUSED */
603 void
ec_delete(w,event,params,num_params)604 ec_delete(w, event, params, num_params)
605 Widget w;
606 XEvent *event;
607 String *params;
608 Cardinal *num_params;
609 {
610 if (w == extended_command_popup) {
611 ec_dismiss();
612 } else {
613 popup_delete(w, event, params, num_params);
614 }
615 }
616
617 /* ARGSUSED */
618 static void
popup_delete(w,event,params,num_params)619 popup_delete(w, event, params, num_params)
620 Widget w;
621 XEvent *event;
622 String *params;
623 Cardinal *num_params;
624 {
625 ps_selected = PS_QUIT;
626 nh_XtPopdown(w);
627 exit_x_event = TRUE; /* leave event loop */
628 }
629
630 static void
ec_dismiss()631 ec_dismiss()
632 {
633 /* unselect while still visible */
634 if (extended_command_selected >= 0)
635 swap_fg_bg(extended_commands[extended_command_selected]);
636 extended_command_selected = -1; /* dismiss */
637 nh_XtPopdown(extended_command_popup);
638 ec_active = FALSE;
639 exit_x_event = TRUE; /* leave event loop */
640 }
641
642 /* ARGSUSED */
643 void
ec_key(w,event,params,num_params)644 ec_key(w, event, params, num_params)
645 Widget w;
646 XEvent *event;
647 String *params;
648 Cardinal *num_params;
649 {
650 char ch;
651 int i;
652 XKeyEvent *xkey = (XKeyEvent *) event;
653
654 ch = key_event_to_char(xkey);
655
656 if (ch == '\0') { /* don't accept nul char/modifier event */
657 /* don't beep */
658 return;
659 } else if (index("\033\n\r", ch)) {
660 if (ch == '\033') {
661 /* unselect while still visible */
662 if (extended_command_selected >= 0)
663 swap_fg_bg(extended_commands[extended_command_selected]);
664 extended_command_selected = -1; /* dismiss */
665 }
666
667 nh_XtPopdown(extended_command_popup);
668 /* unselect while invisible */
669 if (extended_command_selected >= 0)
670 swap_fg_bg(extended_commands[extended_command_selected]);
671
672 exit_x_event = TRUE; /* leave event loop */
673 ec_active = FALSE;
674 return;
675 }
676
677 /* too much time has elapsed */
678 if ((xkey->time - ec_time) > 500)
679 ec_active = FALSE;
680
681 if (!ec_active) {
682 ec_nchars = 0;
683 ec_active = TRUE;
684 }
685
686 ec_time = xkey->time;
687 ec_chars[ec_nchars++] = ch;
688 if (ec_nchars >= EC_NCHARS)
689 ec_nchars = EC_NCHARS-1; /* don't overflow */
690
691 for (i = 0; extcmdlist[i].ef_txt; i++) {
692 if (extcmdlist[i].ef_txt[0] == '?') continue;
693
694 if (!strncmp(ec_chars, extcmdlist[i].ef_txt, ec_nchars)) {
695 if (extended_command_selected != i) {
696 /* I should use set() and unset() actions, but how do */
697 /* I send the an action to the widget? */
698 if (extended_command_selected >= 0)
699 swap_fg_bg(extended_commands[extended_command_selected]);
700 extended_command_selected = i;
701 swap_fg_bg(extended_commands[extended_command_selected]);
702 }
703 break;
704 }
705 }
706 }
707
708 /*
709 * Use our own home-brewed version menu because simpleMenu is designed to
710 * be used from a menubox.
711 */
712 static void
init_extended_commands_popup()713 init_extended_commands_popup()
714 {
715 int i, num_commands;
716 const char **command_list;
717
718 /* count commands */
719 for (num_commands = 0; extcmdlist[num_commands].ef_txt; num_commands++)
720 ; /* do nothing */
721
722 /* If the last entry is "help", don't use it. */
723 if (strcmp(extcmdlist[num_commands-1].ef_txt, "?") == 0)
724 --num_commands;
725
726 command_list =
727 (const char **) alloc((unsigned)num_commands * sizeof(char *));
728
729 for (i = 0; i < num_commands; i++)
730 command_list[i] = extcmdlist[i].ef_txt;
731
732 extended_command_popup = make_menu("extended_commands",
733 "Extended Commands",
734 extended_command_translations,
735 "dismiss", extend_dismiss,
736 "help", extend_help,
737 num_commands, command_list, &extended_commands,
738 extend_select, &extended_command_form);
739
740 free((char *)command_list);
741 }
742
743 /* ------------------------------------------------------------------------- */
744
745 /*
746 * Create a popup widget of the following form:
747 *
748 * popup_label
749 * ----------- ------------
750 * |left_name| |right_name|
751 * ----------- ------------
752 * ------------------------
753 * | name1 |
754 * ------------------------
755 * ------------------------
756 * | name2 |
757 * ------------------------
758 * .
759 * .
760 * ------------------------
761 * | nameN |
762 * ------------------------
763 */
764 static Widget
make_menu(popup_name,popup_label,popup_translations,left_name,left_callback,right_name,right_callback,num_names,widget_names,command_widgets,name_callback,formp)765 make_menu(popup_name, popup_label, popup_translations,
766 left_name, left_callback,
767 right_name, right_callback,
768 num_names, widget_names, command_widgets, name_callback, formp)
769 const char *popup_name;
770 const char *popup_label;
771 const char *popup_translations;
772 const char *left_name;
773 XtCallbackProc left_callback;
774 const char *right_name;
775 XtCallbackProc right_callback;
776 int num_names;
777 const char **widget_names; /* return array of command widgets */
778 Widget **command_widgets;
779 XtCallbackProc name_callback;
780 Widget *formp; /* return */
781 {
782 Widget popup, form, label, above, left, right;
783 Widget *commands, *curr;
784 int i;
785 Arg args[8];
786 Cardinal num_args;
787 Dimension width, max_width;
788 int distance, skip;
789
790
791 commands = (Widget *) alloc((unsigned)num_names * sizeof(Widget));
792
793
794 num_args = 0;
795 XtSetArg(args[num_args], XtNallowShellResize, True); num_args++;
796
797 popup = XtCreatePopupShell(popup_name,
798 transientShellWidgetClass,
799 toplevel, args, num_args);
800 XtOverrideTranslations(popup,
801 XtParseTranslationTable("<Message>WM_PROTOCOLS: ec_delete()"));
802
803 num_args = 0;
804 XtSetArg(args[num_args], XtNtranslations,
805 XtParseTranslationTable(popup_translations)); num_args++;
806 *formp = form = XtCreateManagedWidget("menuform",
807 formWidgetClass,
808 popup,
809 args, num_args);
810
811 /* Get the default distance between objects in the form widget. */
812 num_args = 0;
813 XtSetArg(args[num_args], XtNdefaultDistance, &distance); num_args++;
814 XtGetValues(form, args, num_args);
815
816 /*
817 * Create the label.
818 */
819 num_args = 0;
820 XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;
821 label = XtCreateManagedWidget(popup_label,
822 labelWidgetClass,
823 form,
824 args, num_args);
825
826 /*
827 * Create the left button.
828 */
829 num_args = 0;
830 XtSetArg(args[num_args], XtNfromVert, label); num_args++;
831 /*
832 XtSetArg(args[num_args], XtNshapeStyle,
833 XmuShapeRoundedRectangle); num_args++;
834 */
835 left = XtCreateManagedWidget(left_name,
836 commandWidgetClass,
837 form,
838 args, num_args);
839 XtAddCallback(left, XtNcallback, left_callback, (XtPointer) 0);
840 skip = 3*distance; /* triple the spacing */
841 if(!skip) skip = 3;
842
843 /*
844 * Create right button.
845 */
846 num_args = 0;
847 XtSetArg(args[num_args], XtNfromHoriz, left); num_args++;
848 XtSetArg(args[num_args], XtNfromVert, label); num_args++;
849 /*
850 XtSetArg(args[num_args], XtNshapeStyle,
851 XmuShapeRoundedRectangle); num_args++;
852 */
853 right = XtCreateManagedWidget(right_name,
854 commandWidgetClass,
855 form,
856 args, num_args);
857 XtAddCallback(right, XtNcallback, right_callback, (XtPointer) 0);
858
859 XtInstallAccelerators(form, left);
860 XtInstallAccelerators(form, right);
861
862 /*
863 * Create and place the command widgets.
864 */
865 for (i = 0, above = left, curr = commands; i < num_names; i++) {
866 if (!widget_names[i]) continue;
867 num_args = 0;
868 XtSetArg(args[num_args], XtNfromVert, above); num_args++;
869 if (above == left) {
870 /* if first, we are farther apart */
871 XtSetArg(args[num_args], XtNvertDistance, skip); num_args++;
872 }
873
874 *curr = XtCreateManagedWidget(widget_names[i],
875 commandWidgetClass,
876 form,
877 args, num_args);
878 XtAddCallback(*curr, XtNcallback, name_callback, (XtPointer) i);
879 above = *curr++;
880 }
881
882 /*
883 * Now find the largest width. Start with the width dismiss + help
884 * buttons, since they are adjacent.
885 */
886 XtSetArg(args[0], XtNwidth, &max_width);
887 XtGetValues(left, args, ONE);
888 XtSetArg(args[0], XtNwidth, &width);
889 XtGetValues(right, args, ONE);
890 max_width = max_width + width + distance;
891
892 /* Next, the title. */
893 XtSetArg(args[0], XtNwidth, &width);
894 XtGetValues(label, args, ONE);
895 if (width > max_width) max_width = width;
896
897 /* Finally, the commands. */
898 for (i = 0, curr = commands; i < num_names; i++) {
899 if (!widget_names[i]) continue;
900 XtSetArg(args[0], XtNwidth, &width);
901 XtGetValues(*curr, args, ONE);
902 if (width > max_width) max_width = width;
903 curr++;
904 }
905
906 /*
907 * Finally, set all of the single line widgets to the largest width.
908 */
909 XtSetArg(args[0], XtNwidth, max_width);
910 XtSetValues(label, args, ONE);
911
912 for (i = 0, curr = commands; i < num_names; i++) {
913 if (!widget_names[i]) continue;
914 XtSetArg(args[0], XtNwidth, max_width);
915 XtSetValues(*curr, args, ONE);
916 curr++;
917 }
918
919 if (command_widgets)
920 *command_widgets = commands;
921 else
922 free((char *) commands);
923
924 XtRealizeWidget(popup);
925 XSetWMProtocols(XtDisplay(popup), XtWindow(popup), &wm_delete_window, 1);
926
927 return popup;
928 }
929