* New version 2.21.999
[alpine.git] / alpine / radio.c
blob85bc650b29fd515374488a7f4a730bfbd11d20e8
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: radio.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2018 Eduardo Chappa
8 * Copyright 2006-2007 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include "headers.h"
20 #include "radio.h"
21 #include "keymenu.h"
22 #include "busy.h"
23 #include "status.h"
24 #include "mailcmd.h"
25 #include "titlebar.h"
26 #include "roleconf.h"
27 #include "../pith/state.h"
28 #include "../pith/conf.h"
29 #include "../pith/newmail.h"
30 #include "../pith/util.h"
34 * Internal prototypes
36 int pre_screen_config_want_to(char *, int, int);
37 ESCKEY_S *construct_combined_esclist(ESCKEY_S *, ESCKEY_S *);
38 void radio_help(int, int, HelpType);
39 void draw_radio_prompt(int, unsigned, unsigned, char *);
42 #define RAD_BUT_COL 0
43 #define WANT_TO_BUF 2500
47 * want_to's array passed to radio_buttions...
49 static ESCKEY_S yorn[] = {
50 {'y', 'y', "Y", N_("Yes")},
51 {'n', 'n', "N", N_("No")},
52 {-1, 0, NULL, NULL}
55 int
56 pre_screen_config_want_to(char *question, int dflt, int on_ctrl_C)
58 int ret = 0;
59 char rep[WANT_TO_BUF], *p;
60 #ifdef _WINDOWS
61 rep[0] = '\0';
62 mswin_flush();
63 if(strlen(question) + 3 < WANT_TO_BUF){
64 snprintf(rep, sizeof(rep), "%s ?", question);
65 rep[sizeof(rep)-1] = '\0';
68 switch (mswin_yesno_utf8 (*rep ? rep : question)) {
69 default:
70 case 0: return (on_ctrl_C);
71 case 1: return ('y');
72 case 2: return ('n');
74 #endif
75 while(!ret){
76 if(fprintf(stdout, "%s? [%c]:", question, dflt) < 0
77 || fgets(rep, WANT_TO_BUF, stdin) == NULL)
78 alpine_panic(_("error on fprintf() or fgets()"));
79 if((p = strpbrk(rep, "\r\n")) != NULL)
80 *p = '\0';
81 switch(*rep){
82 case 'Y':
83 case 'y':
84 ret = (int)'y';
85 break;
86 case 'N':
87 case 'n':
88 ret = (int)'n';
89 break;
90 case '\0':
91 ret = dflt;
92 break;
93 default:
94 break;
97 return ret;
101 /*----------------------------------------------------------------------
102 Ask a yes/no question in the status line
104 Args: question -- string to prompt user with
105 dflt -- The default answer to the question (should probably
106 be y or n)
107 on_ctrl_C -- Answer returned on ^C
108 help -- Two line help text
109 flags -- Flags to modify behavior
110 WT_FLUSH_IN - Discard pending input.
111 WT_SEQ_SENSITIVE - Caller is sensitive to sequence
112 number changes caused by
113 unsolicited expunges while we're
114 viewing a message.
116 Result: Messes up the status line,
117 returns y, n, dflt, on_ctrl_C, or SEQ_EXCEPTION
118 ---*/
120 want_to(char *question, int dflt, int on_ctrl_C, HelpType help, int flags)
122 char *free_this = NULL, *free_this2 = NULL, *prompt;
123 int rv, width;
124 size_t len;
126 if(!ps_global->ttyo)
127 return(pre_screen_config_want_to(question, dflt, on_ctrl_C));
128 #ifdef _WINDOWS
129 if (mswin_usedialog ()) {
130 mswin_flush ();
131 switch (mswin_yesno_utf8 (question)) {
132 default:
133 case 0: return (on_ctrl_C);
134 case 1: return ('y');
135 case 2: return ('n');
138 #endif
140 /*----
141 One problem with adding the (y/n) here is that shrinking the
142 screen while in radio_buttons() will cause it to get chopped
143 off. It would be better to truncate the question passed in
144 here and leave the full "(y/n) [x] : " on.
145 ----*/
147 len = strlen(question) + 4;
148 free_this = (char *) fs_get(len);
149 width = utf8_width(question);
151 if(width + 2 < ps_global->ttyo->screen_cols){
152 snprintf(free_this, len, "%s? ", question);
153 free_this[len-1] = '\0';
154 prompt = free_this;
156 else if(width + 1 < ps_global->ttyo->screen_cols){
157 snprintf(free_this, len, "%s?", question);
158 free_this[len-1] = '\0';
159 prompt = free_this;
161 else if(width < ps_global->ttyo->screen_cols){
162 snprintf(free_this, len, "%s", question);
163 free_this[len-1] = '\0';
164 prompt = free_this;
166 else{
167 free_this2 = (char *) fs_get(len);
168 snprintf(free_this2, len, "%s? ", question);
169 prompt = short_str(free_this2, free_this, len, ps_global->ttyo->screen_cols-1, MidDots);
172 if(on_ctrl_C == 'n') /* don't ever let cancel == 'n' */
173 on_ctrl_C = 0;
175 rv = radio_buttons(prompt,
176 (ps_global->ttyo->screen_rows > 4) ? - FOOTER_ROWS(ps_global) : -1,
177 yorn, dflt, on_ctrl_C, help, flags);
179 if(free_this)
180 fs_give((void **) &free_this);
182 if(free_this2)
183 fs_give((void **) &free_this2);
185 return(rv);
190 one_try_want_to(char *question, int dflt, int on_ctrl_C, HelpType help, int flags)
192 char *q2;
193 int rv;
194 size_t l;
196 l = strlen(question) + 5;
197 q2 = fs_get((l+1) * sizeof(char));
198 strncpy(q2, question, l);
199 q2[l] = '\0';
200 (void) utf8_truncate(q2, ps_global->ttyo->screen_cols - 6);
201 strncat(q2, "? ", l+1 - strlen(q2) - 1);
202 q2[l] = '\0';
203 rv = radio_buttons(q2,
204 (ps_global->ttyo->screen_rows > 4) ? - FOOTER_ROWS(ps_global) : -1,
205 yorn, dflt, on_ctrl_C, help, flags | RB_ONE_TRY);
206 fs_give((void **) &q2);
208 return(rv);
212 /*----------------------------------------------------------------------
213 Prompt user for a choice among alternatives
215 Args -- utf8prompt: The prompt for the question/selection
216 line: The line to prompt on, if negative then relative to bottom
217 esc_list: ESC_KEY_S list of keys
218 dflt: The selection when the <CR> is pressed (should probably
219 be one of the chars in esc_list)
220 on_ctrl_C: The selection when ^C is pressed
221 help_text: Text to be displayed on bottom two lines
222 flags: Logically OR'd flags modifying our behavior to:
223 RB_FLUSH_IN - Discard any pending input chars.
224 RB_ONE_TRY - Only give one chance to answer. Returns
225 on_ctrl_C value if not answered acceptably
226 on first try.
227 RB_NO_NEWMAIL - Quell the usual newmail check.
228 RB_SEQ_SENSITIVE - The caller is sensitive to sequence number
229 changes so return on_ctrl_C if an
230 unsolicited expunge happens while we're
231 viewing a message.
232 RB_RET_HELP - Instead of the regular internal handling
233 way of handling help_text, this just causes
234 radio_buttons to return 3 when help is
235 asked for, so that the caller handles it
236 instead.
238 Note: If there are enough keys in the esc_list to need a second
239 screen, and there is no help, then the 13th key will be
240 put in the help position.
242 Result -- Returns the letter pressed. Will be one of the characters in the
243 esc_list argument, or dflt, or on_ctrl_C, or SEQ_EXCEPTION.
245 This will pause for any new status message to be seen and then prompt the user.
246 The prompt will be truncated to fit on the screen. Redraw and resize are
247 handled along with ^Z suspension. Typing ^G will toggle the help text on and
248 off. Character types that are not buttons will result in a beep (unless one_try
249 is set).
250 ----*/
252 radio_buttons(char *utf8prompt, int line, ESCKEY_S *esc_list, int dflt,
253 int on_ctrl_C, HelpType help_text, int flags)
255 UCS ucs;
256 register int ch, real_line;
257 char *q, *ds = NULL;
258 unsigned maxcol;
259 int max_label, i, start, fkey_table[12];
260 int km_popped = 0;
261 struct key rb_keys[12];
262 struct key_menu rb_keymenu;
263 bitmap_t bitmap;
264 struct variable *vars = ps_global->vars;
265 COLOR_PAIR *lastc = NULL, *promptc = NULL;
267 #ifdef _WINDOWS
268 int cursor_shown;
270 if (mswin_usedialog()){
271 MDlgButton button_list[25];
272 LPTSTR free_names[25];
273 LPTSTR free_labels[25];
274 int b, i, ret;
275 char **help;
277 memset(&free_names, 0, sizeof(LPTSTR) * 25);
278 memset(&free_labels, 0, sizeof(LPTSTR) * 25);
279 memset(&button_list, 0, sizeof (MDlgButton) * 25);
280 b = 0;
282 if(flags & RB_RET_HELP){
283 if(help_text != NO_HELP)
284 alpine_panic("RET_HELP and help in radio_buttons!");
286 button_list[b].ch = '?';
287 button_list[b].rval = 3;
288 button_list[b].name = TEXT("?");
289 free_labels[b] = utf8_to_lptstr(N_("Help"));
290 button_list[b].label = free_labels[b];
291 ++b;
294 for(i = 0; esc_list && esc_list[i].ch != -1 && i < 23; ++i){
295 if(esc_list[i].ch != -2){
296 button_list[b].ch = esc_list[i].ch;
297 button_list[b].rval = esc_list[i].rval;
298 free_names[b] = utf8_to_lptstr(esc_list[i].name);
299 button_list[b].name = free_names[b];
300 free_labels[b] = utf8_to_lptstr(esc_list[i].label);
301 button_list[b].label = free_labels[b];
302 ++b;
306 button_list[b].ch = -1;
308 /* assumption here is that HelpType is char ** */
309 help = help_text;
311 ret = mswin_select(utf8prompt, button_list, dflt, on_ctrl_C, help, flags);
312 for(i = 0; i < 25; i++){
313 if(free_names[i])
314 fs_give((void **) &free_names[i]);
315 if(free_labels[i])
316 fs_give((void **) &free_labels[i]);
319 return (ret);
322 #endif /* _WINDOWS */
324 suspend_busy_cue();
325 flush_ordered_messages(); /* show user previous status msgs */
326 mark_status_dirty(); /* clear message next display call */
327 real_line = line > 0 ? line : ps_global->ttyo->screen_rows + line;
328 MoveCursor(real_line, RAD_BUT_COL);
329 CleartoEOLN();
331 /*---- Find widest label ----*/
332 max_label = 0;
333 for(i = 0; esc_list && esc_list[i].ch != -1 && i < 11; i++){
334 if(esc_list[i].ch == -2) /* -2 means to skip this key and leave blank */
335 continue;
336 if(esc_list[i].name)
337 max_label = MAX(max_label, utf8_width(esc_list[i].name));
340 if(ps_global->ttyo->screen_cols - max_label - 1 > 0)
341 maxcol = ps_global->ttyo->screen_cols - max_label - 1;
342 else
343 maxcol = 0;
346 * We need to be able to truncate q, so copy it in case it is
347 * a readonly string.
349 q = cpystr(utf8prompt);
351 /*---- Init structs for keymenu ----*/
352 for(i = 0; i < 12; i++)
353 memset((void *)&rb_keys[i], 0, sizeof(struct key));
355 memset((void *)&rb_keymenu, 0, sizeof(struct key_menu));
356 rb_keymenu.how_many = 1;
357 rb_keymenu.keys = rb_keys;
359 /*---- Setup key menu ----*/
360 start = 0;
361 clrbitmap(bitmap);
362 memset(fkey_table, NO_OP_COMMAND, 12 * sizeof(int));
363 if(flags & RB_RET_HELP && help_text != NO_HELP)
364 alpine_panic("RET_HELP and help in radio_buttons!");
366 /* if shown, always at position 0 */
367 if(help_text != NO_HELP || flags & RB_RET_HELP){
368 rb_keymenu.keys[0].name = "?";
369 rb_keymenu.keys[0].label = N_("Help");
370 setbitn(0, bitmap);
371 fkey_table[0] = ctrl('G');
372 start++;
375 if(on_ctrl_C){
376 rb_keymenu.keys[1].name = "^C";
377 rb_keymenu.keys[1].label = N_("Cancel");
378 setbitn(1, bitmap);
379 fkey_table[1] = ctrl('C');
380 start++;
383 start = start ? 2 : 0;
384 /*---- Show the usual possible keys ----*/
385 for(i=start; esc_list && esc_list[i-start].ch != -1; i++){
387 * If we have an esc_list item we'd like to put in the non-existent
388 * 13th slot, and there is no help, we put it in the help slot
389 * instead. We're hacking now...!
391 * We may also have invisible esc_list items that don't show up
392 * on the screen. We use this when we have two different keys
393 * which are synonyms, like ^P and KEY_UP. If all the slots are
394 * already full we can still fit invisible keys off the screen to
395 * the right. A key is invisible if it's label is "".
397 if(i >= 12){
398 if(esc_list[i-start].label
399 && esc_list[i-start].label[0] != '\0'){ /* visible */
400 if(i == 12){ /* special case where we put it in help slot */
401 if(help_text != NO_HELP)
402 alpine_panic("Programming botch in radio_buttons(): too many keys");
404 if(esc_list[i-start].ch != -2)
405 setbitn(0, bitmap); /* the help slot */
407 fkey_table[0] = esc_list[i-start].ch;
408 rb_keymenu.keys[0].name = esc_list[i-start].name;
409 if(esc_list[i-start].ch != -2
410 && esc_list[i-start].rval == dflt
411 && esc_list[i-start].label){
412 size_t l;
414 l = strlen(esc_list[i-start].label) + 2;
415 ds = (char *)fs_get((l+1) * sizeof(char));
416 snprintf(ds, l+1, "[%s]", esc_list[i-start].label);
417 ds[l] = '\0';
418 rb_keymenu.keys[0].label = ds;
420 else
421 rb_keymenu.keys[0].label = esc_list[i-start].label;
423 else
424 alpine_panic("Botch in radio_buttons(): too many keys");
427 else{
428 if(esc_list[i-start].ch != -2)
429 setbitn(i, bitmap);
431 fkey_table[i] = esc_list[i-start].ch;
432 rb_keymenu.keys[i].name = esc_list[i-start].name;
433 if(esc_list[i-start].ch != -2
434 && esc_list[i-start].rval == dflt
435 && esc_list[i-start].label){
436 size_t l;
438 l = strlen(esc_list[i-start].label) + 2;
439 ds = (char *)fs_get((l+1) * sizeof(char));
440 snprintf(ds, l+1, "[%s]", esc_list[i-start].label);
441 ds[l] = '\0';
442 rb_keymenu.keys[i].label = ds;
444 else
445 rb_keymenu.keys[i].label = esc_list[i-start].label;
449 for(; i < 12; i++)
450 rb_keymenu.keys[i].name = NULL;
452 ps_global->mangled_footer = 1;
454 #ifdef _WINDOWS
455 cursor_shown = mswin_showcaret(1);
456 #endif
458 if(pico_usingcolor() && VAR_PROMPT_FORE_COLOR &&
459 VAR_PROMPT_BACK_COLOR &&
460 pico_is_good_color(VAR_PROMPT_FORE_COLOR) &&
461 pico_is_good_color(VAR_PROMPT_BACK_COLOR)){
462 lastc = pico_get_cur_color();
463 if(lastc){
464 promptc = new_color_pair(VAR_PROMPT_FORE_COLOR,
465 VAR_PROMPT_BACK_COLOR);
466 (void)pico_set_colorp(promptc, PSC_NONE);
469 else
470 StartInverse();
472 draw_radio_prompt(real_line, RAD_BUT_COL, maxcol, q);
474 while(1){
475 fflush(stdout);
477 /*---- Paint the keymenu ----*/
478 if(lastc)
479 (void)pico_set_colorp(lastc, PSC_NONE);
480 else
481 EndInverse();
483 draw_keymenu(&rb_keymenu, bitmap, ps_global->ttyo->screen_cols,
484 1 - FOOTER_ROWS(ps_global), 0, FirstMenu);
485 if(promptc)
486 (void)pico_set_colorp(promptc, PSC_NONE);
487 else
488 StartInverse();
490 MoveCursor(real_line, MIN(RAD_BUT_COL+utf8_width(q), maxcol+1));
492 if(flags & RB_FLUSH_IN)
493 flush_input();
495 newcmd:
496 /* Timeout 5 min to keep imap mail stream alive */
497 ucs = read_char(600);
498 dprint((2,
499 "Want_to read: %s (0x%x)\n", pretty_command(ucs), ucs));
500 if((ucs < 0x80) && isupper((unsigned char) ucs))
501 ucs = tolower((unsigned char) ucs);
503 if(F_ON(F_USE_FK,ps_global)
504 && (((ucs < 0x80) && isalpha((unsigned char) ucs) && !strchr("YyNn",(int) ucs))
505 || ((ucs >= PF1 && ucs <= PF12)
506 && (ucs = fkey_table[ucs - PF1]) == NO_OP_COMMAND))){
508 * The funky test above does two things. It maps
509 * esc_list character commands to function keys, *and* prevents
510 * character commands from input while in function key mode.
511 * NOTE: this breaks if we ever need more than the first
512 * twelve function keys...
514 if(flags & RB_ONE_TRY){
515 ch = ucs = on_ctrl_C;
516 goto out_of_loop;
519 Writechar(BELL, 0);
520 continue;
523 switch(ucs){
525 default:
526 for(i = 0; esc_list && esc_list[i].ch != -1; i++)
527 if(ucs == esc_list[i].ch){
528 int len, n;
530 MoveCursor(real_line,len=MIN(RAD_BUT_COL+utf8_width(q),maxcol+1));
531 for(n = 0, len = ps_global->ttyo->screen_cols - len;
532 esc_list[i].label && esc_list[i].label[n] && len > 0;
533 n++, len--)
534 Writechar(esc_list[i].label[n], 0);
536 ch = esc_list[i].rval;
537 goto out_of_loop;
540 if(flags & RB_ONE_TRY){
541 ch = on_ctrl_C;
542 goto out_of_loop;
545 Writechar(BELL, 0);
546 break;
548 case ctrl('M'):
549 case ctrl('J'):
550 ch = dflt;
551 for(i = 0; esc_list && esc_list[i].ch != -1; i++)
552 if(ch == esc_list[i].rval){
553 int len, n;
555 MoveCursor(real_line,len=MIN(RAD_BUT_COL+utf8_width(q),maxcol+1));
556 for(n = 0, len = ps_global->ttyo->screen_cols - len;
557 esc_list[i].label && esc_list[i].label[n] && len > 0;
558 n++, len--)
559 Writechar(esc_list[i].label[n], 0);
560 break;
563 goto out_of_loop;
565 case ctrl('C'):
566 if(on_ctrl_C || (flags & RB_ONE_TRY)){
567 ch = on_ctrl_C;
568 goto out_of_loop;
571 Writechar(BELL, 0);
572 break;
575 case '?':
576 case ctrl('G'):
577 if(FOOTER_ROWS(ps_global) == 1 && km_popped == 0){
578 km_popped++;
579 FOOTER_ROWS(ps_global) = 3;
580 line = -3;
581 real_line = ps_global->ttyo->screen_rows + line;
582 if(lastc)
583 (void)pico_set_colorp(lastc, PSC_NONE);
584 else
585 EndInverse();
587 clearfooter(ps_global);
588 if(promptc)
589 (void)pico_set_colorp(promptc, PSC_NONE);
590 else
591 StartInverse();
593 draw_radio_prompt(real_line, RAD_BUT_COL, maxcol, q);
594 break;
597 if(flags & RB_RET_HELP){
598 ch = 3;
599 goto out_of_loop;
601 else if(help_text != NO_HELP && FOOTER_ROWS(ps_global) > 1){
602 mark_keymenu_dirty();
603 if(lastc)
604 (void)pico_set_colorp(lastc, PSC_NONE);
605 else
606 EndInverse();
608 MoveCursor(real_line + 1, RAD_BUT_COL);
609 CleartoEOLN();
610 MoveCursor(real_line + 2, RAD_BUT_COL);
611 CleartoEOLN();
612 radio_help(real_line, RAD_BUT_COL, help_text);
613 sleep(5);
614 MoveCursor(real_line, MIN(RAD_BUT_COL+utf8_width(q), maxcol+1));
615 if(promptc)
616 (void)pico_set_colorp(promptc, PSC_NONE);
617 else
618 StartInverse();
620 else
621 Writechar(BELL, 0);
623 break;
626 case NO_OP_COMMAND:
627 goto newcmd; /* misunderstood escape? */
629 case NO_OP_IDLE: /* UNODIR, keep the stream alive */
630 if(flags & RB_NO_NEWMAIL)
631 goto newcmd;
633 i = new_mail(0, VeryBadTime, NM_DEFER_SORT);
634 if(sp_expunge_count(ps_global->mail_stream)
635 && flags & RB_SEQ_SENSITIVE){
636 if(on_ctrl_C)
637 ch = on_ctrl_C;
638 else
639 ch = SEQ_EXCEPTION;
641 goto out_of_loop;
644 if(i < 0)
645 break; /* no changes, get on with life */
646 /* Else fall into redraw to adjust displayed numbers and such */
649 case KEY_RESIZE:
650 case ctrl('L'):
651 real_line = line > 0 ? line : ps_global->ttyo->screen_rows + line;
652 if(lastc)
653 (void)pico_set_colorp(lastc, PSC_NONE);
654 else
655 EndInverse();
657 ClearScreen();
658 redraw_titlebar();
659 if(ps_global->redrawer != NULL)
660 (*ps_global->redrawer)();
661 if(FOOTER_ROWS(ps_global) == 3 || km_popped)
662 redraw_keymenu();
664 if(ps_global->ttyo->screen_cols - max_label - 1 > 0)
665 maxcol = ps_global->ttyo->screen_cols - max_label - 1;
666 else
667 maxcol = 0;
669 if(promptc)
670 (void)pico_set_colorp(promptc, PSC_NONE);
671 else
672 StartInverse();
674 draw_radio_prompt(real_line, RAD_BUT_COL, maxcol, q);
675 break;
677 } /* switch */
680 out_of_loop:
682 #ifdef _WINDOWS
683 if(!cursor_shown)
684 mswin_showcaret(0);
685 #endif
687 fs_give((void **) &q);
688 if(ds)
689 fs_give((void **) &ds);
691 if(lastc){
692 (void) pico_set_colorp(lastc, PSC_NONE);
693 free_color_pair(&lastc);
694 if(promptc)
695 free_color_pair(&promptc);
697 else
698 EndInverse();
700 fflush(stdout);
701 resume_busy_cue(0);
702 if(km_popped){
703 FOOTER_ROWS(ps_global) = 1;
704 clearfooter(ps_global);
705 ps_global->mangled_body = 1;
708 return(ch);
712 #define OTHER_RETURN_VAL 1300
713 #define KEYS_PER_LIST 8
716 * This should really be part of radio_buttons itself, I suppose. It was
717 * easier to do it this way. This is for when there are more than 12
718 * possible commands. We could have all the radio_buttons calls call this
719 * instead of radio_buttons, or rename this to radio_buttons.
721 * Radio_buttons is limited to 10 visible commands unless there is no Help,
722 * in which case it is 11 visible commands.
723 * Double_radio_buttons is limited to 16 visible commands because it uses
724 * slots 3 and 4 for readability and the OTHER CMD.
727 double_radio_buttons(char *prompt, int line, ESCKEY_S *esc_list, int dflt, int on_ctrl_C, HelpType help_text, int flags)
729 ESCKEY_S *list = NULL, *list1 = NULL, *list2 = NULL;
730 int i = 0, j;
731 int v = OTHER_RETURN_VAL, listnum = 0;
733 #ifdef _WINDOWS
734 if(mswin_usedialog())
735 return(radio_buttons(prompt, line, esc_list, dflt, on_ctrl_C,
736 help_text, flags));
737 #endif
739 /* check to see if it will all fit in one */
740 while(esc_list && esc_list[i].ch != -1)
741 i++;
743 i++; /* for ^C */
744 if(help_text != NO_HELP)
745 i++;
747 if(i <= 12)
748 return(radio_buttons(prompt, line, esc_list, dflt, on_ctrl_C,
749 help_text, flags));
752 * Won't fit, split it into two lists.
754 * We can fit at most 8 items in the visible list. The rest of
755 * the commands have to be invisible. Each of list1 and list2 should
756 * have no more than 8 visible (name != "" || label != "") items.
758 list1 = (ESCKEY_S *)fs_get((KEYS_PER_LIST+1) * sizeof(*list1));
759 memset(list1, 0, (KEYS_PER_LIST+1) * sizeof(*list1));
760 list2 = (ESCKEY_S *)fs_get((KEYS_PER_LIST+1) * sizeof(*list2));
761 memset(list2, 0, (KEYS_PER_LIST+1) * sizeof(*list2));
763 for(j=0,i=0; esc_list[i].ch != -1 && j < KEYS_PER_LIST; j++,i++)
764 list1[j] = esc_list[i];
766 list1[j].ch = -1;
768 for(j=0; esc_list[i].ch != -1 && j < KEYS_PER_LIST; j++,i++)
769 list2[j] = esc_list[i];
771 list2[j].ch = -1;
773 list = construct_combined_esclist(list1, list2);
775 while(v == OTHER_RETURN_VAL){
776 v = radio_buttons(prompt,line,list,dflt,on_ctrl_C,help_text,flags);
777 if(v == OTHER_RETURN_VAL){
778 fs_give((void **) &list);
779 listnum = 1 - listnum;
780 list = construct_combined_esclist(listnum ? list2 : list1,
781 listnum ? list1 : list2);
785 if(list)
786 fs_give((void **) &list);
787 if(list1)
788 fs_give((void **) &list1);
789 if(list2)
790 fs_give((void **) &list2);
792 return(v);
796 ESCKEY_S *
797 construct_combined_esclist(ESCKEY_S *list1, ESCKEY_S *list2)
799 ESCKEY_S *list;
800 int i, j=0, count;
802 count = 2; /* for blank key and for OTHER key */
803 for(i=0; list1 && list1[i].ch != -1; i++)
804 count++;
805 for(i=0; list2 && list2[i].ch != -1; i++)
806 count++;
808 list = (ESCKEY_S *) fs_get((count + 1) * sizeof(*list));
809 memset(list, 0, (count + 1) * sizeof(*list));
811 list[j].ch = -2; /* leave blank */
812 list[j].rval = 0;
813 list[j].name = NULL;
814 list[j++].label = NULL;
816 list[j].ch = 'o';
817 list[j].rval = OTHER_RETURN_VAL;
818 list[j].name = "O";
819 list[j].label = N_("OTHER CMDS");
822 * Make sure that O for OTHER CMD or the return val for OTHER CMD
823 * isn't used for something else.
825 for(i=0; list1 && list1[i].ch != -1; i++){
826 if(list1[i].rval == list[j].rval)
827 alpine_panic("1bad rval in d_r");
828 if(F_OFF(F_USE_FK,ps_global) && list1[i].ch == list[j].ch)
829 alpine_panic("1bad ch in ccl");
832 for(i=0; list2 && list2[i].ch != -1; i++){
833 if(list2[i].rval == list[j].rval)
834 alpine_panic("2bad rval in d_r");
835 if(F_OFF(F_USE_FK,ps_global) && list2[i].ch == list[j].ch)
836 alpine_panic("2bad ch in ccl");
839 j++;
841 /* the visible set */
842 for(i=0; list1 && list1[i].ch != -1; i++){
843 if(i >= KEYS_PER_LIST && list1[i].label[0] != '\0')
844 alpine_panic("too many visible keys in ccl");
846 list[j++] = list1[i];
849 /* the rest are invisible */
850 for(i=0; list2 && list2[i].ch != -1; i++){
851 list[j] = list2[i];
852 list[j].label = "";
853 list[j++].name = "";
856 list[j].ch = -1;
858 return(list);
862 /*----------------------------------------------------------------------
864 ----*/
865 void
866 radio_help(int line, int column, HelpType help)
868 char **text;
870 /* assumption here is that HelpType is char ** */
871 text = help;
872 if(text == NULL)
873 return;
875 MoveCursor(line + 1, column);
876 CleartoEOLN();
877 if(text[0])
878 PutLine0(line + 1, column, text[0]);
880 MoveCursor(line + 2, column);
881 CleartoEOLN();
882 if(text[1])
883 PutLine0(line + 2, column, text[1]);
885 fflush(stdout);
889 /*----------------------------------------------------------------------
890 Paint the screen with the radio buttons prompt
891 ----*/
892 void
893 draw_radio_prompt(int line, unsigned start_c, unsigned max_c, char *q)
895 size_t len;
896 unsigned x, width, got_width;
897 char *tmpq = NULL, *useq;
899 width = utf8_width(q);
900 if(width > max_c - start_c + 1){
901 tmpq = (char *) fs_get((len=(strlen(q)+1)) * sizeof(char));
902 (void) utf8_to_width(tmpq, q, len, max_c - start_c + 1, &got_width);
903 useq = tmpq;
904 width = got_width;
906 else
907 useq = q;
909 PutLine0(line, start_c, useq);
910 x = start_c + width;
911 MoveCursor(line, x);
912 while(x++ < ps_global->ttyo->screen_cols)
913 Writechar(' ', 0);
915 MoveCursor(line, start_c + width);
916 fflush(stdout);
917 if(tmpq)
918 fs_give((void **) &tmpq);