1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2021-2023 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 <curses.h> 29 30 #include "bsddialog.h" 31 #include "bsddialog_theme.h" 32 #include "lib_util.h" 33 34 #define MINWTIME 14 /* 3 windows and their borders */ 35 #define HBOX 3 36 #define WBOX 4 37 38 struct clock { 39 unsigned int max; 40 unsigned int value; 41 WINDOW *win; 42 }; 43 44 static void 45 drawsquare(struct bsddialog_conf *conf, WINDOW *win, unsigned int value, 46 bool focus) 47 { 48 draw_borders(conf, win, LOWERED); 49 if (focus) { 50 wattron(win, t.dialog.arrowcolor); 51 mvwhline(win, 0, 1, conf->ascii_lines ? '^' : ACS_UARROW, 2); 52 mvwhline(win, 2, 1, conf->ascii_lines ? 'v' : ACS_DARROW, 2); 53 wattroff(win, t.dialog.arrowcolor); 54 } 55 56 if (focus) 57 wattron(win, t.menu.f_namecolor); 58 mvwprintw(win, 1, 1, "%02u", value); 59 if (focus) 60 wattroff(win, t.menu.f_namecolor); 61 62 wnoutrefresh(win); 63 } 64 65 static int timebox_redraw(struct dialog *d, struct clock *c) 66 { 67 int y, x; 68 69 if (d->built) { 70 hide_dialog(d); 71 refresh(); /* Important for decreasing screen */ 72 } 73 if (dialog_size_position(d, HBOX, MINWTIME, NULL) != 0) 74 return (BSDDIALOG_ERROR); 75 if (draw_dialog(d) != 0) 76 return (BSDDIALOG_ERROR); 77 if (d->built) 78 refresh(); /* Important to fix grey lines expanding screen */ 79 TEXTPAD(d, HBOX + HBUTTONS); 80 81 y = d->y + d->h - BORDER - HBUTTONS - HBOX; 82 x = d->x + d->w/2 - 7; 83 update_box(d->conf, c[0].win, y, x, HBOX, WBOX, LOWERED); 84 mvwaddch(d->widget, d->h - 5, d->w/2 - 3, ':'); 85 update_box(d->conf, c[1].win, y, x += 5, HBOX, WBOX, LOWERED); 86 mvwaddch(d->widget, d->h - 5, d->w/2 + 2, ':'); 87 update_box(d->conf, c[2].win, y, x + 5, HBOX, WBOX, LOWERED); 88 wnoutrefresh(d->widget); /* for mvwaddch(':') */ 89 90 return (0); 91 } 92 93 /* API */ 94 int 95 bsddialog_timebox(struct bsddialog_conf *conf, const char* text, int rows, 96 int cols, unsigned int *hh, unsigned int *mm, unsigned int *ss) 97 { 98 bool loop, focusbuttons; 99 int i, retval, sel; 100 wint_t input; 101 struct dialog d; 102 struct clock c[3] = { 103 {23, *hh, NULL}, 104 {59, *mm, NULL}, 105 {59, *ss, NULL} 106 }; 107 108 CHECK_PTR(hh); 109 CHECK_PTR(mm); 110 CHECK_PTR(ss); 111 if (prepare_dialog(conf, text, rows, cols, &d) != 0) 112 return (BSDDIALOG_ERROR); 113 set_buttons(&d, true, OK_LABEL, CANCEL_LABEL); 114 for (i=0; i<3; i++) { 115 if ((c[i].win = newwin(1, 1, 1, 1)) == NULL) 116 RETURN_FMTERROR("Cannot build WINDOW for time[%d]", i); 117 wbkgd(c[i].win, t.dialog.color); 118 c[i].value = MIN(c[i].value, c[i].max); 119 } 120 if (timebox_redraw(&d, c) != 0) 121 return (BSDDIALOG_ERROR); 122 123 sel = -1; 124 loop = focusbuttons = true; 125 while (loop) { 126 for (i = 0; i < 3; i++) 127 drawsquare(conf, c[i].win, c[i].value, sel == i); 128 doupdate(); 129 if (get_wch(&input) == ERR) 130 continue; 131 switch(input) { 132 case KEY_ENTER: 133 case 10: /* Enter */ 134 if (focusbuttons || conf->button.always_active) { 135 retval = BUTTONVALUE(d.bs); 136 loop = false; 137 } 138 break; 139 case 27: /* Esc */ 140 if (conf->key.enable_esc) { 141 retval = BSDDIALOG_ESC; 142 loop = false; 143 } 144 break; 145 case KEY_RIGHT: 146 case '\t': /* TAB */ 147 if (focusbuttons) { 148 d.bs.curr++; 149 focusbuttons = d.bs.curr < (int)d.bs.nbuttons ? 150 true : false; 151 if (focusbuttons == false) { 152 sel = 0; 153 d.bs.curr = 154 conf->button.always_active ? 0 : -1; 155 } 156 } else { 157 sel++; 158 focusbuttons = sel > 2 ? true : false; 159 if (focusbuttons) { 160 d.bs.curr = 0; 161 } 162 } 163 DRAW_BUTTONS(d); 164 break; 165 case KEY_LEFT: 166 if (focusbuttons) { 167 d.bs.curr--; 168 focusbuttons = d.bs.curr < 0 ? false : true; 169 if (focusbuttons == false) { 170 sel = 2; 171 d.bs.curr = 172 conf->button.always_active ? 0 : -1; 173 } 174 } else { 175 sel--; 176 focusbuttons = sel < 0 ? true : false; 177 if (focusbuttons) 178 d.bs.curr = (int)d.bs.nbuttons - 1; 179 } 180 DRAW_BUTTONS(d); 181 break; 182 case KEY_UP: 183 if (focusbuttons) { 184 sel = 0; 185 focusbuttons = false; 186 d.bs.curr = conf->button.always_active ? 0 : -1; 187 DRAW_BUTTONS(d); 188 } else { 189 c[sel].value = c[sel].value > 0 ? 190 c[sel].value - 1 : c[sel].max; 191 } 192 break; 193 case KEY_DOWN: 194 if (focusbuttons) 195 break; 196 c[sel].value = c[sel].value < c[sel].max ? 197 c[sel].value + 1 : 0; 198 break; 199 case KEY_F(1): 200 if (conf->key.f1_file == NULL && 201 conf->key.f1_message == NULL) 202 break; 203 if (f1help_dialog(conf) != 0) 204 return (BSDDIALOG_ERROR); 205 if (timebox_redraw(&d, c) != 0) 206 return (BSDDIALOG_ERROR); 207 break; 208 case KEY_RESIZE: 209 if (timebox_redraw(&d, c) != 0) 210 return (BSDDIALOG_ERROR); 211 break; 212 default: 213 if (shortcut_buttons(input, &d.bs)) { 214 DRAW_BUTTONS(d); 215 doupdate(); 216 retval = BUTTONVALUE(d.bs); 217 loop = false; 218 } 219 } 220 } 221 222 *hh = c[0].value; 223 *mm = c[1].value; 224 *ss = c[2].value; 225 226 for (i = 0; i < 3; i++) 227 delwin(c[i].win); 228 end_dialog(&d); 229 230 return (retval); 231 } 232