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