* New version 2.26
[alpine.git] / alpine / radio.c
blob4df4ec542d9a9b7940d65df85d35f2b85fe2bbad
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2007 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 #include "headers.h"
16 #include "radio.h"
17 #include "keymenu.h"
18 #include "busy.h"
19 #include "status.h"
20 #include "mailcmd.h"
21 #include "titlebar.h"
22 #include "roleconf.h"
23 #include "../pith/state.h"
24 #include "../pith/conf.h"
25 #include "../pith/newmail.h"
26 #include "../pith/util.h"
30 * Internal prototypes
32 int pre_screen_config_want_to(char *, int, int);
33 ESCKEY_S *construct_combined_esclist(ESCKEY_S *, ESCKEY_S *);
34 void radio_help(int, int, HelpType);
35 void draw_radio_prompt(int, unsigned, unsigned, char *);
38 #define RAD_BUT_COL 0
39 #define WANT_TO_BUF 2500
43 * want_to's array passed to radio_buttions...
45 static ESCKEY_S yorn[] = {
46 {'y', 'y', "Y", N_("Yes")},
47 {'n', 'n', "N", N_("No")},
48 {-1, 0, NULL, NULL}
51 int
52 pre_screen_config_want_to(char *question, int dflt, int on_ctrl_C)
54 int ret = 0;
55 char rep[WANT_TO_BUF], *p;
56 #ifdef _WINDOWS
57 rep[0] = '\0';
58 mswin_flush();
59 if(strlen(question) + 3 < WANT_TO_BUF){
60 snprintf(rep, sizeof(rep), "%s ?", question);
61 rep[sizeof(rep)-1] = '\0';
64 switch (mswin_yesno_utf8 (*rep ? rep : question)) {
65 default:
66 case 0: return (on_ctrl_C);
67 case 1: return ('y');
68 case 2: return ('n');
70 #endif
71 while(!ret){
72 if(fprintf(stdout, "%s? [%c]:", question, dflt) < 0
73 || fgets(rep, WANT_TO_BUF, stdin) == NULL)
74 alpine_panic(_("error on fprintf() or fgets()"));
75 if((p = strpbrk(rep, "\r\n")) != NULL)
76 *p = '\0';
77 switch(*rep){
78 case 'Y':
79 case 'y':
80 ret = (int)'y';
81 break;
82 case 'N':
83 case 'n':
84 ret = (int)'n';
85 break;
86 case '\0':
87 ret = dflt;
88 break;
89 default:
90 break;
93 return ret;
97 /*----------------------------------------------------------------------
98 Ask a yes/no question in the status line
100 Args: question -- string to prompt user with
101 dflt -- The default answer to the question (should probably
102 be y or n)
103 on_ctrl_C -- Answer returned on ^C
104 help -- Two line help text
105 flags -- Flags to modify behavior
106 WT_DING - ding the bell when asking.
107 WT_FLUSH_IN - Discard pending input.
108 WT_SEQ_SENSITIVE - Caller is sensitive to sequence
109 number changes caused by
110 unsolicited expunges while we're
111 viewing a message.
113 Result: Messes up the status line,
114 returns y, n, dflt, on_ctrl_C, or SEQ_EXCEPTION
115 ---*/
117 want_to(char *question, int dflt, int on_ctrl_C, HelpType help, int flags)
119 char *free_this = NULL, *free_this2 = NULL, *prompt;
120 int rv, width;
121 size_t len;
123 if((flags & WT_DING) && F_OFF(F_QUELL_BEEPS, ps_global)){
124 Writechar(BELL, 0);
125 fflush(stdout);
128 if(!ps_global->ttyo)
129 return(pre_screen_config_want_to(question, dflt, on_ctrl_C));
130 #ifdef _WINDOWS
131 if (mswin_usedialog ()) {
132 mswin_flush ();
133 switch (mswin_yesno_utf8 (question)) {
134 default:
135 case 0: return (on_ctrl_C);
136 case 1: return ('y');
137 case 2: return ('n');
140 #endif
142 /*----
143 One problem with adding the (y/n) here is that shrinking the
144 screen while in radio_buttons() will cause it to get chopped
145 off. It would be better to truncate the question passed in
146 here and leave the full "(y/n) [x] : " on.
147 ----*/
149 len = strlen(question) + 4;
150 free_this = (char *) fs_get(len);
151 width = utf8_width(question);
153 if(width + 2 < ps_global->ttyo->screen_cols){
154 snprintf(free_this, len, "%s? ", question);
155 free_this[len-1] = '\0';
156 prompt = free_this;
158 else if(width + 1 < ps_global->ttyo->screen_cols){
159 snprintf(free_this, len, "%s?", question);
160 free_this[len-1] = '\0';
161 prompt = free_this;
163 else if(width < ps_global->ttyo->screen_cols){
164 snprintf(free_this, len, "%s", question);
165 free_this[len-1] = '\0';
166 prompt = free_this;
168 else{
169 free_this2 = (char *) fs_get(len);
170 snprintf(free_this2, len, "%s? ", question);
171 prompt = short_str(free_this2, free_this, len, ps_global->ttyo->screen_cols-1, MidDots);
174 if(on_ctrl_C == 'n') /* don't ever let cancel == 'n' */
175 on_ctrl_C = 0;
177 rv = radio_buttons(prompt,
178 (ps_global->ttyo->screen_rows > 4) ? - FOOTER_ROWS(ps_global) : -1,
179 yorn, dflt, on_ctrl_C, help, flags);
181 if(free_this)
182 fs_give((void **) &free_this);
184 if(free_this2)
185 fs_give((void **) &free_this2);
187 return(rv);
192 one_try_want_to(char *question, int dflt, int on_ctrl_C, HelpType help, int flags)
194 char *q2;
195 int rv;
196 size_t l;
198 l = strlen(question) + 5;
199 q2 = fs_get((l+1) * sizeof(char));
200 strncpy(q2, question, l);
201 q2[l] = '\0';
202 (void) utf8_truncate(q2, ps_global->ttyo->screen_cols - 6);
203 strncat(q2, "? ", l+1 - strlen(q2) - 1);
204 q2[l] = '\0';
205 rv = radio_buttons(q2,
206 (ps_global->ttyo->screen_rows > 4) ? - FOOTER_ROWS(ps_global) : -1,
207 yorn, dflt, on_ctrl_C, help, flags | RB_ONE_TRY);
208 fs_give((void **) &q2);
210 return(rv);
214 /*----------------------------------------------------------------------
215 Prompt user for a choice among alternatives
217 Args -- utf8prompt: The prompt for the question/selection
218 line: The line to prompt on, if negative then relative to bottom
219 esc_list: ESC_KEY_S list of keys
220 dflt: The selection when the <CR> is pressed (should probably
221 be one of the chars in esc_list)
222 on_ctrl_C: The selection when ^C is pressed
223 help_text: Text to be displayed on bottom two lines
224 flags: Logically OR'd flags modifying our behavior to:
225 RB_FLUSH_IN - Discard any pending input chars.
226 RB_ONE_TRY - Only give one chance to answer. Returns
227 on_ctrl_C value if not answered acceptably
228 on first try.
229 RB_NO_NEWMAIL - Quell the usual newmail check.
230 RB_SEQ_SENSITIVE - The caller is sensitive to sequence number
231 changes so return on_ctrl_C if an
232 unsolicited expunge happens while we're
233 viewing a message.
234 RB_RET_HELP - Instead of the regular internal handling
235 way of handling help_text, this just causes
236 radio_buttons to return 3 when help is
237 asked for, so that the caller handles it
238 instead.
240 Note: If there are enough keys in the esc_list to need a second
241 screen, and there is no help, then the 13th key will be
242 put in the help position.
244 Result -- Returns the letter pressed. Will be one of the characters in the
245 esc_list argument, or dflt, or on_ctrl_C, or SEQ_EXCEPTION.
247 This will pause for any new status message to be seen and then prompt the user.
248 The prompt will be truncated to fit on the screen. Redraw and resize are
249 handled along with ^Z suspension. Typing ^G will toggle the help text on and
250 off. Character types that are not buttons will result in a beep (unless one_try
251 is set).
252 ----*/
254 radio_buttons(char *utf8prompt, int line, ESCKEY_S *esc_list, int dflt,
255 int on_ctrl_C, HelpType help_text, int flags)
257 UCS ucs;
258 register int ch, real_line;
259 char *q, *ds = NULL;
260 unsigned maxcol;
261 int max_label, i, start, fkey_table[12];
262 int km_popped = 0;
263 struct key rb_keys[12];
264 struct key_menu rb_keymenu;
265 bitmap_t bitmap;
266 struct variable *vars = ps_global->vars;
267 COLOR_PAIR *lastc = NULL, *promptc = NULL;
269 #ifdef _WINDOWS
270 int cursor_shown;
272 if (mswin_usedialog()){
273 MDlgButton button_list[25];
274 LPTSTR free_names[25];
275 LPTSTR free_labels[25];
276 int b, i, ret;
277 char **help;
279 memset(&free_names, 0, sizeof(LPTSTR) * 25);
280 memset(&free_labels, 0, sizeof(LPTSTR) * 25);
281 memset(&button_list, 0, sizeof (MDlgButton) * 25);
282 b = 0;
284 if(flags & RB_RET_HELP){
285 if(help_text != NO_HELP)
286 alpine_panic("RET_HELP and help in radio_buttons!");
288 button_list[b].ch = '?';
289 button_list[b].rval = 3;
290 button_list[b].name = TEXT("?");
291 free_labels[b] = utf8_to_lptstr(N_("Help"));
292 button_list[b].label = free_labels[b];
293 ++b;
296 for(i = 0; esc_list && esc_list[i].ch != -1 && i < 23; ++i){
297 if(esc_list[i].ch != -2){
298 button_list[b].ch = esc_list[i].ch;
299 button_list[b].rval = esc_list[i].rval;
300 free_names[b] = utf8_to_lptstr(esc_list[i].name);
301 button_list[b].name = free_names[b];
302 free_labels[b] = utf8_to_lptstr(esc_list[i].label);
303 button_list[b].label = free_labels[b];
304 ++b;
308 button_list[b].ch = -1;
310 /* assumption here is that HelpType is char ** */
311 help = help_text;
313 ret = mswin_select(utf8prompt, button_list, dflt, on_ctrl_C, help, flags);
314 for(i = 0; i < 25; i++){
315 if(free_names[i])
316 fs_give((void **) &free_names[i]);
317 if(free_labels[i])
318 fs_give((void **) &free_labels[i]);
321 return (ret);
324 #endif /* _WINDOWS */
326 suspend_busy_cue();
327 flush_ordered_messages(); /* show user previous status msgs */
328 mark_status_dirty(); /* clear message next display call */
329 real_line = line > 0 ? line : ps_global->ttyo->screen_rows + line;
330 MoveCursor(real_line, RAD_BUT_COL);
331 CleartoEOLN();
333 /*---- Find widest label ----*/
334 max_label = 0;
335 for(i = 0; esc_list && esc_list[i].ch != -1 && i < 11; i++){
336 if(esc_list[i].ch == -2) /* -2 means to skip this key and leave blank */
337 continue;
338 if(esc_list[i].name)
339 max_label = MAX(max_label, utf8_width(esc_list[i].name));
342 if(ps_global->ttyo->screen_cols - max_label - 1 > 0)
343 maxcol = ps_global->ttyo->screen_cols - max_label - 1;
344 else
345 maxcol = 0;
348 * We need to be able to truncate q, so copy it in case it is
349 * a readonly string.
351 q = cpystr(utf8prompt);
353 /*---- Init structs for keymenu ----*/
354 for(i = 0; i < 12; i++)
355 memset((void *)&rb_keys[i], 0, sizeof(struct key));
357 memset((void *)&rb_keymenu, 0, sizeof(struct key_menu));
358 rb_keymenu.how_many = 1;
359 rb_keymenu.keys = rb_keys;
361 /*---- Setup key menu ----*/
362 start = 0;
363 clrbitmap(bitmap);
364 memset(fkey_table, NO_OP_COMMAND, 12 * sizeof(int));
365 if(flags & RB_RET_HELP && help_text != NO_HELP)
366 alpine_panic("RET_HELP and help in radio_buttons!");
368 /* if shown, always at position 0 */
369 if(help_text != NO_HELP || flags & RB_RET_HELP){
370 rb_keymenu.keys[0].name = "?";
371 rb_keymenu.keys[0].label = N_("Help");
372 setbitn(0, bitmap);
373 fkey_table[0] = ctrl('G');
374 start++;
377 if(on_ctrl_C){
378 rb_keymenu.keys[1].name = "^C";
379 rb_keymenu.keys[1].label = N_("Cancel");
380 setbitn(1, bitmap);
381 fkey_table[1] = ctrl('C');
382 start++;
385 start = start ? 2 : 0;
386 /*---- Show the usual possible keys ----*/
387 for(i=start; esc_list && esc_list[i-start].ch != -1; i++){
389 * If we have an esc_list item we'd like to put in the non-existent
390 * 13th slot, and there is no help, we put it in the help slot
391 * instead. We're hacking now...!
393 * We may also have invisible esc_list items that don't show up
394 * on the screen. We use this when we have two different keys
395 * which are synonyms, like ^P and KEY_UP. If all the slots are
396 * already full we can still fit invisible keys off the screen to
397 * the right. A key is invisible if it's label is "".
399 if(i >= 12){
400 if(esc_list[i-start].label
401 && esc_list[i-start].label[0] != '\0'){ /* visible */
402 if(i == 12){ /* special case where we put it in help slot */
403 if(help_text != NO_HELP)
404 alpine_panic("Programming botch in radio_buttons(): too many keys");
406 if(esc_list[i-start].ch != -2)
407 setbitn(0, bitmap); /* the help slot */
409 fkey_table[0] = esc_list[i-start].ch;
410 rb_keymenu.keys[0].name = esc_list[i-start].name;
411 if(esc_list[i-start].ch != -2
412 && esc_list[i-start].rval == dflt
413 && esc_list[i-start].label){
414 size_t l;
416 l = strlen(esc_list[i-start].label) + 2;
417 ds = (char *)fs_get((l+1) * sizeof(char));
418 snprintf(ds, l+1, "[%s]", esc_list[i-start].label);
419 ds[l] = '\0';
420 rb_keymenu.keys[0].label = ds;
422 else
423 rb_keymenu.keys[0].label = esc_list[i-start].label;
425 else
426 alpine_panic("Botch in radio_buttons(): too many keys");
429 else{
430 if(esc_list[i-start].ch != -2)
431 setbitn(i, bitmap);
433 fkey_table[i] = esc_list[i-start].ch;
434 rb_keymenu.keys[i].name = esc_list[i-start].name;
435 if(esc_list[i-start].ch != -2
436 && esc_list[i-start].rval == dflt
437 && esc_list[i-start].label){
438 size_t l;
440 l = strlen(esc_list[i-start].label) + 2;
441 ds = (char *)fs_get((l+1) * sizeof(char));
442 snprintf(ds, l+1, "[%s]", esc_list[i-start].label);
443 ds[l] = '\0';
444 rb_keymenu.keys[i].label = ds;
446 else
447 rb_keymenu.keys[i].label = esc_list[i-start].label;
451 for(; i < 12; i++)
452 rb_keymenu.keys[i].name = NULL;
454 ps_global->mangled_footer = 1;
456 #ifdef _WINDOWS
457 cursor_shown = mswin_showcaret(1);
458 #endif
460 if(pico_usingcolor() && VAR_PROMPT_FORE_COLOR &&
461 VAR_PROMPT_BACK_COLOR &&
462 pico_is_good_color(VAR_PROMPT_FORE_COLOR) &&
463 pico_is_good_color(VAR_PROMPT_BACK_COLOR)){
464 lastc = pico_get_cur_color();
465 if(lastc){
466 promptc = new_color_pair(VAR_PROMPT_FORE_COLOR,
467 VAR_PROMPT_BACK_COLOR);
468 (void)pico_set_colorp(promptc, PSC_NONE);
471 else
472 StartInverse();
474 draw_radio_prompt(real_line, RAD_BUT_COL, maxcol, q);
476 while(1){
477 fflush(stdout);
479 /*---- Paint the keymenu ----*/
480 if(lastc)
481 (void)pico_set_colorp(lastc, PSC_NONE);
482 else
483 EndInverse();
485 draw_keymenu(&rb_keymenu, bitmap, ps_global->ttyo->screen_cols,
486 1 - FOOTER_ROWS(ps_global), 0, FirstMenu);
487 if(promptc)
488 (void)pico_set_colorp(promptc, PSC_NONE);
489 else
490 StartInverse();
492 MoveCursor(real_line, MIN(RAD_BUT_COL+utf8_width(q), maxcol+1));
494 if(flags & RB_FLUSH_IN)
495 flush_input();
497 newcmd:
498 /* Timeout 5 min to keep imap mail stream alive */
499 ucs = read_char(600);
500 dprint((2,
501 "Want_to read: %s (0x%x)\n", pretty_command(ucs), ucs));
502 if((ucs < 0x80) && isupper((unsigned char) ucs))
503 ucs = tolower((unsigned char) ucs);
505 if(F_ON(F_USE_FK,ps_global)
506 && (((ucs < 0x80) && isalpha((unsigned char) ucs) && !strchr("YyNn",(int) ucs))
507 || ((ucs >= PF1 && ucs <= PF12)
508 && (ucs = fkey_table[ucs - PF1]) == NO_OP_COMMAND))){
510 * The funky test above does two things. It maps
511 * esc_list character commands to function keys, *and* prevents
512 * character commands from input while in function key mode.
513 * NOTE: this breaks if we ever need more than the first
514 * twelve function keys...
516 if(flags & RB_ONE_TRY){
517 ch = ucs = on_ctrl_C;
518 goto out_of_loop;
521 Writechar(BELL, 0);
522 continue;
525 switch(ucs){
527 default:
528 for(i = 0; esc_list && esc_list[i].ch != -1; i++)
529 if(ucs == esc_list[i].ch){
530 int len, n;
532 MoveCursor(real_line,len=MIN(RAD_BUT_COL+utf8_width(q),maxcol+1));
533 for(n = 0, len = ps_global->ttyo->screen_cols - len;
534 esc_list[i].label && esc_list[i].label[n] && len > 0;
535 n++, len--)
536 Writechar(esc_list[i].label[n], 0);
538 ch = esc_list[i].rval;
539 goto out_of_loop;
542 if(flags & RB_ONE_TRY){
543 ch = on_ctrl_C;
544 goto out_of_loop;
547 Writechar(BELL, 0);
548 break;
550 case ctrl('M'):
551 case ctrl('J'):
552 ch = dflt;
553 for(i = 0; esc_list && esc_list[i].ch != -1; i++)
554 if(ch == esc_list[i].rval){
555 int len, n;
557 MoveCursor(real_line,len=MIN(RAD_BUT_COL+utf8_width(q),maxcol+1));
558 for(n = 0, len = ps_global->ttyo->screen_cols - len;
559 esc_list[i].label && esc_list[i].label[n] && len > 0;
560 n++, len--)
561 Writechar(esc_list[i].label[n], 0);
562 break;
565 goto out_of_loop;
567 case ctrl('C'):
568 if(on_ctrl_C || (flags & RB_ONE_TRY)){
569 ch = on_ctrl_C;
570 goto out_of_loop;
573 Writechar(BELL, 0);
574 break;
577 case '?':
578 case ctrl('G'):
579 if(FOOTER_ROWS(ps_global) == 1 && km_popped == 0){
580 km_popped++;
581 FOOTER_ROWS(ps_global) = 3;
582 line = -3;
583 real_line = ps_global->ttyo->screen_rows + line;
584 if(lastc)
585 (void)pico_set_colorp(lastc, PSC_NONE);
586 else
587 EndInverse();
589 clearfooter(ps_global);
590 if(promptc)
591 (void)pico_set_colorp(promptc, PSC_NONE);
592 else
593 StartInverse();
595 draw_radio_prompt(real_line, RAD_BUT_COL, maxcol, q);
596 break;
599 if(flags & RB_RET_HELP){
600 ch = 3;
601 goto out_of_loop;
603 else if(help_text != NO_HELP && FOOTER_ROWS(ps_global) > 1){
604 mark_keymenu_dirty();
605 if(lastc)
606 (void)pico_set_colorp(lastc, PSC_NONE);
607 else
608 EndInverse();
610 MoveCursor(real_line + 1, RAD_BUT_COL);
611 CleartoEOLN();
612 MoveCursor(real_line + 2, RAD_BUT_COL);
613 CleartoEOLN();
614 radio_help(real_line, RAD_BUT_COL, help_text);
615 sleep(5);
616 MoveCursor(real_line, MIN(RAD_BUT_COL+utf8_width(q), maxcol+1));
617 if(promptc)
618 (void)pico_set_colorp(promptc, PSC_NONE);
619 else
620 StartInverse();
622 else
623 Writechar(BELL, 0);
625 break;
628 case NO_OP_COMMAND:
629 goto newcmd; /* misunderstood escape? */
631 case NO_OP_IDLE: /* UNODIR, keep the stream alive */
632 if(flags & RB_NO_NEWMAIL)
633 goto newcmd;
635 i = new_mail(0, VeryBadTime, NM_DEFER_SORT);
636 if(sp_expunge_count(ps_global->mail_stream)
637 && flags & RB_SEQ_SENSITIVE){
638 if(on_ctrl_C)
639 ch = on_ctrl_C;
640 else
641 ch = SEQ_EXCEPTION;
643 goto out_of_loop;
646 if(i < 0)
647 break; /* no changes, get on with life */
648 /* Else fall into redraw to adjust displayed numbers and such */
651 case KEY_RESIZE:
652 case ctrl('L'):
653 real_line = line > 0 ? line : ps_global->ttyo->screen_rows + line;
654 if(lastc)
655 (void)pico_set_colorp(lastc, PSC_NONE);
656 else
657 EndInverse();
659 ClearScreen();
660 redraw_titlebar();
661 if(ps_global->redrawer != NULL)
662 (*ps_global->redrawer)();
663 if(FOOTER_ROWS(ps_global) == 3 || km_popped)
664 redraw_keymenu();
666 if(ps_global->ttyo->screen_cols - max_label - 1 > 0)
667 maxcol = ps_global->ttyo->screen_cols - max_label - 1;
668 else
669 maxcol = 0;
671 if(promptc)
672 (void)pico_set_colorp(promptc, PSC_NONE);
673 else
674 StartInverse();
676 draw_radio_prompt(real_line, RAD_BUT_COL, maxcol, q);
677 break;
679 } /* switch */
682 out_of_loop:
684 #ifdef _WINDOWS
685 if(!cursor_shown)
686 mswin_showcaret(0);
687 #endif
689 fs_give((void **) &q);
690 if(ds)
691 fs_give((void **) &ds);
693 if(lastc){
694 (void) pico_set_colorp(lastc, PSC_NONE);
695 free_color_pair(&lastc);
696 if(promptc)
697 free_color_pair(&promptc);
699 else
700 EndInverse();
702 fflush(stdout);
703 resume_busy_cue(0);
704 if(km_popped){
705 FOOTER_ROWS(ps_global) = 1;
706 clearfooter(ps_global);
707 ps_global->mangled_body = 1;
710 return(ch);
714 #define OTHER_RETURN_VAL 1300
715 #define KEYS_PER_LIST 8
718 * This should really be part of radio_buttons itself, I suppose. It was
719 * easier to do it this way. This is for when there are more than 12
720 * possible commands. We could have all the radio_buttons calls call this
721 * instead of radio_buttons, or rename this to radio_buttons.
723 * Radio_buttons is limited to 10 visible commands unless there is no Help,
724 * in which case it is 11 visible commands.
725 * Double_radio_buttons is limited to 16 visible commands because it uses
726 * slots 3 and 4 for readability and the OTHER CMD.
729 double_radio_buttons(char *prompt, int line, ESCKEY_S *esc_list, int dflt, int on_ctrl_C, HelpType help_text, int flags)
731 ESCKEY_S *list = NULL, *list1 = NULL, *list2 = NULL;
732 int i = 0, j;
733 int v = OTHER_RETURN_VAL, listnum = 0;
735 #ifdef _WINDOWS
736 if(mswin_usedialog())
737 return(radio_buttons(prompt, line, esc_list, dflt, on_ctrl_C,
738 help_text, flags));
739 #endif
741 /* check to see if it will all fit in one */
742 while(esc_list && esc_list[i].ch != -1)
743 i++;
745 i++; /* for ^C */
746 if(help_text != NO_HELP)
747 i++;
749 if(i <= 12)
750 return(radio_buttons(prompt, line, esc_list, dflt, on_ctrl_C,
751 help_text, flags));
754 * Won't fit, split it into two lists.
756 * We can fit at most 8 items in the visible list. The rest of
757 * the commands have to be invisible. Each of list1 and list2 should
758 * have no more than 8 visible (name != "" || label != "") items.
760 list1 = (ESCKEY_S *)fs_get((KEYS_PER_LIST+1) * sizeof(*list1));
761 memset(list1, 0, (KEYS_PER_LIST+1) * sizeof(*list1));
762 list2 = (ESCKEY_S *)fs_get((KEYS_PER_LIST+1) * sizeof(*list2));
763 memset(list2, 0, (KEYS_PER_LIST+1) * sizeof(*list2));
765 for(j=0,i=0; esc_list[i].ch != -1 && j < KEYS_PER_LIST; j++,i++)
766 list1[j] = esc_list[i];
768 list1[j].ch = -1;
770 for(j=0; esc_list[i].ch != -1 && j < KEYS_PER_LIST; j++,i++)
771 list2[j] = esc_list[i];
773 list2[j].ch = -1;
775 list = construct_combined_esclist(list1, list2);
777 while(v == OTHER_RETURN_VAL){
778 v = radio_buttons(prompt,line,list,dflt,on_ctrl_C,help_text,flags);
779 if(v == OTHER_RETURN_VAL){
780 fs_give((void **) &list);
781 listnum = 1 - listnum;
782 list = construct_combined_esclist(listnum ? list2 : list1,
783 listnum ? list1 : list2);
787 if(list)
788 fs_give((void **) &list);
789 if(list1)
790 fs_give((void **) &list1);
791 if(list2)
792 fs_give((void **) &list2);
794 return(v);
798 ESCKEY_S *
799 construct_combined_esclist(ESCKEY_S *list1, ESCKEY_S *list2)
801 ESCKEY_S *list;
802 int i, j=0, count;
804 count = 2; /* for blank key and for OTHER key */
805 for(i=0; list1 && list1[i].ch != -1; i++)
806 count++;
807 for(i=0; list2 && list2[i].ch != -1; i++)
808 count++;
810 list = (ESCKEY_S *) fs_get((count + 1) * sizeof(*list));
811 memset(list, 0, (count + 1) * sizeof(*list));
813 list[j].ch = -2; /* leave blank */
814 list[j].rval = 0;
815 list[j].name = NULL;
816 list[j++].label = NULL;
818 list[j].ch = 'o';
819 list[j].rval = OTHER_RETURN_VAL;
820 list[j].name = "O";
821 list[j].label = N_("OTHER CMDS");
824 * Make sure that O for OTHER CMD or the return val for OTHER CMD
825 * isn't used for something else.
827 for(i=0; list1 && list1[i].ch != -1; i++){
828 if(list1[i].rval == list[j].rval)
829 alpine_panic("1bad rval in d_r");
830 if(F_OFF(F_USE_FK,ps_global) && list1[i].ch == list[j].ch)
831 alpine_panic("1bad ch in ccl");
834 for(i=0; list2 && list2[i].ch != -1; i++){
835 if(list2[i].rval == list[j].rval)
836 alpine_panic("2bad rval in d_r");
837 if(F_OFF(F_USE_FK,ps_global) && list2[i].ch == list[j].ch)
838 alpine_panic("2bad ch in ccl");
841 j++;
843 /* the visible set */
844 for(i=0; list1 && list1[i].ch != -1; i++){
845 if(i >= KEYS_PER_LIST && list1[i].label[0] != '\0')
846 alpine_panic("too many visible keys in ccl");
848 list[j++] = list1[i];
851 /* the rest are invisible */
852 for(i=0; list2 && list2[i].ch != -1; i++){
853 list[j] = list2[i];
854 list[j].label = "";
855 list[j++].name = "";
858 list[j].ch = -1;
860 return(list);
864 /*----------------------------------------------------------------------
866 ----*/
867 void
868 radio_help(int line, int column, HelpType help)
870 char **text;
872 /* assumption here is that HelpType is char ** */
873 text = help;
874 if(text == NULL)
875 return;
877 MoveCursor(line + 1, column);
878 CleartoEOLN();
879 if(text[0])
880 PutLine0(line + 1, column, text[0]);
882 MoveCursor(line + 2, column);
883 CleartoEOLN();
884 if(text[1])
885 PutLine0(line + 2, column, text[1]);
887 fflush(stdout);
891 /*----------------------------------------------------------------------
892 Paint the screen with the radio buttons prompt
893 ----*/
894 void
895 draw_radio_prompt(int line, unsigned start_c, unsigned max_c, char *q)
897 size_t len;
898 unsigned x, width, got_width;
899 char *tmpq = NULL, *useq;
901 width = utf8_width(q);
902 if(width > max_c - start_c + 1){
903 tmpq = (char *) fs_get((len=(strlen(q)+1)) * sizeof(char));
904 (void) utf8_to_width(tmpq, q, len, max_c - start_c + 1, &got_width);
905 useq = tmpq;
906 width = got_width;
908 else
909 useq = q;
911 PutLine0(line, start_c, useq);
912 x = start_c + width;
913 MoveCursor(line, x);
914 while(x++ < ps_global->ttyo->screen_cols)
915 Writechar(' ', 0);
917 MoveCursor(line, start_c + width);
918 fflush(stdout);
919 if(tmpq)
920 fs_give((void **) &tmpq);