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 #include <string.h> 32c76f0793SBaptiste Daroussin 33c76f0793SBaptiste Daroussin #ifdef PORTNCURSES 34*8c4f4028SBaptiste Daroussin #include <ncurses/ncurses.h> 35c76f0793SBaptiste Daroussin #else 36*8c4f4028SBaptiste Daroussin #include <ncurses.h> 37c76f0793SBaptiste Daroussin #endif 38c76f0793SBaptiste Daroussin 39c76f0793SBaptiste Daroussin #include "bsddialog.h" 40c76f0793SBaptiste Daroussin #include "lib_util.h" 41c76f0793SBaptiste Daroussin #include "bsddialog_theme.h" 42c76f0793SBaptiste Daroussin 43c76f0793SBaptiste Daroussin /* "Message": msgbox - yesno */ 44c76f0793SBaptiste Daroussin 45c76f0793SBaptiste Daroussin #define AUTO_WIDTH (COLS / 3U) 46c76f0793SBaptiste Daroussin /* 47c76f0793SBaptiste Daroussin * Min height = 5: 2 up & down borders + 2 label & up border buttons + 1 line 48c76f0793SBaptiste Daroussin * for text, at least 1 line is important for widget_withtextpad_init() to avoid 49c76f0793SBaptiste Daroussin * "Cannot build the pad window for text". 50c76f0793SBaptiste Daroussin */ 51c76f0793SBaptiste Daroussin #define MIN_HEIGHT 5 52c76f0793SBaptiste Daroussin 53c76f0793SBaptiste Daroussin extern struct bsddialog_theme t; 54c76f0793SBaptiste Daroussin 55c76f0793SBaptiste Daroussin static int 56f499134dSBaptiste Daroussin message_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w, 57c76f0793SBaptiste Daroussin char *text, struct buttons bs) 58c76f0793SBaptiste Daroussin { 59c76f0793SBaptiste Daroussin int maxword, maxline, nlines, line; 60c76f0793SBaptiste Daroussin 61c76f0793SBaptiste Daroussin if (get_text_properties(conf, text, &maxword, &maxline, &nlines) != 0) 62c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 63c76f0793SBaptiste Daroussin 64c76f0793SBaptiste Daroussin if (cols == BSDDIALOG_AUTOSIZE) { 65c76f0793SBaptiste Daroussin *w = VBORDERS; 66c76f0793SBaptiste Daroussin /* buttons size */ 67c76f0793SBaptiste Daroussin *w += bs.nbuttons * bs.sizebutton; 68f499134dSBaptiste Daroussin *w += bs.nbuttons > 0 ? (bs.nbuttons-1) * t.button.space : 0; 69c76f0793SBaptiste Daroussin /* text size */ 70f499134dSBaptiste Daroussin line = MIN(maxline + VBORDERS + t.text.hmargin * 2, AUTO_WIDTH); 71f499134dSBaptiste Daroussin line = MAX(line, (int) (maxword + VBORDERS + t.text.hmargin * 2)); 72c76f0793SBaptiste Daroussin *w = MAX(*w, line); 73*8c4f4028SBaptiste Daroussin /* conf.auto_minwidth */ 74*8c4f4028SBaptiste Daroussin *w = MAX(*w, (int)conf->auto_minwidth); 75c76f0793SBaptiste Daroussin /* avoid terminal overflow */ 76c76f0793SBaptiste Daroussin *w = MIN(*w, widget_max_width(conf)); 77c76f0793SBaptiste Daroussin } 78c76f0793SBaptiste Daroussin 79c76f0793SBaptiste Daroussin if (rows == BSDDIALOG_AUTOSIZE) { 80c76f0793SBaptiste Daroussin *h = MIN_HEIGHT - 1; 81c76f0793SBaptiste Daroussin if (maxword > 0) 82*8c4f4028SBaptiste Daroussin *h += MAX(nlines, (int)(*w / GET_ASPECT_RATIO(conf))); 83c76f0793SBaptiste Daroussin *h = MAX(*h, MIN_HEIGHT); 84*8c4f4028SBaptiste Daroussin /* conf.auto_minheight */ 85*8c4f4028SBaptiste Daroussin *h = MAX(*h, (int)conf->auto_minheight); 86c76f0793SBaptiste Daroussin /* avoid terminal overflow */ 87c76f0793SBaptiste Daroussin *h = MIN(*h, widget_max_height(conf)); 88c76f0793SBaptiste Daroussin } 89c76f0793SBaptiste Daroussin 90c76f0793SBaptiste Daroussin return 0; 91c76f0793SBaptiste Daroussin } 92c76f0793SBaptiste Daroussin 93c76f0793SBaptiste Daroussin static int message_checksize(int rows, int cols, struct buttons bs) 94c76f0793SBaptiste Daroussin { 95c76f0793SBaptiste Daroussin int mincols; 96c76f0793SBaptiste Daroussin 97c76f0793SBaptiste Daroussin mincols = VBORDERS; 98c76f0793SBaptiste Daroussin mincols += bs.nbuttons * bs.sizebutton; 99f499134dSBaptiste Daroussin mincols += bs.nbuttons > 0 ? (bs.nbuttons-1) * t.button.space : 0; 100c76f0793SBaptiste Daroussin 101c76f0793SBaptiste Daroussin if (cols < mincols) 102c76f0793SBaptiste Daroussin RETURN_ERROR("Few cols, Msgbox and Yesno need at least width "\ 103c76f0793SBaptiste Daroussin "for borders, buttons and spaces between buttons"); 104c76f0793SBaptiste Daroussin 105c76f0793SBaptiste Daroussin if (rows < MIN_HEIGHT) 106c76f0793SBaptiste Daroussin RETURN_ERROR("Msgbox and Yesno need at least height 5"); 107c76f0793SBaptiste Daroussin 108c76f0793SBaptiste Daroussin return 0; 109c76f0793SBaptiste Daroussin } 110c76f0793SBaptiste Daroussin 111c76f0793SBaptiste Daroussin static void 112c76f0793SBaptiste Daroussin buttonsupdate(WINDOW *widget, int h, int w, struct buttons bs, bool shortkey) 113c76f0793SBaptiste Daroussin { 114c76f0793SBaptiste Daroussin draw_buttons(widget, h-2, w, bs, shortkey); 115c76f0793SBaptiste Daroussin wnoutrefresh(widget); 116c76f0793SBaptiste Daroussin } 117c76f0793SBaptiste Daroussin 118c76f0793SBaptiste Daroussin static void 119c76f0793SBaptiste Daroussin textupdate(WINDOW *widget, int y, int x, int h, int w, WINDOW *textpad, 120c76f0793SBaptiste Daroussin int htextpad, int textrow) 121c76f0793SBaptiste Daroussin { 122c76f0793SBaptiste Daroussin 123c76f0793SBaptiste Daroussin if (htextpad > h - 4) { 124c76f0793SBaptiste Daroussin mvwprintw(widget, h-3, w-6, "%3d%%", 125c76f0793SBaptiste Daroussin 100 * (textrow+h-4)/ htextpad); 126c76f0793SBaptiste Daroussin wnoutrefresh(widget); 127c76f0793SBaptiste Daroussin } 128c76f0793SBaptiste Daroussin 129c76f0793SBaptiste Daroussin pnoutrefresh(textpad, textrow, 0, y+1, x+2, y+h-4, x+w-2); 130c76f0793SBaptiste Daroussin } 131c76f0793SBaptiste Daroussin 132c76f0793SBaptiste Daroussin static int 133f499134dSBaptiste Daroussin do_widget(struct bsddialog_conf *conf, char *text, int rows, int cols, 134c76f0793SBaptiste Daroussin struct buttons bs, bool shortkey) 135c76f0793SBaptiste Daroussin { 136c76f0793SBaptiste Daroussin WINDOW *widget, *textpad, *shadow; 137c76f0793SBaptiste Daroussin bool loop; 138c76f0793SBaptiste Daroussin int i, y, x, h, w, input, output, htextpad, textrow; 139c76f0793SBaptiste Daroussin 140c76f0793SBaptiste Daroussin if (set_widget_size(conf, rows, cols, &h, &w) != 0) 141c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 142c76f0793SBaptiste Daroussin if (message_autosize(conf, rows, cols, &h, &w, text, bs) != 0) 143c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 144c76f0793SBaptiste Daroussin if (message_checksize(h, w, bs) != 0) 145c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 146c76f0793SBaptiste Daroussin if (set_widget_position(conf, &y, &x, h, w) != 0) 147c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 148c76f0793SBaptiste Daroussin 149c76f0793SBaptiste Daroussin if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED, 150c76f0793SBaptiste Daroussin &textpad, &htextpad, text, true) != 0) 151c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 152c76f0793SBaptiste Daroussin 153c76f0793SBaptiste Daroussin textrow = 0; 154c76f0793SBaptiste Daroussin loop = true; 155c76f0793SBaptiste Daroussin buttonsupdate(widget, h, w, bs, shortkey); 156c76f0793SBaptiste Daroussin textupdate(widget, y, x, h, w, textpad, htextpad, textrow); 157c76f0793SBaptiste Daroussin while(loop) { 158c76f0793SBaptiste Daroussin doupdate(); 159c76f0793SBaptiste Daroussin input = getch(); 160c76f0793SBaptiste Daroussin switch (input) { 161c76f0793SBaptiste Daroussin case 10: /* Enter */ 162c76f0793SBaptiste Daroussin output = bs.value[bs.curr]; 163c76f0793SBaptiste Daroussin loop = false; 164c76f0793SBaptiste Daroussin break; 165c76f0793SBaptiste Daroussin case 27: /* Esc */ 166c76f0793SBaptiste Daroussin output = BSDDIALOG_ESC; 167c76f0793SBaptiste Daroussin loop = false; 168c76f0793SBaptiste Daroussin break; 169c76f0793SBaptiste Daroussin case '\t': /* TAB */ 170c76f0793SBaptiste Daroussin bs.curr = (bs.curr + 1) % bs.nbuttons; 171c76f0793SBaptiste Daroussin buttonsupdate(widget, h, w, bs, shortkey); 172c76f0793SBaptiste Daroussin break; 173c76f0793SBaptiste Daroussin case KEY_F(1): 174*8c4f4028SBaptiste Daroussin if (conf->f1_file == NULL && conf->f1_message == NULL) 175c76f0793SBaptiste Daroussin break; 176c76f0793SBaptiste Daroussin if (f1help(conf) != 0) 177c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 178c76f0793SBaptiste Daroussin /* No break! the terminal size can change */ 179c76f0793SBaptiste Daroussin case KEY_RESIZE: 180f499134dSBaptiste Daroussin hide_widget(y, x, h, w,conf->shadow); 181c76f0793SBaptiste Daroussin 182c76f0793SBaptiste Daroussin /* 183c76f0793SBaptiste Daroussin * Unnecessary, but, when the columns decrease the 184c76f0793SBaptiste Daroussin * following "refresh" seem not work 185c76f0793SBaptiste Daroussin */ 186c76f0793SBaptiste Daroussin refresh(); 187c76f0793SBaptiste Daroussin 188c76f0793SBaptiste Daroussin if (set_widget_size(conf, rows, cols, &h, &w) != 0) 189c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 190c76f0793SBaptiste Daroussin if (message_autosize(conf, rows, cols, &h, &w, text, bs) != 0) 191c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 192c76f0793SBaptiste Daroussin if (message_checksize(h, w, bs) != 0) 193c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 194c76f0793SBaptiste Daroussin if (set_widget_position(conf, &y, &x, h, w) != 0) 195c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 196c76f0793SBaptiste Daroussin 197c76f0793SBaptiste Daroussin wclear(shadow); 198f499134dSBaptiste Daroussin mvwin(shadow, y + t.shadow.h, x + t.shadow.w); 199c76f0793SBaptiste Daroussin wresize(shadow, h, w); 200c76f0793SBaptiste Daroussin 201c76f0793SBaptiste Daroussin wclear(widget); 202c76f0793SBaptiste Daroussin mvwin(widget, y, x); 203c76f0793SBaptiste Daroussin wresize(widget, h, w); 204c76f0793SBaptiste Daroussin 205c76f0793SBaptiste Daroussin htextpad = 1; 206c76f0793SBaptiste Daroussin wclear(textpad); 207f499134dSBaptiste Daroussin wresize(textpad, 1, w - HBORDERS - t.text.hmargin * 2); 208c76f0793SBaptiste Daroussin 209c76f0793SBaptiste Daroussin if(update_widget_withtextpad(conf, shadow, widget, h, w, 210c76f0793SBaptiste Daroussin RAISED, textpad, &htextpad, text, true) != 0) 211c76f0793SBaptiste Daroussin return BSDDIALOG_ERROR; 212c76f0793SBaptiste Daroussin 213c76f0793SBaptiste Daroussin buttonsupdate(widget, h, w, bs, shortkey); 214c76f0793SBaptiste Daroussin textupdate(widget, y, x, h, w, textpad, htextpad, textrow); 215c76f0793SBaptiste Daroussin 216c76f0793SBaptiste Daroussin /* Important to fix grey lines expanding screen */ 217c76f0793SBaptiste Daroussin refresh(); 218c76f0793SBaptiste Daroussin break; 219c76f0793SBaptiste Daroussin case KEY_UP: 220c76f0793SBaptiste Daroussin if (textrow == 0) 221c76f0793SBaptiste Daroussin break; 222c76f0793SBaptiste Daroussin textrow--; 223c76f0793SBaptiste Daroussin textupdate(widget, y, x, h, w, textpad, htextpad, textrow); 224c76f0793SBaptiste Daroussin break; 225c76f0793SBaptiste Daroussin case KEY_DOWN: 226c76f0793SBaptiste Daroussin if (textrow + h - 4 >= htextpad) 227c76f0793SBaptiste Daroussin break; 228c76f0793SBaptiste Daroussin textrow++; 229c76f0793SBaptiste Daroussin textupdate(widget, y, x, h, w, textpad, htextpad, textrow); 230c76f0793SBaptiste Daroussin break; 231c76f0793SBaptiste Daroussin case KEY_LEFT: 232c76f0793SBaptiste Daroussin if (bs.curr > 0) { 233c76f0793SBaptiste Daroussin bs.curr--; 234c76f0793SBaptiste Daroussin buttonsupdate(widget, h, w, bs, shortkey); 235c76f0793SBaptiste Daroussin } 236c76f0793SBaptiste Daroussin break; 237c76f0793SBaptiste Daroussin case KEY_RIGHT: 238c76f0793SBaptiste Daroussin if (bs.curr < (int) bs.nbuttons - 1) { 239c76f0793SBaptiste Daroussin bs.curr++; 240c76f0793SBaptiste Daroussin buttonsupdate(widget, h, w, bs, shortkey); 241c76f0793SBaptiste Daroussin } 242c76f0793SBaptiste Daroussin break; 243c76f0793SBaptiste Daroussin default: 244c76f0793SBaptiste Daroussin if (shortkey == false) 245c76f0793SBaptiste Daroussin break; 246c76f0793SBaptiste Daroussin 247c76f0793SBaptiste Daroussin for (i = 0; i < (int) bs.nbuttons; i++) 248c76f0793SBaptiste Daroussin if (tolower(input) == tolower((bs.label[i])[0])) { 249c76f0793SBaptiste Daroussin output = bs.value[i]; 250c76f0793SBaptiste Daroussin loop = false; 251c76f0793SBaptiste Daroussin } 252c76f0793SBaptiste Daroussin } 253c76f0793SBaptiste Daroussin } 254c76f0793SBaptiste Daroussin 255c76f0793SBaptiste Daroussin end_widget_withtextpad(conf, widget, h, w, textpad, shadow); 256c76f0793SBaptiste Daroussin 257c76f0793SBaptiste Daroussin return output; 258c76f0793SBaptiste Daroussin } 259c76f0793SBaptiste Daroussin 260c76f0793SBaptiste Daroussin /* API */ 261c76f0793SBaptiste Daroussin 262c76f0793SBaptiste Daroussin int 263f499134dSBaptiste Daroussin bsddialog_msgbox(struct bsddialog_conf *conf, char* text, int rows, int cols) 264c76f0793SBaptiste Daroussin { 265c76f0793SBaptiste Daroussin struct buttons bs; 266c76f0793SBaptiste Daroussin 267c76f0793SBaptiste Daroussin get_buttons(conf, &bs, BUTTONLABEL(ok_label), BUTTONLABEL(extra_label), 268c76f0793SBaptiste Daroussin NULL /* nocancel */, BUTTONLABEL(help_label)); 269c76f0793SBaptiste Daroussin 270c76f0793SBaptiste Daroussin return (do_widget(conf, text, rows, cols, bs, true)); 271c76f0793SBaptiste Daroussin } 272c76f0793SBaptiste Daroussin 273c76f0793SBaptiste Daroussin int 274f499134dSBaptiste Daroussin bsddialog_yesno(struct bsddialog_conf *conf, char* text, int rows, int cols) 275c76f0793SBaptiste Daroussin { 276c76f0793SBaptiste Daroussin struct buttons bs; 277c76f0793SBaptiste Daroussin 278*8c4f4028SBaptiste Daroussin get_buttons(conf, &bs, 279*8c4f4028SBaptiste Daroussin conf->button.ok_label == NULL ? "Yes" : conf->button.ok_label, 280*8c4f4028SBaptiste Daroussin BUTTONLABEL(extra_label), 281*8c4f4028SBaptiste Daroussin conf->button.cancel_label == NULL ? "No" : conf->button.cancel_label, 282*8c4f4028SBaptiste Daroussin BUTTONLABEL(help_label)); 283c76f0793SBaptiste Daroussin 284c76f0793SBaptiste Daroussin return (do_widget(conf, text, rows, cols, bs, true)); 285c76f0793SBaptiste Daroussin } 286