1c76f0793SBaptiste Daroussin /*- 2c76f0793SBaptiste Daroussin * SPDX-License-Identifier: BSD-2-Clause 3c76f0793SBaptiste Daroussin * 4c76f0793SBaptiste Daroussin * Copyright (c) 2021 Alfonso Sabato Siciliano 5c76f0793SBaptiste Daroussin * 6c76f0793SBaptiste Daroussin * Redistribution and use in source and binary forms, with or without 7c76f0793SBaptiste Daroussin * modification, are permitted provided that the following conditions 8c76f0793SBaptiste Daroussin * are met: 9c76f0793SBaptiste Daroussin * 1. Redistributions of source code must retain the above copyright 10c76f0793SBaptiste Daroussin * notice, this list of conditions and the following disclaimer. 11c76f0793SBaptiste Daroussin * 2. Redistributions in binary form must reproduce the above copyright 12c76f0793SBaptiste Daroussin * notice, this list of conditions and the following disclaimer in the 13c76f0793SBaptiste Daroussin * documentation and/or other materials provided with the distribution. 14c76f0793SBaptiste Daroussin * 15c76f0793SBaptiste Daroussin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16c76f0793SBaptiste Daroussin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17c76f0793SBaptiste Daroussin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18c76f0793SBaptiste Daroussin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19c76f0793SBaptiste Daroussin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20c76f0793SBaptiste Daroussin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21c76f0793SBaptiste Daroussin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22c76f0793SBaptiste Daroussin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23c76f0793SBaptiste Daroussin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24c76f0793SBaptiste Daroussin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25c76f0793SBaptiste Daroussin * SUCH DAMAGE. 26c76f0793SBaptiste Daroussin */ 27c76f0793SBaptiste Daroussin 28c76f0793SBaptiste Daroussin #include <sys/param.h> 29c76f0793SBaptiste Daroussin 30c76f0793SBaptiste Daroussin #include <ctype.h> 31c76f0793SBaptiste Daroussin #ifdef PORTNCURSES 328c4f4028SBaptiste Daroussin #include <ncurses/ncurses.h> 33c76f0793SBaptiste Daroussin #else 348c4f4028SBaptiste Daroussin #include <ncurses.h> 35c76f0793SBaptiste Daroussin #endif 36*d93b4d32SBaptiste Daroussin #include <string.h> 37c76f0793SBaptiste Daroussin 38c76f0793SBaptiste Daroussin #include "bsddialog.h" 39c76f0793SBaptiste Daroussin #include "lib_util.h" 40c76f0793SBaptiste Daroussin #include "bsddialog_theme.h" 41c76f0793SBaptiste Daroussin 42c76f0793SBaptiste Daroussin #define AUTO_WIDTH (COLS / 3U) 43*d93b4d32SBaptiste Daroussin /* at least 1 line text for its pad building in widget_withtextpad_init() */ 44*d93b4d32SBaptiste Daroussin #define MIN_HEIGHT (HBORDERS + 2 /*buttons*/ + 1 /*text*/) 45c76f0793SBaptiste Daroussin 46c76f0793SBaptiste Daroussin extern struct bsddialog_theme t; 47c76f0793SBaptiste Daroussin 48c76f0793SBaptiste Daroussin static int 49*d93b4d32SBaptiste Daroussin message_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, 50*d93b4d32SBaptiste Daroussin int *w, char *text, struct buttons bs) 51c76f0793SBaptiste Daroussin { 52c76f0793SBaptiste Daroussin int maxword, maxline, nlines, line; 53c76f0793SBaptiste Daroussin 54c76f0793SBaptiste Daroussin if (get_text_properties(conf, text, &maxword, &maxline, &nlines) != 0) 55c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 56c76f0793SBaptiste Daroussin 57c76f0793SBaptiste Daroussin if (cols == BSDDIALOG_AUTOSIZE) { 58c76f0793SBaptiste Daroussin *w = VBORDERS; 59c76f0793SBaptiste Daroussin /* buttons size */ 60c76f0793SBaptiste Daroussin *w += bs.nbuttons * bs.sizebutton; 61f499134dSBaptiste Daroussin *w += bs.nbuttons > 0 ? (bs.nbuttons-1) * t.button.space : 0; 62c76f0793SBaptiste Daroussin /* text size */ 63f499134dSBaptiste Daroussin line = MIN(maxline + VBORDERS + t.text.hmargin * 2, AUTO_WIDTH); 64f499134dSBaptiste Daroussin line = MAX(line, (int) (maxword + VBORDERS + t.text.hmargin * 2)); 65c76f0793SBaptiste Daroussin *w = MAX(*w, line); 668c4f4028SBaptiste Daroussin /* conf.auto_minwidth */ 678c4f4028SBaptiste Daroussin *w = MAX(*w, (int)conf->auto_minwidth); 68c76f0793SBaptiste Daroussin /* avoid terminal overflow */ 69c76f0793SBaptiste Daroussin *w = MIN(*w, widget_max_width(conf)); 70c76f0793SBaptiste Daroussin } 71c76f0793SBaptiste Daroussin 72c76f0793SBaptiste Daroussin if (rows == BSDDIALOG_AUTOSIZE) { 73c76f0793SBaptiste Daroussin *h = MIN_HEIGHT - 1; 74c76f0793SBaptiste Daroussin if (maxword > 0) 758c4f4028SBaptiste Daroussin *h += MAX(nlines, (int)(*w / GET_ASPECT_RATIO(conf))); 76c76f0793SBaptiste Daroussin *h = MAX(*h, MIN_HEIGHT); 778c4f4028SBaptiste Daroussin /* conf.auto_minheight */ 788c4f4028SBaptiste Daroussin *h = MAX(*h, (int)conf->auto_minheight); 79c76f0793SBaptiste Daroussin /* avoid terminal overflow */ 80c76f0793SBaptiste Daroussin *h = MIN(*h, widget_max_height(conf)); 81c76f0793SBaptiste Daroussin } 82c76f0793SBaptiste Daroussin 83c76f0793SBaptiste Daroussin return 0; 84c76f0793SBaptiste Daroussin } 85c76f0793SBaptiste Daroussin 86c76f0793SBaptiste Daroussin static int message_checksize(int rows, int cols, struct buttons bs) 87c76f0793SBaptiste Daroussin { 88c76f0793SBaptiste Daroussin int mincols; 89c76f0793SBaptiste Daroussin 90c76f0793SBaptiste Daroussin mincols = VBORDERS; 91c76f0793SBaptiste Daroussin mincols += bs.nbuttons * bs.sizebutton; 92f499134dSBaptiste Daroussin mincols += bs.nbuttons > 0 ? (bs.nbuttons-1) * t.button.space : 0; 93c76f0793SBaptiste Daroussin 94c76f0793SBaptiste Daroussin if (cols < mincols) 95c76f0793SBaptiste Daroussin RETURN_ERROR("Few cols, Msgbox and Yesno need at least width "\ 96c76f0793SBaptiste Daroussin "for borders, buttons and spaces between buttons"); 97c76f0793SBaptiste Daroussin 98c76f0793SBaptiste Daroussin if (rows < MIN_HEIGHT) 99c76f0793SBaptiste Daroussin RETURN_ERROR("Msgbox and Yesno need at least height 5"); 100c76f0793SBaptiste Daroussin 101c76f0793SBaptiste Daroussin return 0; 102c76f0793SBaptiste Daroussin } 103c76f0793SBaptiste Daroussin 104c76f0793SBaptiste Daroussin static void 105*d93b4d32SBaptiste Daroussin buttonsupdate(WINDOW *widget, int h, int w, struct buttons bs) 106c76f0793SBaptiste Daroussin { 107*d93b4d32SBaptiste Daroussin draw_buttons(widget, h-2, w, bs, true); 108c76f0793SBaptiste Daroussin wnoutrefresh(widget); 109c76f0793SBaptiste Daroussin } 110c76f0793SBaptiste Daroussin 111c76f0793SBaptiste Daroussin static void 112c76f0793SBaptiste Daroussin textupdate(WINDOW *widget, int y, int x, int h, int w, WINDOW *textpad, 113c76f0793SBaptiste Daroussin int htextpad, int textrow) 114c76f0793SBaptiste Daroussin { 115c76f0793SBaptiste Daroussin if (htextpad > h - 4) { 116c76f0793SBaptiste Daroussin mvwprintw(widget, h-3, w-6, "%3d%%", 117c76f0793SBaptiste Daroussin 100 * (textrow+h-4)/ htextpad); 118c76f0793SBaptiste Daroussin wnoutrefresh(widget); 119c76f0793SBaptiste Daroussin } 120c76f0793SBaptiste Daroussin 121c76f0793SBaptiste Daroussin pnoutrefresh(textpad, textrow, 0, y+1, x+2, y+h-4, x+w-2); 122c76f0793SBaptiste Daroussin } 123c76f0793SBaptiste Daroussin 124c76f0793SBaptiste Daroussin static int 125*d93b4d32SBaptiste Daroussin do_dialog(struct bsddialog_conf *conf, char *text, int rows, int cols, 126*d93b4d32SBaptiste Daroussin struct buttons bs) 127c76f0793SBaptiste Daroussin { 128c76f0793SBaptiste Daroussin WINDOW *widget, *textpad, *shadow; 129c76f0793SBaptiste Daroussin bool loop; 130c76f0793SBaptiste Daroussin int i, y, x, h, w, input, output, htextpad, textrow; 131c76f0793SBaptiste Daroussin 132c76f0793SBaptiste Daroussin if (set_widget_size(conf, rows, cols, &h, &w) != 0) 133c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 134c76f0793SBaptiste Daroussin if (message_autosize(conf, rows, cols, &h, &w, text, bs) != 0) 135c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 136c76f0793SBaptiste Daroussin if (message_checksize(h, w, bs) != 0) 137c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 138c76f0793SBaptiste Daroussin if (set_widget_position(conf, &y, &x, h, w) != 0) 139c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 140c76f0793SBaptiste Daroussin 141c76f0793SBaptiste Daroussin if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED, 142c76f0793SBaptiste Daroussin &textpad, &htextpad, text, true) != 0) 143c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 144c76f0793SBaptiste Daroussin 145c76f0793SBaptiste Daroussin textrow = 0; 146c76f0793SBaptiste Daroussin loop = true; 147*d93b4d32SBaptiste Daroussin buttonsupdate(widget, h, w, bs); 148c76f0793SBaptiste Daroussin textupdate(widget, y, x, h, w, textpad, htextpad, textrow); 149c76f0793SBaptiste Daroussin while(loop) { 150c76f0793SBaptiste Daroussin doupdate(); 151c76f0793SBaptiste Daroussin input = getch(); 152c76f0793SBaptiste Daroussin switch (input) { 153c76f0793SBaptiste Daroussin case 10: /* Enter */ 154c76f0793SBaptiste Daroussin output = bs.value[bs.curr]; 155c76f0793SBaptiste Daroussin loop = false; 156c76f0793SBaptiste Daroussin break; 157c76f0793SBaptiste Daroussin case 27: /* Esc */ 158c76f0793SBaptiste Daroussin output = BSDDIALOG_ESC; 159c76f0793SBaptiste Daroussin loop = false; 160c76f0793SBaptiste Daroussin break; 161c76f0793SBaptiste Daroussin case '\t': /* TAB */ 162c76f0793SBaptiste Daroussin bs.curr = (bs.curr + 1) % bs.nbuttons; 163*d93b4d32SBaptiste Daroussin buttonsupdate(widget, h, w, bs); 164c76f0793SBaptiste Daroussin break; 165c76f0793SBaptiste Daroussin case KEY_F(1): 1668c4f4028SBaptiste Daroussin if (conf->f1_file == NULL && conf->f1_message == NULL) 167c76f0793SBaptiste Daroussin break; 168c76f0793SBaptiste Daroussin if (f1help(conf) != 0) 169c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 170c76f0793SBaptiste Daroussin /* No break! the terminal size can change */ 171c76f0793SBaptiste Daroussin case KEY_RESIZE: 172f499134dSBaptiste Daroussin hide_widget(y, x, h, w,conf->shadow); 173c76f0793SBaptiste Daroussin 174c76f0793SBaptiste Daroussin /* 175c76f0793SBaptiste Daroussin * Unnecessary, but, when the columns decrease the 176c76f0793SBaptiste Daroussin * following "refresh" seem not work 177c76f0793SBaptiste Daroussin */ 178c76f0793SBaptiste Daroussin refresh(); 179c76f0793SBaptiste Daroussin 180c76f0793SBaptiste Daroussin if (set_widget_size(conf, rows, cols, &h, &w) != 0) 181c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 182c76f0793SBaptiste Daroussin if (message_autosize(conf, rows, cols, &h, &w, text, bs) != 0) 183c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 184c76f0793SBaptiste Daroussin if (message_checksize(h, w, bs) != 0) 185c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 186c76f0793SBaptiste Daroussin if (set_widget_position(conf, &y, &x, h, w) != 0) 187c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 188c76f0793SBaptiste Daroussin 189c76f0793SBaptiste Daroussin wclear(shadow); 190f499134dSBaptiste Daroussin mvwin(shadow, y + t.shadow.h, x + t.shadow.w); 191c76f0793SBaptiste Daroussin wresize(shadow, h, w); 192c76f0793SBaptiste Daroussin 193c76f0793SBaptiste Daroussin wclear(widget); 194c76f0793SBaptiste Daroussin mvwin(widget, y, x); 195c76f0793SBaptiste Daroussin wresize(widget, h, w); 196c76f0793SBaptiste Daroussin 197c76f0793SBaptiste Daroussin htextpad = 1; 198c76f0793SBaptiste Daroussin wclear(textpad); 199f499134dSBaptiste Daroussin wresize(textpad, 1, w - HBORDERS - t.text.hmargin * 2); 200c76f0793SBaptiste Daroussin 201c76f0793SBaptiste Daroussin if(update_widget_withtextpad(conf, shadow, widget, h, w, 202c76f0793SBaptiste Daroussin RAISED, textpad, &htextpad, text, true) != 0) 203c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 204c76f0793SBaptiste Daroussin 205*d93b4d32SBaptiste Daroussin buttonsupdate(widget, h, w, bs); 206c76f0793SBaptiste Daroussin textupdate(widget, y, x, h, w, textpad, htextpad, textrow); 207c76f0793SBaptiste Daroussin 208c76f0793SBaptiste Daroussin /* Important to fix grey lines expanding screen */ 209c76f0793SBaptiste Daroussin refresh(); 210c76f0793SBaptiste Daroussin break; 211c76f0793SBaptiste Daroussin case KEY_UP: 212c76f0793SBaptiste Daroussin if (textrow == 0) 213c76f0793SBaptiste Daroussin break; 214c76f0793SBaptiste Daroussin textrow--; 215c76f0793SBaptiste Daroussin textupdate(widget, y, x, h, w, textpad, htextpad, textrow); 216c76f0793SBaptiste Daroussin break; 217c76f0793SBaptiste Daroussin case KEY_DOWN: 218c76f0793SBaptiste Daroussin if (textrow + h - 4 >= htextpad) 219c76f0793SBaptiste Daroussin break; 220c76f0793SBaptiste Daroussin textrow++; 221c76f0793SBaptiste Daroussin textupdate(widget, y, x, h, w, textpad, htextpad, textrow); 222c76f0793SBaptiste Daroussin break; 223c76f0793SBaptiste Daroussin case KEY_LEFT: 224c76f0793SBaptiste Daroussin if (bs.curr > 0) { 225c76f0793SBaptiste Daroussin bs.curr--; 226*d93b4d32SBaptiste Daroussin buttonsupdate(widget, h, w, bs); 227c76f0793SBaptiste Daroussin } 228c76f0793SBaptiste Daroussin break; 229c76f0793SBaptiste Daroussin case KEY_RIGHT: 230c76f0793SBaptiste Daroussin if (bs.curr < (int) bs.nbuttons - 1) { 231c76f0793SBaptiste Daroussin bs.curr++; 232*d93b4d32SBaptiste Daroussin buttonsupdate(widget, h, w, bs); 233c76f0793SBaptiste Daroussin } 234c76f0793SBaptiste Daroussin break; 235c76f0793SBaptiste Daroussin default: 236c76f0793SBaptiste Daroussin for (i = 0; i < (int) bs.nbuttons; i++) 237*d93b4d32SBaptiste Daroussin if (tolower(input) == tolower(bs.label[i][0])) { 238c76f0793SBaptiste Daroussin output = bs.value[i]; 239c76f0793SBaptiste Daroussin loop = false; 240c76f0793SBaptiste Daroussin } 241c76f0793SBaptiste Daroussin } 242c76f0793SBaptiste Daroussin } 243c76f0793SBaptiste Daroussin 244c76f0793SBaptiste Daroussin end_widget_withtextpad(conf, widget, h, w, textpad, shadow); 245c76f0793SBaptiste Daroussin 246c76f0793SBaptiste Daroussin return output; 247c76f0793SBaptiste Daroussin } 248c76f0793SBaptiste Daroussin 249c76f0793SBaptiste Daroussin /* API */ 250c76f0793SBaptiste Daroussin int 251f499134dSBaptiste Daroussin bsddialog_msgbox(struct bsddialog_conf *conf, char* text, int rows, int cols) 252c76f0793SBaptiste Daroussin { 253c76f0793SBaptiste Daroussin struct buttons bs; 254c76f0793SBaptiste Daroussin 255c76f0793SBaptiste Daroussin get_buttons(conf, &bs, BUTTONLABEL(ok_label), BUTTONLABEL(extra_label), 256*d93b4d32SBaptiste Daroussin NULL, BUTTONLABEL(help_label)); 257c76f0793SBaptiste Daroussin 258*d93b4d32SBaptiste Daroussin return (do_dialog(conf, text, rows, cols, bs)); 259c76f0793SBaptiste Daroussin } 260c76f0793SBaptiste Daroussin 261c76f0793SBaptiste Daroussin int 262f499134dSBaptiste Daroussin bsddialog_yesno(struct bsddialog_conf *conf, char* text, int rows, int cols) 263c76f0793SBaptiste Daroussin { 264c76f0793SBaptiste Daroussin struct buttons bs; 265c76f0793SBaptiste Daroussin 2668c4f4028SBaptiste Daroussin get_buttons(conf, &bs, 2678c4f4028SBaptiste Daroussin conf->button.ok_label == NULL ? "Yes" : conf->button.ok_label, 2688c4f4028SBaptiste Daroussin BUTTONLABEL(extra_label), 2698c4f4028SBaptiste Daroussin conf->button.cancel_label == NULL ? "No" : conf->button.cancel_label, 2708c4f4028SBaptiste Daroussin BUTTONLABEL(help_label)); 271c76f0793SBaptiste Daroussin 272*d93b4d32SBaptiste Daroussin return (do_dialog(conf, text, rows, cols, bs)); 273c76f0793SBaptiste Daroussin } 274