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
draw_bar(WINDOW * win,int y,int x,int barlen,int perc,bool withlabel,int label)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
bar_autosize(struct bsddialog_conf * conf,int rows,int cols,int * h,int * w,const char * text,struct buttons * bs)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
bar_checksize(int rows,int cols,struct buttons * bs)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
bsddialog_gauge(struct bsddialog_conf * conf,const char * text,int rows,int cols,unsigned int perc,int fd,const char * sep)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
do_mixedgauge(struct bsddialog_conf * conf,const char * text,int rows,int cols,unsigned int mainperc,unsigned int nminibars,const char ** minilabels,int * minipercs,bool color)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
bsddialog_mixedgauge(struct bsddialog_conf * conf,const char * text,int rows,int cols,unsigned int mainperc,unsigned int nminibars,const char ** minilabels,int * minipercs)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
bsddialog_progressview(struct bsddialog_conf * conf,const char * text,int rows,int cols,struct bsddialog_progviewconf * pvconf,unsigned int nminibar,struct bsddialog_fileminibar * minibar)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
bsddialog_rangebox(struct bsddialog_conf * conf,const char * text,int rows,int cols,int min,int max,int * value)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
bsddialog_pause(struct bsddialog_conf * conf,const char * text,int rows,int cols,unsigned int sec)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 }