* Clear out some gcc warnings, and code improvement. Work in progress.
[alpine.git] / alpine / radio.c
blob8469b30da7c76cae7c53ed134c48ee02bb2cb50b
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-2021 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_DING - ding the bell when asking.
111 WT_FLUSH_IN - Discard pending input.
112 WT_SEQ_SENSITIVE - Caller is sensitive to sequence
113 number changes caused by
114 unsolicited expunges while we're
115 viewing a message.
117 Result: Messes up the status line,
118 returns y, n, dflt, on_ctrl_C, or SEQ_EXCEPTION
119 ---*/
121 want_to(char *question, int dflt, int on_ctrl_C, HelpType help, int flags)
123 char *free_this = NULL, *free_this2 = NULL, *prompt;
124 int rv, width;
125 size_t len;
127 if((flags & WT_DING) && F_OFF(F_QUELL_BEEPS, ps_global)){
128 Writechar(BELL, 0);
129 fflush(stdout);
132 if(!ps_global->ttyo)
133 return(pre_screen_config_want_to(question, dflt, on_ctrl_C));
134 #ifdef _WINDOWS
135 if (mswin_usedialog ()) {
136 mswin_flush ();
137 switch (mswin_yesno_utf8 (question)) {
138 default:
139 case 0: return (on_ctrl_C);
140 case 1: return ('y');
141 case 2: return ('n');
144 #endif
146 /*----
147 One problem with adding the (y/n) here is that shrinking the
148 screen while in radio_buttons() will cause it to get chopped
149 off. It would be better to truncate the question passed in
150 here and leave the full "(y/n) [x] : " on.
151 ----*/
153 len = strlen(question) + 4;
154 free_this = (char *) fs_get(len);
155 width = utf8_width(question);
157 if(width + 2 < ps_global->ttyo->screen_cols){
158 snprintf(free_this, len, "%s? ", question);
159 free_this[len-1] = '\0';
160 prompt = free_this;
162 else if(width + 1 < ps_global->ttyo->screen_cols){
163 snprintf(free_this, len, "%s?", question);
164 free_this[len-1] = '\0';
165 prompt = free_this;
167 else if(width < ps_global->ttyo->screen_cols){
168 snprintf(free_this, len, "%s", question);
169 free_this[len-1] = '\0';
170 prompt = free_this;
172 else{
173 free_this2 = (char *) fs_get(len);
174 snprintf(free_this2, len, "%s? ", question);
175 prompt = short_str(free_this2, free_this, len, ps_global->ttyo->screen_cols-1, MidDots);
178 if(on_ctrl_C == 'n') /* don't ever let cancel == 'n' */
179 on_ctrl_C = 0;
181 rv = radio_buttons(prompt,
182 (ps_global->ttyo->screen_rows > 4) ? - FOOTER_ROWS(ps_global) : -1,
183 yorn, dflt, on_ctrl_C, help, flags);
185 if(free_this)
186 fs_give((void **) &free_this);
188 if(free_this2)
189 fs_give((void **) &free_this2);
191 return(rv);
196 one_try_want_to(char *question, int dflt, int on_ctrl_C, HelpType help, int flags)
198 char *q2;
199 int rv;
200 size_t l;
202 l = strlen(question) + 5;
203 q2 = fs_get((l+1) * sizeof(char));
204 strncpy(q2, question, l);
205 q2[l] = '\0';
206 (void) utf8_truncate(q2, ps_global->ttyo->screen_cols - 6);
207 strncat(q2, "? ", l+1 - strlen(q2) - 1);
208 q2[l] = '\0';
209 rv = radio_buttons(q2,
210 (ps_global->ttyo->screen_rows > 4) ? - FOOTER_ROWS(ps_global) : -1,
211 yorn, dflt, on_ctrl_C, help, flags | RB_ONE_TRY);
212 fs_give((void **) &q2);
214 return(rv);
218 /*----------------------------------------------------------------------
219 Prompt user for a choice among alternatives
221 Args -- utf8prompt: The prompt for the question/selection
222 line: The line to prompt on, if negative then relative to bottom
223 esc_list: ESC_KEY_S list of keys
224 dflt: The selection when the <CR> is pressed (should probably
225 be one of the chars in esc_list)
226 on_ctrl_C: The selection when ^C is pressed
227 help_text: Text to be displayed on bottom two lines
228 flags: Logically OR'd flags modifying our behavior to:
229 RB_FLUSH_IN - Discard any pending input chars.
230 RB_ONE_TRY - Only give one chance to answer. Returns
231 on_ctrl_C value if not answered acceptably
232 on first try.
233 RB_NO_NEWMAIL - Quell the usual newmail check.
234 RB_SEQ_SENSITIVE - The caller is sensitive to sequence number
235 changes so return on_ctrl_C if an
236 unsolicited expunge happens while we're
237 viewing a message.
238 RB_RET_HELP - Instead of the regular internal handling
239 way of handling help_text, this just causes
240 radio_buttons to return 3 when help is
241 asked for, so that the caller handles it
242 instead.
244 Note: If there are enough keys in the esc_list to need a second
245 screen, and there is no help, then the 13th key will be
246 put in the help position.
248 Result -- Returns the letter pressed. Will be one of the characters in the
249 esc_list argument, or dflt, or on_ctrl_C, or SEQ_EXCEPTION.
251 This will pause for any new status message to be seen and then prompt the user.
252 The prompt will be truncated to fit on the screen. Redraw and resize are
253 handled along with ^Z suspension. Typing ^G will toggle the help text on and
254 off. Character types that are not buttons will result in a beep (unless one_try
255 is set).
256 ----*/
258 radio_buttons(char *utf8prompt, int line, ESCKEY_S *esc_list, int dflt,
259 int on_ctrl_C, HelpType help_text, int flags)
261 UCS ucs;
262 register int ch, real_line;
263 char *q, *ds = NULL;
264 unsigned maxcol;
265 int max_label, i, start, fkey_table[12];
266 int km_popped = 0;
267 struct key rb_keys[12];
268 struct key_menu rb_keymenu;
269 bitmap_t bitmap;
270 struct variable *vars = ps_global->vars;
271 COLOR_PAIR *lastc = NULL, *promptc = NULL;
273 #ifdef _WINDOWS
274 int cursor_shown;
276 if (mswin_usedialog()){
277 MDlgButton button_list[25];
278 LPTSTR free_names[25];
279 LPTSTR free_labels[25];
280 int b, i, ret;
281 char **help;
283 memset(&free_names, 0, sizeof(LPTSTR) * 25);
284 memset(&free_labels, 0, sizeof(LPTSTR) * 25);
285 memset(&button_list, 0, sizeof (MDlgButton) * 25);
286 b = 0;
288 if(flags & RB_RET_HELP){
289 if(help_text != NO_HELP)
290 alpine_panic("RET_HELP and help in radio_buttons!");
292 button_list[b].ch = '?';
293 button_list[b].rval = 3;
294 button_list[b].name = TEXT("?");
295 free_labels[b] = utf8_to_lptstr(N_("Help"));
296 button_list[b].label = free_labels[b];
297 ++b;
300 for(i = 0; esc_list && esc_list[i].ch != -1 && i < 23; ++i){
301 if(esc_list[i].ch != -2){
302 button_list[b].ch = esc_list[i].ch;
303 button_list[b].rval = esc_list[i].rval;
304 free_names[b] = utf8_to_lptstr(esc_list[i].name);
305 button_list[b].name = free_names[b];
306 free_labels[b] = utf8_to_lptstr(esc_list[i].label);
307 button_list[b].label = free_labels[b];
308 ++b;
312 button_list[b].ch = -1;
314 /* assumption here is that HelpType is char ** */
315 help = help_text;
317 ret = mswin_select(utf8prompt, button_list, dflt, on_ctrl_C, help, flags);
318 for(i = 0; i < 25; i++){
319 if(free_names[i])
320 fs_give((void **) &free_names[i]);
321 if(free_labels[i])
322 fs_give((void **) &free_labels[i]);
325 return (ret);
328 #endif /* _WINDOWS */
330 suspend_busy_cue();
331 flush_ordered_messages(); /* show user previous status msgs */
332 mark_status_dirty(); /* clear message next display call */
333 real_line = line > 0 ? line : ps_global->ttyo->screen_rows + line;
334 MoveCursor(real_line, RAD_BUT_COL);
335 CleartoEOLN();
337 /*---- Find widest label ----*/
338 max_label = 0;
339 for(i = 0; esc_list && esc_list[i].ch != -1 && i < 11; i++){
340 if(esc_list[i].ch == -2) /* -2 means to skip this key and leave blank */
341 continue;
342 if(esc_list[i].name)
343 max_label = MAX(max_label, utf8_width(esc_list[i].name));
346 if(ps_global->ttyo->screen_cols - max_label - 1 > 0)
347 maxcol = ps_global->ttyo->screen_cols - max_label - 1;
348 else
349 maxcol = 0;
352 * We need to be able to truncate q, so copy it in case it is
353 * a readonly string.
355 q = cpystr(utf8prompt);
357 /*---- Init structs for keymenu ----*/
358 for(i = 0; i < 12; i++)
359 memset((void *)&rb_keys[i], 0, sizeof(struct key));
361 memset((void *)&rb_keymenu, 0, sizeof(struct key_menu));
362 rb_keymenu.how_many = 1;
363 rb_keymenu.keys = rb_keys;
365 /*---- Setup key menu ----*/
366 start = 0;
367 clrbitmap(bitmap);
368 memset(fkey_table, NO_OP_COMMAND, 12 * sizeof(int));
369 if(flags & RB_RET_HELP && help_text != NO_HELP)
370 alpine_panic("RET_HELP and help in radio_buttons!");
372 /* if shown, always at position 0 */
373 if(help_text != NO_HELP || flags & RB_RET_HELP){
374 rb_keymenu.keys[0].name = "?";
375 rb_keymenu.keys[0].label = N_("Help");
376 setbitn(0, bitmap);
377 fkey_table[0] = ctrl('G');
378 start++;
381 if(on_ctrl_C){
382 rb_keymenu.keys[1].name = "^C";
383 rb_keymenu.keys[1].label = N_("Cancel");
384 setbitn(1, bitmap);
385 fkey_table[1] = ctrl('C');
386 start++;
389 start = start ? 2 : 0;
390 /*---- Show the usual possible keys ----*/
391 for(i=start; esc_list && esc_list[i-start].ch != -1; i++){
393 * If we have an esc_list item we'd like to put in the non-existent
394 * 13th slot, and there is no help, we put it in the help slot
395 * instead. We're hacking now...!
397 * We may also have invisible esc_list items that don't show up
398 * on the screen. We use this when we have two different keys
399 * which are synonyms, like ^P and KEY_UP. If all the slots are
400 * already full we can still fit invisible keys off the screen to
401 * the right. A key is invisible if it's label is "".
403 if(i >= 12){
404 if(esc_list[i-start].label
405 && esc_list[i-start].label[0] != '\0'){ /* visible */
406 if(i == 12){ /* special case where we put it in help slot */
407 if(help_text != NO_HELP)
408 alpine_panic("Programming botch in radio_buttons(): too many keys");
410 if(esc_list[i-start].ch != -2)
411 setbitn(0, bitmap); /* the help slot */
413 fkey_table[0] = esc_list[i-start].ch;
414 rb_keymenu.keys[0].name = esc_list[i-start].name;
415 if(esc_list[i-start].ch != -2
416 && esc_list[i-start].rval == dflt
417 && esc_list[i-start].label){
418 size_t l;
420 l = strlen(esc_list[i-start].label) + 2;
421 ds = (char *)fs_get((l+1) * sizeof(char));
422 snprintf(ds, l+1, "[%s]", esc_list[i-start].label);
423 ds[l] = '\0';
424 rb_keymenu.keys[0].label = ds;
426 else
427 rb_keymenu.keys[0].label = esc_list[i-start].label;
429 else
430 alpine_panic("Botch in radio_buttons(): too many keys");
433 else{
434 if(esc_list[i-start].ch != -2)
435 setbitn(i, bitmap);
437 fkey_table[i] = esc_list[i-start].ch;
438 rb_keymenu.keys[i].name = esc_list[i-start].name;
439 if(esc_list[i-start].ch != -2
440 && esc_list[i-start].rval == dflt
441 && esc_list[i-start].label){
442 size_t l;
444 l = strlen(esc_list[i-start].label) + 2;
445 ds = (char *)fs_get((l+1) * sizeof(char));
446 snprintf(ds, l+1, "[%s]", esc_list[i-start].label);
447 ds[l] = '\0';
448 rb_keymenu.keys[i].label = ds;
450 else
451 rb_keymenu.keys[i].label = esc_list[i-start].label;
455 for(; i < 12; i++)
456 rb_keymenu.keys[i].name = NULL;
458 ps_global->mangled_footer = 1;
460 #ifdef _WINDOWS
461 cursor_shown = mswin_showcaret(1);
462 #endif
464 if(pico_usingcolor() && VAR_PROMPT_FORE_COLOR &&
465 VAR_PROMPT_BACK_COLOR &&
466 pico_is_good_color(VAR_PROMPT_FORE_COLOR) &&
467 pico_is_good_color(VAR_PROMPT_BACK_COLOR)){
468 lastc = pico_get_cur_color();
469 if(lastc){
470 promptc = new_color_pair(VAR_PROMPT_FORE_COLOR,
471 VAR_PROMPT_BACK_COLOR);
472 (void)pico_set_colorp(promptc, PSC_NONE);
475 else
476 StartInverse();
478 draw_radio_prompt(real_line, RAD_BUT_COL, maxcol, q);
480 while(1){
481 fflush(stdout);
483 /*---- Paint the keymenu ----*/
484 if(lastc)
485 (void)pico_set_colorp(lastc, PSC_NONE);
486 else
487 EndInverse();
489 draw_keymenu(&rb_keymenu, bitmap, ps_global->ttyo->screen_cols,
490 1 - FOOTER_ROWS(ps_global), 0, FirstMenu);
491 if(promptc)
492 (void)pico_set_colorp(promptc, PSC_NONE);
493 else
494 StartInverse();
496 MoveCursor(real_line, MIN(RAD_BUT_COL+utf8_width(q), maxcol+1));
498 if(flags & RB_FLUSH_IN)
499 flush_input();
501 newcmd:
502 /* Timeout 5 min to keep imap mail stream alive */
503 ucs = read_char(600);
504 dprint((2,
505 "Want_to read: %s (0x%x)\n", pretty_command(ucs), ucs));
506 if((ucs < 0x80) && isupper((unsigned char) ucs))
507 ucs = tolower((unsigned char) ucs);
509 if(F_ON(F_USE_FK,ps_global)
510 && (((ucs < 0x80) && isalpha((unsigned char) ucs) && !strchr("YyNn",(int) ucs))
511 || ((ucs >= PF1 && ucs <= PF12)
512 && (ucs = fkey_table[ucs - PF1]) == NO_OP_COMMAND))){
514 * The funky test above does two things. It maps
515 * esc_list character commands to function keys, *and* prevents
516 * character commands from input while in function key mode.
517 * NOTE: this breaks if we ever need more than the first
518 * twelve function keys...
520 if(flags & RB_ONE_TRY){
521 ch = ucs = on_ctrl_C;
522 goto out_of_loop;
525 Writechar(BELL, 0);
526 continue;
529 switch(ucs){
531 default:
532 for(i = 0; esc_list && esc_list[i].ch != -1; i++)
533 if(ucs == esc_list[i].ch){
534 int len, n;
536 MoveCursor(real_line,len=MIN(RAD_BUT_COL+utf8_width(q),maxcol+1));
537 for(n = 0, len = ps_global->ttyo->screen_cols - len;
538 esc_list[i].label && esc_list[i].label[n] && len > 0;
539 n++, len--)
540 Writechar(esc_list[i].label[n], 0);
542 ch = esc_list[i].rval;
543 goto out_of_loop;
546 if(flags & RB_ONE_TRY){
547 ch = on_ctrl_C;
548 goto out_of_loop;
551 Writechar(BELL, 0);
552 break;
554 case ctrl('M'):
555 case ctrl('J'):
556 ch = dflt;
557 for(i = 0; esc_list && esc_list[i].ch != -1; i++)
558 if(ch == esc_list[i].rval){
559 int len, n;
561 MoveCursor(real_line,len=MIN(RAD_BUT_COL+utf8_width(q),maxcol+1));
562 for(n = 0, len = ps_global->ttyo->screen_cols - len;
563 esc_list[i].label && esc_list[i].label[n] && len > 0;
564 n++, len--)
565 Writechar(esc_list[i].label[n], 0);
566 break;
569 goto out_of_loop;
571 case ctrl('C'):
572 if(on_ctrl_C || (flags & RB_ONE_TRY)){
573 ch = on_ctrl_C;
574 goto out_of_loop;
577 Writechar(BELL, 0);
578 break;
581 case '?':
582 case ctrl('G'):
583 if(FOOTER_ROWS(ps_global) == 1 && km_popped == 0){
584 km_popped++;
585 FOOTER_ROWS(ps_global) = 3;
586 line = -3;
587 real_line = ps_global->ttyo->screen_rows + line;
588 if(lastc)
589 (void)pico_set_colorp(lastc, PSC_NONE);
590 else
591 EndInverse();
593 clearfooter(ps_global);
594 if(promptc)
595 (void)pico_set_colorp(promptc, PSC_NONE);
596 else
597 StartInverse();
599 draw_radio_prompt(real_line, RAD_BUT_COL, maxcol, q);
600 break;
603 if(flags & RB_RET_HELP){
604 ch = 3;
605 goto out_of_loop;
607 else if(help_text != NO_HELP && FOOTER_ROWS(ps_global) > 1){
608 mark_keymenu_dirty();
609 if(lastc)
610 (void)pico_set_colorp(lastc, PSC_NONE);
611 else
612 EndInverse();
614 MoveCursor(real_line + 1, RAD_BUT_COL);
615 CleartoEOLN();
616 MoveCursor(real_line + 2, RAD_BUT_COL);
617 CleartoEOLN();
618 radio_help(real_line, RAD_BUT_COL, help_text);
619 sleep(5);
620 MoveCursor(real_line, MIN(RAD_BUT_COL+utf8_width(q), maxcol+1));
621 if(promptc)
622 (void)pico_set_colorp(promptc, PSC_NONE);
623 else
624 StartInverse();
626 else
627 Writechar(BELL, 0);
629 break;
632 case NO_OP_COMMAND:
633 goto newcmd; /* misunderstood escape? */
635 case NO_OP_IDLE: /* UNODIR, keep the stream alive */
636 if(flags & RB_NO_NEWMAIL)
637 goto newcmd;
639 i = new_mail(0, VeryBadTime, NM_DEFER_SORT);
640 if(sp_expunge_count(ps_global->mail_stream)
641 && flags & RB_SEQ_SENSITIVE){
642 if(on_ctrl_C)
643 ch = on_ctrl_C;
644 else
645 ch = SEQ_EXCEPTION;
647 goto out_of_loop;
650 if(i < 0)
651 break; /* no changes, get on with life */
652 /* Else fall into redraw to adjust displayed numbers and such */
655 case KEY_RESIZE:
656 case ctrl('L'):
657 real_line = line > 0 ? line : ps_global->ttyo->screen_rows + line;
658 if(lastc)
659 (void)pico_set_colorp(lastc, PSC_NONE);
660 else
661 EndInverse();
663 ClearScreen();
664 redraw_titlebar();
665 if(ps_global->redrawer != NULL)
666 (*ps_global->redrawer)();
667 if(FOOTER_ROWS(ps_global) == 3 || km_popped)
668 redraw_keymenu();
670 if(ps_global->ttyo->screen_cols - max_label - 1 > 0)
671 maxcol = ps_global->ttyo->screen_cols - max_label - 1;
672 else
673 maxcol = 0;
675 if(promptc)
676 (void)pico_set_colorp(promptc, PSC_NONE);
677 else
678 StartInverse();
680 draw_radio_prompt(real_line, RAD_BUT_COL, maxcol, q);
681 break;
683 } /* switch */
686 out_of_loop:
688 #ifdef _WINDOWS
689 if(!cursor_shown)
690 mswin_showcaret(0);
691 #endif
693 fs_give((void **) &q);
694 if(ds)
695 fs_give((void **) &ds);
697 if(lastc){
698 (void) pico_set_colorp(lastc, PSC_NONE);
699 free_color_pair(&lastc);
700 if(promptc)
701 free_color_pair(&promptc);
703 else
704 EndInverse();
706 fflush(stdout);
707 resume_busy_cue(0);
708 if(km_popped){
709 FOOTER_ROWS(ps_global) = 1;
710 clearfooter(ps_global);
711 ps_global->mangled_body = 1;
714 return(ch);
718 #define OTHER_RETURN_VAL 1300
719 #define KEYS_PER_LIST 8
722 * This should really be part of radio_buttons itself, I suppose. It was
723 * easier to do it this way. This is for when there are more than 12
724 * possible commands. We could have all the radio_buttons calls call this
725 * instead of radio_buttons, or rename this to radio_buttons.
727 * Radio_buttons is limited to 10 visible commands unless there is no Help,
728 * in which case it is 11 visible commands.
729 * Double_radio_buttons is limited to 16 visible commands because it uses
730 * slots 3 and 4 for readability and the OTHER CMD.
733 double_radio_buttons(char *prompt, int line, ESCKEY_S *esc_list, int dflt, int on_ctrl_C, HelpType help_text, int flags)
735 ESCKEY_S *list = NULL, *list1 = NULL, *list2 = NULL;
736 int i = 0, j;
737 int v = OTHER_RETURN_VAL, listnum = 0;
739 #ifdef _WINDOWS
740 if(mswin_usedialog())
741 return(radio_buttons(prompt, line, esc_list, dflt, on_ctrl_C,
742 help_text, flags));
743 #endif
745 /* check to see if it will all fit in one */
746 while(esc_list && esc_list[i].ch != -1)
747 i++;
749 i++; /* for ^C */
750 if(help_text != NO_HELP)
751 i++;
753 if(i <= 12)
754 return(radio_buttons(prompt, line, esc_list, dflt, on_ctrl_C,
755 help_text, flags));
758 * Won't fit, split it into two lists.
760 * We can fit at most 8 items in the visible list. The rest of
761 * the commands have to be invisible. Each of list1 and list2 should
762 * have no more than 8 visible (name != "" || label != "") items.
764 list1 = (ESCKEY_S *)fs_get((KEYS_PER_LIST+1) * sizeof(*list1));
765 memset(list1, 0, (KEYS_PER_LIST+1) * sizeof(*list1));
766 list2 = (ESCKEY_S *)fs_get((KEYS_PER_LIST+1) * sizeof(*list2));
767 memset(list2, 0, (KEYS_PER_LIST+1) * sizeof(*list2));
769 for(j=0,i=0; esc_list[i].ch != -1 && j < KEYS_PER_LIST; j++,i++)
770 list1[j] = esc_list[i];
772 list1[j].ch = -1;
774 for(j=0; esc_list[i].ch != -1 && j < KEYS_PER_LIST; j++,i++)
775 list2[j] = esc_list[i];
777 list2[j].ch = -1;
779 list = construct_combined_esclist(list1, list2);
781 while(v == OTHER_RETURN_VAL){
782 v = radio_buttons(prompt,line,list,dflt,on_ctrl_C,help_text,flags);
783 if(v == OTHER_RETURN_VAL){
784 fs_give((void **) &list);
785 listnum = 1 - listnum;
786 list = construct_combined_esclist(listnum ? list2 : list1,
787 listnum ? list1 : list2);
791 if(list)
792 fs_give((void **) &list);
793 if(list1)
794 fs_give((void **) &list1);
795 if(list2)
796 fs_give((void **) &list2);
798 return(v);
802 ESCKEY_S *
803 construct_combined_esclist(ESCKEY_S *list1, ESCKEY_S *list2)
805 ESCKEY_S *list;
806 int i, j=0, count;
808 count = 2; /* for blank key and for OTHER key */
809 for(i=0; list1 && list1[i].ch != -1; i++)
810 count++;
811 for(i=0; list2 && list2[i].ch != -1; i++)
812 count++;
814 list = (ESCKEY_S *) fs_get((count + 1) * sizeof(*list));
815 memset(list, 0, (count + 1) * sizeof(*list));
817 list[j].ch = -2; /* leave blank */
818 list[j].rval = 0;
819 list[j].name = NULL;
820 list[j++].label = NULL;
822 list[j].ch = 'o';
823 list[j].rval = OTHER_RETURN_VAL;
824 list[j].name = "O";
825 list[j].label = N_("OTHER CMDS");
828 * Make sure that O for OTHER CMD or the return val for OTHER CMD
829 * isn't used for something else.
831 for(i=0; list1 && list1[i].ch != -1; i++){
832 if(list1[i].rval == list[j].rval)
833 alpine_panic("1bad rval in d_r");
834 if(F_OFF(F_USE_FK,ps_global) && list1[i].ch == list[j].ch)
835 alpine_panic("1bad ch in ccl");
838 for(i=0; list2 && list2[i].ch != -1; i++){
839 if(list2[i].rval == list[j].rval)
840 alpine_panic("2bad rval in d_r");
841 if(F_OFF(F_USE_FK,ps_global) && list2[i].ch == list[j].ch)
842 alpine_panic("2bad ch in ccl");
845 j++;
847 /* the visible set */
848 for(i=0; list1 && list1[i].ch != -1; i++){
849 if(i >= KEYS_PER_LIST && list1[i].label[0] != '\0')
850 alpine_panic("too many visible keys in ccl");
852 list[j++] = list1[i];
855 /* the rest are invisible */
856 for(i=0; list2 && list2[i].ch != -1; i++){
857 list[j] = list2[i];
858 list[j].label = "";
859 list[j++].name = "";
862 list[j].ch = -1;
864 return(list);
868 /*----------------------------------------------------------------------
870 ----*/
871 void
872 radio_help(int line, int column, HelpType help)
874 char **text;
876 /* assumption here is that HelpType is char ** */
877 text = help;
878 if(text == NULL)
879 return;
881 MoveCursor(line + 1, column);
882 CleartoEOLN();
883 if(text[0])
884 PutLine0(line + 1, column, text[0]);
886 MoveCursor(line + 2, column);
887 CleartoEOLN();
888 if(text[1])
889 PutLine0(line + 2, column, text[1]);
891 fflush(stdout);
895 /*----------------------------------------------------------------------
896 Paint the screen with the radio buttons prompt
897 ----*/
898 void
899 draw_radio_prompt(int line, unsigned start_c, unsigned max_c, char *q)
901 size_t len;
902 unsigned x, width, got_width;
903 char *tmpq = NULL, *useq;
905 width = utf8_width(q);
906 if(width > max_c - start_c + 1){
907 tmpq = (char *) fs_get((len=(strlen(q)+1)) * sizeof(char));
908 (void) utf8_to_width(tmpq, q, len, max_c - start_c + 1, &got_width);
909 useq = tmpq;
910 width = got_width;
912 else
913 useq = q;
915 PutLine0(line, start_c, useq);
916 x = start_c + width;
917 MoveCursor(line, x);
918 while(x++ < ps_global->ttyo->screen_cols)
919 Writechar(' ', 0);
921 MoveCursor(line, start_c + width);
922 fflush(stdout);
923 if(tmpq)
924 fs_give((void **) &tmpq);