]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bsddialog/lib/barbox.c
zfs: merge openzfs/zfs@f795e90a1
[FreeBSD/FreeBSD.git] / contrib / bsddialog / lib / barbox.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2021-2022 Alfonso Sabato Siciliano
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/param.h>
29
30 #include <ctype.h>
31 #include <curses.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <time.h>
35 #include <unistd.h>
36
37 #include "bsddialog.h"
38 #include "bsddialog_progressview.h"
39 #include "bsddialog_theme.h"
40 #include "lib_util.h"
41
42 #define BARPADDING     2
43 #define MINBARLEN      15
44 #define MINBARWIDTH    (2 + 2 * BARPADDING + MINBARLEN)
45 #define MINMGBARLEN    18
46 #define MINMGBARWIDTH  (2 + 2 * BARPADDING + MINMGBARLEN)
47
48 bool bsddialog_interruptprogview;
49 bool bsddialog_abortprogview;
50 int  bsddialog_total_progview;
51
52 static void
53 draw_bar(WINDOW *win, int y, int x, int barlen, int perc, bool withlabel,
54     int label)
55 {
56         int i, blue_x, color, stringlen;
57         char labelstr[128];
58
59         blue_x = perc > 0 ? (perc * barlen) / 100 : -1;
60
61         wmove(win, y, x);
62         for (i = 0; i < barlen; i++) {
63                 color = (i <= blue_x) ? t.bar.f_color : t.bar.color;
64                 wattron(win, color);
65                 waddch(win, ' ');
66                 wattroff(win, color);
67         }
68
69         if (withlabel)
70                 sprintf(labelstr, "%d", label);
71         else
72                 sprintf(labelstr, "%3d%%", perc);
73         stringlen = (int)strlen(labelstr); /* number, always 1-byte-ch string */
74         wmove(win, y, x + barlen/2 - stringlen/2);
75         for (i = 0; i < stringlen; i++) {
76                 color = (blue_x + 1 <= barlen/2 - stringlen/2 + i ) ?
77                     t.bar.color : t.bar.f_color;
78                 wattron(win, color);
79                 waddch(win, labelstr[i]);
80                 wattroff(win, color);
81         }
82 }
83
84 static int
85 bar_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w,
86     const char *text, struct buttons *bs)
87 {
88         int htext, wtext;
89
90         if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) {
91                 if (text_size(conf, rows, cols, text, bs, 3, MINBARWIDTH,
92                     &htext, &wtext) != 0)
93                         return (BSDDIALOG_ERROR);
94         }
95
96         if (cols == BSDDIALOG_AUTOSIZE)
97                 *w = widget_min_width(conf, wtext, MINBARWIDTH, bs);
98
99         if (rows == BSDDIALOG_AUTOSIZE)
100                 *h = widget_min_height(conf, htext, 3 /* bar */, bs != NULL);
101
102         return (0);
103 }
104
105 static int
106 bar_checksize(int rows, int cols, struct buttons *bs)
107 {
108         int minheight, minwidth;
109
110         minwidth = 0;
111         if (bs != NULL) /* gauge has not buttons */
112                 minwidth = buttons_min_width(*bs);
113
114         minwidth = MAX(minwidth, MINBARWIDTH);
115         minwidth += VBORDERS;
116
117         if (cols < minwidth)
118                 RETURN_ERROR("Few cols to draw bar and/or buttons");
119
120         minheight = HBORDERS + 3;
121         if (bs != NULL)
122                 minheight += 2;
123         if (rows < minheight)
124                 RETURN_ERROR("Few rows to draw bar");
125
126         return (0);
127 }
128
129 int
130 bsddialog_gauge(struct bsddialog_conf *conf, const char *text, int rows,
131     int cols, unsigned int perc, int fd, const char *sep)
132 {
133         bool mainloop;
134         int y, x, h, w, fd2;
135         FILE *input;
136         WINDOW *widget, *textpad, *bar, *shadow;
137         char inputbuf[2048], ntext[2048], *pntext;
138
139         if (set_widget_size(conf, rows, cols, &h, &w) != 0)
140                 return (BSDDIALOG_ERROR);
141         if (bar_autosize(conf, rows, cols, &h, &w, text, NULL) != 0)
142                 return (BSDDIALOG_ERROR);
143         if (bar_checksize(h, w, NULL) != 0)
144                 return (BSDDIALOG_ERROR);
145         if (set_widget_position(conf, &y, &x, h, w) != 0)
146                 return (BSDDIALOG_ERROR);
147
148         if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, NULL,
149             false) != 0)
150                 return (BSDDIALOG_ERROR);
151
152         bar = new_boxed_window(conf, y+h-4, x+3, 3, w-6, RAISED);
153
154         input = NULL;
155         if (fd >= 0) {
156                 fd2 = dup(fd);
157                 if ((input = fdopen(fd2, "r")) == NULL)
158                         RETURN_ERROR("Cannot build FILE* from fd");
159         }
160
161         mainloop = true;
162         while (mainloop) {
163                 wrefresh(widget);
164                 prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-4,
165                     x+w-1-TEXTHMARGIN);
166                 draw_borders(conf, bar, 3, w-6, RAISED);
167                 draw_bar(bar, 1, 1, w-8, perc, false, -1 /*unused*/);
168                 wrefresh(bar);
169                 if (input == NULL) /* that is fd < 0 */
170                         break;
171
172                 while (true) {
173                         fscanf(input, "%s", inputbuf);
174                         if (strcmp(inputbuf,"EOF") == 0) {
175                                 mainloop = false;
176                                 break;
177                         }
178                         if (strcmp(inputbuf, sep) == 0)
179                                 break;
180                 }
181                 if (mainloop == false)
182                         break;
183                 fscanf(input, "%d", &perc);
184                 perc = perc > 100 ? 100 : perc;
185                 pntext = &ntext[0];
186                 ntext[0] = '\0';
187                 while (true) {
188                         fscanf(input, "%s", inputbuf);
189                         if (strcmp(inputbuf,"EOF") == 0) {
190                                 mainloop = false;
191                                 break;
192                         }
193                         if (strcmp(inputbuf, sep) == 0)
194                                 break;
195                         strcpy(pntext, inputbuf);
196                         pntext += strlen(inputbuf); /* end string, no strlen */
197                         pntext[0] = ' ';
198                         pntext++;
199                 }
200                 pntext[0] = '\0';
201                 if (update_dialog(conf, shadow, widget, y, x, h, w, textpad,
202                     ntext, NULL, false) != 0)
203                         return (BSDDIALOG_ERROR);
204         }
205
206         if (input != NULL)
207                 fclose(input);
208         delwin(bar);
209         end_dialog(conf, shadow, widget, textpad);
210
211         return (BSDDIALOG_OK);
212 }
213
214 /* Mixedgauge */
215 static int
216 do_mixedgauge(struct bsddialog_conf *conf, const char *text, int rows, int cols,
217     unsigned int mainperc, unsigned int nminibars, const char **minilabels,
218     int *minipercs, bool color)
219 {
220         int i, retval, miniperc, y, x, h, w, ypad, max_minbarlen;
221         int htextpad, htext, wtext;
222         int colorperc, red, green;
223         WINDOW *widget, *textpad, *bar, *shadow;
224         char states[12][14] = {
225                 "  Succeeded  ", /* -1  */
226                 "   Failed    ", /* -2  */
227                 "   Passed    ", /* -3  */
228                 "  Completed  ", /* -4  */
229                 "   Checked   ", /* -5  */
230                 "    Done     ", /* -6  */
231                 "   Skipped   ", /* -7  */
232                 " In Progress ", /* -8  */
233                 "(blank)      ", /* -9  */
234                 "     N/A     ", /* -10 */
235                 "   Pending   ", /* -11 */
236                 "   UNKNOWN   ", /* < -11, no API */
237         };
238
239         red   = bsddialog_color(BSDDIALOG_WHITE,BSDDIALOG_RED,  BSDDIALOG_BOLD);
240         green = bsddialog_color(BSDDIALOG_WHITE,BSDDIALOG_GREEN,BSDDIALOG_BOLD);
241
242         max_minbarlen = 0;
243         for (i = 0; i < (int)nminibars; i++)
244                 max_minbarlen = MAX(max_minbarlen, (int)strcols(minilabels[i]));
245         max_minbarlen += 3 + 16; /* seps + [...] */
246         max_minbarlen = MAX(max_minbarlen, MINMGBARWIDTH); /* mainbar */
247
248         if (set_widget_size(conf, rows, cols, &h, &w) != 0)
249                 return (BSDDIALOG_ERROR);
250
251         /* mixedgauge autosize */
252         if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) {
253                 if (text_size(conf, rows, cols, text, NULL, nminibars + 3,
254                     max_minbarlen, &htext, &wtext) != 0)
255                         return (BSDDIALOG_ERROR);
256         }
257         if (cols == BSDDIALOG_AUTOSIZE)
258                 w = widget_min_width(conf, wtext, max_minbarlen, NULL);
259         if (rows == BSDDIALOG_AUTOSIZE)
260                 h = widget_min_height(conf, htext, nminibars + 3, false);
261
262         /* mixedgauge checksize */
263         if (w < max_minbarlen + 2)
264                 RETURN_ERROR("Few cols for this mixedgauge");
265         if (h < 5 + (int)nminibars)
266                 RETURN_ERROR("Few rows for this mixedgauge");
267
268         if (set_widget_position(conf, &y, &x, h, w) != 0)
269                 return (BSDDIALOG_ERROR);
270
271         retval = new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text,
272             NULL, false);
273         if (retval == BSDDIALOG_ERROR)
274                 return (retval);
275
276         /* mini bars */
277         for (i = 0; i < (int)nminibars; i++) {
278                 miniperc = minipercs[i];
279                 if (miniperc == BSDDIALOG_MG_BLANK)
280                         continue;
281                 /* label */
282                 if (color && (miniperc >= 0))
283                         wattron(widget, A_BOLD);
284                 mvwaddstr(widget, i+1, 2, minilabels[i]);
285                 if (color && (miniperc >= 0))
286                         wattroff(widget, A_BOLD);
287                 /* perc */
288                 if (miniperc < -11)
289                         mvwaddstr(widget, i+1, w-2-15, states[11]);
290                 else if (miniperc < 0) {
291                         mvwaddstr(widget, i+1, w-2-15, "[             ]");
292                         colorperc = -1;
293                         if (color && miniperc == BSDDIALOG_MG_FAILED)
294                                 colorperc = red;
295                         if (color && miniperc == BSDDIALOG_MG_DONE)
296                                 colorperc = green;
297                         if (colorperc != -1)
298                                 wattron(widget, colorperc);
299                         miniperc = abs(miniperc + 1);
300                         mvwaddstr(widget, i+1, 1+w-2-15, states[miniperc]);
301                         if (colorperc != -1)
302                                 wattroff(widget, colorperc);
303                 }
304                 else { /* miniperc >= 0 */
305                         if (miniperc > 100)
306                                 miniperc = 100;
307                         mvwaddstr(widget, i+1, w-2-15, "[             ]");
308                         draw_bar(widget, i+1, 1+w-2-15, 13, miniperc, false,
309                             -1 /*unused*/);
310                 }
311         }
312
313         wrefresh(widget);
314         getmaxyx(textpad, htextpad, i /* unused */);
315         ypad =  y + h - 4 - htextpad;
316         ypad = ypad < y+(int)nminibars ? y+(int)nminibars : ypad;
317         prefresh(textpad, 0, 0, ypad, x+2, y+h-4, x+w-2);
318
319         /* main bar */
320         bar = new_boxed_window(conf, y+h -4, x+3, 3, w-6, RAISED);
321
322         draw_bar(bar, 1, 1, w-8, mainperc, false, -1 /*unused*/);
323
324         wattron(bar, t.bar.color);
325         mvwaddstr(bar, 0, 2, "Overall Progress");
326         wattroff(bar, t.bar.color);
327
328         wrefresh(bar);
329
330         /* getch(); alternate mode (port devel/ncurses) shows nothing */
331
332         delwin(bar);
333         end_dialog(conf, shadow, widget, textpad);
334
335         return (BSDDIALOG_OK);
336 }
337
338 int
339 bsddialog_mixedgauge(struct bsddialog_conf *conf, const char *text, int rows,
340     int cols, unsigned int mainperc, unsigned int nminibars,
341     const char **minilabels, int *minipercs)
342 {
343         int retval;
344
345         retval = do_mixedgauge(conf, text, rows, cols, mainperc, nminibars,
346             minilabels, minipercs, false);
347
348         return (retval);
349 }
350
351 int
352 bsddialog_progressview (struct bsddialog_conf *conf, const char *text, int rows,
353     int cols, struct bsddialog_progviewconf *pvconf, unsigned int nminibar,
354     struct bsddialog_fileminibar *minibar)
355 {
356         bool update;
357         int perc, retval, *minipercs;
358         unsigned int i, mainperc, totaltodo;
359         float readforsec;
360         const char **minilabels;
361         time_t tstart, told, tnew, refresh;
362
363         if ((minilabels = calloc(nminibar, sizeof(char*))) == NULL)
364                 RETURN_ERROR("Cannot allocate memory for minilabels");
365         if ((minipercs = calloc(nminibar, sizeof(int))) == NULL)
366                 RETURN_ERROR("Cannot allocate memory for minipercs");
367
368         totaltodo = 0;
369         for (i = 0; i < nminibar; i++) {
370                 totaltodo += minibar[i].size;
371                 minilabels[i] = minibar[i].label;
372                 minipercs[i] = minibar[i].status;
373         }
374
375         refresh = pvconf->refresh == 0 ? 0 : pvconf->refresh - 1;
376         retval = BSDDIALOG_OK;
377         i = 0;
378         update = true;
379         time(&told);
380         tstart = told;
381         while (!(bsddialog_interruptprogview || bsddialog_abortprogview)) {
382                 if (bsddialog_total_progview == 0 || totaltodo == 0)
383                         mainperc = 0;
384                 else
385                         mainperc = (bsddialog_total_progview * 100) / totaltodo;
386
387                 time(&tnew);
388                 if (update || tnew > told + refresh) {
389                         retval = do_mixedgauge(conf, text, rows, cols, mainperc,
390                             nminibar, minilabels, minipercs, true);
391                         if (retval == BSDDIALOG_ERROR)
392                                 return (BSDDIALOG_ERROR);
393
394                         move(SCREENLINES - 1, 2);
395                         clrtoeol();
396                         readforsec = ((tnew - tstart) == 0) ? 0 :
397                             bsddialog_total_progview / (float)(tnew - tstart);
398                         printw(pvconf->fmtbottomstr, bsddialog_total_progview,
399                             readforsec);
400                         refresh();
401
402                         time(&told);
403                         update = false;
404                 }
405
406                 if (i >= nminibar)
407                         break;
408                 if (minibar[i].status == BSDDIALOG_MG_FAILED)
409                         break;
410
411                 perc = pvconf->callback(&minibar[i]);
412
413                 if (minibar[i].status == BSDDIALOG_MG_DONE) { /*||perc >= 100)*/
414                         minipercs[i] = BSDDIALOG_MG_DONE;
415                         update = true;
416                         i++;
417                 } else if (minibar[i].status == BSDDIALOG_MG_FAILED || perc < 0) {
418                         minipercs[i] = BSDDIALOG_MG_FAILED;
419                         update = true;
420                 } else /* perc >= 0 */
421                         minipercs[i] = perc;
422         }
423
424         free(minilabels);
425         free(minipercs);
426         return (retval);
427 }
428
429 int
430 bsddialog_rangebox(struct bsddialog_conf *conf, const char *text, int rows,
431     int cols, int min, int max, int *value)
432 {
433         bool loop, buttupdate, barupdate;
434         int y, x, h, w;
435         int currvalue, retval, sizebar, bigchange, positions;
436         wint_t input;
437         float perc;
438         WINDOW *widget, *textpad, *bar, *shadow;
439         struct buttons bs;
440
441         if (value == NULL)
442                 RETURN_ERROR("*value cannot be NULL");
443
444         if (min >= max)
445                 RETURN_ERROR("min >= max");
446
447         currvalue = *value;
448         positions = max - min + 1;
449
450         get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL);
451
452         if (set_widget_size(conf, rows, cols, &h, &w) != 0)
453                 return (BSDDIALOG_ERROR);
454         if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0)
455                 return (BSDDIALOG_ERROR);
456         if (bar_checksize(h, w, &bs) != 0)
457                 return (BSDDIALOG_ERROR);
458         if (set_widget_position(conf, &y, &x, h, w) != 0)
459                 return (BSDDIALOG_ERROR);
460
461         if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs,
462             true) != 0)
463                 return (BSDDIALOG_ERROR);
464
465         doupdate();
466
467         prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-7, x+w-1-TEXTHMARGIN);
468
469         sizebar = w - HBORDERS - (2 * BARPADDING) - 2;
470         bigchange = MAX(1, sizebar/10);
471
472         bar = new_boxed_window(conf, y + h - 6, x + 1 + BARPADDING, 3,
473             sizebar + 2, RAISED);
474
475         loop = buttupdate = barupdate = true;
476         while (loop) {
477                 if (buttupdate) {
478                         draw_buttons(widget, bs, true);
479                         wrefresh(widget);
480                         buttupdate = false;
481                 }
482                 if (barupdate) {
483                         perc = ((float)(currvalue - min)*100) / (positions-1);
484                         draw_bar(bar, 1, 1, sizebar, perc, true, currvalue);
485                         barupdate = false;
486                         wrefresh(bar);
487                 }
488
489                 if (get_wch(&input) == ERR)
490                         continue;
491                 switch(input) {
492                 case KEY_ENTER:
493                 case 10: /* Enter */
494                         retval = bs.value[bs.curr];
495                         *value = currvalue;
496                         loop = false;
497                         break;
498                 case 27: /* Esc */
499                         if (conf->key.enable_esc) {
500                                 retval = BSDDIALOG_ESC;
501                                 loop = false;
502                         }
503                         break;
504                 case '\t': /* TAB */
505                         bs.curr = (bs.curr + 1) % bs.nbuttons;
506                         buttupdate = true;
507                         break;
508                 case KEY_LEFT:
509                         if (bs.curr > 0) {
510                                 bs.curr--;
511                                 buttupdate = true;
512                         }
513                         break;
514                 case KEY_RIGHT:
515                         if (bs.curr < (int) bs.nbuttons - 1) {
516                                 bs.curr++;
517                                 buttupdate = true;
518                         }
519                         break;
520                 case KEY_HOME:
521                         currvalue = max;
522                         barupdate = true;
523                         break;
524                 case KEY_END:
525                         currvalue = min;
526                         barupdate = true;
527                         break;
528                 case KEY_NPAGE:
529                         currvalue -= bigchange;
530                         if (currvalue < min)
531                                 currvalue = min;
532                         barupdate = true;
533                         break;
534                 case KEY_PPAGE:
535                         currvalue += bigchange;
536                         if (currvalue > max)
537                                 currvalue = max;
538                         barupdate = true;
539                         break;
540                 case KEY_UP:
541                         if (currvalue < max) {
542                                 currvalue++;
543                                 barupdate = true;
544                         }
545                         break;
546                 case KEY_DOWN:
547                         if (currvalue > min) {
548                                 currvalue--;
549                                 barupdate = true;
550                         }
551                         break;
552                 case KEY_F(1):
553                         if (conf->key.f1_file == NULL &&
554                             conf->key.f1_message == NULL)
555                                 break;
556                         if (f1help(conf) != 0)
557                                 return (BSDDIALOG_ERROR);
558                         /* No break, screen size can change */
559                 case KEY_RESIZE:
560                         /* Important for decreasing screen */
561                         hide_widget(y, x, h, w, conf->shadow);
562                         refresh();
563
564                         if (set_widget_size(conf, rows, cols, &h, &w) != 0)
565                                 return (BSDDIALOG_ERROR);
566                         if (bar_autosize(conf, rows, cols, &h, &w, text,
567                             &bs) != 0)
568                                 return (BSDDIALOG_ERROR);
569                         if (bar_checksize(h, w, &bs) != 0)
570                                 return (BSDDIALOG_ERROR);
571                         if (set_widget_position(conf, &y, &x, h, w) != 0)
572                                 return (BSDDIALOG_ERROR);
573
574                         if (update_dialog(conf, shadow, widget,y, x, h, w,
575                             textpad, text, &bs, true) != 0)
576                                 return (BSDDIALOG_ERROR);
577
578                         doupdate();
579
580                         sizebar = w - HBORDERS - (2 * BARPADDING) - 2;
581                         bigchange = MAX(1, sizebar/10);
582                         wclear(bar);
583                         mvwin(bar, y + h - 6, x + 1 + BARPADDING);
584                         wresize(bar, 3, sizebar + 2);
585                         draw_borders(conf, bar, 3, sizebar+2, RAISED);
586
587                         prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-7,
588                             x+w-1-TEXTHMARGIN);
589
590                         barupdate = true;
591                         break;
592                 default:
593                         if (shortcut_buttons(input, &bs)) {
594                                 retval = bs.value[bs.curr];
595                                 loop = false;
596                         }
597                 }
598         }
599
600         delwin(bar);
601         end_dialog(conf, shadow, widget, textpad);
602
603         return (retval);
604 }
605
606 int
607 bsddialog_pause(struct bsddialog_conf *conf, const char *text, int rows,
608     int cols, unsigned int sec)
609 {
610         bool loop, buttupdate, barupdate;
611         int retval, y, x, h, w, tout, sizebar;
612         wint_t input;
613         float perc;
614         WINDOW *widget, *textpad, *bar, *shadow;
615         struct buttons bs;
616
617         get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL);
618
619         if (set_widget_size(conf, rows, cols, &h, &w) != 0)
620                 return (BSDDIALOG_ERROR);
621         if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0)
622                 return (BSDDIALOG_ERROR);
623         if (bar_checksize(h, w, &bs) != 0)
624                 return (BSDDIALOG_ERROR);
625         if (set_widget_position(conf, &y, &x, h, w) != 0)
626                 return (BSDDIALOG_ERROR);
627
628         if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs,
629             true) != 0)
630                 return (BSDDIALOG_ERROR);
631
632         doupdate();
633
634         prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-7, x+w-1-TEXTHMARGIN);
635
636         sizebar = w - HBORDERS - (2 * BARPADDING) - 2;
637         bar = new_boxed_window(conf, y + h - 6, x + 1 + BARPADDING, 3,
638             sizebar + 2, RAISED);
639
640         tout = sec;
641         nodelay(stdscr, TRUE);
642         timeout(1000);
643         loop = buttupdate = barupdate = true;
644         while (loop) {
645                 if (barupdate) {
646                         perc = (float)tout * 100 / sec;
647                         draw_bar(bar, 1, 1, sizebar, perc, true, tout);
648                         barupdate = false;
649                         wrefresh(bar);
650                 }
651
652                 if (buttupdate) {
653                         draw_buttons(widget, bs, true);
654                         wrefresh(widget);
655                         buttupdate = false;
656                 }
657
658                 if (get_wch(&input) == ERR) { /* timeout */
659                         tout--;
660                         if (tout < 0) {
661                                 retval = BSDDIALOG_TIMEOUT;
662                                 break;
663                         }
664                         else {
665                                 barupdate = true;
666                                 continue;
667                         }
668                 }
669                 switch(input) {
670                 case KEY_ENTER:
671                 case 10: /* Enter */
672                         retval = bs.value[bs.curr];
673                         loop = false;
674                         break;
675                 case 27: /* Esc */
676                         if (conf->key.enable_esc) {
677                                 retval = BSDDIALOG_ESC;
678                                 loop = false;
679                         }
680                         break;
681                 case '\t': /* TAB */
682                         bs.curr = (bs.curr + 1) % bs.nbuttons;
683                         buttupdate = true;
684                         break;
685                 case KEY_LEFT:
686                         if (bs.curr > 0) {
687                                 bs.curr--;
688                                 buttupdate = true;
689                         }
690                         break;
691                 case KEY_RIGHT:
692                         if (bs.curr < (int) bs.nbuttons - 1) {
693                                 bs.curr++;
694                                 buttupdate = true;
695                         }
696                         break;
697                 case KEY_F(1):
698                         if (conf->key.f1_file == NULL &&
699                             conf->key.f1_message == NULL)
700                                 break;
701                         if (f1help(conf) != 0)
702                                 return (BSDDIALOG_ERROR);
703                         /* No break, screen size can change */
704                 case KEY_RESIZE:
705                         /* Important for decreasing screen */
706                         hide_widget(y, x, h, w, conf->shadow);
707                         refresh();
708
709                         if (set_widget_size(conf, rows, cols, &h, &w) != 0)
710                                 return (BSDDIALOG_ERROR);
711                         if (bar_autosize(conf, rows, cols, &h, &w, text,
712                             &bs) != 0)
713                                 return (BSDDIALOG_ERROR);
714                         if (bar_checksize(h, w, &bs) != 0)
715                                 return (BSDDIALOG_ERROR);
716                         if (set_widget_position(conf, &y, &x, h, w) != 0)
717                                 return (BSDDIALOG_ERROR);
718
719                         if (update_dialog(conf, shadow, widget,y, x, h, w,
720                             textpad, text, &bs, true) != 0)
721                                 return (BSDDIALOG_ERROR);
722
723                         doupdate();
724
725                         sizebar = w - HBORDERS - (2 * BARPADDING) - 2;
726                         wclear(bar);
727                         mvwin(bar, y + h - 6, x + 1 + BARPADDING);
728                         wresize(bar, 3, sizebar + 2);
729                         draw_borders(conf, bar, 3, sizebar+2, LOWERED);
730
731                         prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-7,
732                             x+w-1-TEXTHMARGIN);
733
734                         barupdate = true;
735                         break;
736                 default:
737                         if (shortcut_buttons(input, &bs)) {
738                                 retval = bs.value[bs.curr];
739                                 loop = false;
740                         }
741                 }
742         }
743
744         nodelay(stdscr, FALSE);
745
746         delwin(bar);
747         end_dialog(conf, shadow, widget, textpad);
748
749         return (retval);
750 }