diff options
74 files changed, 10755 insertions, 5928 deletions
diff --git a/.gitignore b/.gitignore index ee80e5f5a073..c8fc68ed8a0e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,25 +1,26 @@ bsddialog +.depend* *.o +*.so* +*.a +*.gz +*.core *~ -examples_library/buildlist +BSDDIALOG.geany +BSDDIALOG.tags +examples_library/calendar examples_library/checklist examples_library/datebox examples_library/form +examples_library/gauge +examples_library/infobox examples_library/menu +examples_library/mixedgauge examples_library/mixedlist -examples_library/radiolist -examples_library/theme -examples_library/treeview -examples_library/infobox examples_library/msgbox examples_library/pause +examples_library/radiolist examples_library/rangebox -examples_library/sade +examples_library/theme examples_library/timebox examples_library/yesno -*.gz -lib/libbsddialog.so* -BSDDIALOG.geany -BSDDIALOG.tags -*.core -bsdinstall/* diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 000000000000..e6295768d906 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,354 @@ +2023-08-01 Version 1.0 + + Utility: + * add: comments to --save-theme output file. + * add: blink, halfbright, highlight to --save-theme and --load-theme. + * add: theme.menu.[f_]prefixcolor to --save-theme and --load-theme. + * add: --datebox-format <d/m/y|m/d/y|y/m/d> to set --datebox UI. + * add: --help-print-items (--help-status becomes alias). + * add: --text-escape (--colors becomes alias). + * add: new escapes \Zd, \Zk, \Zs, \ZD, \ZK, \ZS, for --text-escape. + * add: env NO_COLOR, to set blackwhite theme. + * add: $HOME/.bsddialog.conf startup theme file. + * add: env BSDDIALOG_THEMEFILE startup theme file. + * add: --left1-button <label>. + * add: --left2-button <label>. + * add: --left3-button <label>. + * add: --right1-button <label>. + * add: --right2-button <label>. + * add: --right3-button <label>. + * add: dynamic exit codes. + - add: --error-exit-code. + - add: --ok-exit-code. + - add: --cancel-exit-code. + - add: --help-exit-code. + - add: --extra-exit-code. + - add: --timeout-exit-code. + - add: --esc-exit-code. + - add: --left1-exit-code. + - add: --left2-exit-code. + - add: --left3-exit-code. + - add: --right1-exit-code. + - add: --right2-exit-code. + - add: --right3-exit-code. + - add: env BSDDIALOG_ERROR. + - add: env BSDDIALOG_OK. + - add: env BSDDIALOG_CANCEL. + - add: env BSDDIALOG_HELP. + - add: env BSDDIALOG_EXTRA. + - add: env BSDDIALOG_TIMEOUT. + - add: env BSDDIALOG_ESC. + - add: env BSDDIALOG_LEFT1. + - add: env BSDDIALOG_LEFT2. + - add: env BSDDIALOG_LEFT3. + - add: env BSDDIALOG_RIGHT1. + - add: env BSDDIALOG_RIGHT2. + - add: env BSDDIALOG_RIGHT3. + * add: undocumented envs for bsdconfig(8) compatibility. + - env BSDDIALOG_COMPATRC for use_shadow setting. + - env BSDDIALOG_ITEM_HELP to add/set exit code. + * change: rename themes --theme <3d|blackwhite|flat>. + * change: --no-names and --no-descriptions mutually exclusive (via lia). + * change: quote only checklist output items if necessary. Previously + also radiolist item. + * change: dialogs with user input print always values except with ERROR, + ESC, Cancel. Previously the situation was quite heterogeneous. + * improve: DIAGNOSTIC messages adding fmt string errors. + * improve: disable theme setting (opt and env) with no-color terminals. + * improve: menus on|off status (strcasecmp, diagnostic, real off check). + * improve: --bikeshed with button delimiter and --date-format. + * improve: --textbox accepts button options. + * improve: Forms with Help button. + - print "HELP" (like menus). + - accept --help-list-items. + - accept --help-print-name. + * fix: --load-theme attributes. + * fix: --clear-screen with --and-dialog. + * delete: --theme <bsddialog> (partially implemented). + * delete: --esc-return-cancel (replaced by new env and option). + * delete: --generic-button1 (replaced by --right1-button). + * delete: --generic-button2 (replaced by --right2-button). + * refactor: modularize in more files (main, cli, builders, theme). + + Library: + * add: bsddialog_inmode(). + * add: bsddialog_clear(y) for utility --clear-screen. + * add: bsddialog_refresh() for utility terminal mode options. + * add: conf.date.format="d/m/y"|"m/d/y"|"y/m/d" to customize + bsddialog_datebox() UI (boxes) with a date format. + * add: 'const char *end' to bsddialog_gauge(). + * add: draw focus on the shortcut-key-selected button at exit. + * add: escapes for conf.text.highlight \Zd, \Zk, \Zs, \ZD, \ZK, \ZS. + * add: other theme flags. + - BSDDIALOG_BLINK. + - BSDDIALOG_HALFBRIGHT. + - BSDDIALOG_HIGHLIGHT. + * add: generic buttons. + - conf.button.left1_label, BSDDIALOG_LEFT1 return value. + - conf.button.left2_label, BSDDIALOG_LEFT2 return value. + - conf.button.left3_label, BSDDIALOG_LEFT3 return value. + - conf.button.right1.label, BSDDIALOG_RIGHT1 return value. + - conf.button.right2.label, BSDDIALOG_RIGHT2 return value. + - conf.button.right3.label, BSDDIALOG_RIGHT3 return value. + * add: unused bsddialog_menugroup.min_on for future features. + * add: theme.menu.f_prefixcolor and theme.menu.prefixcolor. + * improve: check (when possible) API pointers. + * improve: circolar buttons with left and right keys for msgbox, yesno, + menus, rangebox and pause. + * improve: bsddialog_textbox() handles conf.buttons. + * improve: bsddialog_datebox() a box change affects the others as well. + * improve: bsddialog_geterror() with fmt strings. + * change: API NULL strings handled like "", except gauge *sep and *end. + * change: menus and form less restrictive with text, hide text with + little screens (same behavior as other dialogs). + * change: mixedgauge BSDDIALOG_MG_BLANK does not draw minibar but prints + minilabel. The change allows mixedgauge to add sections. To restore + the previous behavior setting minilabel to "". + * change: check/set bsddialog_gauge() perc max 100. + * change: check/set bsddialog_mixedgauge() mainperc max 100. + * change: conf.menu.no_name and conf.menu.no_desc mutually exclusive. + * change: bsddialog_pause() sec -> *sec to know remaining time at exit. + * change: add *focusitem to bsddialog_form() like menus. + * change: "pointer" values are always set except when BSDDIALOG_ERROR + occurs. Examples *yy/*mm/*ss, rangebox *value. + - delete conf.menu.on_without_ok. + - delete conf.form.value_without_ok. + * rename: conf.text.highlight -> conf.text.escape. + * rename: theme.menu.namesepcolor -> theme.menu.sepnamecolor. + * rename: theme.menu.descsepcolor -> theme.menu.sepdesccolor. + * fix: bsddialog_pause() elevation bar after resize. + * fix: bsddialog_textbox() key '0'. + * fix: timebox.c checksize (boxes width). + * fix: extend menurows after shrink and enlarge. + * fix: menu pad and form pad "re-expansion" after shrink and enlarge. + * fix: shadow top-left corner (trick wresize() before wmove()). + * fix: increment bsddialog_total_progview size for more general use. + * delete: BSDDIALOG_THEME_BSDDIALOG (partially implemented). + * delete: conf.button.generic1_label (for new conf.button.right1_label). + * delete: BSDDIALOG_GENERIC1 return value (new BSDDIALOG_RIGHT1). + * delete: conf.button.generic2_label (for new conf.button.right2_label). + * delete: BSDDIALOG_GENERIC2 return value (new BSDDIALOG_RIGHT2). + * delete: bsddialog_clearterminal(), replaced by bsddialog_clear(y). + * refactor: internal implementation. + - add: internal structures to represent components. + - merge: (when possible) dialogs autosize. + - merge: (when possible) dialogs checksize. + - merge: for each dialog "build" with "update" -> <dialog>_redraw(). + - merge: new_dialog() with update_dialog() -> draw_dialog(). + - merge: infobox.c with messagebox.c (delete infobox.c) + - merge: bsddialog_datebox() + bsddialog_calendar() -> datebox.c + - change: flat and blackwhite real themes, 3d adapted from flat. + - improve: replace wrefresh() -> wnoutrefresh()/doupdate(). + - improve: replace prefresh() -> pnoutrefresh()/doupdate(). + - improve: menu split code to build private items. + - improve: form split code to build private items. + - delete: -Wno-implicit-fallthrough. + + +2023-06-12 Version 0.4.2 + + Library: + * fix: compile error with aarch64-gcc12 for "\Z[0-7]" check; + https://gitlab.com/alfix/bsddialog/-/issues/5. + * fix: BSDDIALOG_FIELDCURSOREND with multiple items + (warning aarch64-gcc12). + + +2023-01-02 Version 0.4.1 + + Utility: + * fix: default space separator menus output, except if --separator " ". + * rename: GNUMakefile to GNUmakefile to simplify linux build. + Thanks to https://gitlab.com/alfix/bsddialog/-/merge_requests/2. + + Library: + * fix: t.dialog.linelowercolor no bold-black, some terminal draws grey. + * fix: text wrapping (actual string length) with --colors. + * rename: GNUMakefile to GNUmakefile to simplify linux build. + Thanks to https://gitlab.com/alfix/bsddialog/-/merge_requests/2. + + +2022-09-24 Version 0.4 + + Utility: + * add: --normal-screen to set normal mode. + * add: --alternate-screen to set alternate mode. + * add: --keep-tite as --alternate-screen alias. + * add: --and-dialog to build other dialogs. + * add: --and-widget as --and-dialog alias. + * add: --no-names (--no-tags becomes alias). + * add: --no-descriptions (--no-items becomes alias). + * add: --help-print-name (--help-tags becomes alias). + * add: --item-bottom-desc (--item-help becomes alias). + * add: --cr-wrap (was partially implemented) to keep '\n' with "\n". + * add: --text-unchanged to avoid default modification. + * add: --tab-escape to enable "\t" in text. + * add: --clear-screen to clear the screen. + * add: --clear-dialog to clear the dialog (was --clear). + * add: --calendar dialog to select a date. + * add: DIAGNOSTICS messages for bad arguments number. + * add: DIAGNOSTICS messages for missing and unexpected options. + * change: --clear becomes alias for --clear-screen. + * change: --print-maxsize format output. + * change: --menu, --radiolist, --checklist and --treeview output. + - no printed items with Cancel or ESC. + - --separator prints <sepstr> before each item except HELP. + - --separator and --separate-output print <sepstr> after each item. + - quoted item name/desc only when needed. + - --menu avoids to print selected item after focused HELP item. + * change: text default modification. + - without a "\n": '\t' -> space, '\n' -> '\n', trim spaces. + - with a "\n": '\t' -> space, '\n' -> space, "\n" -> '\n', no trim. + - delete '\n' after "\n" (also with --cr-wrap). + * change: --datebox input and output format yy/mm/dd -> dd/mm/yy. + * delete: --no-collapse (partially implemented). + * delete: --no-nl-expand (partially implemented). + * delete: --trim (partially implemented). + + Library: + * add: bsddialog_msgbox() HOME, END, PPAGE and NPAGE keys. + * add: bsddialog_yesno() HOME, END, PPAGE and NPAGE keys. + * add: bsddialog_menu() SPACE key (equivalent to ENTER). + * add: bsddialog_calendar() to select a date. + * change: rename enum bsddialog_grouptype -> enum bsddialog_menutype. + * change: fixed-menurows becomes at most menurows (depending on text). + * change: fixed-rows becomes at most rows, min(rows, screenH - shadow). + * change: fixed-cols becomes at most cols, min(cols, screenW - shadow). + * delete: undocumented internal bsddialog_menuitem.depth factor (was 2). + + +2022-08-29 Version 0.3 + + Utility: + * add: --textbox accepts options for the first button. + * add: --columns-per-row for text autosizing. + * add: --load-theme to read and set a custom theme at runtime. + * add: --save-theme to save current theme. + * add: --bikeshed for random settings. + * add: --switch-buttons to enable buttons/input widgets focus switching. + Available for: --form, --inputbox, --mixedform, --passwordform, + --passwordbox, --timebox and --datebox. + * change: rename --esc-cancelvalue to --esc-return-cancel. + * change: form field value is printed like multibyte charachter string, + previously widechar string. + * change: --timebox output with zero padding. + * change: --datebox output mm and dd with zero padding. + * fix: --hline with empty string. + * fix: avoid to overlay the backtitle by setting a top margin. + * fix: avoid to overlay down shadow with menus and forms bottomdesc + by setting a down margin. + * fix: --form read-only flag with multiple fields. + + Library: + * add: conf.auto_topmargin and conf.auto_downmargin. + * add: bsddialog_textbox() accepts conf.button.* for the first button. + * add: bsddialog_textbox() arrows and percentage. + * add: conf.text.cols_per_row to set a ratio for text autosizing. + * add: timebox and datebox arrows and focus background for boxes. + * add: timebox and datebox UP key to switch focus. + * add: bsddialog_init_notheme() in bsddialog.h. + * add: bsddialog_hascolors() in bsddialog_theme.h. + * add: theme.form.bottomdesccolor and theme.menu.bottomdesccolor. + * add: conf.button.always_active to disable buttons/input-boxes switch. + * add: dynamic buttons margin. + - add: theme.button.minmargin and theme.button.maxmargin. + - delete: theme.button.hmargin. + * add: Unicode. + - UI handles multicolumn charachters: backtitle, title, + text (word wrapping, autosizing), menus (shortcuts, name, desc), + forms (label, field), textbox, mixedgauge (minilabel), + buttons (label, shortcuts), bottomtitle. + - API handles char* arguments like multibyte charachter string, + depending on the current locale. + - Internally wide charachters are used to get input from keyboard + and to adapt word wrapping and dynamic text autosizing to + muticolumn charachters. + * refactoring: (rewrite) form.c. + - delete: libformw dep implementing its features from scratch. + - delete: maxvaluelen >= valuelen constraint. + - delete: conf.form.enable_wchar, get always unicode (wchar) input. + - add: KEY_HOME, KEY_END, KEY_PPAGE, KEY_NPAGE keys in field. + - add: KEY_UP can move focus from buttons to fields. + - add: KEY_DOWN can move focus from item to buttons, if nitem is 1. + - add: conf.form.securembch secure multibyte charachter. + - add: BSDDIALOG_FIELDNOCOLOR for formitem.flags. + - add: BSDDIALOG_FIELDCURSOREND for formitem.flags. + - add: BSDDIALOG_FIELDEXTEND for formitem.flags. + - add: BSDDIALOG_FIELDSINGLEBYTE for formitem.flags. + - add: resizing and refresh after KEY_RESIZE (SIGWINCH). + - add: items scrolling. + - add: conf.form.value_wchar, value is wchar_t* instead of MB-char*. + - add: formheight autosizing. + - add: dynamic item position. + * fix: bsddialog_gauge() with fd < 0. + * fix: bsddialog_gauge() refresh new text. + * fix: internal segmentation fault with disabled shadow. + * fix: center position without shadow. + * fix: bsddialog_infobox() with zero text length. + * fix: text wrapping with more than 1024 words. + * fix: rename theme.shadow.h to theme.shadow.y. + * fix: rename theme.shadow.w to theme.shadow.x. + * fix: menurows autosize with fixed rows improving text_size(). + * fix: messagebox.c scrolling and checksize without text. + + +2022-03-02 Version 0.2 + + Utility: + * add: (this) CHANGELOG. + * add: "menus" print item with focus (except with OK and ERROR). + * add: pause.sh example. + * add: timebox.sh example. + * change: --theme name "default" -> "flat". + * delete: treeview.sh example. + * fix: --separate-output does not quote (except with --quoted). + * fix: --datebox and --date-format month in output. + * improve: examples handle exit status. + + Library: + * add: conf.form.enable_wchar for wide characters in bsddialog_form(). + * add: theme.menu.f_selectorcolor. + * add: formw.c example. + * change: move conf.f1_file and conf.f1_message in conf.key. + * change: theme.button.[left|right]ch -> theme.button.[left|right]delim. + * change: theme.button.space -> theme.button.hmargin. + * change: theme.menu.arrowcolor -> theme.dialog.arrowcolor. + * change: internal bsddialog_menuitem.depth factor 4 -> 2. + * fix: disable HOME, PPAGE, END and NPAGE keys in bsddialog_form(). + * fix: visible cursor for timebox.c and form.c in VM VirtualBox. + * fix: mixedlist, center position of separator with big pad. + * fix: timebox and datebox set values only with BSDDIALOG_OK. + * fix: menurows autosize with fullscreen. + * fix: bar color with 0%. + * fix: bar label position. + * improve: timebox and datebox navigation (keys, buttons and shortcuts). + * improve: "menus" colors for accessibility. + + +2022-01-27 Version 0.1 + + * Options: --ascii-lines, --backtitle <backtitle>, --begin-x <x>, + --begin-y <y>, --cancel-label <label>, --clear, --colors, --cr-wrap, + --date-format <format>, --defaultno, --default-button <label>, + --default-no, --default-item <name>, --disable-esc, + --esc-cancelvalue, --exit-label <label>, --extra-button, + --extra-label <label>, --generic-button1 <label>, + --generic-button2 <label>, --help, --help-button, + --help-label <label>, --help-status, --help-tags, + --hfile <filename>, --hline <string>, --hmsg <string>, --ignore, + --insecure, --item-depth, --item-help, --items-prefix, + --max-input <size>, --no-cancel, --nocancel, --no-collapse, + --no-items, --no-label <label>, --no-lines, --no-nl-expand, + --no-ok, --nook, --no-shadow, --no-tags, --ok-label <label>, + --output-fd <fd>, --output-separator <sep>, --print-maxsize, + --print-size, --print-version, --quoted, --separate-output, + --separator <sep>, --shadow, --single-quoted, --sleep <secs>, + --stderr, --stdout, --tab-len <spaces>, + --theme <blackwhite|bsddialog|default|dialog>, + --time-format <format>, --title <title>, --trim, --version, + --yes-label <label>. + * Dialogs: --checklist, --datebox, --form, --gauge, --infobox, + --inputbox, --menu, --mixedform, --mixedgauge, --msgbox, + --passwordbox, --passwordform, --pause, --radiolist, --rangebox, + --textbox, --timebox, --treeview, --yesno. + * Manuals: bsddialog.1, bsddialog.3. diff --git a/GNUMakefile b/GNUMakefile deleted file mode 100644 index 7480ae33ec21..000000000000 --- a/GNUMakefile +++ /dev/null @@ -1,28 +0,0 @@ -# PUBLIC DOMAIN - NO WARRANTY, see: -# <http://creativecommons.org/publicdomain/zero/1.0/> -# -# Written by Alfonso Sabato Siciliano - -OUTPUT= bsddialog -SOURCES= bsddialog.c -OBJECTS= $(SOURCES:.c=.o) -LIBPATH= ./lib -LIBBSDDIALOG= $(LIBPATH)/libbsddialog.so -CFLAGS= -g -Wall -I$(LIBPATH) -LDFLAGS= -Wl,-rpath=$(LIBPATH) -L$(LIBPATH) -lbsddialog -RM = rm -f - -all : $(OUTPUT) - -$(OUTPUT): $(LIBBSDDIALOG) $(OBJECTS) - $(CC) $^ -o $@ $(LDFLAGS) - -${LIBBSDDIALOG}: - make -C ${LIBPATH} -f GNUMakefile - -%.o: %.c $(LIBBSDDIALOG) - $(CC) $(CFLAGS) -c $< - -clean: - make -C ${LIBPATH} -f GNUMakefile clean - $(RM) $(OUTPUT) *.o *~ @@ -1,6 +1,6 @@ BSD 2-Clause License -Copyright (c) 2021, Alfonso Sabato Siciliano +Copyright (c) 2021-2023, Alfonso Sabato Siciliano Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -1,49 +1,38 @@ -# Any copyright is dedicated to the Public Domain, see: +# PUBLIC DOMAIN - NO WARRANTY, see: # <http://creativecommons.org/publicdomain/zero/1.0/> # -# Written by Alfonso Sabato Siciliano +# Written in 2023 by Alfonso Sabato Siciliano -OUTPUT= bsddialog -SOURCES= bsddialog.c -OBJECTS= ${SOURCES:.c=.o} -LIBPATH= ${.CURDIR}/lib -LIBBSDDIALOG= ${LIBPATH}/libbsddialog.so +OUTPUT = bsddialog +export VERSION=1.0 +.CURDIR ?= ${CURDIR} +LIBPATH = ${.CURDIR}/lib +LIBBSDDIALOG = ${LIBPATH}/libbsddialog.so +UTILITYPATH = ${.CURDIR}/utility -CFLAGS= -Wall -I${LIBPATH} -LDFLAGS= -Wl,-rpath=${LIBPATH} -L${LIBPATH} -lbsddialog - -BINDIR= /usr/local/bin -MAN= ${OUTPUT}.1 -GZIP= gzip -cn -MANDIR= /usr/local/share/man/man1 - -INSTALL= install RM= rm -f +LN = ln -s -f + +### cli options ### +# port/pkg Makefile: 'MAKE_ARGS = -DNORPATH' +NORPATH ?= +export DISABLERPATH=${NORPATH} +# `make -DDEBUG` +# `gmake DEBUG=1` +DEBUG ?= +export ENABLEDEBUG=${DEBUG} all : ${OUTPUT} -${OUTPUT}: ${LIBBSDDIALOG} ${OBJECTS} - ${CC} ${LDFLAGS} ${OBJECTS} -o ${.PREFIX} +${OUTPUT}: ${LIBBSDDIALOG} + ${MAKE} -C ${UTILITYPATH} LIBPATH=${LIBPATH} + ${LN} ${UTILITYPATH}/${OUTPUT} ${.CURDIR}/${OUTPUT} ${LIBBSDDIALOG}: -.if defined(PORTNCURSES) - make -C ${LIBPATH} -DPORTNCURSES -.else - make -C ${LIBPATH} -.endif - -.c.o: - ${CC} ${CFLAGS} -c ${.IMPSRC} -o ${.TARGET} - -install: - ${INSTALL} -s -m 555 ${OUTPUT} ${BINDIR} - ${GZIP} ${MAN} > ${MAN}.gz - ${INSTALL} -m 444 ${MAN}.gz ${MANDIR} - -unistall: - ${RM} ${BINDIR}/${OUTPUT} - ${RM} ${MANDIR}/${MAN}.gz + ${MAKE} -C ${LIBPATH} clean: - make -C ${LIBPATH} clean - ${RM} ${OUTPUT} *.o *~ *.core ${MAN}.gz + ${MAKE} -C ${LIBPATH} clean + ${MAKE} -C ${UTILITYPATH} clean + ${RM} ${OUTPUT} *.core + diff --git a/README.md b/README.md index 31579f519ab6..f4846ec07745 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,17 @@ -# BSDDialog +# BSDDialog 1.0 -**Work In Progress!** +This project provides **bsddialog** and **libbsddialog**, an utility +and a library to build scripts and tools with TUI dialogs and widgets. -This project provides **bsddialog** and **libbsddialog**, an utility and a -library to build scripts and tools with *TUI Widgets*. -Description: -<https://www.freebsd.org/status/report-2021-04-2021-06/#_bsddialog_tui_widgets> +## Demo -Screenshots: -<https://www.flickr.com/photos/alfonsosiciliano/albums/72157720215006074> +[Screenshots](https://www.flickr.com/photos/alfonsosiciliano/albums/72157720215006074). ## Getting Started -FreeBSD: +FreeBSD and Linux: ``` % git clone https://gitlab.com/alfix/bsddialog.git @@ -23,36 +20,42 @@ FreeBSD: % ./bsddialog --msgbox "Hello World!" 8 20 ``` -If you are using XFCE install -[devel/ncurses](https://www.freshports.org/devel/ncurses/) - -``` -% sudo pkg install ncurses -% git clone https://gitlab.com/alfix/bsddialog.git -% cd bsddialog -% make -DPORTNCURSES -% ./bsddialog --msgbox "Hello World!" 8 20 -``` - Output: ![screenshot](screenshot.png) -Examples utility: +## Utility + +**Dialogs:** + +--calendar, --checklist, --datebox, --form, --gauge, --infobox, --inputbox, +--menu, --mixedform, --mixedgauge, --msgbox, --passwordbox, --passwordform, +--pause, --radiolist, --rangebox, --textbox, --timebox, --treeview, --yesno. + +**Manual** + + - [bsddialog(1)](https://alfonsosiciliano.gitlab.io/posts/2022-01-26-manual-bsddialog.html) + + +**Examples**: + ``` -% ./bsddialog --title msgbox --msgbox "Hello World!" 5 30 -% ./bsddialog --theme default --title msgbox --msgbox "Hello World!" 5 30 -% ./bsddialog --begin-y 2 --title yesno --yesno "Hello World!" 5 30 -% ./bsddialog --ascii-lines --pause "Hello World!" 8 50 5 -% ./bsddialog --checklist "Space to select" 0 0 0 Name1 Desc1 off Name2 Desc2 on Name3 Desc3 off -% ./bsddialog --backtitle "TITLE" --title yesno --hline "bsddialog" --yesno "Hello World!" 5 25 -% ./bsddialog --extra-button --help-button --defaultno --yesno "Hello World!" 0 0 +% ./bsddialog --backtitle "TITLE" --title msgbox --msgbox "Hello World!" 5 30 +% ./bsddialog --theme blackwhite --title msgbox --msgbox "Hello World!" 5 30 +% ./bsddialog --begin-y 2 --default-no --title yesno --yesno "Hello World!" 5 30 +% ./bsddialog --ascii-lines --pause "Hello World!" 8 50 10 +% ./bsddialog --checklist "Space to select" 0 0 0 Name1 Desc1 off Name2 Desc2 on +% ./bsddialog --title yesno --hline "bsddialog" --yesno "Hello World!" 5 25 +% ./bsddialog --extra-button --help-button --yesno "Hello World!" 0 0 ``` -and + +and [Examples](https://gitlab.com/alfix/bsddialog/-/tree/main/examples_utility) +in the _Public Domain_ to build new projects: ``` -% sh ./examples_utility/buildlist.sh +% sh ./examples_utility/calendar.sh % sh ./examples_utility/checklist.sh +% sh ./examples_utility/datebox.sh % sh ./examples_utility/form.sh % sh ./examples_utility/gauge.sh % sh ./examples_utility/infobox.sh @@ -63,105 +66,66 @@ and % sh ./examples_utility/msgbox.sh % sh ./examples_utility/passwordbox.sh % sh ./examples_utility/passwordform.sh +% sh ./examples_utility/pause.sh % sh ./examples_utility/radiolist.sh -% sh ./examples_utility/treeview.sh +% sh ./examples_utility/rangebox.sh +% sh ./examples_utility/timebox.sh % sh ./examples_utility/yesno.sh ``` -Examples library: +## Library + +**API** + + - [bsddialog.h](https://gitlab.com/alfix/bsddialog/-/blob/main/lib/bsddialog.h) + - [bsddialog\_theme.h](https://gitlab.com/alfix/bsddialog/-/blob/main/lib/bsddialog_theme.h) + + +**Manual** + + - [bsddialog(3)](https://alfonsosiciliano.gitlab.io/posts/2022-01-15-manual-libbsddialog.html) + + +**Examples**: + +[Examples](https://gitlab.com/alfix/bsddialog/-/tree/main/examples_library) +in the _Public Domain_ to build new projects: ``` % cd examples_library % sh compile -% ./buildlist -% ./compile +% ./calendar +% ./checklist % ./datebox % ./form +% ./gauge % ./infobox % ./menu +% ./mixedgauge % ./mixedlist % ./msgbox % ./pause % ./radiolist % ./rangebox -% ./sade % ./theme % ./timebox -% ./treeview % ./yesno ``` -Use Cases: - - - [portconfig](https://gitlab.com/alfix/portconfig) - - -## Features - -**Common Options:** - ---ascii-lines, --aspect *ratio* (for infobox, msgbox and yesno), ---backtitle *backtitle*, --begin-x *x* (--begin *y y*), -(--begin *y x*), --cancel-label *string*, -clear (test with multiple widgets), ---colors, --date-format *format*, --default-button *string*, --defaultno, ---default-item *string*, ---exit-label *string*, --extra-button, --extra-label *string*, ---hfile *filename* (for completed widgets), --help, --help-button, ---help-label *string*, --help-status, --help-tags, --hline *string*, --ignore, ---insecure, --item-help, --max-input *size*, --no-cancel, --nocancel, ---no-label *string*, --no-items, --no-lines, --no-ok, ---nook, --no-shadow, --no-tags, --ok-label *string*, --output-fd *fd*, ---output-separator *string*, --print-version, ---print-size (todo move lib -> utility), --quoted (quotes all != dialog), ---print-maxsize, --shadow, --single-quoted (add --quote-with *ch*?), ---separator *string* (alias --output-separator *string*), ---separate-output (rename --separate-output-withnl?), --sleep *secs*, --stderr, ---stdout, --theme *string* ("bsddialog", "dialog", "blackwhite"), ---time-format *format*, --title *title*, --version, --yes-label *string*. - -**Widgets:** - - infobox (do not clear the screen), msgbox, - yesno (dialog renames "yes/no" -> "ok/cancel" with --extra-button --help-button). - checklist, radiolist, menu, mixedlist, treeview, textbox, mixedgauge, datebox, - timebox, gauge, rangebox, pause. - - - Without resize: - - form, inputbox, mixedform, passwordbox, passwordform. - - - Without autosize, resize, F1: - - buildlist - - - -## TODO - - -**Common Options:** - -| Option | Status | Note | -| ---------------------------- | ----------- | ------------------------------- | -| --cr-wrap | In progress | text | -| --no-collapse | In progress | text | -| --no-nl-expand | In progress | text | -| --trim | In progress | text | - - -To evaluate / Not planned in the short term / not in bsdinstall: - ---create-rc *file*, --iso-week, --no-mouse, --print-text-only *str h w*, ---print-text-size *str h w*, --reorder, -scrollbar, --separate-widget *string*, ---size-err, --timeout *secs*,--trace *filename*, --visit-items, ---week-start *day*, --keep-tite, --keep-window, --last-key, --no-kill, ---column-separator *string*, --input-fd *fd*, --tab-correct, --tab-len *n* - - -**Widgets:** - -To evaluate / Not planned in the short term: -tailbox (textbox/fseek), tailboxbg, dselect, fselect, inputmenu, editbox, -calendar (use datebox), prgbox, programbox, progressbox. +## TODO and Ideas + + - menubar feature + - key callback + - Right-To-Left text + - some terminal does not hide the cursor, move it bottom-right before to getch. + - refactor backtitle: multiline, conf.backtitle, WINDOW \*dialog.backtitle. + - refactor bottomdesc: WINDOW \*dialog.bottomdesc -> fix expandig screen. + - accessibility https://wiki.freebsd.org/Accessibility/Wishlist/Base + - add bool conf.menu.depthlines. + - implement custom getopt\_long(). + - refactor/redesign gauge(). + - improve grey lines expanding terminal (maybe redrawwin() in hide\_dialog()). + - more restrictive strtol() and strtoul(). + - implement global buttons handler. + - add/move external tutorial. + - implement menutype.min_on. diff --git a/bsddialog.1 b/bsddialog.1 deleted file mode 100644 index 2846b77547a9..000000000000 --- a/bsddialog.1 +++ /dev/null @@ -1,52 +0,0 @@ -.\" -.\" Copyright (c) 2021 Alfonso Sabato Siciliano -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.Dd December 11, 2021 -.Dt BSDDIALOG 1 -.Os -.Sh NAME -.Nm bsddialog -.Nd build dialogs and widgets with a TUI -.Sh SYNOPSIS -.Nm bsddialog -.Op Fl options -.Ar -.Sh DESCRIPTION -The -.Nm -utility processes files ... -.\" .Sh ENVIRONMENT -.\" For sections 1, 6, 7, and 8 only. -.\" .Sh FILES -.\" .Sh EXIT STATUS -.\" For sections 1, 6, and 8 only. -.\" .Sh EXAMPLES -.\" .Sh DIAGNOSTICS -.\" For sections 1, 4, 6, 7, 8, and 9 printf/stderr messages only. -.\" .Sh SEE ALSO -.\" .Xr foobar 1 -.\" .Sh HISTORY -.\" .Sh AUTHORS -.\" .Sh CAVEATS -.\" .Sh BUGS diff --git a/bsddialog.c b/bsddialog.c deleted file mode 100644 index 6fc0d834a1ad..000000000000 --- a/bsddialog.c +++ /dev/null @@ -1,1349 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2021 Alfonso Sabato Siciliano - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/ioctl.h> - -#include <getopt.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <time.h> -#include <unistd.h> - -#include <bsddialog.h> -#include <bsddialog_theme.h> - -#define BSDDIALOG_VERSION "0.0.1" - -enum OPTS { - /* Common options */ - ASCII_LINES = '?' + 1, - ASPECT_RATIO, - BACKTITLE, - BEGIN_X, - BEGIN_Y, - CANCEL_LABEL, - CLEAR, - COLORS, - CR_WRAP, - DATE_FORMAT, - DEFAULTNO, - DEFAULT_BUTTON, - DEFAULT_ITEM, - EXIT_LABEL, - EXTRA_BUTTON, - EXTRA_LABEL, - HELP, - HELP_BUTTON, - HELP_LABEL, - HELP_STATUS, - HELP_TAGS, - HFILE, - HLINE, - IGNORE, - INSECURE, - ITEM_HELP, - ITEM_PREFIX, - MAX_INPUT, - NO_CANCEL, - NOCANCEL, - NO_COLLAPSE, - NO_ITEMS, - NO_LABEL, - NO_LINES, - NO_NL_EXPAND, - NO_OK, - NOOK, - NO_TAGS, - NO_SHADOW, - OK_LABEL, - OUTPUT_FD, - OUTPUT_SEPARATOR, - PRINT_MAXSIZE, - PRINT_SIZE, - PRINT_VERSION, - QUOTED, - SEPARATE_OUTPUT, - SEPARATOR, - SHADOW, - SINGLE_QUOTED, - SLEEP, - STDERR, - STDOUT, - THEME, - TIME_FORMAT, - TITLE, - TRIM, - VERSION, - YES_LABEL, - /* Widgets */ - BUILDLIST, - CHECKLIST, - DATEBOX, - FORM, - GAUGE, - INFOBOX, - INPUTBOX, - MENU, - MIXEDFORM, - MIXEDGAUGE, - MSGBOX, - PASSWORDBOX, - PASSWORDFORM, - PAUSE, - RADIOLIST, - RANGEBOX, - TEXTBOX, - TIMEBOX, - TREEVIEW, - YESNO, -}; - -/* libbsddialog does not support NULL string for now */ -static char *nostring = ""; -/* Menus flags and options */ -static bool item_prefix_flag, item_bottomdesc_flag, item_output_sepnl_flag; -static bool item_singlequote_flag, list_items_on_flag, item_tag_help_flag; -static bool item_always_quote_flag; -static char *item_output_sep_flag; -/* Time and calendar options */ -static char *date_fmt_flag, *time_fmt_flag; -/* Forms */ -static int max_input_form_flag; -/* General flags and options */ -static int output_fd_flag; - -void usage(void); -/* widgets */ -#define BUILDER_ARGS struct bsddialog_conf conf, char* text, int rows, \ - int cols, int argc, char **argv, char *errbuf -int buildlist_builder(BUILDER_ARGS); -int checklist_builder(BUILDER_ARGS); -int datebox_builder(BUILDER_ARGS); -int form_builder(BUILDER_ARGS); -int gauge_builder(BUILDER_ARGS); -int infobox_builder(BUILDER_ARGS); -int inputbox_builder(BUILDER_ARGS); -int menu_builder(BUILDER_ARGS); -int mixedform_builder(BUILDER_ARGS); -int mixedgauge_builder(BUILDER_ARGS); -int msgbox_builder(BUILDER_ARGS); -int passwordbox_builder(BUILDER_ARGS); -int passwordform_builder(BUILDER_ARGS); -int pause_builder(BUILDER_ARGS); -int radiolist_builder(BUILDER_ARGS); -int rangebox_builder(BUILDER_ARGS); -int textbox_builder(BUILDER_ARGS); -int timebox_builder(BUILDER_ARGS); -int treeview_builder(BUILDER_ARGS); -int yesno_builder(BUILDER_ARGS); - -static void -custom_text(bool cr_wrap, bool no_collapse, bool no_nl_expand, bool trim, - char *text, char *buf) -{ - int i, j; - - i = j = 0; - while (text[i] != '\0') { - switch (text[i]) { - case '\\': - buf[j] = '\\'; - switch (text[i+1]) { - case '\\': - i++; - break; - case 'n': - if (no_nl_expand) { - j++; - buf[j] = 'n'; - } else - buf[j] = '\n'; - i++; - break; - case 't': - if (no_collapse) { - j++; - buf[j] = 't'; - } else - buf[j] = '\t'; - i++; - break; - } - break; - case '\n': - buf[j] = cr_wrap ? ' ' : '\n'; - break; - case '\t': - buf[j] = no_collapse ? '\t' : ' '; - break; - default: - buf[j] = text[i]; - } - i++; - j += (buf[j] == ' ' && trim && j > 0 && buf[j-1] == ' ') ? - 0 : 1; - } - buf[j] = '\0'; -} - -void usage(void) -{ - - printf("usage: bsddialog --help\n" - " bsddialog --version\n" - " bsddialog [--<common-opts>] --<widget> <text> " - "<height> <width> [--<widget-opts>]\n"); - printf("\n"); - printf("Common Options:\n"); - printf("--ascii-lines, --aspect <ratio>, --backtitle <backtitle>, " - "--begin-x <x>, --begin-y <y>, --cancel-label <string>, " - "--clear, --colors, --date-format <format>, " - "--default-button <label>, --defaultno, --default-item <name>," - "--exit-label <label>, --extra-button, --extra-label <label>," - "--hfile <filename>, --help-button, --help-label <label>, " - "--help-status, --help-tags, --hline string, --ignore, " - "--insecure, --item-help, --items-prefix, --max-input <size>, " - "--no-cancel, --nocancel, --no-label <label>, --no-items, " - "--no-lines, --no-ok, --nook, --no-shadow, --no-tags, " - "--ok-label <label>, --output-fd <fd>, " - "--output-separator <sep>, --print-version, --print-size, " - "--quoted, --print-maxsize, --shadow, --single-quoted, " - "--separator <sep>, --separate-output, --sleep <secs>, " - "--stderr, --stdout, --theme <blackwhite|bsddialog|dialog>, " - "--time-format <format>, --title <title>, " - "--yes-label <string>.\n"); - printf("\n"); - printf("Widgets:\n"); - printf("--buildlist <text> <rows> <cols> <menurows> [<name> <desc> " - "<on|off> ...]\n" - "--checklist <text> <rows> <cols> <menurows> [<name> <desc> " - "<on|off> ...]\n" - "--datebox <text> <rows> <cols> [<yy> <mm> <dd>]\n" - "--form <text> <rows> <cols> <formrows> [<label> <ylabel> " - "<xlabel> <init> <yfield> <xfield> <fieldlen> <maxvalue> " - "...]\n" - "--gauge <text> <rows> <cols> [<perc\\n> [<text> ...] XXX " - "...] EOF\n" - "--infobox <text> <rows> <cols>\n" - "--inputbox <text> <rows> <cols> [init]\n" - "--menu <text> <rows> <cols> <menurows> [<name> <desc> ...]\n" - "--mixedform <text> <rows> <cols> <formrows> [<label> <ylabel> " - "<xlabe> <init> <yfield> <xfield> <fieldlen> <maxvalue> " - "<0|1|2> ...]\n" - "--mixedgauge <text> <rows> <cols> <mainperc> [<label> " - "<01234567|-perc> ...]\n" - "--msgbox <text> <rows> <cols>\n" - "--passwordbox <text> <rows> <cols> [init]\n" - "--passwordform <text> <rows> <cols> <formrows> [<label> " - "<ylabel> <xlabe> <init> <yfield> <xfield> <fieldlen> " - "<maxvalue> ...]\n" - "--pause <text> <rows> <cols> <secs>\n" - "--radiolist <text> <rows> <cols> <menurows> [<name> <desc> " - "<on|off> ...]\n" - "--rangebox <text> <rows> <cols> <min> <max> <default>\n" - "--textbox <file> <rows> <cols>\n" - "--timebox <text> <rows> <cols> [<hh> <mm> <ss>]\n" - "--treeview <text> <rows> <cols> <menurows> [<depth> <name> " - "<desc> <on|off> ...]\n" - "--yesno <text> <rows> <cols>\n"); - - -} - -int main(int argc, char *argv[argc]) -{ - char *text, *backtitle_flag, errorbuilder[1024]; - int input, rows, cols, output, getH, getW; - int (*widgetbuilder)(BUILDER_ARGS) = NULL; - bool ignore_flag, print_maxsize_flag; - struct winsize ws; - struct bsddialog_conf conf; - enum bsddialog_default_theme theme_flag; - bool cr_wrap_flag, no_collapse_flag, no_nl_expand_flag, trim_flag; - - bsddialog_initconf(&conf); - - backtitle_flag = NULL; - theme_flag = -1; - output_fd_flag = STDERR_FILENO; - print_maxsize_flag = false; - ignore_flag = false; - errorbuilder[0] = '\0'; - cr_wrap_flag = no_collapse_flag = no_nl_expand_flag = trim_flag = false; - - item_output_sepnl_flag = item_singlequote_flag = false; - item_prefix_flag = item_bottomdesc_flag = false; - list_items_on_flag = item_tag_help_flag = false; - item_always_quote_flag = false; - item_output_sep_flag = NULL; - - date_fmt_flag = time_fmt_flag = NULL; - - max_input_form_flag = 0; - - /* options descriptor */ - struct option longopts[] = { - /* common options */ - {"ascii-lines", no_argument, NULL, ASCII_LINES }, - {"aspect", required_argument, NULL, ASPECT_RATIO }, - {"backtitle", required_argument, NULL, BACKTITLE }, - {"begin-x", required_argument, NULL, BEGIN_X }, - {"begin-y", required_argument, NULL, BEGIN_Y }, - {"cancel-label", required_argument, NULL, CANCEL_LABEL }, - {"clear", no_argument, NULL, CLEAR }, - {"colors", no_argument, NULL, COLORS }, - {"cr-wrap", no_argument, NULL, CR_WRAP }, - {"date-format", required_argument, NULL, DATE_FORMAT }, - {"defaultno", no_argument, NULL, DEFAULTNO }, - {"default-button", required_argument, NULL, DEFAULT_BUTTON }, - {"default-item", required_argument, NULL, DEFAULT_ITEM }, - {"exit-label", required_argument, NULL, EXIT_LABEL }, - {"extra-button", no_argument, NULL, EXTRA_BUTTON }, - {"extra-label", required_argument, NULL, EXTRA_LABEL }, - {"help", no_argument, NULL, HELP }, - {"help-button", no_argument, NULL, HELP_BUTTON }, - {"help-label", required_argument, NULL, HELP_LABEL }, - {"help-status", no_argument, NULL, HELP_STATUS }, - {"help-tags", no_argument, NULL, HELP_TAGS }, - {"hfile", required_argument, NULL, HFILE }, - {"hline", required_argument, NULL, HLINE }, - {"ignore", no_argument, NULL, IGNORE }, - {"insecure", no_argument, NULL, INSECURE }, - {"item-help", no_argument, NULL, ITEM_HELP }, - {"item-prefix", no_argument, NULL, ITEM_PREFIX }, - {"max-input", required_argument, NULL, MAX_INPUT }, - {"no-cancel", no_argument, NULL, NO_CANCEL }, - {"nocancel", no_argument, NULL, NOCANCEL }, - {"no-collapse", no_argument, NULL, NO_COLLAPSE }, - {"no-items", no_argument, NULL, NO_ITEMS }, - {"no-label", required_argument, NULL, NO_LABEL }, - {"no-lines", no_argument, NULL, NO_LINES }, - {"no-nl-expand", no_argument, NULL, NO_NL_EXPAND }, - {"no-ok", no_argument, NULL, NO_OK }, - {"nook ", no_argument, NULL, NOOK }, - {"no-tags", no_argument, NULL, NO_TAGS }, - {"no-shadow", no_argument, NULL, NO_SHADOW }, - {"ok-label", required_argument, NULL, OK_LABEL }, - {"output-fd", required_argument, NULL, OUTPUT_FD }, - {"separator", required_argument, NULL, SEPARATOR }, - {"output-separator",required_argument, NULL, OUTPUT_SEPARATOR }, - {"print-maxsize", no_argument, NULL, PRINT_MAXSIZE }, - {"print-size", no_argument, NULL, PRINT_SIZE }, - {"print-version", no_argument, NULL, PRINT_VERSION }, - {"quoted", no_argument, NULL, QUOTED }, - {"separate-output", no_argument, NULL, SEPARATE_OUTPUT }, - {"shadow", no_argument, NULL, SHADOW }, - {"single-quoted", no_argument, NULL, SINGLE_QUOTED }, - {"sleep", required_argument, NULL, SLEEP }, - {"stderr", no_argument, NULL, STDERR }, - {"stdout", no_argument, NULL, STDOUT }, - {"theme_flag", required_argument, NULL, THEME }, - {"time-format", required_argument, NULL, TIME_FORMAT }, - {"title", required_argument, NULL, TITLE }, - {"trim", no_argument, NULL, TRIM }, - {"version", no_argument, NULL, VERSION }, - {"yes-label", required_argument, NULL, YES_LABEL }, - /* Widgets */ - {"buildlist", no_argument, NULL, BUILDLIST }, - {"checklist", no_argument, NULL, CHECKLIST }, - {"datebox", no_argument, NULL, DATEBOX }, - {"form", no_argument, NULL, FORM }, - {"gauge", no_argument, NULL, GAUGE }, - {"infobox", no_argument, NULL, INFOBOX }, - {"inputbox", no_argument, NULL, INPUTBOX }, - {"menu", no_argument, NULL, MENU }, - {"mixedform", no_argument, NULL, MIXEDFORM }, - {"mixedgauge", no_argument, NULL, MIXEDGAUGE }, - {"msgbox", no_argument, NULL, MSGBOX }, - {"passwordbox", no_argument, NULL, PASSWORDBOX }, - {"passwordform", no_argument, NULL, PASSWORDFORM }, - {"pause", no_argument, NULL, PAUSE }, - {"radiolist", no_argument, NULL, RADIOLIST }, - {"rangebox", no_argument, NULL, RANGEBOX }, - {"textbox", no_argument, NULL, TEXTBOX }, - {"timebox", no_argument, NULL, TIMEBOX }, - {"treeview", no_argument, NULL, TREEVIEW }, - {"yesno", no_argument, NULL, YESNO }, - /* END */ - { NULL, 0, NULL, 0 } - }; - - while ((input = getopt_long(argc, argv, "", longopts, NULL)) != -1) { - switch (input) { - /* Common options */ - case ASCII_LINES: - conf.ascii_lines = true; - break; - case ASPECT_RATIO: - conf.aspect_ratio = atoi(optarg); - if (conf.aspect_ratio < 1) { - printf("Error: aspect cannot be < 1"); - return (BSDDIALOG_ERROR); - } - break; - case BACKTITLE: - backtitle_flag = optarg; - break; - case BEGIN_X: - conf.x = atoi(optarg); - if (conf.x < BSDDIALOG_CENTER) { - printf("Error: --begin-x %d, cannot be < %d", - conf.x, BSDDIALOG_CENTER); - return (BSDDIALOG_ERROR); - } - break; - case BEGIN_Y: - conf.y = atoi(optarg); - if (conf.y < BSDDIALOG_CENTER) { - printf("Error: --begin-y %d, cannot be < %d", - conf.y, BSDDIALOG_CENTER); - return (BSDDIALOG_ERROR); - } - break; - case CANCEL_LABEL: - conf.button.cancel_label = optarg; - break; - case CLEAR: - conf.clear = true; - break; - case COLORS: - conf.text.colors = true; - break; - case CR_WRAP: - cr_wrap_flag = true; - break; - case DATE_FORMAT: - date_fmt_flag = optarg; - break; - case DEFAULT_BUTTON: - conf.button.default_label = optarg; - break; - case DEFAULT_ITEM: - conf.menu.default_item = optarg; - break; - case DEFAULTNO: - conf.button.default_cancel = true; - break; - case EXIT_LABEL: - conf.button.exit_label = optarg; - break; - case EXTRA_BUTTON: - conf.button.with_extra = true; - break; - case EXTRA_LABEL: - conf.button.extra_label = optarg; - break; - case HELP: - usage(); - return (BSDDIALOG_OK); - case HELP_BUTTON: - conf.button.with_help = true; - break; - case HELP_LABEL: - conf.button.help_label = optarg; - break; - case HELP_STATUS: - list_items_on_flag = true; - break; - case HELP_TAGS: - item_tag_help_flag = true; - break; - case HFILE: - conf.f1_file = optarg; - break; - case HLINE: - conf.bottomtitle = optarg; - break; - case IGNORE: - ignore_flag = true; - break; - case INSECURE: - conf.form.securech = '*'; - break; - case ITEM_HELP: - item_bottomdesc_flag = true; - break; - case ITEM_PREFIX: - item_prefix_flag = true; - break; - case MAX_INPUT: - max_input_form_flag = atoi(optarg); - break; - case NO_ITEMS: - conf.menu.no_desc = true; - break; - case NOCANCEL: - case NO_CANCEL: - conf.button.without_cancel = true; - break; - case NO_COLLAPSE: - no_collapse_flag = true; - break; - case NO_LABEL: - conf.button.cancel_label = optarg; - break; - case NO_LINES: - conf.no_lines = true; - break; - case NO_NL_EXPAND: - no_nl_expand_flag = true; - break; - case NOOK: - case NO_OK: - conf.button.without_ok = true; - break; - case NO_TAGS: - conf.menu.no_name = true; - break; - case NO_SHADOW: - conf.shadow = false; - break; - case OK_LABEL: - conf.button.ok_label = optarg; - break; - case OUTPUT_FD: - output_fd_flag = atoi(optarg); - break; - case SEPARATOR: - case OUTPUT_SEPARATOR: - item_output_sep_flag = optarg; - break; - case QUOTED: - item_always_quote_flag = true; - break; - case PRINT_MAXSIZE: - print_maxsize_flag = true; - break; - case PRINT_SIZE: - conf.get_height = &getH;; - conf.get_width = &getW; - break; - case PRINT_VERSION: - printf("bsddialog version %s\n", BSDDIALOG_VERSION); - break; - case SEPARATE_OUTPUT: - item_output_sepnl_flag = true; - break; - case SHADOW: - conf.shadow = true; - break; - case SINGLE_QUOTED: - item_singlequote_flag = true; - break; - case SLEEP: - conf.sleep = atoi(optarg); - break; - case STDERR: - output_fd_flag = STDERR_FILENO; - break; - case STDOUT: - output_fd_flag = STDOUT_FILENO; - break; - case THEME: - if (strcmp(optarg, "bsddialog") == 0) - theme_flag = BSDDIALOG_THEME_BSDDIALOG; - else if (strcmp(optarg, "blackwhite") == 0) - theme_flag = BSDDIALOG_THEME_BLACKWHITE; - else if (strcmp(optarg, "dialog") == 0) - theme_flag = BSDDIALOG_THEME_DIALOG; - else { - printf("Unknown theme, possible values: "); - printf("blackwhite, bsddialog, dialog"); - return (BSDDIALOG_ERROR); - } - break; - case TIME_FORMAT: - time_fmt_flag = optarg; - break; - case TITLE: - conf.title = optarg; - break; - case TRIM: - trim_flag = true; - break; - case VERSION: - printf("bsddialog %s (libbsddialog %s).\n", - BSDDIALOG_VERSION, LIBBSDDIALOG_VERSION); - return (BSDDIALOG_OK); - case YES_LABEL: - conf.button.ok_label = optarg; - break; - /* Widgets */ - case BUILDLIST: - widgetbuilder = buildlist_builder; - break; - case CHECKLIST: - widgetbuilder = checklist_builder; - break; - case DATEBOX: - widgetbuilder = datebox_builder; - break; - case FORM: - widgetbuilder = form_builder; - break; - case GAUGE: - widgetbuilder = gauge_builder; - break; - case INFOBOX: - widgetbuilder = infobox_builder; - break; - case INPUTBOX: - widgetbuilder = inputbox_builder; - break; - case MENU: - widgetbuilder = menu_builder; - break; - case MIXEDFORM: - widgetbuilder = mixedform_builder; - break; - case MIXEDGAUGE: - widgetbuilder = mixedgauge_builder; - break; - case MSGBOX: - widgetbuilder = msgbox_builder; - break; - case PAUSE: - widgetbuilder = pause_builder; - break; - case PASSWORDBOX: - widgetbuilder = passwordbox_builder; - break; - case PASSWORDFORM: - widgetbuilder = passwordform_builder; - break; - case RADIOLIST: - widgetbuilder = radiolist_builder; - break; - case RANGEBOX: - widgetbuilder = rangebox_builder; - break; - case TEXTBOX: - widgetbuilder = textbox_builder; - break; - case TIMEBOX: - widgetbuilder = timebox_builder; - break; - case TREEVIEW: - widgetbuilder = treeview_builder; - break; - case YESNO: - widgetbuilder = yesno_builder; - break; - /* Error */ - default: - if (ignore_flag == true) - break; - usage(); - return (BSDDIALOG_ERROR); - } - } - argc -= optind; - argv += optind; - - if (print_maxsize_flag) { - ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws); - dprintf(output_fd_flag, "MaxSize: %d, %d\n", - ws.ws_row, ws.ws_col); - if (argc == 0) - return (BSDDIALOG_OK); - } - - if (argc < 3) { - usage(); - return (BSDDIALOG_ERROR); - } - if (widgetbuilder == textbox_builder) - text = argv[0]; - else { - text = malloc(strlen(argv[0] + 1)); - custom_text(cr_wrap_flag, no_collapse_flag, no_nl_expand_flag, - trim_flag, argv[0], text); - } - rows = atoi(argv[1]); - cols = atoi(argv[2]); - argc -= 3; - argv += 3; - - /* bsddialog terminal mode */ - if(bsddialog_init() != 0) { - printf("Error: %s\n", bsddialog_geterror()); - return (BSDDIALOG_ERROR); - } - - if (theme_flag >= 0) - bsddialog_set_default_theme(theme_flag); - - if (backtitle_flag != NULL) - bsddialog_backtitle(&conf, backtitle_flag); - - output = BSDDIALOG_OK; - if (widgetbuilder != NULL) - output = widgetbuilder(conf, text, rows, cols, argc, argv, - errorbuilder); - - if (widgetbuilder != textbox_builder) - free(text); - - bsddialog_end(); - /* end bsddialog terminal mode */ - - if (output == BSDDIALOG_ERROR) { - if (errorbuilder[0] != '\0') - printf("Error: %s\n", errorbuilder); - else - printf("Error: %s\n", bsddialog_geterror()); - } - - if (conf.get_height != NULL && conf.get_width != NULL) - dprintf(output_fd_flag, "Widget size: (%d - %d)\n", - *conf.get_height, *conf.get_width); - - return (output); -} - -/* Widgets */ - -int gauge_builder(BUILDER_ARGS) -{ - int output, perc; - - if (argc > 0) { - perc = argc > 0 ? atoi (argv[0]) : 0; - perc = perc < 0 ? 0 : perc; - perc = perc > 100 ? 100 : perc; - } - else - perc = 0; - - output = bsddialog_gauge(&conf, text, rows, cols, perc); - - return (output); -} - -int infobox_builder(BUILDER_ARGS) -{ - int output; - - output = bsddialog_infobox(&conf, text, rows, cols); - - return (output); -} - -int mixedgauge_builder(BUILDER_ARGS) -{ - int i, output, mainperc, nminibars, *minipercs; - char **minilabels; - - if (argc < 1 || (((argc-1) % 2) != 0) ) { - strcpy(errbuf, "bad --mixedgauge arguments\n"); - return (BSDDIALOG_ERROR); - } - - mainperc = atoi(argv[0]); - mainperc = mainperc < 0 ? 0 : mainperc; - mainperc = mainperc > 100 ? 100 : mainperc; - argc--; - argv++; - - nminibars = argc / 2; - if ((minilabels = calloc(nminibars, sizeof(char*))) == NULL) { - strcpy(errbuf, "Cannot allocate memory for minilabels\n"); - return BSDDIALOG_ERROR; - } - if ((minipercs = calloc(nminibars, sizeof(int))) == NULL) { - strcpy(errbuf, "Cannot allocate memory for minipercs\n"); - return BSDDIALOG_ERROR; - } - - for (i = 0; i < nminibars; i++) { - minilabels[i] = argv[i * 2]; - minipercs[i] = atoi(argv[i * 2 + 1]); - } - - output = bsddialog_mixedgauge(&conf, text, rows, cols, mainperc, - nminibars, minilabels, minipercs); - - return (output); -} - -int msgbox_builder(BUILDER_ARGS) -{ - int output; - - output = bsddialog_msgbox(&conf, text, rows, cols); - - return (output); -} - -int pause_builder(BUILDER_ARGS) -{ - int output, sec; - - if (argc < 1) { - strcpy(errbuf, "not <seconds> argument for --pause\n"); - return (BSDDIALOG_ERROR); - } - - sec = atoi(argv[0]); - output = bsddialog_pause(&conf, text, rows, cols, sec); - - return (output); -} - -int rangebox_builder(BUILDER_ARGS) -{ - int output, min, max, value; - - if (argc < 2) { - strcpy(errbuf, "usage --rangebox <text> <rows> <cols> <min> " - "<max> [<init>]\n"); - return (BSDDIALOG_ERROR); - } - - min = atoi(argv[0]); - max = atoi(argv[1]); - - if (argc > 2) { - value = atoi(argv[2]); - value = value < min ? min : value; - value = value > max ? max : value; - } - else - value = min; - - output = bsddialog_rangebox(&conf, text, rows, cols, min, max, &value); - - dprintf(output_fd_flag, "%d", value); - - return (output); -} - -int textbox_builder(BUILDER_ARGS) -{ - int output; - - output = bsddialog_textbox(&conf, text, rows, cols); - - return (output); -} - -int yesno_builder(BUILDER_ARGS) -{ - int output; - - output = bsddialog_yesno(&conf, text, rows, cols); - - return (output); -} - -/* DATE and TIME */ -int datebox_builder(BUILDER_ARGS) -{ - int output; - unsigned int yy, mm, dd; - time_t cal; - struct tm *localtm; - char stringdate[1024]; - - time(&cal); - localtm = localtime(&cal); - yy = localtm->tm_year + 1900; - mm = localtm->tm_mon + 1; - dd = localtm->tm_mday; - - /* --calendar text h w [year month day] */ - if (argc == 3) { - yy = atoi(argv[0]); - mm = atoi(argv[1]); - dd = atoi(argv[2]); - } - - output = bsddialog_datebox(&conf, text, rows, cols, &yy, &mm, &dd); - if (output != BSDDIALOG_OK) - return (output); - - if (date_fmt_flag == NULL) { - dprintf(output_fd_flag, "%u/%u/%u", yy, mm, dd); - } - else { - time(&cal); - localtm = localtime(&cal); - localtm->tm_year = yy - 1900; - localtm->tm_mon = mm; - localtm->tm_mday = dd; - strftime(stringdate, 1024, date_fmt_flag, localtm); - dprintf(output_fd_flag, "%s", stringdate); - } - - return (output); -} - -int timebox_builder(BUILDER_ARGS) -{ - int output; - unsigned int hh, mm, ss; - time_t clock; - struct tm *localtm; - char stringtime[1024]; - - time(&clock); - localtm = localtime(&clock); - hh = localtm->tm_hour; - mm = localtm->tm_min; - ss = localtm->tm_sec; - - /* --timebox text h w [hour minute second] */ - if (argc == 3) { - hh = atoi(argv[0]); - mm = atoi(argv[1]); - ss = atoi(argv[2]); - } - - output = bsddialog_timebox(&conf, text, rows, cols, &hh, &mm, &ss); - if (output != BSDDIALOG_OK) - return (output); - - if (time_fmt_flag == NULL) { - dprintf(output_fd_flag, "%u:%u:%u", hh, mm, ss); - } - else { - time(&clock); - localtm = localtime(&clock); - localtm->tm_hour = hh; - localtm->tm_min = mm; - localtm->tm_sec = ss; - strftime(stringtime, 1024, time_fmt_flag, localtm); - dprintf(output_fd_flag, "%s", stringtime); - } - - return (output); -} - -/* MENU */ -static int -get_menu_items(char *errbuf, int argc, char **argv, bool setprefix, - bool setdepth, bool setname, bool setdesc, bool setstatus, bool sethelp, - int *nitems, struct bsddialog_menuitem *items) -{ - int i, j, sizeitem; - - sizeitem = 0; - if (setprefix) sizeitem++; - if (setdepth) sizeitem++; - if (setname) sizeitem++; - if (setdesc) sizeitem++; - if (setstatus) sizeitem++; - if (sethelp) sizeitem++; - if ((argc % sizeitem) != 0) { - strcpy(errbuf, "bad number of arguments for this menu\n"); - return (BSDDIALOG_ERROR); - } - - *nitems = argc / sizeitem; - j = 0; - for (i=0; i<*nitems; i++) { - items[i].prefix = setprefix ? argv[j++] : nostring; - items[i].depth = setdepth ? atoi(argv[j++]) : 0; - items[i].name = setname ? argv[j++] : nostring; - items[i].desc = setdesc ? argv[j++] : nostring; - if (setstatus) - items[i].on = strcmp(argv[j++], "on") == 0 ? - true : false; - else - items[i].on = false; - items[i].bottomdesc = sethelp ? argv[j++] : nostring; - } - - return (BSDDIALOG_OK); -} - -static void -print_menu_items(struct bsddialog_conf *conf, int output, int nitems, - struct bsddialog_menuitem *items, int focusitem) -{ - int i; - bool sep, toquote; - char *sepstr, quotech, *helpvalue; - - sep = false; - quotech = item_singlequote_flag ? '\'' : '"'; - sepstr = item_output_sep_flag != NULL ? item_output_sep_flag : " "; - - if (output == BSDDIALOG_HELP && focusitem >= 0) { - dprintf(output_fd_flag, "HELP "); - - helpvalue = items[focusitem].name; - if (item_bottomdesc_flag && item_tag_help_flag == false) - helpvalue = items[focusitem].bottomdesc; - - toquote = item_always_quote_flag || - strchr(helpvalue, ' ') != NULL; - - if (toquote) - dprintf(output_fd_flag, "%c", quotech); - dprintf(output_fd_flag, "%s", helpvalue); - if (toquote) - dprintf(output_fd_flag, "%c", quotech); - - if (list_items_on_flag == false) - return; - - sep = true; - } - - if (output != BSDDIALOG_OK && list_items_on_flag == false) - return; - - for (i = 0; i < nitems; i++) { - if (items[i].on == false) - continue; - - if (sep == true) { - dprintf(output_fd_flag, "%s", sepstr); - if (item_output_sepnl_flag) - dprintf(output_fd_flag, "\n"); - } - sep = true; - - toquote = item_always_quote_flag || - strchr(items[i].name, ' ') != NULL; - - if (toquote) - dprintf(output_fd_flag, "%c", quotech); - dprintf(output_fd_flag, "%s", items[i].name); - if (toquote) - dprintf(output_fd_flag, "%c", quotech); - } -} - -int buildlist_builder(BUILDER_ARGS) -{ - int output, menurows, nitems, focusitem; - struct bsddialog_menuitem items[1024]; - - if (argc < 1) { - strcpy(errbuf, "<menurows> not provided"); - return (BSDDIALOG_ERROR); - } - - menurows = atoi(argv[0]); - - output = get_menu_items(errbuf, argc-1, argv+1, item_prefix_flag, false, - true, true, true, item_bottomdesc_flag, &nitems, items); - if (output != 0) - return (output); - - output = bsddialog_buildlist(&conf, text, rows, cols, menurows, nitems, - items, &focusitem); - if (output == BSDDIALOG_ERROR) - return (BSDDIALOG_ERROR); - - print_menu_items(&conf, output, nitems, items, focusitem); - - return (output); -} - -int checklist_builder(BUILDER_ARGS) -{ - int output, menurows, nitems, focusitem; - struct bsddialog_menuitem items[1024]; - - if (argc < 1) { - strcpy(errbuf, "<menurows> not provided"); - return (BSDDIALOG_ERROR); - } - - menurows = atoi(argv[0]); - - output = get_menu_items(errbuf, argc-1, argv+1, item_prefix_flag, false, - true, true, true, item_bottomdesc_flag, &nitems, items); - if (output != 0) - return (output); - - output = bsddialog_checklist(&conf, text, rows, cols, menurows, nitems, - items, &focusitem); - - print_menu_items(&conf, output, nitems, items, focusitem); - - return (output); -} - -int menu_builder(BUILDER_ARGS) -{ - int output, menurows, nitems, focusitem; - struct bsddialog_menuitem items[1024]; - - if (argc < 1) { - strcpy(errbuf, "<menurows> not provided"); - return (BSDDIALOG_ERROR); - } - - menurows = atoi(argv[0]); - - output = get_menu_items(errbuf, argc-1, argv+1, item_prefix_flag, false, - true, true, false, item_bottomdesc_flag, &nitems, items); - if (output != 0) - return (output); - - output = bsddialog_menu(&conf, text, rows, cols, menurows, nitems, - items, &focusitem); - - print_menu_items(&conf, output, nitems, items, focusitem); - - return (output); -} - -int radiolist_builder(BUILDER_ARGS) -{ - int output, menurows, nitems, focusitem; - struct bsddialog_menuitem items[1024]; - - if (argc < 1) { - strcpy(errbuf, "<menurows> not provided"); - return (BSDDIALOG_ERROR); - } - - menurows = atoi(argv[0]); - - output = get_menu_items(errbuf, argc-1, argv+1, item_prefix_flag, false, - true, true, true, item_bottomdesc_flag, &nitems, items); - if (output != 0) - return (output); - - output = bsddialog_radiolist(&conf, text, rows, cols, menurows, nitems, - items, &focusitem); - - print_menu_items(&conf, output, nitems, items, focusitem); - - return (output); -} - -int treeview_builder(BUILDER_ARGS) -{ - int output, menurows, nitems, focusitem; - struct bsddialog_menuitem items[1024]; - - if (argc < 1) { - strcpy(errbuf, "<menurows> not provided"); - return (BSDDIALOG_ERROR); - } - - menurows = atoi(argv[0]); - - output = get_menu_items(errbuf, argc-1, argv+1, item_prefix_flag, true, - true, true, true, item_bottomdesc_flag, &nitems, items); - if (output != 0) - return (output); - - conf.menu.no_name = true; - conf.menu.align_left = true; - - output = bsddialog_radiolist(&conf, text, rows, cols, menurows, nitems, - items, &focusitem); - - print_menu_items(&conf, output, nitems, items, focusitem); - - return (output); -} - -/* FORM */ -static void -print_form_items(struct bsddialog_conf *conf, int output, int nitems, - struct bsddialog_formitem *items) -{ - int i; - - if (output == BSDDIALOG_ERROR) - return; - - for (i=0; i < nitems; i++) { - dprintf(output_fd_flag, "%s\n", items[i].value); - free(items[i].value); - } -} - -int form_builder(BUILDER_ARGS) -{ - int i, output, formheight, nitems, fieldlen, valuelen; - struct bsddialog_formitem items[1024]; - unsigned int flags = 0; - - if (argc < 1 || (((argc-1) % 8) != 0) ) { - strcpy(errbuf, "bad number of arguments for this form\n"); - return (BSDDIALOG_ERROR); - } - - formheight = atoi(argv[0]); - - argc--; - argv++; - - nitems = argc / 8; - for (i=0; i<nitems; i++) { - items[i].label = argv[8*i]; - items[i].ylabel = atoi(argv[8*i+1]); - items[i].xlabel = atoi(argv[8*i+2]); - items[i].init = argv[8*i+3]; - items[i].yfield = atoi(argv[8*i+4]); - items[i].xfield = atoi(argv[8*i+5]); - - fieldlen = atoi(argv[8*i+6]); - items[i].fieldlen = abs(fieldlen); - - valuelen = atoi(argv[8*i+7]); - items[i].maxvaluelen = valuelen == 0 ? abs(fieldlen) : valuelen; - - flags |= (fieldlen < 0 ? BSDDIALOG_FIELDREADONLY : 0); - items[i].flags = flags; - } - - output = bsddialog_form(&conf, text, rows, cols, formheight, nitems, - items); - print_form_items(&conf, output, nitems, items); - - return (output); -} - -int inputbox_builder(BUILDER_ARGS) -{ - int output; - struct bsddialog_formitem item; - - item.label = ""; - item.ylabel = 0; - item.xlabel = 0; - item.init = argc > 0 ? argv[0] : ""; - item.yfield = 1; - item.xfield = 1; - item.fieldlen = cols > 4 ? cols-4 : 25; - item.maxvaluelen = max_input_form_flag > 0 ? max_input_form_flag : 2048; - item.flags = 0; - - output = bsddialog_form(&conf, text, rows, cols, 1, 1, &item); - print_form_items(&conf, output, 1, &item); - - return (output); -} - -int mixedform_builder(BUILDER_ARGS) -{ - int i, output, formheight, nitems; - struct bsddialog_formitem items[1024]; - - if (argc < 1 || (((argc-1) % 9) != 0) ) { - strcpy(errbuf, "bad number of arguments for this form\n"); - return (BSDDIALOG_ERROR); - } - - formheight = atoi(argv[0]); - - argc--; - argv++; - - nitems = argc / 9; - for (i=0; i<nitems; i++) { - items[i].label = argv[9*i]; - items[i].ylabel = atoi(argv[9*i+1]); - items[i].xlabel = atoi(argv[9*i+2]); - items[i].init = argv[9*i+3]; - items[i].yfield = atoi(argv[9*i+4]); - items[i].xfield = atoi(argv[9*i+5]); - items[i].fieldlen = atoi(argv[9*i+6]); - items[i].maxvaluelen = atoi(argv[9*i+7]); - items[i].flags = atoi(argv[9*i+8]); - } - - output = bsddialog_form(&conf, text, rows, cols, formheight, nitems, - items); - print_form_items(&conf, output, nitems, items); - - return (output); -} - -int passwordbox_builder(BUILDER_ARGS) -{ - int output; - struct bsddialog_formitem item; - - item.label = ""; - item.ylabel = 0; - item.xlabel = 0; - item.init = argc > 0 ? argv[0] : ""; - item.yfield = 1; - item.xfield = 1; - item.fieldlen = cols > 4 ? cols-4 : 25; - item.maxvaluelen = max_input_form_flag > 0 ? max_input_form_flag : 2048; - item.flags = BSDDIALOG_FIELDHIDDEN; - - output = bsddialog_form(&conf, text, rows, cols, 1, 1, &item); - print_form_items(&conf, output, 1, &item); - - return (output); -} - -int passwordform_builder(BUILDER_ARGS) -{ - int i, output, formheight, nitems, fieldlen, valuelen; - struct bsddialog_formitem items[1024]; - unsigned int flags = BSDDIALOG_FIELDHIDDEN; - - if (argc < 1 || (((argc-1) % 8) != 0) ) { - strcpy(errbuf, "bad number of arguments for this form\n"); - return (BSDDIALOG_ERROR); - } - - formheight = atoi(argv[0]); - - argc--; - argv++; - - nitems = argc / 8; - for (i=0; i<nitems; i++) { - items[i].label = argv[8*i]; - items[i].ylabel = atoi(argv[8*i+1]); - items[i].xlabel = atoi(argv[8*i+2]); - items[i].init = argv[8*i+3]; - items[i].yfield = atoi(argv[8*i+4]); - items[i].xfield = atoi(argv[8*i+5]); - - fieldlen = atoi(argv[8*i+6]); - items[i].fieldlen = abs(fieldlen); - - valuelen = atoi(argv[8*i+7]); - items[i].maxvaluelen = valuelen == 0 ? abs(fieldlen) : valuelen; - - flags |= (fieldlen < 0 ? BSDDIALOG_FIELDREADONLY : 0); - items[i].flags = flags; - } - - output = bsddialog_form(&conf, text, rows, cols, formheight, - nitems, items); - print_form_items(&conf, output, nitems, items); - - return (output); -} diff --git a/examples_library/buildlist.c b/examples_library/buildlist.c deleted file mode 100644 index 37c1e342ace5..000000000000 --- a/examples_library/buildlist.c +++ /dev/null @@ -1,44 +0,0 @@ -/*- - * SPDX-License-Identifier: CC0-1.0 - * - * Written in 2021 by Alfonso Sabato Siciliano. - * To the extent possible under law, the author has dedicated all copyright - * and related and neighboring rights to this software to the public domain - * worldwide. This software is distributed without any warranty, see: - * <http://creativecommons.org/publicdomain/zero/1.0/>. - */ - -#include <stdio.h> -#include <string.h> - -#include <bsddialog.h> - -int main() -{ - int i, output; - struct bsddialog_conf conf; - struct bsddialog_menuitem items[5] = { - {"", false, 0, "Name 1", "Desc 1", "Bottom Desc 1"}, - {"", true, 0, "Name 2", "Desc 2", "Bottom Desc 2"}, - {"", false, 0, "Name 3", "Desc 3", "Bottom Desc 3"}, - {"", true, 0, "Name 4", "Desc 4", "Bottom Desc 4"}, - {"", false, 0, "Name 5", "Desc 5", "Bottom Desc 5"} - }; - - bsddialog_initconf(&conf); - conf.title = "radiolist"; - - if (bsddialog_init() < 0) - return -1; - - output = bsddialog_buildlist(&conf, "Example", 15, 30, 5, 5, items, NULL); - - bsddialog_end(); - - printf("Buildlist:\n"); - for (i=0; i<5; i++) - printf(" [%c] %s\n", items[i].on ? 'X' : ' ', items[i].name); - - - return output; -} diff --git a/examples_library/calendar.c b/examples_library/calendar.c new file mode 100644 index 000000000000..5899fc4986fd --- /dev/null +++ b/examples_library/calendar.c @@ -0,0 +1,45 @@ +/*- + * SPDX-License-Identifier: CC0-1.0 + * + * Written in 2022 by Alfonso Sabato Siciliano. + * To the extent possible under law, the author has dedicated all copyright + * and related and neighboring rights to this software to the public domain + * worldwide. This software is distributed without any warranty, see: + * <http://creativecommons.org/publicdomain/zero/1.0/>. + */ + +#include <bsddialog.h> +#include <stdio.h> +#include <time.h> + +int main() +{ + int output; + unsigned int yy, mm, dd; + struct bsddialog_conf conf; + time_t cal; + struct tm *localtm; + + time(&cal); + localtm = localtime(&cal); + yy = localtm->tm_year + 1900; + mm = localtm->tm_mon + 1; + dd = localtm->tm_mday; + + if (bsddialog_init() == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } + bsddialog_initconf(&conf); + conf.title = "calendar"; + output = bsddialog_calendar(&conf, "Example", 18, 40, &yy, &mm, &dd); + bsddialog_end(); + if (output == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } + + printf("Date: %u/%u/%u\n", yy, mm, dd); + + return (0); +}
\ No newline at end of file diff --git a/examples_library/checklist.c b/examples_library/checklist.c index d1c4a4db3597..a2b178d14270 100644 --- a/examples_library/checklist.c +++ b/examples_library/checklist.c @@ -8,37 +8,38 @@ * <http://creativecommons.org/publicdomain/zero/1.0/>. */ -#include <stdio.h> -#include <string.h> - #include <bsddialog.h> +#include <stdio.h> int main() { int i, output; struct bsddialog_conf conf; struct bsddialog_menuitem items[5] = { - {"", true, 0, "Name 1", "Desc 1", "Bottom Desc 1"}, - {"", false, 0, "Name 2", "Desc 2", "Bottom Desc 2"}, - {"", true, 0, "Name 3", "Desc 3", "Bottom Desc 3"}, - {"", false, 0, "Name 4", "Desc 4", "Bottom Desc 4"}, - {"", true, 0, "Name 5", "Desc 5", "Bottom Desc 5"} + {"i", true, 0, "Name 1", "Desc 1", "Bottom Desc 1"}, + {"ii", false, 0, "Name 2", "Desc 2", "Bottom Desc 2"}, + {"iii", true, 0, "Name 3", "Desc 3", "Bottom Desc 3"}, + {"iv", false, 0, "Name 4", "Desc 4", "Bottom Desc 4"}, + {"v", true, 0, "Name 5", "Desc 5", "Bottom Desc 5"} }; + if (bsddialog_init() == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } bsddialog_initconf(&conf); conf.title = "checklist"; - - if (bsddialog_init() < 0) - return -1; - - output = bsddialog_checklist(&conf, "Example", 15, 30, 5, 5, items, NULL); - + output = bsddialog_checklist(&conf, "Example", 15, 30, 5, 5, items, + NULL); bsddialog_end(); + if (output == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } printf("Checklist:\n"); - for (i=0; i<5; i++) + for (i = 0; i < 5; i++) printf(" [%c] %s\n", items[i].on ? 'X' : ' ', items[i].name); - - - return output; -} + + return (0); +}
\ No newline at end of file diff --git a/examples_library/compile b/examples_library/compile index 4427568a7f0f..9025f35426d9 100755 --- a/examples_library/compile +++ b/examples_library/compile @@ -1,10 +1,21 @@ #!/bin/sh +#- +# SPDX-License-Identifier: CC0-1.0 +# +# Written in 2021 by Alfonso Sabato Siciliano. +# To the extent possible under law, the author has dedicated all copyright +# and related and neighboring rights to this software to the public domain +# worldwide. This software is distributed without any warranty, see: +# <http://creativecommons.org/publicdomain/zero/1.0/>. libpath=../lib -examples="buildlist menu treeview checklist radiolist mixedlist theme \ - infobox yesno msgbox datebox form timebox sade rangebox pause" +examples="menu checklist radiolist mixedlist theme infobox yesno msgbox \ + datebox form timebox rangebox pause calendar gauge mixedgauge" + +rm -f $examples for e in $examples do - cc -g -Wall -I$libpath ${e}.c -o $e -L$libpath -lbsddialog -Wl,-rpath=$libpath + cc -g -Wall -Wextra -I$libpath ${e}.c -o $e -L$libpath -lbsddialog \ + -Wl,-rpath=$libpath done diff --git a/examples_library/datebox.c b/examples_library/datebox.c index 4839c3dbd895..e0741319d388 100644 --- a/examples_library/datebox.c +++ b/examples_library/datebox.c @@ -8,12 +8,10 @@ * <http://creativecommons.org/publicdomain/zero/1.0/>. */ +#include <bsddialog.h> #include <stdio.h> -#include <string.h> #include <time.h> -#include <bsddialog.h> - int main() { int output; @@ -28,32 +26,19 @@ int main() mm = localtm->tm_mon + 1; dd = localtm->tm_mday; + if (bsddialog_init() == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } bsddialog_initconf(&conf); conf.title = "datebox"; - conf.bottomtitle = "Press TAB and arrows"; - - if (bsddialog_init() < 0) - return -1; - - output = bsddialog_datebox(&conf, "Example", 10, 50, &yy, &mm, &dd); - + output = bsddialog_datebox(&conf, "Example", 9, 35, &yy, &mm, &dd); bsddialog_end(); - - switch (output) { - case BSDDIALOG_OK: - printf("Date: %u/%u/%u", yy, mm, dd); - break; - case BSDDIALOG_ESC: - printf("ESC\n"); - break; - case BSDDIALOG_CANCEL: - printf("Cancel"); - break; - case BSDDIALOG_ERROR: - printf("Error: %s", bsddialog_geterror()); - break; + if (output == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); } - printf("\n"); + printf("Date: %u/%u/%u\n", yy, mm, dd); - return output; -} + return (0); +}
\ No newline at end of file diff --git a/examples_library/form.c b/examples_library/form.c index efb997f31d36..4c6336610852 100644 --- a/examples_library/form.c +++ b/examples_library/form.c @@ -8,12 +8,10 @@ * <http://creativecommons.org/publicdomain/zero/1.0/>. */ - +#include <bsddialog.h> +#include <locale.h> #include <stdio.h> #include <stdlib.h> -#include <string.h> - -#include <bsddialog.h> #define H BSDDIALOG_FIELDHIDDEN #define RO BSDDIALOG_FIELDREADONLY @@ -23,29 +21,32 @@ int main() int i, output; struct bsddialog_conf conf; struct bsddialog_formitem items[3] = { - {"Input:", 1, 1, "value", 1, 11, 30, 50, NULL, 0, "desc 1"}, - {"Input:", 2, 1, "read only", 2, 11, 30, 50, NULL, RO, "desc 2"}, - {"Password:", 3, 1, "", 3, 11, 30, 50, NULL, H, "desc 3"} + {"Input:", 0, 0, "value", 0, 10, 30, 50, NULL, 0, "desc 1"}, + {"Input:", 1, 0, "read only", 1, 10, 30, 50, NULL, RO, "desc 2"}, + {"Password:", 2, 0, "", 2, 10, 30, 50, NULL, H, "desc 3"} }; + /* Optional, unless for unicode/multi-column characters */ + setlocale(LC_ALL, ""); + + if (bsddialog_init() == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } bsddialog_initconf(&conf); conf.title = "form"; conf.form.securech = '*'; - - if (bsddialog_init() < 0) - return -1; - - output = bsddialog_form(&conf, "Example", 10, 50, 3, 3, items); - + output = bsddialog_form(&conf, "Example", 10, 50, 3, 3, items, NULL); bsddialog_end(); - - if (output == BSDDIALOG_ERROR) + if (output == BSDDIALOG_ERROR) { printf("Error: %s", bsddialog_geterror()); + return (1); + } - for (i=0; i<3; i++) { + for (i = 0; i < 3; i++) { printf("%s \"%s\"\n", items[i].label, items[i].value); free(items[i].value); } - - return output; + + return (0); } diff --git a/examples_library/gauge.c b/examples_library/gauge.c new file mode 100644 index 000000000000..3b38a80f3f2c --- /dev/null +++ b/examples_library/gauge.c @@ -0,0 +1,57 @@ +/*- + * SPDX-License-Identifier: CC0-1.0 + * + * Written in 2023 by Alfonso Sabato Siciliano. + * To the extent possible under law, the author has dedicated all copyright + * and related and neighboring rights to this software to the public domain + * worldwide. This software is distributed without any warranty, see: + * <http://creativecommons.org/publicdomain/zero/1.0/>. + */ + +#include <bsddialog.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +static void sender(int fd) +{ + int i; + + for (i = 1; i <= 10; i++) { + sleep(1); + dprintf(fd, "SEP\n"); + dprintf(fd, "%d\n", i * 10); + dprintf(fd, "In Progress... [%d / 10]\n", i); + dprintf(fd, "SEP\n"); + } + sleep(1); + dprintf(fd, "EOF\n"); +} + +int main() +{ + int rv, fd[2]; + struct bsddialog_conf conf; + + /* add checks and sync */ + pipe(fd); + if (fork() == 0) { + close(fd[0]); + sender(fd[1]); + exit (0); + } + close(fd[1]); + + if (bsddialog_init() == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } + bsddialog_initconf(&conf); + conf.title = "gauge"; + rv = bsddialog_gauge(&conf, "Example", 7, 30, 0, fd[0], "SEP", "EOF"); + bsddialog_end(); + if(rv == BSDDIALOG_ERROR) + printf("Error: %s\n", bsddialog_geterror()); + + return (0); +}
\ No newline at end of file diff --git a/examples_library/infobox.c b/examples_library/infobox.c index 74f34bba91e1..bbd7f665d5a6 100644 --- a/examples_library/infobox.c +++ b/examples_library/infobox.c @@ -8,25 +8,27 @@ * <http://creativecommons.org/publicdomain/zero/1.0/>. */ -#include <stdio.h> -#include <string.h> - #include <bsddialog.h> +#include <stdio.h> int main() { int output; struct bsddialog_conf conf; + if (bsddialog_init() == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } bsddialog_initconf(&conf); conf.title = "infobox"; - - if (bsddialog_init() < 0) - return -1; - - output = bsddialog_infobox(&conf, "Example", 7, 20); - + conf.sleep = 3; + output = bsddialog_infobox(&conf, "Example\n(3 seconds)", 7, 20); bsddialog_end(); + if (output == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } - return output; -} + return (0); +}
\ No newline at end of file diff --git a/examples_library/menu.c b/examples_library/menu.c index 59999dabd1e5..5c4941f6d5de 100644 --- a/examples_library/menu.c +++ b/examples_library/menu.c @@ -8,37 +8,37 @@ * <http://creativecommons.org/publicdomain/zero/1.0/>. */ -#include <stdio.h> -#include <string.h> - #include <bsddialog.h> +#include <stdio.h> int main() { int i, output; struct bsddialog_conf conf; struct bsddialog_menuitem items[5] = { - {"", true, 0, "Name 1", "Desc 1", "Bottom Desc 1"}, - {"", false, 0, "Name 2", "Desc 2", "Bottom Desc 2"}, - {"", true, 0, "Name 3", "Desc 3", "Bottom Desc 3"}, - {"", false, 0, "Name 4", "Desc 4", "Bottom Desc 4"}, - {"", true, 0, "Name 5", "Desc 5", "Bottom Desc 5"} + {"I", true, 0, "Name 1", "Desc 1", "Bottom Desc 1"}, + {"II", false, 0, "Name 2", "Desc 2", "Bottom Desc 2"}, + {"III", true, 0, "Name 3", "Desc 3", "Bottom Desc 3"}, + {"IV", false, 0, "Name 4", "Desc 4", "Bottom Desc 4"}, + {"V", true, 0, "Name 5", "Desc 5", "Bottom Desc 5"} }; + if (bsddialog_init() == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } bsddialog_initconf(&conf); conf.title = "menu"; - - if (bsddialog_init() < 0) - return -1; - output = bsddialog_menu(&conf, "Example", 15, 30, 5, 5, items, NULL); - bsddialog_end(); + if (output == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } printf("Menu:\n"); - for (i=0; i<5; i++) + for (i = 0; i < 5; i++) printf(" [%c] %s\n", items[i].on ? 'X' : ' ', items[i].name); - - - return output; -} + + return (0); +}
\ No newline at end of file diff --git a/examples_library/mixedgauge.c b/examples_library/mixedgauge.c new file mode 100644 index 000000000000..7666867902fb --- /dev/null +++ b/examples_library/mixedgauge.c @@ -0,0 +1,79 @@ +/*- + * SPDX-License-Identifier: CC0-1.0 + * + * Written in 2023 by Alfonso Sabato Siciliano. + * To the extent possible under law, the author has dedicated all copyright + * and related and neighboring rights to this software to the public domain + * worldwide. This software is distributed without any warranty, see: + * <http://creativecommons.org/publicdomain/zero/1.0/>. + */ + +#include <bsddialog.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +#define NMINIBAR 13 + +static const char *minilabels[NMINIBAR] = { + "Label 1", + "Label 2", + "Label 3", + "Label 4", + "Label 5", + "Label 6", + "Label 7", + "Label 8", + "Label 9", + "Label 10", + "Label 11", + "Label X", + "Label Y", +}; + +static int minipercs[NMINIBAR] = { + BSDDIALOG_MG_SUCCEEDED, + BSDDIALOG_MG_FAILED, + BSDDIALOG_MG_PASSED, + BSDDIALOG_MG_COMPLETED, + BSDDIALOG_MG_CHECKED, + BSDDIALOG_MG_DONE, + BSDDIALOG_MG_SKIPPED, + BSDDIALOG_MG_INPROGRESS, + BSDDIALOG_MG_BLANK, + BSDDIALOG_MG_NA, + BSDDIALOG_MG_PENDING, + 67, + 0, +}; + +static void exit_error() +{ + if (bsddialog_inmode()) + bsddialog_end(); + printf("Error: %s\n", bsddialog_geterror()); + exit (1); +} + +int main() +{ + int retval, i; + struct bsddialog_conf conf; + + if (bsddialog_init() == BSDDIALOG_ERROR) + exit_error(); + bsddialog_initconf(&conf); + conf.title = "mixedgauge"; + for (i = 0; i <= 10; i++) { + minipercs[11] += 3; + minipercs[12] = i * 10; + retval= bsddialog_mixedgauge(&conf, "Example", 20, 40, + 50 + i * 5, NMINIBAR, minilabels, minipercs); + if(retval == BSDDIALOG_ERROR) + exit_error(); + sleep(1); + } + bsddialog_end(); + + return (0); +}
\ No newline at end of file diff --git a/examples_library/mixedlist.c b/examples_library/mixedlist.c index 2a4e9102164c..6d286996931b 100644 --- a/examples_library/mixedlist.c +++ b/examples_library/mixedlist.c @@ -8,16 +8,18 @@ * <http://creativecommons.org/publicdomain/zero/1.0/>. */ -#include <stdio.h> -#include <string.h> - #include <bsddialog.h> +#include <stdio.h> int main() { - int i, j, output; + int output; + unsigned int i, j; struct bsddialog_conf conf; struct bsddialog_menuitem item; + struct bsddialog_menuitem sep1[1] = { + { "", true, 0, "Checklist", "(desc)", "" } + }; struct bsddialog_menuitem check[5] = { { "+", true, 0, "Name 1", "Desc 1", "Bottom Desc 1" }, { "" , false, 0, "Name 2", "Desc 2", "Bottom Desc 2" }, @@ -25,8 +27,9 @@ int main() { "" , false, 0, "Name 4", "Desc 4", "Bottom Desc 4" }, { "+", true, 0, "Name 5", "Desc 5", "Bottom Desc 5" } }; - struct bsddialog_menuitem sep[1] = { - { "", true, 0, "Radiolist", "(desc)", "" } + struct bsddialog_menuitem sep2[2] = { + { "", true, 0, "Radiolist", "(desc)", "" }, + { "", true, 0, "Subtitle", "(desc)", "" } }; struct bsddialog_menuitem radio[5] = { { "", true, 0, "Name 1", "Desc 1", "Bottom Desc 1" }, @@ -35,36 +38,41 @@ int main() { "+", false, 0, "Name 4", "Desc 4", "Bottom Desc 4" }, { "", false, 0, "Name 5", "Desc 5", "Bottom Desc 5" } }; - struct bsddialog_menugroup group[3] = { - { BSDDIALOG_CHECKLIST, 5, check }, - { BSDDIALOG_SEPARATOR, 1, sep }, - { BSDDIALOG_RADIOLIST, 5, radio } + struct bsddialog_menugroup group[4] = { + { BSDDIALOG_SEPARATOR, 1, sep1, 0 }, + { BSDDIALOG_CHECKLIST, 5, check, 0 }, + { BSDDIALOG_SEPARATOR, 2, sep2, 0 }, + { BSDDIALOG_RADIOLIST, 5, radio, 0 } }; + if (bsddialog_init() == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } bsddialog_initconf(&conf); - conf.title = "mixedmenu"; - - if (bsddialog_init() < 0) - return -1; - - output = bsddialog_mixedlist(&conf, "dialog4ports", 20, 30, 11, 3, group, - NULL,NULL); - + conf.title = "mixedlist"; + output = bsddialog_mixedlist(&conf, "Example", 20, 0, 13, 4, group, + NULL, NULL); bsddialog_end(); + if (output == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } printf("Mixedlist:\n"); - for (i=0; i<3; i++) { - for (j=0; j<group[i].nitems; j++) { + for (i = 0; i < 4; i++) { + for (j = 0; j < group[i].nitems; j++) { item = group[i].items[j]; if (group[i].type == BSDDIALOG_SEPARATOR) printf("----- %s -----\n", item.name); else if (group[i].type == BSDDIALOG_RADIOLIST) - printf(" (%c) %s\n", item.on ? '*' : ' ', item.name); - else /* BSDDIALOG_PORTCHECKLIST */ - printf(" [%c] %s\n", item.on ? 'X' : ' ', item.name); + printf(" (%c) %s\n", + item.on ? '*' : ' ', item.name); + else /* BSDDIALOG_CHECKLIST */ + printf(" [%c] %s\n", + item.on ? 'X' : ' ', item.name); } } - - - return output; + + return (0); } diff --git a/examples_library/msgbox.c b/examples_library/msgbox.c index 487266745c65..988e2976ee8e 100644 --- a/examples_library/msgbox.c +++ b/examples_library/msgbox.c @@ -8,35 +8,31 @@ * <http://creativecommons.org/publicdomain/zero/1.0/>. */ -#include <stdio.h> -#include <string.h> - #include <bsddialog.h> +#include <stdio.h> int main() { - int input; + int output; struct bsddialog_conf conf; - /* Configuration */ - bsddialog_initconf(&conf); - conf.title = "msgbox"; - - /* Run BSDDialog */ if (bsddialog_init() == BSDDIALOG_ERROR) { printf("Error: %s\n", bsddialog_geterror()); - return -1; + return (1); } - input = bsddialog_msgbox(&conf, "Example", 7, 20); + bsddialog_initconf(&conf); + conf.title = "msgbox"; + output = bsddialog_msgbox(&conf, "Example", 7, 20); bsddialog_end(); - /* User Input */ - printf("User input: "); - switch (input) { - case BSDDIALOG_ERROR: printf("Error %s\n", bsddialog_geterror()); break; - case BSDDIALOG_OK: printf("OK\n"); break; - case BSDDIALOG_ESC: printf("ESC\n"); break; + switch (output) { + case BSDDIALOG_ERROR: + printf("Error %s\n", bsddialog_geterror()); + return (1); + case BSDDIALOG_OK: + printf("[OK]\n"); + break; } - return input; -} + return (0); +}
\ No newline at end of file diff --git a/examples_library/pause.c b/examples_library/pause.c index eb693325a18d..fd79d33aa4c0 100644 --- a/examples_library/pause.c +++ b/examples_library/pause.c @@ -8,43 +8,39 @@ * <http://creativecommons.org/publicdomain/zero/1.0/>. */ -#include <stdio.h> -#include <string.h> - #include <bsddialog.h> +#include <stdio.h> int main() { int output; + unsigned int sec; struct bsddialog_conf conf; + if (bsddialog_init() == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } bsddialog_initconf(&conf); conf.title = "pause"; - - if (bsddialog_init() < 0) - return -1; - - output = bsddialog_pause(&conf, "Example", 8, 50, 10); - + sec = 10; + output = bsddialog_pause(&conf, "Example", 8, 50, &sec); bsddialog_end(); switch (output) { + case BSDDIALOG_ERROR: + printf("Error: %s\n", bsddialog_geterror()); + return (1); case BSDDIALOG_OK: - printf("OK\n"); - break; - case BSDDIALOG_ESC: - printf("ESC\n"); + printf("[OK] remaining time: %u\n", sec); break; case BSDDIALOG_CANCEL: - printf("Cancel\n"); - break; - case BSDDIALOG_ERROR: - printf("Error: %s\n", bsddialog_geterror()); + printf("[Cancel] remaining time: %u\n", sec); break; case BSDDIALOG_TIMEOUT: printf("Timeout\n"); break; } - return output; + return (0); } diff --git a/examples_library/radiolist.c b/examples_library/radiolist.c index d4c93c3d406f..09df32ac2e74 100644 --- a/examples_library/radiolist.c +++ b/examples_library/radiolist.c @@ -8,37 +8,38 @@ * <http://creativecommons.org/publicdomain/zero/1.0/>. */ -#include <stdio.h> -#include <string.h> - #include <bsddialog.h> +#include <stdio.h> int main() { int i, output; struct bsddialog_conf conf; struct bsddialog_menuitem items[5] = { - {"", true, 0, "Name 1", "Desc 1", "Bottom Desc 1"}, - {"", false, 0, "Name 2", "Desc 2", "Bottom Desc 2"}, - {"", true, 0, "Name 3", "Desc 3", "Bottom Desc 3"}, - {"", false, 0, "Name 4", "Desc 4", "Bottom Desc 4"}, - {"", true, 0, "Name 5", "Desc 5", "Bottom Desc 5"} + {"I", true, 0, "Name 1", "Desc 1", "Bottom Desc 1"}, + {"II", false, 0, "Name 2", "Desc 2", "Bottom Desc 2"}, + {"III", true, 0, "Name 3", "Desc 3", "Bottom Desc 3"}, + {"IV", false, 0, "Name 4", "Desc 4", "Bottom Desc 4"}, + {"V", true, 0, "Name 5", "Desc 5", "Bottom Desc 5"} }; + if (bsddialog_init() == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } bsddialog_initconf(&conf); conf.title = "radiolist"; - - if (bsddialog_init() < 0) - return -1; - - output = bsddialog_radiolist(&conf, "Example", 15, 30, 5, 5, items, NULL); - + output = bsddialog_radiolist(&conf, "Example", 15, 30, 5, 5, items, + NULL); bsddialog_end(); + if (output == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } printf("Radiolist:\n"); - for (i=0; i<5; i++) + for (i = 0; i < 5; i++) printf(" (%c) %s\n", items[i].on ? '*' : ' ', items[i].name); - - - return output; -} + + return (0); +}
\ No newline at end of file diff --git a/examples_library/rangebox.c b/examples_library/rangebox.c index 624bd4e438e8..50498c459fc6 100644 --- a/examples_library/rangebox.c +++ b/examples_library/rangebox.c @@ -8,28 +8,28 @@ * <http://creativecommons.org/publicdomain/zero/1.0/>. */ -#include <stdio.h> -#include <string.h> - #include <bsddialog.h> +#include <stdio.h> int main() { int value, output; struct bsddialog_conf conf; + if (bsddialog_init() == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } bsddialog_initconf(&conf); conf.title = "rangebox"; - - if (bsddialog_init() < 0) - return -1; - value = 5; output = bsddialog_rangebox(&conf, "Example", 8, 50, 0, 10, &value); - bsddialog_end(); - - printf("Value: %d", value); - - return output; -} + if (output == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } + printf("Value: %d\n", value); + + return (0); +}
\ No newline at end of file diff --git a/examples_library/sade.c b/examples_library/sade.c deleted file mode 100644 index a159ab2afc0b..000000000000 --- a/examples_library/sade.c +++ /dev/null @@ -1,64 +0,0 @@ -/*- - * SPDX-License-Identifier: CC0-1.0 - * - * Written in 2021 by Alfonso Sabato Siciliano. - * To the extent possible under law, the author has dedicated all copyright - * and related and neighboring rights to this software to the public domain - * worldwide. This software is distributed without any warranty, see: - * <http://creativecommons.org/publicdomain/zero/1.0/>. - */ - -#include <stdio.h> -#include <string.h> - -#include <bsddialog.h> - -/* Figure 15 - https://docs.freebsd.org/en/books/handbook/bsdinstall/ */ -int main() -{ - int i, output; - struct bsddialog_conf conf; - struct bsddialog_menuitem items[5] = { - {"", false, 0, "ada0", "16 GB GPT", ""}, - {"", false, 1, "ada0p1", "512 KB freebsd-boot", ""}, - {"", false, 1, "ada0p2", "15 GB freebsd-ufs", ""}, - {"", false, 1, "ada0p3", "819 MB freebsd-swap none", ""}, - {"", false, 0, "ada1", "16 GB", ""} - }; - - bsddialog_initconf(&conf); - conf.title = "Partition Editor"; - char *text = "Please review the disk setup. When complete, press the "\ - "Finish button"; - - conf.menu.align_left = true; - - conf.button.ok_label = "Create"; - - conf.button.with_extra = true; - conf.button.extra_label = "Delete"; - - conf.button.cancel_label = "Cancel"; - - conf.button.with_help = true; - conf.button.help_label = "Revert"; - - conf.button.generic1_label = "Auto"; - conf.button.generic2_label = "Finish"; - - conf.button.default_label= "Finish"; - - if (bsddialog_init() < 0) - return -1; - - output = bsddialog_menu(&conf, text, 20, 0, 10, 5, items, NULL); - - bsddialog_end(); - - printf("Menu:\n"); - for (i=0; i<5; i++) - printf(" [%c] %s\n", items[i].on ? 'X' : ' ', items[i].name); - - - return output; -} diff --git a/examples_library/theme.c b/examples_library/theme.c index 305692d39cff..689633e624ed 100644 --- a/examples_library/theme.c +++ b/examples_library/theme.c @@ -8,55 +8,57 @@ * <http://creativecommons.org/publicdomain/zero/1.0/>. */ -#include <stdio.h> -#include <string.h> - #include <bsddialog.h> #include <bsddialog_theme.h> +#include <stdio.h> int main() { - int output; + int output, focusitem; struct bsddialog_conf conf; enum bsddialog_default_theme theme; struct bsddialog_menuitem items[4] = { - {"", false, 0, "Dialog", "Current dialog theme", "BSDDIALOG_THEME_DIALOG" }, - {"", false, 0, "BSDDialog", "Future default theme", "BSDDIALOG_THEME_DEFAULT"}, - {"", false, 0, "BlackWhite","Black and White theme", "BSDDIALOG_THEME_BLACKWHITE"}, - {"", false, 0, "Quit", "Exit", "Quit or Cancel to exit" } + {"", false, 0, "Flat", "default flat theme", + "enum bsddialog_default_theme BSDDIALOG_THEME_FLAT" }, + {"", false, 0, "3D", "pseudo 3D theme", + "enum bsddialog_default_theme BSDDIALOG_THEME_3D" }, + {"", false, 0, "BlackWhite","black and white theme", + "enum bsddialog_default_theme BSDDIALOG_THEME_BLACKWHITE" }, + {"", false, 0, "Quit", "Exit", "Quit, Cancel or ESC to exit" } }; + if (bsddialog_init() == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } + bsddialog_initconf(&conf); + conf.ascii_lines = true; + bsddialog_backtitle(&conf, "Theme Example"); bsddialog_initconf(&conf); + conf.key.enable_esc = true; conf.title = " Theme "; - - if (bsddialog_init() == BSDDIALOG_ERROR) - return BSDDIALOG_ERROR; - + focusitem = -1; while (true) { - bsddialog_backtitle(&conf, "Theme Example"); - - output = bsddialog_menu(&conf, "Choose theme", 15, 40, 4, 4, items, NULL); + output = bsddialog_menu(&conf, "Choose theme", 15, 45, 4, 4, + items, &focusitem); if (output != BSDDIALOG_OK || items[3].on) break; if (items[0].on) { - theme = BSDDIALOG_THEME_DIALOG; - conf.menu.default_item = items[0].name; - } - else if (items[1].on) { - theme = BSDDIALOG_THEME_BSDDIALOG; - conf.menu.default_item = items[1].name; - } - else if (items[2].on) { + theme = BSDDIALOG_THEME_FLAT; + focusitem = 0; + } else if (items[1].on) { + theme = BSDDIALOG_THEME_3D; + focusitem = 1; + } else if (items[2].on) { theme = BSDDIALOG_THEME_BLACKWHITE; - conf.menu.default_item = items[2].name; + focusitem = 2; } - bsddialog_set_default_theme(theme); } - bsddialog_end(); + bsddialog_end(); - return output; -} + return (0); +}
\ No newline at end of file diff --git a/examples_library/timebox.c b/examples_library/timebox.c index 117d45dcdb7b..a9354c99c9be 100644 --- a/examples_library/timebox.c +++ b/examples_library/timebox.c @@ -8,12 +8,10 @@ * <http://creativecommons.org/publicdomain/zero/1.0/>. */ +#include <bsddialog.h> #include <stdio.h> -#include <string.h> #include <time.h> -#include <bsddialog.h> - int main() { int output; @@ -28,31 +26,19 @@ int main() mm = localtm->tm_min; ss = localtm->tm_sec; + if (bsddialog_init() == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } bsddialog_initconf(&conf); conf.title = "timebox"; - conf.bottomtitle = "Press TAB and arrows"; - - if (bsddialog_init() < 0) - return -1; - - output = bsddialog_timebox(&conf, "Example", 10, 50, &hh, &mm, &ss); - + output = bsddialog_timebox(&conf, "Example", 9, 35, &hh, &mm, &ss); bsddialog_end(); - - switch (output) { - case BSDDIALOG_OK: - printf("Time: [%u:%u:%u]\n", hh, mm, ss); - break; - case BSDDIALOG_ESC: - printf("ESC\n"); - break; - case BSDDIALOG_CANCEL: - printf("Cancel\n"); - break; - case BSDDIALOG_ERROR: + if (output == BSDDIALOG_ERROR) { printf("Error: %s\n", bsddialog_geterror()); - break; + return (1); } + printf("Time: %u:%u:%u\n", hh, mm, ss); - return output; -} + return (0); +}
\ No newline at end of file diff --git a/examples_library/treeview.c b/examples_library/treeview.c deleted file mode 100644 index 7fa3d229adf9..000000000000 --- a/examples_library/treeview.c +++ /dev/null @@ -1,46 +0,0 @@ -/*- - * SPDX-License-Identifier: CC0-1.0 - * - * Written in 2021 by Alfonso Sabato Siciliano. - * To the extent possible under law, the author has dedicated all copyright - * and related and neighboring rights to this software to the public domain - * worldwide. This software is distributed without any warranty, see: - * <http://creativecommons.org/publicdomain/zero/1.0/>. - */ - -#include <stdio.h> -#include <string.h> - -#include <bsddialog.h> - -int main() -{ - int i, output; - struct bsddialog_conf conf; - struct bsddialog_menuitem items[5] = { - {"", false, 0, "Name 1", "Desc 1", "Bottom Desc 1"}, - {"", false, 1, "Name 2", "Desc 2", "Bottom Desc 2"}, - {"", false, 1, "Name 3", "Desc 3", "Bottom Desc 3"}, - {"", false, 2, "Name 4", "Desc 4", "Bottom Desc 4"}, - {"", false, 1, "Name 5", "Desc 5", "Bottom Desc 5"} - }; - - bsddialog_initconf(&conf); - conf.title = "radiolist"; - conf.menu.no_name = true; - conf.menu.align_left = true; - - if (bsddialog_init() < 0) - return -1; - - output = bsddialog_radiolist(&conf, "Example", 15, 30, 5, 5, items, NULL); - - bsddialog_end(); - - printf("Treeview:\n"); - for (i=0; i<5; i++) - printf(" (%c) %s\n", items[i].on ? '*' : ' ', items[i].name); - - - return output; -} diff --git a/examples_library/yesno.c b/examples_library/yesno.c index 783fcf57cbb6..36cb59fa013b 100644 --- a/examples_library/yesno.c +++ b/examples_library/yesno.c @@ -8,25 +8,34 @@ * <http://creativecommons.org/publicdomain/zero/1.0/>. */ -#include <stdio.h> -#include <string.h> - #include <bsddialog.h> +#include <stdio.h> int main() { int output; struct bsddialog_conf conf; + if (bsddialog_init() == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } bsddialog_initconf(&conf); conf.title = "yesno"; - - if (bsddialog_init() < 0) - return -1; - output = bsddialog_yesno(&conf, "Example", 7, 25); - bsddialog_end(); - return output; -} + switch (output) { + case BSDDIALOG_ERROR: + printf("Error %s\n", bsddialog_geterror()); + return (1); + case BSDDIALOG_YES: + printf("[YES]\n"); + break; + case BSDDIALOG_NO: + printf("[NO]\n"); + break; + } + + return (0); +}
\ No newline at end of file diff --git a/examples_utility/buildlist.sh b/examples_utility/buildlist.sh deleted file mode 100755 index cefeea8f3c51..000000000000 --- a/examples_utility/buildlist.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh -#- -# SPDX-License-Identifier: CC0-1.0 -# -# Written in 2021 by Alfonso Sabato Siciliano. -# To the extent possible under law, the author has dedicated all copyright -# and related and neighboring rights to this software to the public domain -# worldwide. This software is distributed without any warranty, see: -# <http://creativecommons.org/publicdomain/zero/1.0/>. - -./bsddialog --title buildlist --buildlist "Hello World!" 15 40 5 \ - "Tag 1" "DESC 1 xyz" off \ - "Tag 2" "DESC 2 xyz" off \ - "Tag 3" "DESC 3 xyz" on \ - "Tag 4" "DESC 4 xyz" off \ - "Tag 5" "DESC 5 xyz" off \ - 2>out.txt ; cat out.txt ; rm out.txt diff --git a/examples_utility/calendar.sh b/examples_utility/calendar.sh new file mode 100644 index 000000000000..a7ce4f1bb1d5 --- /dev/null +++ b/examples_utility/calendar.sh @@ -0,0 +1,34 @@ +#!/bin/sh +#- +# SPDX-License-Identifier: CC0-1.0 +# +# Written in 2022 by Alfonso Sabato Siciliano. +# +# To the extent possible under law, the author has dedicated all copyright +# and related and neighboring rights to this software to the public domain +# worldwide. This software is distributed without any warranty, see: +# <http://creativecommons.org/publicdomain/zero/1.0/>. + +: ${BSDDIALOG_ERROR=255} +: ${BSDDIALOG_OK=0} +: ${BSDDIALOG_CANCEL=1} +: ${BSDDIALOG_ESC=5} + +DATE=$(./bsddialog --title " calendar " --date-format "%x" \ + --calendar "Hello World!" 20 40 \ +3>&1 1>&2 2>&3 3>&-) + +case $? in + $BSDDIALOG_ERROR ) + exit 1 + ;; + $BSDDIALOG_ESC ) + echo "[ESC]" + ;; + $BSDDIALOG_CANCEL ) + echo "[Cancel]" + ;; + $BSDDIALOG_OK ) + echo "[OK] $DATE" + ;; +esac diff --git a/examples_utility/checklist.sh b/examples_utility/checklist.sh index 5f9a17cb64b1..7ff525cf765d 100755 --- a/examples_utility/checklist.sh +++ b/examples_utility/checklist.sh @@ -3,15 +3,36 @@ # SPDX-License-Identifier: CC0-1.0 # # Written in 2021 by Alfonso Sabato Siciliano. +# # To the extent possible under law, the author has dedicated all copyright # and related and neighboring rights to this software to the public domain # worldwide. This software is distributed without any warranty, see: -# <http://creativecommons.org/publicdomain/zero/1.0/>. +# <http://creativecommons.org/publicdomain/zero/1.0/>. + +: ${BSDDIALOG_ERROR=255} +: ${BSDDIALOG_OK=0} +: ${BSDDIALOG_CANCEL=1} +: ${BSDDIALOG_ESC=5} + +ITEMS=$(./bsddialog --title " checklist " --checklist "Hello World!" 15 30 5 \ + "1 Name" "DESC 1 xyz" on \ + "2 Name" "DESC 2 xyz" off \ + "3 Name" "DESC 3 xyz" on \ + "4 Name" "DESC 4 xyz" off \ + "5 Name" "DESC 5 xyz" on \ +3>&1 1>&2 2>&3 3>&-) -./bsddialog --title checklist --checklist "Hello World!" 15 30 5 \ - "Tag 1" "DESC 1 xyz" on \ - "Tag 2" "DESC 2 xyz" off \ - "Tag 3" "DESC 3 xyz" on \ - "Tag 4" "DESC 4 xyz" off \ - "Tag 5" "DESC 5 xyz" on \ - 2>out.txt ; cat out.txt ; rm out.txt +case $? in + $BSDDIALOG_ERROR ) + exit 1 + ;; + $BSDDIALOG_ESC ) + echo "[ESC]" + ;; + $BSDDIALOG_CANCEL ) + echo "[Cancel]" + ;; + $BSDDIALOG_OK ) + echo "[OK] $ITEMS" + ;; +esac diff --git a/examples_utility/datebox.sh b/examples_utility/datebox.sh new file mode 100755 index 000000000000..bea4559dfbec --- /dev/null +++ b/examples_utility/datebox.sh @@ -0,0 +1,34 @@ +#!/bin/sh +#- +# SPDX-License-Identifier: CC0-1.0 +# +# Written in 2023 by Alfonso Sabato Siciliano. +# +# To the extent possible under law, the author has dedicated all copyright +# and related and neighboring rights to this software to the public domain +# worldwide. This software is distributed without any warranty, see: +# <http://creativecommons.org/publicdomain/zero/1.0/>. + +: ${BSDDIALOG_ERROR=255} +: ${BSDDIALOG_OK=0} +: ${BSDDIALOG_CANCEL=1} +: ${BSDDIALOG_ESC=5} + +DATE=$(./bsddialog --title " datebox " --date-format "%x" \ + --datebox "Hello World!" 9 30 \ +3>&1 1>&2 2>&3 3>&-) + +case $? in + $BSDDIALOG_ERROR ) + exit 1 + ;; + $BSDDIALOG_ESC ) + echo "[ESC]" + ;; + $BSDDIALOG_CANCEL ) + echo "[Cancel]" + ;; + $BSDDIALOG_OK ) + echo "[OK] $DATE" + ;; +esac diff --git a/examples_utility/form.sh b/examples_utility/form.sh index 5d6b6ee1b606..ee25fa9cf352 100755 --- a/examples_utility/form.sh +++ b/examples_utility/form.sh @@ -3,17 +3,36 @@ # SPDX-License-Identifier: CC0-1.0 # # Written in 2021 by Alfonso Sabato Siciliano. +# # To the extent possible under law, the author has dedicated all copyright # and related and neighboring rights to this software to the public domain # worldwide. This software is distributed without any warranty, see: -# <http://creativecommons.org/publicdomain/zero/1.0/>. +# <http://creativecommons.org/publicdomain/zero/1.0/>. -./bsddialog --title " form " --form "Hello World!" 12 40 5 \ - Label1: 1 1 Value1 1 9 18 25 \ - Label2: 2 1 Value2 2 9 18 25 \ - Label3: 3 1 Value3 3 9 18 25 \ - Label4: 4 1 Value4 4 9 18 25 \ - Label5: 5 1 Value5 5 9 18 25 \ - 2>out.txt ; cat out.txt ; rm out.txt +: ${BSDDIALOG_ERROR=255} +: ${BSDDIALOG_OK=0} +: ${BSDDIALOG_CANCEL=1} +: ${BSDDIALOG_ESC=5} +FORMS=$(./bsddialog --title " form " --form "Hello World!" 12 40 5 \ + Label1: 0 0 Value1 0 8 18 25 \ + Label2: 1 0 Value2 1 8 18 25 \ + Label3: 2 0 Value3 2 8 18 25 \ + Label4: 3 0 Value4 3 8 18 25 \ + Label5: 4 0 Value5 4 8 18 25 \ +3>&1 1>&2 2>&3 3>&-) +case $? in + $BSDDIALOG_ERROR ) + exit 1 + ;; + $BSDDIALOG_ESC ) + echo "[ESC]" + ;; + $BSDDIALOG_CANCEL ) + echo "[Cancel]" + ;; + $BSDDIALOG_OK ) + echo "[OK] $FORMS" + ;; +esac diff --git a/examples_utility/gauge.sh b/examples_utility/gauge.sh index 82d15655efe2..a06a77034b75 100755 --- a/examples_utility/gauge.sh +++ b/examples_utility/gauge.sh @@ -3,26 +3,26 @@ # SPDX-License-Identifier: CC0-1.0 # # Written in 2021 by Alfonso Sabato Siciliano. +# # To the extent possible under law, the author has dedicated all copyright # and related and neighboring rights to this software to the public domain # worldwide. This software is distributed without any warranty, see: -# <http://creativecommons.org/publicdomain/zero/1.0/>. +# <http://creativecommons.org/publicdomain/zero/1.0/>. -input="A B C D E F G" -total=`echo $input | awk '{print split($0, a)}'` -curr=1 -for i in $input +characters="A B C D E F G" +total=`echo $characters | awk '{print split($0, a)}'` +i=1 +for c in $characters do sleep 1 - perc="$(expr $(expr $curr "*" 100 ) "/" $total )" - echo XXX - echo $perc - echo "[$curr/$total] Input: $i" - echo XXX - if [ $curr -eq $total ] - then - echo EOF - fi - curr=`expr $curr + 1` -done | ./bsddialog --title gauge --gauge "[0/$total] Starting..." 10 70 0 - + echo XXX + echo "$(expr $(expr $i "*" 100) "/" $total)" + echo "[$i/$total] Char: $c" + echo XXX + if [ $i -eq $total ] + then + sleep 1 + echo EOF + fi + i=`expr $i + 1` +done | ./bsddialog --title " gauge " --gauge "[0/$total] Starting..." 10 70 diff --git a/examples_utility/infobox.sh b/examples_utility/infobox.sh index 468ecff826fd..b782e8cbf69e 100755 --- a/examples_utility/infobox.sh +++ b/examples_utility/infobox.sh @@ -3,10 +3,10 @@ # SPDX-License-Identifier: CC0-1.0 # # Written in 2021 by Alfonso Sabato Siciliano. +# # To the extent possible under law, the author has dedicated all copyright # and related and neighboring rights to this software to the public domain # worldwide. This software is distributed without any warranty, see: -# <http://creativecommons.org/publicdomain/zero/1.0/>. - +# <http://creativecommons.org/publicdomain/zero/1.0/>. -./bsddialog --sleep 3 --title infobox --infobox "Hello World!\n3 seconds" 6 20 +./bsddialog --normal-screen --title " infobox " --infobox "Hello World!" 6 20 diff --git a/examples_utility/inputbox.sh b/examples_utility/inputbox.sh index 27073468a478..756aec3c0241 100755 --- a/examples_utility/inputbox.sh +++ b/examples_utility/inputbox.sh @@ -3,10 +3,31 @@ # SPDX-License-Identifier: CC0-1.0 # # Written in 2021 by Alfonso Sabato Siciliano. +# # To the extent possible under law, the author has dedicated all copyright # and related and neighboring rights to this software to the public domain # worldwide. This software is distributed without any warranty, see: -# <http://creativecommons.org/publicdomain/zero/1.0/>. +# <http://creativecommons.org/publicdomain/zero/1.0/>. + +: ${BSDDIALOG_ERROR=255} +: ${BSDDIALOG_OK=0} +: ${BSDDIALOG_CANCEL=1} +: ${BSDDIALOG_ESC=5} + +FORM=$(./bsddialog --title " inputbox " --inputbox "Hello World!" 12 40 init \ +3>&1 1>&2 2>&3 3>&-) -./bsddialog --title " inputbox " --inputbox "Hello World!" 12 40 init \ - 2>out.txt ; cat out.txt ; rm out.txt +case $? in + $BSDDIALOG_ERROR ) + exit 1 + ;; + $BSDDIALOG_ESC ) + echo "[ESC]" + ;; + $BSDDIALOG_CANCEL ) + echo "[Cancel]" + ;; + $BSDDIALOG_OK ) + echo "[OK] $FORM" + ;; +esac diff --git a/examples_utility/menu.sh b/examples_utility/menu.sh index 66c9f54beb4c..5b2f090037d8 100755 --- a/examples_utility/menu.sh +++ b/examples_utility/menu.sh @@ -3,15 +3,36 @@ # SPDX-License-Identifier: CC0-1.0 # # Written in 2021 by Alfonso Sabato Siciliano. +# # To the extent possible under law, the author has dedicated all copyright # and related and neighboring rights to this software to the public domain # worldwide. This software is distributed without any warranty, see: -# <http://creativecommons.org/publicdomain/zero/1.0/>. +# <http://creativecommons.org/publicdomain/zero/1.0/>. + +: ${BSDDIALOG_ERROR=255} +: ${BSDDIALOG_OK=0} +: ${BSDDIALOG_CANCEL=1} +: ${BSDDIALOG_ESC=5} + +ITEM=$(./bsddialog --title " menu " --menu "Hello World!" 15 30 5 \ + "1 Name" "DESC 1 xyz" \ + "2 Name" "DESC 2 xyz" \ + "3 Name" "DESC 3 xyz" \ + "4 Name" "DESC 4 xyz" \ + "5 Name" "DESC 5 xyz" \ +3>&1 1>&2 2>&3 3>&-) -./bsddialog --title menu --menu "Hello World!" 15 30 5 \ - "Tag 1" "DESC 1 xyz" \ - "Tag 2" "DESC 2 xyz" \ - "Tag 3" "DESC 3 xyz" \ - "Tag 4" "DESC 4 xyz" \ - "Tag 5" "DESC 5 xyz" \ - 2>out.txt ; cat out.txt ; rm out.txt +case $? in + $BSDDIALOG_ERROR ) + exit 1 + ;; + $BSDDIALOG_ESC ) + echo "[ESC]" + ;; + $BSDDIALOG_CANCEL ) + echo "[Cancel]" + ;; + $BSDDIALOG_OK ) + echo "[OK] $ITEM" + ;; +esac diff --git a/examples_utility/mixedform.sh b/examples_utility/mixedform.sh index 6677c20781ad..6b690e7e5b8c 100755 --- a/examples_utility/mixedform.sh +++ b/examples_utility/mixedform.sh @@ -3,13 +3,35 @@ # SPDX-License-Identifier: CC0-1.0 # # Written in 2021 by Alfonso Sabato Siciliano. +# # To the extent possible under law, the author has dedicated all copyright # and related and neighboring rights to this software to the public domain # worldwide. This software is distributed without any warranty, see: -# <http://creativecommons.org/publicdomain/zero/1.0/>. +# <http://creativecommons.org/publicdomain/zero/1.0/>. + +: ${BSDDIALOG_ERROR=255} +: ${BSDDIALOG_OK=0} +: ${BSDDIALOG_CANCEL=1} +: ${BSDDIALOG_ESC=5} + +FORMS=$(./bsddialog --insecure --title " mixedform " \ + --mixedform "Hello World!" 12 40 3 \ + Label: 0 0 Entry 0 10 18 25 0 \ + Label: 1 0 Read-Only 1 10 18 25 2 \ + Password: 2 0 "" 2 10 18 25 1 \ +3>&1 1>&2 2>&3 3>&-) -./bsddialog --insecure --title " mixedform " --mixedform "Hello World!" 12 40 5 \ - Label: 1 1 Entry 1 11 18 25 0 \ - Label: 2 1 Read-Only 2 11 18 25 2 \ - Password: 3 1 Value2 3 11 18 25 1 \ - 2>out.txt ; cat out.txt ; rm out.txt +case $? in + $BSDDIALOG_ERROR ) + exit 1 + ;; + $BSDDIALOG_ESC ) + echo "[ESC]" + ;; + $BSDDIALOG_CANCEL ) + echo "[Cancel]" + ;; + $BSDDIALOG_OK ) + echo "[OK] $FORMS" + ;; +esac diff --git a/examples_utility/mixedgauge.sh b/examples_utility/mixedgauge.sh index ce867973859a..aa4ff2910cd9 100755 --- a/examples_utility/mixedgauge.sh +++ b/examples_utility/mixedgauge.sh @@ -3,32 +3,31 @@ # SPDX-License-Identifier: CC0-1.0 # # Written in 2021 by Alfonso Sabato Siciliano. +# # To the extent possible under law, the author has dedicated all copyright # and related and neighboring rights to this software to the public domain # worldwide. This software is distributed without any warranty, see: -# <http://creativecommons.org/publicdomain/zero/1.0/>. - +# <http://creativecommons.org/publicdomain/zero/1.0/>. -input="A B C D E F G H" -total=`echo $input | awk '{print split($0, a)}'` -curr=1 -for i in $input +perc=0 +mainperc=50 +while [ $perc -le 100 ] do - perc="$(expr $(expr $curr "*" 100 ) "/" $total )" - curr=`expr $curr + 1` - ./bsddialog --sleep 1 --title " mixedgauge " --mixedgauge "Example" 25 50 $perc \ - "Hidden!" 8 \ - "Label 1" 0 \ - "Label 2" 1 \ - "Label 3" 2 \ - "Label 4" 3 \ - "Label 5" 4 \ - "Label 6" 5 \ - "Label 7" 6 \ - "Label 8" 7 \ - "Label 9" 9 \ - "Label 10" 10 \ - "Label X" " -$perc" - #sleep 1 -done + ./bsddialog --sleep 1 --title " mixedgauge " \ + --mixedgauge "Example..." 20 45 $mainperc \ + "Label 1" " -1" \ + "Label 2" " -2" \ + "Label 3" " -3" \ + "Label 4" " -4" \ + "Label 5" " -5" \ + "Label 6" " -6" \ + "Label 7" " -7" \ + "Label 8" " -8" \ + "Label 9" " -9" \ + "Label 10" " -10" \ + "Label 11" " -11" \ + "Label X" $perc + perc=`expr $perc + 20` + mainperc=`expr $mainperc + 10` +done diff --git a/examples_utility/msgbox.sh b/examples_utility/msgbox.sh index 49cb626f4055..cad3c9ecb72a 100755 --- a/examples_utility/msgbox.sh +++ b/examples_utility/msgbox.sh @@ -3,9 +3,26 @@ # SPDX-License-Identifier: CC0-1.0 # # Written in 2021 by Alfonso Sabato Siciliano. +# # To the extent possible under law, the author has dedicated all copyright # and related and neighboring rights to this software to the public domain # worldwide. This software is distributed without any warranty, see: -# <http://creativecommons.org/publicdomain/zero/1.0/>. +# <http://creativecommons.org/publicdomain/zero/1.0/>. + +: ${BSDDIALOG_ERROR=255} +: ${BSDDIALOG_OK=0} +: ${BSDDIALOG_ESC=5} + +./bsddialog --title " msgbox " --msgbox "Hello World!" 6 20 -./bsddialog --title msgbox --msgbox "Hello World!" 6 20 +case $? in + $BSDDIALOG_ERROR ) + exit 1 + ;; + $BSDDIALOG_ESC ) + echo "[ESC]" + ;; + $BSDDIALOG_OK ) + echo "[OK]" + ;; +esac diff --git a/examples_utility/passwordbox.sh b/examples_utility/passwordbox.sh index 5c6a91e0f057..b43ef6f6b994 100755 --- a/examples_utility/passwordbox.sh +++ b/examples_utility/passwordbox.sh @@ -3,10 +3,32 @@ # SPDX-License-Identifier: CC0-1.0 # # Written in 2021 by Alfonso Sabato Siciliano. +# # To the extent possible under law, the author has dedicated all copyright # and related and neighboring rights to this software to the public domain # worldwide. This software is distributed without any warranty, see: -# <http://creativecommons.org/publicdomain/zero/1.0/>. +# <http://creativecommons.org/publicdomain/zero/1.0/>. + +: ${BSDDIALOG_ERROR=255} +: ${BSDDIALOG_OK=0} +: ${BSDDIALOG_CANCEL=1} +: ${BSDDIALOG_ESC=5} + +FORM=$(./bsddialog --insecure --title " password " \ + --passwordbox "Hello World!" 12 40 \ +3>&1 1>&2 2>&3 3>&-) -./bsddialog --insecure --title " password " --passwordbox "Hello World!" 12 40 \ - 2>out.txt ; cat out.txt ; rm out.txt +case $? in + $BSDDIALOG_ERROR ) + exit 1 + ;; + $BSDDIALOG_ESC ) + echo "[ESC]" + ;; + $BSDDIALOG_CANCEL ) + echo "[Cancel]" + ;; + $BSDDIALOG_OK ) + echo "[OK] $FORM" + ;; +esac diff --git a/examples_utility/passwordform.sh b/examples_utility/passwordform.sh index 5cfe0b0fa0a1..874a185ee66f 100755 --- a/examples_utility/passwordform.sh +++ b/examples_utility/passwordform.sh @@ -3,15 +3,37 @@ # SPDX-License-Identifier: CC0-1.0 # # Written in 2021 by Alfonso Sabato Siciliano. +# # To the extent possible under law, the author has dedicated all copyright # and related and neighboring rights to this software to the public domain # worldwide. This software is distributed without any warranty, see: -# <http://creativecommons.org/publicdomain/zero/1.0/>. +# <http://creativecommons.org/publicdomain/zero/1.0/>. + +: ${BSDDIALOG_ERROR=255} +: ${BSDDIALOG_OK=0} +: ${BSDDIALOG_CANCEL=1} +: ${BSDDIALOG_ESC=5} + +FORMS=$(./bsddialog --insecure --title " passwordform " \ + --passwordform "Example" 12 40 5 \ + Password1: 0 0 "" 0 11 18 25 \ + Password2: 1 0 "" 1 11 18 25 \ + Password3: 2 0 "" 2 11 18 25 \ + Password4: 3 0 "" 3 11 18 25 \ + Password5: 4 0 "" 4 11 18 25 \ +3>&1 1>&2 2>&3 3>&-) -./bsddialog --insecure --title " passwordform " --passwordform "Example" 12 40 5 \ - Password1: 1 1 Value1 1 12 18 25 \ - Password2: 2 1 Value2 2 12 18 25 \ - Password3: 3 1 Value3 3 12 18 25 \ - Password4: 4 1 Value4 4 12 18 25 \ - Password5: 5 1 Value5 5 12 18 25 \ - 2>out.txt ; cat out.txt ; rm out.txt +case $? in + $BSDDIALOG_ERROR ) + exit 1 + ;; + $BSDDIALOG_ESC ) + echo "[ESC]" + ;; + $BSDDIALOG_CANCEL ) + echo "[Cancel]" + ;; + $BSDDIALOG_OK ) + echo "[OK] $FORMS" + ;; +esac diff --git a/examples_utility/pause.sh b/examples_utility/pause.sh new file mode 100755 index 000000000000..225549dddffb --- /dev/null +++ b/examples_utility/pause.sh @@ -0,0 +1,36 @@ +#!/bin/sh +#- +# SPDX-License-Identifier: CC0-1.0 +# +# Written in 2021 by Alfonso Sabato Siciliano. +# +# To the extent possible under law, the author has dedicated all copyright +# and related and neighboring rights to this software to the public domain +# worldwide. This software is distributed without any warranty, see: +# <http://creativecommons.org/publicdomain/zero/1.0/>. + +: ${BSDDIALOG_ERROR=255} +: ${BSDDIALOG_OK=0} +: ${BSDDIALOG_CANCEL=1} +: ${BSDDIALOG_TIMEOUT=4} +: ${BSDDIALOG_ESC=5} + +./bsddialog --title " pause " --pause "Hello World!" 7 35 10 + +case $? in + $BSDDIALOG_ERROR ) + exit 1 + ;; + $BSDDIALOG_ESC ) + echo "[ESC]" + ;; + $BSDDIALOG_TIMEOUT ) + echo "[TIMEOUT]" + ;; + $BSDDIALOG_CANCEL ) + echo "[Cancel]" + ;; + $BSDDIALOG_OK ) + echo "[OK]" + ;; +esac diff --git a/examples_utility/radiolist.sh b/examples_utility/radiolist.sh index c1c7724eb09d..359473abf35e 100755 --- a/examples_utility/radiolist.sh +++ b/examples_utility/radiolist.sh @@ -3,15 +3,36 @@ # SPDX-License-Identifier: CC0-1.0 # # Written in 2021 by Alfonso Sabato Siciliano. +# # To the extent possible under law, the author has dedicated all copyright # and related and neighboring rights to this software to the public domain # worldwide. This software is distributed without any warranty, see: -# <http://creativecommons.org/publicdomain/zero/1.0/>. +# <http://creativecommons.org/publicdomain/zero/1.0/>. + +: ${BSDDIALOG_ERROR=255} +: ${BSDDIALOG_OK=0} +: ${BSDDIALOG_CANCEL=1} +: ${BSDDIALOG_ESC=5} + +ITEM=$(./bsddialog --title " radiolist " --radiolist "Hello World!" 15 30 5 \ + "1 Name" "DESC 1 xyz" off \ + "2 Name" "DESC 2 xyz" off \ + "3 Name" "DESC 3 xyz" on \ + "4 Name" "DESC 4 xyz" off \ + "5 Name" "DESC 5 xyz" off \ +3>&1 1>&2 2>&3 3>&-) -./bsddialog --title radiolist --radiolist "Hello World!" 15 30 5 \ - "Tag 1" "DESC 1 xyz" off \ - "Tag 2" "DESC 2 xyz" off \ - "Tag 3" "DESC 3 xyz" on \ - "Tag 4" "DESC 4 xyz" off \ - "Tag 5" "DESC 5 xyz" off \ - 2>out.txt ; cat out.txt ; rm out.txt +case $? in + $BSDDIALOG_ERROR ) + exit 1 + ;; + $BSDDIALOG_ESC ) + echo "[ESC]" + ;; + $BSDDIALOG_CANCEL ) + echo "[Cancel]" + ;; + $BSDDIALOG_OK ) + echo "[OK] $ITEM" + ;; +esac diff --git a/examples_utility/rangebox.sh b/examples_utility/rangebox.sh new file mode 100644 index 000000000000..9b3213d8acad --- /dev/null +++ b/examples_utility/rangebox.sh @@ -0,0 +1,33 @@ +#!/bin/sh +#- +# SPDX-License-Identifier: CC0-1.0 +# +# Written in 2023 by Alfonso Sabato Siciliano. +# +# To the extent possible under law, the author has dedicated all copyright +# and related and neighboring rights to this software to the public domain +# worldwide. This software is distributed without any warranty, see: +# <http://creativecommons.org/publicdomain/zero/1.0/>. + +: ${BSDDIALOG_ERROR=255} +: ${BSDDIALOG_OK=0} +: ${BSDDIALOG_CANCEL=1} +: ${BSDDIALOG_ESC=5} + +VALUE=$(./bsddialog --title " rangebox " --rangebox "Hello World!" 7 35 0 10 5 \ +3>&1 1>&2 2>&3 3>&-) + +case $? in + $BSDDIALOG_ERROR ) + exit 1 + ;; + $BSDDIALOG_ESC ) + echo "[ESC]" + ;; + $BSDDIALOG_CANCEL ) + echo "[Cancel]" + ;; + $BSDDIALOG_OK ) + echo "[OK] Value: $VALUE" + ;; +esac diff --git a/examples_utility/timebox.sh b/examples_utility/timebox.sh new file mode 100755 index 000000000000..233434b29195 --- /dev/null +++ b/examples_utility/timebox.sh @@ -0,0 +1,33 @@ +#!/bin/sh +#- +# SPDX-License-Identifier: CC0-1.0 +# +# Written in 2021 by Alfonso Sabato Siciliano. +# +# To the extent possible under law, the author has dedicated all copyright +# and related and neighboring rights to this software to the public domain +# worldwide. This software is distributed without any warranty, see: +# <http://creativecommons.org/publicdomain/zero/1.0/>. + +: ${BSDDIALOG_ERROR=255} +: ${BSDDIALOG_OK=0} +: ${BSDDIALOG_CANCEL=1} +: ${BSDDIALOG_ESC=5} + +TIME=$(./bsddialog --title " timebox " --timebox "Hello World!" 8 25 \ +3>&1 1>&2 2>&3 3>&-) + +case $? in + $BSDDIALOG_ERROR ) + exit 1 + ;; + $BSDDIALOG_ESC ) + echo "[ESC]" + ;; + $BSDDIALOG_CANCEL ) + echo "[Cancel]" + ;; + $BSDDIALOG_OK ) + echo "[OK] $TIME" + ;; +esac diff --git a/examples_utility/treeview.sh b/examples_utility/treeview.sh deleted file mode 100755 index 5286de673a1d..000000000000 --- a/examples_utility/treeview.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh -#- -# SPDX-License-Identifier: CC0-1.0 -# -# Written in 2021 by Alfonso Sabato Siciliano. -# To the extent possible under law, the author has dedicated all copyright -# and related and neighboring rights to this software to the public domain -# worldwide. This software is distributed without any warranty, see: -# <http://creativecommons.org/publicdomain/zero/1.0/>. - -./bsddialog --title treeview --treeview "Hello World!" 15 40 5 \ - 0 "Tag 1" "DESC 1 xyz" off \ - 1 "Tag 2" "DESC 2 xyz" off \ - 2 "Tag 3" "DESC 3 xyz" on \ - 1 "Tag 4" "DESC 4 xyz" off \ - 1 "Tag 5" "DESC 5 xyz" off \ - 2>out.txt ; cat out.txt ; rm out.txt diff --git a/examples_utility/yesno.sh b/examples_utility/yesno.sh index 7d1d33530606..d384086657e6 100755 --- a/examples_utility/yesno.sh +++ b/examples_utility/yesno.sh @@ -3,9 +3,30 @@ # SPDX-License-Identifier: CC0-1.0 # # Written in 2021 by Alfonso Sabato Siciliano. +# # To the extent possible under law, the author has dedicated all copyright # and related and neighboring rights to this software to the public domain # worldwide. This software is distributed without any warranty, see: -# <http://creativecommons.org/publicdomain/zero/1.0/>. +# <http://creativecommons.org/publicdomain/zero/1.0/>. + +: ${BSDDIALOG_ERROR=255} +: ${BSDDIALOG_YES=0} +: ${BSDDIALOG_NO=1} +: ${BSDDIALOG_ESC=5} + +./bsddialog --title " yesno " --yesno "Hello World!" 6 25 -./bsddialog --title yesno --yesno "Hello World!" 6 25 +case $? in + $BSDDIALOG_ERROR ) + exit 1 + ;; + $BSDDIALOG_ESC ) + echo "[ESC]" + ;; + $BSDDIALOG_NO ) + echo "[NO]" + ;; + $BSDDIALOG_YES ) + echo "[YES]" + ;; +esac diff --git a/lib/GNUMakefile b/lib/GNUmakefile index 045b58828a07..7c7a9bc25ee4 100644 --- a/lib/GNUMakefile +++ b/lib/GNUmakefile @@ -1,17 +1,21 @@ # PUBLIC DOMAIN - NO WARRANTY, see: -# <http://creativecommons.org/publicdomain/zero/1.0/> +# <http://creativecommons.org/publicdomain/zero/1.0/> # # Written in 2021 by Alfonso Sabato Siciliano -VERSION = 0.1 LIBRARY = bsddialog LIBRARY_SO = lib${LIBRARY:=.so} -HEADERS = bsddialog.h bsddialog_theme.h -SOURCES = barbox.c formbox.c infobox.c libbsddialog.c lib_util.c menubox.c \ - messagebox.c textbox.c theme.c timebox.c +HEADERS = bsddialog.h bsddialog_theme.h bsddialog_progressview.h +SOURCES = barbox.c datebox.c formbox.c libbsddialog.c lib_util.c \ + menubox.c messagebox.c textbox.c theme.c timebox.c OBJECTS = $(SOURCES:.c=.o) -CFLAGS = -g -Wall -Werror -fpic -LDFLAGS = -lform -lncurses -ltinfo + +ifneq ($(ENABLEDEBUG),) +CFLAGS += -g +endif +CFLAGS += -D_XOPEN_SOURCE_EXTENDED -D_XOPEN_SOURCE -D_GNU_SOURCE \ + -Wall -Wextra -Werror -fpic +LDFLAGS += -lncursesw -ltinfo LIBFLAG = -shared RM = rm -f diff --git a/lib/Makefile b/lib/Makefile index 08d8db5191dc..252b33f79848 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,77 +1,44 @@ -# Any copyright is dedicated to the Public Domain, see: +# PUBLIC DOMAIN - NO WARRANTY, see: # <http://creativecommons.org/publicdomain/zero/1.0/> # # Written in 2021 by Alfonso Sabato Siciliano -VERSION = 0.0.1 LIBRARY = bsddialog LIBRARY_SO = lib${LIBRARY:=.so} -HEADERS = bsddialog.h bsddialog_theme.h -SOURCES = barbox.c formbox.c infobox.c libbsddialog.c lib_util.c menubox.c \ - messagebox.c textbox.c theme.c timebox.c -OBJECTS= ${SOURCES:.c=.o} -FBSDFLAGS= -O2 -pipe -std=gnu99 -Wno-format-zero-length \ - -fstack-protector-strong -Qunused-arguments -CFLAGS = -fPIC -Wall -Wextra +LIBRARY_A = lib${LIBRARY:=.a} +HEADERS = bsddialog.h bsddialog_theme.h bsddialog_progressview.h +SOURCES = barbox.c datebox.c formbox.c libbsddialog.c lib_util.c \ + menubox.c messagebox.c textbox.c theme.c timebox.c +OBJECTS = ${SOURCES:.c=.o} + .if defined(DEBUG) CFLAGS += -g -.else -CFLAGS += ${FBSDFLAGS} .endif -LDFLAGS = -fstack-protector-strong -shared -Wl,-x -Wl,--fatal-warnings \ - -Wl,--warn-shared-textrel -Wl,-soname,${LIBRARY_SO}.${VERSION} +CFLAGS += -D_XOPEN_SOURCE_EXTENDED -fPIC -Wall -Wextra -std=gnu99 \ + -fstack-protector-strong +LDFLAGS += -fstack-protector-strong -shared -Wl,-x -Wl,--fatal-warnings \ + -Wl,--warn-shared-textrel -Wl,-soname,${LIBRARY_SO}.${VERSION} \ + -L/usr/lib -lncursesw -ltinfow -INSTALL_PREFIX=/usr/local LN = ln -s -f RM = rm -f -CP = cp -GZIP = gzip -cn -LDCONFIG = /sbin/ldconfig -m - -.if defined(PORTNCURSES) -# PORT ncurses `make -DPORTNCURSES` -CFLAGS += -DPORTNCURSES -I/usr/local/include -LDFLAGS += -L/usr/local/lib -lform -lncurses -ltinfo -.else -# BASE ncurses -LDFLAGS += -L/usr/lib -lform -lncurses -ltinfo -.endif -MAN= ${OUTPUT}.3 -GZIP= gzip -cn -MANDIR= /usr/local/share/man/man3 - -INSTALL= install -RM= rm -f - -#all : man ${LIBRARY} all : ${LIBRARY} -${LIBRARY}: ${OBJECTS} +${LIBRARY}: ${LIBRARY_SO} ${LIBRARY_A} + +${LIBRARY_SO}.${VERSION}: ${OBJECTS} ${CC} ${LDFLAGS} ${.ALLSRC} -o ${LIBRARY_SO}.${VERSION} - # LN for devel + +${LIBRARY_SO}: ${LIBRARY_SO}.${VERSION} ${LN} ${LIBRARY_SO}.${VERSION} ${LIBRARY_SO} +${LIBRARY_A}: ${OBJECTS} + ${AR} cr ${.TARGET} ${OBJECTS} + ${RANLIB} ${.TARGET} + .c.o: ${CC} ${CFLAGS} -c ${.IMPSRC} -o ${.TARGET} -man: - ${GZIP} ${LIBRARY}.3 > ${LIBRARY}.3.gz - clean: - ${RM} ${LIBRARY_SO}* *.o *~ *.gz - - -install: - ${CP} ${LIBRARY}.h ${INSTALL_PREFIX}/include - ${CP} ${LIBRARY_SO}.${VERSION} ${INSTALL_PREFIX}/lib/ - ${LN} ${INSTALL_PREFIX}/lib/${LIBRARY_SO}.${VERSION} ${INSTALL_PREFIX}/lib/${LIBRARY_SO} - ${LDCONFIG} ${INSTALL_PREFIX}/lib - ${CP} ${LIBRARY}.3.gz ${INSTALL_PREFIX}/man/man3/ - -unistall: - ${RM} ${INSTALL_PREFIX}/include/${LIBRARY}.h - ${RM} ${INSTALL_PREFIX}/lib/${LIBRARY_SO} - ${RM} ${INSTALL_PREFIX}/lib/${LIBRARY_SO}.${VERSION} - ${LDCONFIG} ${INSTALL_PREFIX}/lib - ${RM} ${INSTALL_PREFIX}/man/man3/${LIBRARY}.3.gz + ${RM} ${LIBRARY_SO}* *.o *~ *.gz ${LIBRARY_A} diff --git a/lib/barbox.c b/lib/barbox.c index da4f0b72c1d6..cd9fee836f8a 100644 --- a/lib/barbox.c +++ b/lib/barbox.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021 Alfonso Sabato Siciliano + * Copyright (c) 2021-2023 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,648 +25,641 @@ * SUCH DAMAGE. */ -#include <sys/param.h> - -#include <ctype.h> +#include <curses.h> #include <stdlib.h> #include <string.h> - -#ifdef PORTNCURSES -#include <ncurses/ncurses.h> -#else -#include <ncurses.h> -#endif +#include <time.h> +#include <unistd.h> #include "bsddialog.h" -#include "lib_util.h" +#include "bsddialog_progressview.h" #include "bsddialog_theme.h" +#include "lib_util.h" -#define BARMARGIN 3 -#define MINBARWIDTH 10 -#define MINWIDTH (VBORDERS + MINBARWIDTH + BARMARGIN * 2) -#define MINHEIGHT 7 /* without text */ - -/* "Bar": gauge - mixedgauge - rangebox - pause */ - -extern struct bsddialog_theme t; - -static void -draw_perc_bar(WINDOW *win, int y, int x, int size, int perc, bool withlabel, - int label) -{ - char labelstr[128]; - int i, blue_x, color; - - blue_x = (int)((perc*(size))/100); - - wmove(win, y, x); - for (i = 0; i < size; i++) { - color = (i <= blue_x) ? t.bar.f_color : t.bar.color; - wattron(win, color); - waddch(win, ' '); - wattroff(win, color); - } - - if (withlabel) - sprintf(labelstr, "%d", label); - else - sprintf(labelstr, "%3d%%", perc); - wmove(win, y, x + size/2 - 2); - for (i=0; i < (int) strlen(labelstr); i++) { - color = (blue_x + 1 <= size/2 - (int)strlen(labelstr)/2 + i ) ? - t.bar.color : t.bar.f_color; - wattron(win, color); - waddch(win, labelstr[i]); - wattroff(win, color); - } -} - -static int -bar_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w, - char *text, struct buttons *bs) +#define BARPADDING 2 /* widget border | BARPADDING | box bar */ +#define BOXBORDERS 2 +#define MIN_WBAR 15 +#define MIN_WBOX (BARPADDING + BOXBORDERS + MIN_WBAR + BARPADDING) +#define MIN_WMGBAR 18 +#define MIN_WMGBOX (BARPADDING + BOXBORDERS + MIN_WMGBAR + BARPADDING) +#define HBOX 3 +#define WBOX(d) ((d)->w - BORDERS - BARPADDING - BARPADDING) +#define WBAR(d) (WBOX(d) - BOXBORDERS) + +bool bsddialog_interruptprogview; +bool bsddialog_abortprogview; +long long int bsddialog_total_progview; + +static const char states[12][14] = { + " Succeeded ", /* -1 */ + " Failed ", /* -2 */ + " Passed ", /* -3 */ + " Completed ", /* -4 */ + " Checked ", /* -5 */ + " Done ", /* -6 */ + " Skipped ", /* -7 */ + " In Progress ", /* -8 */ + "(blank) ", /* -9 */ + " N/A ", /* -10 */ + " Pending ", /* -11 */ + " UNKNOWN ", /* < -11, no API */ +}; + +struct bar { + bool toupdate; + WINDOW *win; + int y; /* bar y in win */ + int x; /* bar x in win */ + int w; /* width in win */ + int perc; /* barlen = (w * perc) / 100 */ + const char* fmt; /* format for label */ + int label; /* rangebox and pause perc!=label */ +}; + +static void draw_bar(struct bar *b) { - int maxword, maxline, nlines, buttonswidth; - - if (get_text_properties(conf, text, &maxword, &maxline, &nlines) != 0) - return BSDDIALOG_ERROR; - - buttonswidth = 0; - if (bs != NULL) { /* gauge has not buttons */ - buttonswidth= bs->nbuttons * bs->sizebutton; - if (bs->nbuttons > 0) - buttonswidth += (bs->nbuttons-1) * t.button.space; - } - - if (cols == BSDDIALOG_AUTOSIZE) { - *w = VBORDERS; - /* buttons size */ - *w += buttonswidth; - /* bar size */ - *w = MAX(*w, MINWIDTH); - /* text size*/ - *w = MAX((int)(maxline + VBORDERS + t.text.hmargin * 2), *w); - /* conf.auto_minwidth */ - *w = MAX(*w, (int)conf->auto_minwidth); - /* avoid terminal overflow */ - *w = MIN(*w, widget_max_width(conf)); - } - - if (rows == BSDDIALOG_AUTOSIZE) { - *h = MINHEIGHT; - if (maxword > 0) - *h += 1; - /* conf.auto_minheight */ - *h = MAX(*h, (int)conf->auto_minheight); - /* avoid terminal overflow */ - *h = MIN(*h, widget_max_height(conf)); - } - - return (0); + int barlen, xlabel; + chtype ch; + char label[128]; + + barlen = b->perc > 0 ? (b->perc * b->w) / 100 : 0; + + ch = ' ' | t.bar.f_color; + mvwhline(b->win, b->y, b->x, ch, barlen); + ch = ' ' | t.bar.color; + mvwhline(b->win, b->y, b->x + barlen, ch, b->w - barlen); + + sprintf(label, b->fmt, b->label); + xlabel = b->x + b->w/2 - (int)strlen(label)/2; /* 1-byte-char string */ + wattron(b->win, t.bar.color); /* x+barlen < xlabel */ + mvwaddstr(b->win, b->y, xlabel, label); + wattroff(b->win, t.bar.color); + wattron(b->win, t.bar.f_color); /* x+barlen >= xlabel */ + mvwaddnstr(b->win, b->y, xlabel, label, MAX((b->x+barlen) - xlabel, 0)); + wattroff(b->win, t.bar.f_color); + + if (b->toupdate) + wnoutrefresh(b->win); + b->toupdate = false; } -static int -bar_checksize(char *text, int rows, int cols, struct buttons *bs) +static void update_barbox(struct dialog *d, struct bar *b, bool buttons) { - int minheight, minwidth; + int y; - minwidth = 0; - if (bs != NULL) { /* gauge has not buttons */ - minwidth = bs->nbuttons * bs->sizebutton; - if (bs->nbuttons > 0) - minwidth += (bs->nbuttons-1) * t.button.space; - } - minwidth = MAX(minwidth + VBORDERS, MINBARWIDTH); - - if (cols< minwidth) - RETURN_ERROR("Few cols for this widget"); - - minheight = MINHEIGHT + ((text != NULL && strlen(text) > 0) ? 1 : 0); - if (rows < minheight) - RETURN_ERROR("Few rows for this mixedgauge"); - - return 0; + y = d->y + d->h - BORDER - HBOX; + if (buttons) + y -= HBUTTONS; + update_box(d->conf, b->win, y, d->x + BORDER + BARPADDING, HBOX, + WBOX(d), RAISED); } int -bsddialog_gauge(struct bsddialog_conf *conf, char* text, int rows, int cols, - unsigned int perc) +bsddialog_gauge(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int perc, int fd, const char *sep, const char *end) { - WINDOW *widget, *textpad, *bar, *shadow; - char input[2048], ntext[2048], *pntext; - int y, x, h, w, htextpad; bool mainloop; + int fd2; + FILE *input; + char inputbuf[2048], ntext[2048], *pntext; + struct bar b; + struct dialog d; + + if (prepare_dialog(conf, text, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + if ((b.win = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW bar"); + b.y = b.x = 1; + b.fmt = "%3d%%"; + + input = NULL; + if (fd >= 0) { + CHECK_PTR(sep); + CHECK_PTR(end); + + fd2 = dup(fd); + if ((input = fdopen(fd2, "r")) == NULL) + RETURN_FMTERROR("Cannot build FILE* from fd %d", fd); + } - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return BSDDIALOG_ERROR; - if (bar_autosize(conf, rows, cols, &h, &w, text, NULL) != 0) - return BSDDIALOG_ERROR; - if (bar_checksize(text, h, w, NULL) != 0) - return BSDDIALOG_ERROR; - if (set_widget_position(conf, &y, &x, h, w) != 0) - return BSDDIALOG_ERROR; - - if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED, - &textpad, &htextpad, text, false) != 0) - return BSDDIALOG_ERROR; - - bar = new_boxed_window(conf, y+h-4, x+3, 3, w-6, RAISED); - + perc = MIN(perc, 100); mainloop = true; while (mainloop) { - wrefresh(widget); - prefresh(textpad, 0, 0, y+1, x+1+t.text.hmargin, y+h-4, - x+w-1-t.text.hmargin); - draw_perc_bar(bar, 1, 1, w-8, perc, false, -1 /*unused*/); - wrefresh(bar); + if (d.built) { + hide_dialog(&d); + refresh(); /* Important for decreasing screen */ + } + if (dialog_size_position(&d, HBOX, MIN_WBOX, NULL) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(&d)) + return (BSDDIALOG_ERROR); + if (d.built) + refresh(); /* fix grey lines expanding screen */ + TEXTPAD(&d, HBOX); + update_barbox(&d, &b, false); + b.w = WBAR(&d); + b.perc = b.label = perc; + b.toupdate = true; + draw_bar(&b); + doupdate(); + if (input == NULL) /* that is fd < 0 */ + break; while (true) { - scanf("%s", input); - if (strcmp(input,"EOF") == 0) { + fscanf(input, "%s", inputbuf); + if (strcmp(inputbuf, end) == 0) { mainloop = false; break; } - if (strcmp(input,"XXX") == 0) + if (strcmp(inputbuf, sep) == 0) break; } - scanf("%d", &perc); - perc = perc < 0 ? 0 : perc; - perc = perc > 100 ? 100 : perc; - htextpad = 1; - wclear(textpad); + if (mainloop == false) + break; + fscanf(input, "%d", &perc); + perc = MIN(perc, 100); pntext = &ntext[0]; ntext[0] = '\0'; while (true) { - scanf("%s", input); - if (strcmp(input,"EOF") == 0) { + fscanf(input, "%s", inputbuf); + if (strcmp(inputbuf, end) == 0) { mainloop = false; break; } - if (strcmp(input,"XXX") == 0) + if (strcmp(inputbuf, sep) == 0) break; + strcpy(pntext, inputbuf); + pntext += strlen(inputbuf); /* end string, no strlen */ pntext[0] = ' '; pntext++; - strcpy(pntext, input); - pntext += strlen(input); } - print_textpad(conf, textpad, &htextpad, w-2-t.text.hmargin*2, - ntext); + pntext[0] = '\0'; + d.text = ntext; } - delwin(bar); - end_widget_withtextpad(conf, widget, h, w, textpad, shadow); + if (input != NULL) + fclose(input); + delwin(b.win); + end_dialog(&d); - return BSDDIALOG_OK; + return (BSDDIALOG_OK); } -int -bsddialog_mixedgauge(struct bsddialog_conf *conf, char* text, int rows, - int cols, unsigned int mainperc, unsigned int nminibars, char **minilabels, - int *minipercs) +/* Mixedgauge */ +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) { - WINDOW *widget, *textpad, *bar, *shadow; - int i, output, miniperc, y, x, h, w, max_minbarlen; - int maxword, maxline, nlines, htextpad, ypad; - char states[12][16] = { - "[ Succeeded ]", /* 0 */ - "[ Failed ]", /* 1 */ - "[ Passed ]", /* 2 */ - "[ Completed ]", /* 3 */ - "[ Checked ]", /* 4 */ - "[ Done ]", /* 5 */ - "[ Skipped ]", /* 6 */ - "[ In Progress ]", /* 7 */ - "(blank) ", /* 8 */ - "[ N/A ]", /* 9 */ - "[ Pending ]", /* 10 */ - "[ UNKNOWN ]", /* 10+ */ - }; - - max_minbarlen = 0; - for (i=0; i < (int)nminibars; i++) - max_minbarlen = MAX(max_minbarlen, (int)strlen(minilabels[i])); - max_minbarlen += 3 + 16 /* seps + [...] or mainbar */; - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return BSDDIALOG_ERROR; - - /* mixedgauge autosize */ - if (get_text_properties(conf, text, &maxword, &maxline, &nlines) != 0) - return BSDDIALOG_ERROR; - - if (cols == BSDDIALOG_AUTOSIZE) { - w = max_minbarlen + HBORDERS; - w = MAX(max_minbarlen, maxline + 4); - w = MAX(w, (int)conf->auto_minwidth); - w = MIN(w, widget_max_width(conf) - 1); - } - if (rows == BSDDIALOG_AUTOSIZE) { - h = 5; /* borders + mainbar */ - h += nminibars; - h += (strlen(text) > 0 ? 3 : 0); - h = MAX(h, (int)conf->auto_minheight); - h = MIN(h, widget_max_height(conf) -1); - } - - /* mixedgauge checksize */ - if (w < max_minbarlen + 2) - RETURN_ERROR("Few cols for this mixedgauge"); - if (h < 5 + (int)nminibars + (strlen(text) > 0 ? 1 : 0)) - RETURN_ERROR("Few rows for this mixedgauge"); - - if (set_widget_position(conf, &y, &x, h, w) != 0) - return BSDDIALOG_ERROR; - - output = new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, - RAISED, &textpad, &htextpad, text, false); - if (output == BSDDIALOG_ERROR) - return output; + int i, miniperc, max_minibarlen; + int ystext, htext; + int minicolor, red, green; + struct bar b; + struct dialog d; + + CHECK_ARRAY(nminibars, minilabels); + CHECK_ARRAY(nminibars, minipercs); + + red = bsddialog_color(BSDDIALOG_WHITE,BSDDIALOG_RED, BSDDIALOG_BOLD); + green = bsddialog_color(BSDDIALOG_WHITE,BSDDIALOG_GREEN,BSDDIALOG_BOLD); + + max_minibarlen = 0; + for (i = 0; i < (int)nminibars; i++) + max_minibarlen = MAX(max_minibarlen, + (int)strcols(CHECK_STR(minilabels[i]))); + max_minibarlen += 3 + 16; /* seps + [...] */ + max_minibarlen = MAX(max_minibarlen, MIN_WMGBOX); /* mainbar */ + + if (prepare_dialog(conf, text, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + if (dialog_size_position(&d, nminibars + HBOX, max_minibarlen, + &htext) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(&d) != 0) + return (BSDDIALOG_ERROR); /* mini bars */ - for (i=0; i < (int)nminibars; i++) { + b.win = d.widget; + b.x = 1 + d.w - 2 - 15; + b.w = 13; + b.fmt = "%3d%%"; + b.toupdate = false; + for (i = 0; i < (int)nminibars; i++) { miniperc = minipercs[i]; - if (miniperc == 8) + /* label */ + if (color && miniperc >= 0) + wattron(d.widget, A_BOLD); + mvwaddstr(d.widget, i+1, 2, CHECK_STR(minilabels[i])); + if (color && miniperc >= 0) + wattroff(d.widget, A_BOLD); + /* perc */ + if (miniperc == BSDDIALOG_MG_BLANK) continue; - mvwaddstr(widget, i+1, 2, minilabels[i]); - if (miniperc > 10) - mvwaddstr(widget, i+1, w-2-15, states[11]); - else if (miniperc >= 0 && miniperc <= 10) - mvwaddstr(widget, i+1, w-2-15, states[miniperc]); - else { /* miniperc < 0 */ - miniperc = abs(miniperc); - mvwaddstr(widget, i+1, w-2-15, "[ ]"); - draw_perc_bar(widget, i+1, 1+w-2-15, 13, miniperc, - false, -1 /*unused*/); + mvwaddstr(d.widget, i+1, d.w-2-15, "[ ]"); + if (miniperc >= 0) { + b.y = i + 1; + b.perc = b.label = MIN(miniperc, 100); + draw_bar(&b); + } else { /* miniperc < 0 */ + if (miniperc < BSDDIALOG_MG_PENDING) + miniperc = -12; /* UNKNOWN */ + minicolor = t.dialog.color; + if (color && miniperc == BSDDIALOG_MG_FAILED) + minicolor = red; + else if (color && miniperc == BSDDIALOG_MG_DONE) + minicolor = green; + wattron(d.widget, minicolor); + miniperc = abs(miniperc + 1); + mvwaddstr(d.widget, i+1, 1+d.w-2-15, states[miniperc]); + wattroff(d.widget, minicolor); } } + wnoutrefresh(d.widget); + + /* text */ + ystext = MAX(d.h - BORDERS - htext - HBOX, (int)nminibars); + rtextpad(&d, 0, 0, ystext, HBOX); - wrefresh(widget); - ypad = y + h - 5 - htextpad; - ypad = ypad < y+(int)nminibars ? y+nminibars : ypad; - prefresh(textpad, 0, 0, ypad, x+2, y+h-4, x+w-2); - /* main bar */ - bar = new_boxed_window(conf, y+h -4, x+3, 3, w-6, RAISED); - - draw_perc_bar(bar, 1, 1, w-8, mainperc, false, -1 /*unused*/); + if ((b.win = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW bar"); + update_barbox(&d, &b, false); + wattron(b.win, t.bar.color); + mvwaddstr(b.win, 0, 2, "Overall Progress"); + wattroff(b.win, t.bar.color); + + b.y = b.x = 1; + b.w = WBAR(&d); + b.fmt = "%3d%%"; + b.perc = b.label = MIN(mainperc, 100); + b.toupdate = true; + draw_bar(&b); + + doupdate(); + /* getch(); to test with "alternate mode" */ + + delwin(b.win); + end_dialog(&d); + + return (BSDDIALOG_OK); +} - wattron(bar, t.bar.color); - mvwaddstr(bar, 0, 2, "Overall Progress"); - wattroff(bar, t.bar.color); +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) +{ + int retval; - wrefresh(bar); + retval = do_mixedgauge(conf, text, rows, cols, mainperc, nminibars, + minilabels, minipercs, false); - /* getch(); port ncurses shows nothing */ + return (retval); +} - delwin(bar); - end_widget_withtextpad(conf, widget, h, w, textpad, shadow); +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) +{ + bool update; + int perc, retval, *minipercs; + unsigned int i, mainperc, totaltodo; + float readforsec; + const char **minilabels; + time_t tstart, told, tnew, refresh; + + if ((minilabels = calloc(nminibar, sizeof(char*))) == NULL) + RETURN_ERROR("Cannot allocate memory for minilabels"); + if ((minipercs = calloc(nminibar, sizeof(int))) == NULL) + RETURN_ERROR("Cannot allocate memory for minipercs"); + + totaltodo = 0; + for (i = 0; i < nminibar; i++) { + totaltodo += minibar[i].size; + minilabels[i] = minibar[i].label; + minipercs[i] = minibar[i].status; + } - return BSDDIALOG_OK; + refresh = pvconf->refresh == 0 ? 0 : pvconf->refresh - 1; + retval = BSDDIALOG_OK; + i = 0; + update = true; + time(&told); + tstart = told; + while (!(bsddialog_interruptprogview || bsddialog_abortprogview)) { + if (bsddialog_total_progview == 0 || totaltodo == 0) + mainperc = 0; + else + mainperc = (bsddialog_total_progview * 100) / totaltodo; + + time(&tnew); + if (update || tnew > told + refresh) { + retval = do_mixedgauge(conf, text, rows, cols, mainperc, + nminibar, minilabels, minipercs, true); + if (retval == BSDDIALOG_ERROR) + return (BSDDIALOG_ERROR); + + move(SCREENLINES - 1, 2); + clrtoeol(); + readforsec = ((tnew - tstart) == 0) ? 0 : + bsddialog_total_progview / (float)(tnew - tstart); + printw(pvconf->fmtbottomstr, bsddialog_total_progview, + readforsec); + refresh(); + + time(&told); + update = false; + } + + if (i >= nminibar) + break; + if (minibar[i].status == BSDDIALOG_MG_FAILED) + break; + + perc = pvconf->callback(&minibar[i]); + + if (minibar[i].status == BSDDIALOG_MG_DONE) { /*||perc >= 100)*/ + minipercs[i] = BSDDIALOG_MG_DONE; + update = true; + i++; + } else if (minibar[i].status == BSDDIALOG_MG_FAILED || + perc < 0) { + minipercs[i] = BSDDIALOG_MG_FAILED; + update = true; + } else /* perc >= 0 */ + minipercs[i] = perc; + } + + free(minilabels); + free(minipercs); + return (retval); } -int -bsddialog_rangebox(struct bsddialog_conf *conf, char* text, int rows, int cols, - int min, int max, int *value) +static int rangebox_redraw(struct dialog *d, struct bar *b, int *bigchange) { - WINDOW *widget, *textpad, *bar, *shadow; - int i, y, x, h, w, htextpad; - bool loop, buttupdate, barupdate; - int input, currvalue, output, sizebar, bigchange, positions; - float perc; - struct buttons bs; + if (d->built) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ + } + if (dialog_size_position(d, HBOX, MIN_WBOX, NULL) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) + return (BSDDIALOG_ERROR); + if (d->built) + refresh(); /* Important to fix grey lines expanding screen */ + TEXTPAD(d, HBOX + HBUTTONS); + + b->w = WBAR(d); + *bigchange = MAX(1, b->w / 10); + update_barbox(d, b, true); + b->toupdate = true; + + return (0); +} - if (value == NULL) - RETURN_ERROR("*value cannot be NULL"); +int +bsddialog_rangebox(struct bsddialog_conf *conf, const char *text, int rows, + int cols, int min, int max, int *value) +{ + bool loop; + int currvalue, retval, bigchange, positions; + wint_t input; + struct bar b; + struct dialog d; + CHECK_PTR(value); if (min >= max) - RETURN_ERROR("min >= max"); + RETURN_FMTERROR("min (%d) >= max (%d)", min, max); + if (*value < min) + RETURN_FMTERROR("value (%d) < min (%d)", *value, min); + if (*value > max) + RETURN_FMTERROR("value (%d) > max (%d)", *value, max); currvalue = *value; positions = max - min + 1; - get_buttons(conf, &bs, BUTTONLABEL(ok_label), BUTTONLABEL(extra_label), - BUTTONLABEL(cancel_label), BUTTONLABEL(help_label)); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return BSDDIALOG_ERROR; - if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0) - return BSDDIALOG_ERROR; - if (bar_checksize(text, h, w, &bs) != 0) - return BSDDIALOG_ERROR; - if (set_widget_position(conf, &y, &x, h, w) != 0) - return BSDDIALOG_ERROR; - - if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED, - &textpad, &htextpad, text, true) != 0) - return BSDDIALOG_ERROR; - - prefresh(textpad, 0, 0, y+1, x+1+t.text.hmargin, y+h-7, - x+w-1-t.text.hmargin); - - sizebar = w - HBORDERS - 2 - BARMARGIN * 2; - bigchange = MAX(1, sizebar/10); - - bar = new_boxed_window(conf, y + h - 6, x + 1 + BARMARGIN, 3, - sizebar + 2, RAISED); - - loop = buttupdate = barupdate = true; - while(loop) { - if (buttupdate) { - draw_buttons(widget, h-2, w, bs, true); - wrefresh(widget); - buttupdate = false; - } - if (barupdate) { - perc = ((float)(currvalue - min)*100) / (positions-1); - draw_perc_bar(bar, 1, 1, sizebar, perc, true, currvalue); - barupdate = false; - wrefresh(bar); + if (prepare_dialog(conf, text, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + set_buttons(&d, true, OK_LABEL, CANCEL_LABEL); + if ((b.win = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW bar"); + b.y = b.x = 1; + b.fmt = "%d"; + if (rangebox_redraw(&d, &b, &bigchange) != 0) + return (BSDDIALOG_ERROR); + + loop = true; + while (loop) { + if (b.toupdate) { + b.perc = ((float)(currvalue - min)*100) / (positions-1); + b.label = currvalue; + draw_bar(&b); } - - input = getch(); + doupdate(); + if (get_wch(&input) == ERR) + continue; switch(input) { case KEY_ENTER: case 10: /* Enter */ - output = bs.value[bs.curr]; - *value = currvalue; + retval = BUTTONVALUE(d.bs); loop = false; break; case 27: /* Esc */ - output = BSDDIALOG_ESC; - loop = false; + if (conf->key.enable_esc) { + retval = BSDDIALOG_ESC; + loop = false; + } break; case '\t': /* TAB */ - bs.curr = (bs.curr + 1) % bs.nbuttons; - buttupdate = true; + case KEY_RIGHT: + d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons; + DRAW_BUTTONS(d); break; case KEY_LEFT: - if (bs.curr > 0) { - bs.curr--; - buttupdate = true; - } - break; - case KEY_RIGHT: - if (bs.curr < (int) bs.nbuttons - 1) { - bs.curr++; - buttupdate = true; - } + d.bs.curr--; + if (d.bs.curr < 0) + d.bs.curr = d.bs.nbuttons - 1; + DRAW_BUTTONS(d); break; case KEY_HOME: currvalue = max; - barupdate = true; + b.toupdate = true; break; case KEY_END: currvalue = min; - barupdate = true; + b.toupdate = true; break; case KEY_NPAGE: currvalue -= bigchange; if (currvalue < min) currvalue = min; - barupdate = true; + b.toupdate = true; break; case KEY_PPAGE: currvalue += bigchange; if (currvalue > max) currvalue = max; - barupdate = true; + b.toupdate = true; break; case KEY_UP: if (currvalue < max) { currvalue++; - barupdate = true; + b.toupdate = true; } break; case KEY_DOWN: if (currvalue > min) { currvalue--; - barupdate = true; + b.toupdate = true; } break; case KEY_F(1): - if (conf->f1_file == NULL && conf->f1_message == NULL) + if (conf->key.f1_file == NULL && + conf->key.f1_message == NULL) break; - if (f1help(conf) != 0) - return BSDDIALOG_ERROR; - /* No break! the terminal size can change */ + if (f1help_dialog(conf) != 0) + return (BSDDIALOG_ERROR); + if (rangebox_redraw(&d, &b, &bigchange) != 0) + return (BSDDIALOG_ERROR); + break; case KEY_RESIZE: - hide_widget(y, x, h, w,conf->shadow); - - /* - * Unnecessary, but, when the columns decrease the - * following "refresh" seem not work - */ - refresh(); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return BSDDIALOG_ERROR; - if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0) - return BSDDIALOG_ERROR; - if (bar_checksize(text, h, w, &bs) != 0) - return BSDDIALOG_ERROR; - if (set_widget_position(conf, &y, &x, h, w) != 0) - return BSDDIALOG_ERROR; - - wclear(shadow); - mvwin(shadow, y + t.shadow.h, x + t.shadow.w); - wresize(shadow, h, w); - - wclear(widget); - mvwin(widget, y, x); - wresize(widget, h, w); - - htextpad = 1; - wclear(textpad); - wresize(textpad, 1, w - HBORDERS - t.text.hmargin * 2); - - sizebar = w - HBORDERS - 2 - BARMARGIN * 2; - bigchange = MAX(1, sizebar/10); - wclear(bar); - mvwin(bar, y + h - 6, x + 1 + BARMARGIN); - wresize(bar, 3, sizebar + 2); - - if(update_widget_withtextpad(conf, shadow, widget, h, w, - RAISED, textpad, &htextpad, text, true) != 0) - return BSDDIALOG_ERROR; - - prefresh(textpad, 0, 0, y+1, x+1+t.text.hmargin, y+h-7, - x+w-1-t.text.hmargin); - - draw_borders(conf, bar, 3, sizebar + 2, RAISED); - - barupdate = true; - buttupdate = true; + if (rangebox_redraw(&d, &b, &bigchange) != 0) + return (BSDDIALOG_ERROR); break; default: - for (i = 0; i < (int) bs.nbuttons; i++) - if (tolower(input) == tolower((bs.label[i])[0])) { - output = bs.value[i]; - loop = false; + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); + loop = false; } } } - delwin(bar); - end_widget_withtextpad(conf, widget, h, w, textpad, shadow); + *value = currvalue; + + delwin(b.win); + end_dialog(&d); - return output; + return (retval); +} + +static int pause_redraw(struct dialog *d, struct bar *b) +{ + if (d->built) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ + } + if (dialog_size_position(d, HBOX, MIN_WBOX, NULL) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) + return (BSDDIALOG_ERROR); + if (d->built) + refresh(); /* Important to fix grey lines expanding screen */ + TEXTPAD(d, HBOX + HBUTTONS); + + b->w = WBAR(d); + update_barbox(d, b, true); + b->toupdate = true; + + return (0); } int -bsddialog_pause(struct bsddialog_conf *conf, char* text, int rows, int cols, - unsigned int sec) +bsddialog_pause(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int *seconds) { - WINDOW *widget, *textpad, *bar, *shadow; - int i, output, y, x, h, w, htextpad; - bool loop, buttupdate, barupdate; - int input, tout, sizebar; - float perc; - struct buttons bs; - - get_buttons(conf, &bs, BUTTONLABEL(ok_label), BUTTONLABEL(extra_label), - BUTTONLABEL(cancel_label), BUTTONLABEL(help_label)); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return BSDDIALOG_ERROR; - if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0) - return BSDDIALOG_ERROR; - if (bar_checksize(text, h, w, &bs) != 0) - return BSDDIALOG_ERROR; - if (set_widget_position(conf, &y, &x, h, w) != 0) - return BSDDIALOG_ERROR; - - if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED, - &textpad, &htextpad, text, true) != 0) - return BSDDIALOG_ERROR; - - prefresh(textpad, 0, 0, y+1, x+1+t.text.hmargin, y+h-7, - x+w-1-t.text.hmargin); - - sizebar = w - HBORDERS - 2 - BARMARGIN * 2; - bar = new_boxed_window(conf, y + h - 6, x + 1 + BARMARGIN, 3, - sizebar + 2, RAISED); - - tout = sec; + bool loop; + int retval, tout; + wint_t input; + struct bar b; + struct dialog d; + + CHECK_PTR(seconds); + if (prepare_dialog(conf, text, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + set_buttons(&d, true, OK_LABEL, CANCEL_LABEL); + if ((b.win = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW bar"); + b.y = b.x = 1; + b.fmt = "%d"; + if (pause_redraw(&d, &b) != 0) + return (BSDDIALOG_ERROR); + + tout = *seconds; nodelay(stdscr, TRUE); timeout(1000); - loop = buttupdate = barupdate = true; - while(loop) { - if (barupdate) { - perc = (float)tout * 100 / sec; - draw_perc_bar(bar, 1, 1, sizebar, perc, true, tout); - barupdate = false; - wrefresh(bar); + loop = true; + while (loop) { + if (b.toupdate) { + b.perc = (float)tout * 100 / *seconds; + b.label = tout; + draw_bar(&b); } - - if (buttupdate) { - draw_buttons(widget, h-2, w, bs, true); - wrefresh(widget); - buttupdate = false; - } - - input = getch(); - if(input < 0) { /* timeout */ + doupdate(); + if (get_wch(&input) == ERR) { /* timeout */ tout--; if (tout < 0) { - output = BSDDIALOG_TIMEOUT; + retval = BSDDIALOG_TIMEOUT; break; } else { - barupdate = true; + b.toupdate = true; continue; } } switch(input) { case KEY_ENTER: case 10: /* Enter */ - output = bs.value[bs.curr]; + retval = BUTTONVALUE(d.bs); loop = false; break; case 27: /* Esc */ - output = BSDDIALOG_ESC; - loop = false; + if (conf->key.enable_esc) { + retval = BSDDIALOG_ESC; + loop = false; + } break; case '\t': /* TAB */ - bs.curr = (bs.curr + 1) % bs.nbuttons; - buttupdate = true; + case KEY_RIGHT: + d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons; + DRAW_BUTTONS(d); break; case KEY_LEFT: - if (bs.curr > 0) { - bs.curr--; - buttupdate = true; - } - break; - case KEY_RIGHT: - if (bs.curr < (int) bs.nbuttons - 1) { - bs.curr++; - buttupdate = true; - } + d.bs.curr--; + if (d.bs.curr < 0) + d.bs.curr = d.bs.nbuttons - 1; + DRAW_BUTTONS(d); break; case KEY_F(1): - if (conf->f1_file == NULL && conf->f1_message == NULL) + if (conf->key.f1_file == NULL && + conf->key.f1_message == NULL) break; - if (f1help(conf) != 0) - return BSDDIALOG_ERROR; - /* No break! the terminal size can change */ + if (f1help_dialog(conf) != 0) + return (BSDDIALOG_ERROR); + if (pause_redraw(&d, &b) != 0) + return (BSDDIALOG_ERROR); + break; case KEY_RESIZE: - hide_widget(y, x, h, w,conf->shadow); - - /* - * Unnecessary, but, when the columns decrease the - * following "refresh" seem not work - */ - refresh(); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return BSDDIALOG_ERROR; - if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0) - return BSDDIALOG_ERROR; - if (bar_checksize(text, h, w, &bs) != 0) - return BSDDIALOG_ERROR; - if (set_widget_position(conf, &y, &x, h, w) != 0) - return BSDDIALOG_ERROR; - - wclear(shadow); - mvwin(shadow, y + t.shadow.h, x + t.shadow.w); - wresize(shadow, h, w); - - wclear(widget); - mvwin(widget, y, x); - wresize(widget, h, w); - - htextpad = 1; - wclear(textpad); - wresize(textpad, 1, w - HBORDERS - t.text.hmargin * 2); - - sizebar = w - HBORDERS - 2 - BARMARGIN * 2; - wclear(bar); - mvwin(bar, y + h - 6, x + 1 + BARMARGIN); - wresize(bar, 3, sizebar + 2); - - if(update_widget_withtextpad(conf, shadow, widget, h, w, - RAISED, textpad, &htextpad, text, true) != 0) - return BSDDIALOG_ERROR; - - prefresh(textpad, 0, 0, y+1, x+1+t.text.hmargin, y+h-7, - x+w-1-t.text.hmargin); - - draw_borders(conf, bar, 3, sizebar + 2, RAISED); - - barupdate = true; - buttupdate = true; + if (pause_redraw(&d, &b) != 0) + return (BSDDIALOG_ERROR); break; default: - for (i = 0; i < (int) bs.nbuttons; i++) - if (tolower(input) == tolower((bs.label[i])[0])) { - output = bs.value[i]; - loop = false; + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); + loop = false; } } } - nodelay(stdscr, FALSE); - delwin(bar); - end_widget_withtextpad(conf, widget, h, w, textpad, shadow); + *seconds = MAX(tout, 0); + + delwin(b.win); + end_dialog(&d); - return output; + return (retval); } diff --git a/lib/bsddialog.3 b/lib/bsddialog.3 index 7e1238f38e52..38885bfe3ea4 100644 --- a/lib/bsddialog.3 +++ b/lib/bsddialog.3 @@ -1,42 +1,1183 @@ -.Dd $Mdocdate$ -.Dt PROGNAME section +.\" +.\" Copyright (c) 2021-2023 Alfonso Sabato Siciliano +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd July 28, 2023 +.Dt BSDDIALOG 3 .Os .Sh NAME -.Nm progname -.Nd one line about what it does -.\" .Sh LIBRARY -.\" For sections 2, 3, and 9 only. -.\" Not used in OpenBSD. +.Nm bsddialog_backtitle , +.Nm bsddialog_calendar , +.Nm bsddialog_clear , +.Nm bsddialog_color , +.Nm bsddialog_color_attrs , +.Nm bsddialog_checklist , +.Nm bsddialog_datebox , +.Nm bsddialog_end , +.Nm bsddialog_form , +.Nm bsddialog_gauge , +.Nm bsddialog_geterror , +.Nm bsddialog_get_theme , +.Nm bsddialog_hascolors , +.Nm bsddialog_infobox , +.Nm bsddialog_init , +.Nm bsddialog_init_notheme , +.Nm bsddialog_initconf , +.Nm bsddialog_inmode , +.Nm bsddialog_menu , +.Nm bsddialog_mixedgauge , +.Nm bsddialog_mixedlist , +.Nm bsddialog_msgbox , +.Nm bsddialog_pause , +.Nm bsddialog_radiolist , +.Nm bsddialog_rangebox , +.Nm bsddialog_refresh , +.Nm bsddialog_set_theme , +.Nm bsddialog_set_default_theme , +.Nm bsddialog_textbox , +.Nm bsddialog_timebox , +.Nm bsddialog_yesno +.Nd TUI dialogs +.Sh LIBRARY +.Lb libbsddialog .Sh SYNOPSIS -.Nm progname -.Op Fl options -.Ar +.In bsddialog.h +.Ft int +.Fn bsddialog_backtitle "struct bsddialog_conf *conf" "const char *backtitle" +.Ft int +.Fo bsddialog_calendar +.Fa "struct bsddialog_conf *conf" +.Fa "const char *text" +.Fa "int rows" +.Fa "int cols" +.Fa "unsigned int *year" +.Fa "unsigned int *month" +.Fa "unsigned int *day" +.Fc +.Ft int +.Fo bsddialog_checklist +.Fa "struct bsddialog_conf *conf" +.Fa "const char *text" +.Fa "int rows" +.Fa "int cols" +.Fa "unsigned int menurows" +.Fa "unsigned int nitems" +.Fa "struct bsddialog_menuitem *items" +.Fa "int *focusitem" +.Fc +.Ft void +.Fn bsddialog_clear "unsigned int y" +.Ft int +.Fo bsddialog_datebox +.Fa "struct bsddialog_conf *conf" +.Fa "const char *text" +.Fa "int rows" +.Fa "int cols" +.Fa "unsigned int *year" +.Fa "unsigned int *month" +.Fa "unsigned int *day" +.Fc +.Ft int +.Fn bsddialog_end "void" +.Ft int +.Fo bsddialog_form +.Fa "struct bsddialog_conf *conf" +.Fa "const char *text" +.Fa "int rows" +.Fa "int cols" +.Fa "unsigned int formrows" +.Fa "unsigned int nitems" +.Fa "struct bsddialog_formitem *items" +.Fa "int *focusitem" +.Fc +.Ft int +.Fo bsddialog_gauge +.Fa "struct bsddialog_conf *conf" +.Fa "const char *text" +.Fa "int rows" +.Fa "int cols" +.Fa "unsigned int perc" +.Fa "int fd" +.Fa "const char *sep" +.Fa "const char *end" +.Fc +.Ft const char * +.Fn bsddialog_geterror "void" +.Ft int +.Fo bsddialog_infobox +.Fa "struct bsddialog_conf *conf" +.Fa "const char *text" +.Fa "int rows" +.Fa "int cols" +.Fc +.Ft int +.Fn bsddialog_init "void" +.Ft int +.Fn bsddialog_init_notheme "void" +.Ft bool +.Fn bsddialog_inmode "void" +.Ft int +.Fn bsddialog_initconf "struct bsddialog_conf *conf" +.Ft int +.Fo bsddialog_menu +.Fa "struct bsddialog_conf *conf" +.Fa "const char *text" +.Fa "int rows" +.Fa "int cols" +.Fa "unsigned int menurows" +.Fa "unsigned int nitems" +.Fa "struct bsddialog_menuitem *items" +.Fa "int *focusitem" +.Fc +.Ft int +.Fo bsddialog_mixedgauge +.Fa "struct bsddialog_conf *conf" +.Fa "const char *text" +.Fa "int rows" +.Fa "int cols" +.Fa "unsigned int mainperc" +.Fa "unsigned int nminibars" +.Fa "char **minilabels" +.Fa "int *minipercs" +.Fc +.Ft int +.Fo bsddialog_mixedlist +.Fa "struct bsddialog_conf *conf" +.Fa "const char *text" +.Fa "int rows" +.Fa "int cols" +.Fa "unsigned int menurows" +.Fa "unsigned int ngroups" +.Fa "struct bsddialog_menugroup *groups" +.Fa "int *focuslist" +.Fa "int *focusitem" +.Fc +.Ft int +.Fo bsddialog_msgbox +.Fa "struct bsddialog_conf *conf" +.Fa "const char *text" +.Fa "int rows" +.Fa "int cols" +.Fc +.Ft int +.Fo bsddialog_pause +.Fa "struct bsddialog_conf *conf" +.Fa "const char *text" +.Fa "int rows" +.Fa "int cols" +.Fa "unsigned int *seconds" +.Fc +.Ft int +.Fo bsddialog_radiolist +.Fa "struct bsddialog_conf *conf" +.Fa "const char *text" +.Fa "int rows" +.Fa "int cols" +.Fa "unsigned int menurows" +.Fa "unsigned int nitems" +.Fa "struct bsddialog_menuitem *items" +.Fa "int *focusitem" +.Fc +.Ft int +.Fo bsddialog_rangebox +.Fa "struct bsddialog_conf *conf" +.Fa "const char *text" +.Fa "int rows" +.Fa "int cols" +.Fa "int min" +.Fa "int max" +.Fa "int *value" +.Fc +.Ft void +.Fn bsddialog_refresh "void" +.Ft int +.Fo bsddialog_textbox +.Fa "struct bsddialog_conf *conf" +.Fa "const char *file" +.Fa "int rows" +.Fa "int cols" +.Fc +.Ft int +.Fo bsddialog_timebox +.Fa "struct bsddialog_conf *conf" +.Fa "const char *text" +.Fa "int rows" +.Fa "int cols" +.Fa "unsigned int *hh" +.Fa "unsigned int *mm" +.Fa "unsigned int *ss" +.Fc +.Ft int +.Fo bsddialog_yesno +.Fa "struct bsddialog_conf *conf" +.Fa "const char *text" +.Fa "int rows" +.Fa "int cols" +.Fc +.In bsddialog_theme.h +.Ft int +.Fo bsddialog_color +.Fa "enum bsddialog_color foreground" +.Fa "enum bsddialog_color background" +.Fa "unsigned int flags" +.Fc +.Ft int +.Fo bsddialog_color_attrs +.Fa "int color" +.Fa "enum bsddialog_color *foreground" +.Fa "enum bsddialog_color *background" +.Fa "unsigned int *flags" +.Fc +.Ft int +.Fn bsddialog_get_theme "struct bsddialog_theme *theme" +.Ft bool +.Fn bsddialog_hascolors "void" +.Ft int +.Fn bsddialog_set_default_theme "enum bsddialog_default_theme theme" +.Ft int +.Fn bsddialog_set_theme "struct bsddialog_theme *theme" .Sh DESCRIPTION The -.Nm -utility processes files ... -.\" .Sh CONTEXT -.\" For section 9 functions only. -.\" .Sh IMPLEMENTATION NOTES -.\" Not used in OpenBSD. -.\" .Sh RETURN VALUES -.\" For sections 2, 3, and 9 function return values only. -.\" .Sh ENVIRONMENT -.\" For sections 1, 6, 7, and 8 only. -.\" .Sh FILES -.\" .Sh EXIT STATUS -.\" For sections 1, 6, and 8 only. -.\" .Sh EXAMPLES -.\" .Sh DIAGNOSTICS -.\" For sections 1, 4, 6, 7, 8, and 9 printf/stderr messages only. -.\" .Sh ERRORS -.\" For sections 2, 3, 4, and 9 errno settings only. -.\" .Sh SEE ALSO -.\" .Xr foobar 1 -.\" .Sh STANDARDS -.\" .Sh HISTORY -.\" .Sh AUTHORS -.\" .Sh CAVEATS -.\" .Sh BUGS -.\" .Sh SECURITY CONSIDERATIONS -.\" Not used in OpenBSD. +.Nm bsddialog +library provides an API to build Text User Interface dialogs and widgets. +.Pp +.Fn bsddialog_init +initializes the library, the only functions that can be called before is +.Fn bsddialog_initconf +described later. +After the initialization the input and output should be handled via the library +API. +.Pp +.Fn bsddialog_init_notheme +is equivalent to +.Fn bsddialog_init +except it does not set the default graphical theme; see +.Sx Theme +subsection to set a theme explicitly. +.Pp +.Fn bsddialog_end +restores the screen like before +.Fn bsddialog_init . +After the call is not possible to use the library functions. +.Pp +.Fn bsddialog_inmode +returns +.Dv true +after +.Fn bsddialog_init +or +.Fn bsddialog_init_notheme +and before +.Fn bsddialog_end , +.Dv false +otherwise. +.Pp +.Fn bsddialog_backtitle +prints +.Fa backtitle +on the top of the screen. +The function handles +.Fa conf.ascii_lines +and +.Fa conf.no_lines +described later. +.Pp +.Fn bsddialog_error +returns a string to describe the last error. +The function should be called after a +.Dv BSDDIALOG_ERROR +returned value. +.Pp +.Fn bsddialog_clear +clears the screen from +.Fa y . +.Pp +.Fn bsddialog_refresh +useful to refresh the screen after a terminal mode change, see +.Xr terminfo 5 . +.Ss Dialogs +The dialogs have common arguments. +.Fa text +is a string printed inside the dialog. +Each +.Fa char* +parameter can be a multibyte character string depending on current locale, see +.Xr setlocale 3 . +.Fa rows +and +.Fa cols +are height and width, their value can be a fixed size, +.Dv BSDDIALOG_AUTOSIZE +or +.Dv BSDDIALOG_FULLSCREEN . +.Fa conf +is a struct to customize the current dialog, it does not set global properties +to the library. +.Pp +.Bd -literal -offset indent -compact +struct bsddialog_conf { + bool ascii_lines; + unsigned int auto_minheight; + unsigned int auto_minwidth; + unsigned int auto_topmargin; + unsigned int auto_downmargin; + const char *bottomtitle; + bool clear; + int *get_height; + int *get_width; + bool no_lines; + bool shadow; + unsigned int sleep; + const char *title; + int y; + int x; + struct { + bool enable_esc; + const char *f1_file; + const char *f1_message; + } key; + struct { + unsigned int cols_per_row; + bool escape; + unsigned int tablen; + } text; + struct { + bool align_left; + bool no_desc; + bool no_name; + bool shortcut_buttons; + } menu; + struct { + char securech; + char *securembch; + bool value_wchar; + } form; + struct { + const char *format; + } date; + struct { + bool always_active; + const char *left1_label; + const char *left2_label; + const char *left3_label; + bool without_ok; + const char *ok_label; + bool with_extra; + const char *extra_label; + bool without_cancel; + const char *cancel_label; + bool default_cancel; + bool with_help; + const char *help_label; + const char *right1_label; + const char *right2_label; + const char *right3_label; + const char *default_label; + } button; +}; +.Ed +.Pp +.Bl -column -compact +.It Fa conf.ascii_lines +ascii characters to draw lines, default wide characters. +.It Fa conf.auto_minheight +minimum height if +.Fa rows +is +.Dv BSDDIALOG_AUTOSIZE . +.It Fa conf.auto_minwidth +minimum width if +.Fa cols +is +.Dv BSDDIALOG_AUTOSIZE . +.It Fa conf.auto_topmargin +top margin if +.Fa rows +is +.Dv BSDDIALOG_AUTOSIZE +or +.Dv BSDDIALOG_FULLSCREEN , +.Fa conf.y +has to be +.Dv BSDDIALOG_CENTER . +.It Fa conf.auto_downmargin +down margin if +.Fa rows +is +.Dv BSDDIALOG_AUTOSIZE +or +.Dv BSDDIALOG_FULLSCREEN . +.It Fa conf.bottomtitle +dialog subtitle. +.It Fa conf.clear +hide the dialog at exit. +.It Fa conf.get_height +if not +.Dv NULL +is set like the dialog height. +.It Fa conf.get_width +if not +.Dv NULL +is set like the dialog width. +.It Fa conf.no_lines +not draw lines. +.It Fa conf.shadow +draw shadow. +.It Fa conf.sleep +wait before to return, the value is in seconds. +.It Fa conf.title +dialog title. +.It Fa conf.y +dialog vertical position, 0 is top screen, can be +.Dv BSDDIALOG_CENTER . +.It Fa conf.x +dialog horizontal position, 0 is left screen, can be +.Dv BSDDIALOG_CENTER . +.El +.Pp +.Bl -column -compact +.It Fa conf.key.enable_esc +enable +.Dv ESC +key to close the dialog. +.It Fa conf.key.f1_file +open a file in a textbox if F1 is pressed. +.It Fa conf.key.f1_message +build a msgbox with message if F1 is pressed. +.El +.Pp +.Bl -column -compact +.It Fa conf.text.cols_per_row +Try to set the number of columns for a row of +.Fa text +with autosizing, default +.Dv 10 . +.It Fa conf.text.escape +enable escapes in +.Fa text : +.It Dq \eZ0 +black. +.It Dq \eZ1 +red. +.It Dq \eZ2 +green. +.It Dq \eZ3 +yellow. +.It Dq \eZ4 +blue. +.It Dq \eZ5 +magenta. +.It Dq \eZ6 +cyan. +.It Dq \eZ7 +white. +.It Dq \eZb +bold. +.It Dq \eZB +disable bold. +.It Dq \eZd +Half bright. +.It Dq \eZD +disable half bright. +.It Dq \eZk +Blink. +.It Dq \eZK +disable blinking. +.It Dq \eZr +reverse foreground and background. +.It Dq \eZR +disable reverse. +.It Dq \eZs +Highlight. +.It Dq \eZS +disable highlighting. +.It Dq \eZu +underline. +.It Dq \eZU +disable underline. +.It Dq \eZn +disable each customization. +.It Fa conf.text.tablen +tab length for +.Fa text +argument and +.Fn bsddialog_textbox +function. +.El +.Pp +.Bl -column -compact +.It Fa conf.button.always_active +buttons always active, avoiding focus switch between buttons and input fields or +input boxes in +.Fn bsddialog_form , +.Fn bsddialog_datebox , +.Fn bsddialog_calendar +and +.Fn bsddialog_timebox . +.It Fa conf.button.left1_label +add a button with the specified label. +.It Fa conf.button.left2_label +add a button with the specified label. +.It Fa conf.button.left3_label +add a button with the specified label. +.It Fa conf.button.without_ok +disable OK button. +.It Fa conf.button.ok_label +set label for OK button. +.It Fa conf.button.with_extra +add Extra button. +.It Fa conf.button.extra_label +set a label for Extra button. +.It Fa conf.button.without_cancel +disable Cancel button. +.It Fa conf.button.cancel_label +sets a label for Cancel button. +.It Fa conf.button.default_cancel +on startup focus on the Cancel button. +.It Fa conf.button.with_help +add Help button. +.It Fa conf.button.help_label +set a label for Help button. +.It Fa conf.button.right1_label +add a button with the specified label. +.It Fa conf.button.right2_label +add a button with the specified label. +.It Fa conf.button.right3_label +add a button with the specified label. +.It Fa conf.button.default_label +focus on the button with the specified label. +.El +.Pp +.Fn bsddialog_initconf +initializes +.Fa conf +disabling each property, except +.Fa conf.shadow +to true, +.Fa conf.y +and +.Fa conf.x +to +.Dv BSDDIALOG_CENTER , +.Fa conf.text.cols_per_row +to +.Dv 10 . +.Pp +.Fn bsddialog_calendar +builds a dialog to select a date. +.Fa year , +.Fa month , +and +.Fa day +are default values on startup, selected date at exit. +.Pp +.Fn bsddialog_checklist +builds dialogs to select some item from a list via the SPACE key, can be +customized by +.Fa conf.menu.* . +See +.Fn bsddialog_menu . +.Pp +.Fn bsddialog_datebox +builds a dialog to select a date. +.Fa year , +.Fa month , +and +.Fa day +are default values on startup, selected date at exit. +The function can be customized by: +.Bl -column -compact +.It Fa conf.date.format +date format user interface, possible values: +.Dq d/m/y , +.Dq m/d/y , +.Dq y/m/d . +.El +.Pp +.Fn bsddialog_form +builds a dialog to display an array of +.Fa items +of +.Fa nitems +elements to get input strings. +.Fa formrows +is the graphical height for the items inside the dialog, +.Dv 0 +for autosizing. +If not +.Dv NULL +.Fa focusitem +is the default item index on startup and the last focused item at exit, a +negative value if no item is focused. +An item is defined like: +.Pp +.Bd -literal -offset indent -compact +struct bsddialog_formitem { + const char *label; + unsigned int ylabel; + unsigned int xlabel; + + const char *init; + unsigned int yfield; + unsigned int xfield; + unsigned int fieldlen; + unsigned int maxvaluelen; + char *value; + + unsigned int flags; + + const char *bottomdesc; +}; +.Ed +.Pp +.Fa label +is a string to describe the request at the position +.Fa ylabel +and +.Fa xlabel . +The field for the input is at the position +.Fa yfield +and +.Fa xfield , +.Fa fieldlen +is its graphical width, while +.Fa maxvalelen +is the maximum number of characters of the input string. +.Fa init +is the default field value. +If no error occurs +.Fa value +is the allocated memory with the current field string at exit, its size depends +on the current locale. +.Fa flags +is an OR value to set the field: +.Dv BSDDIALOG_FIELDHIDDEN , +.Dv BSDDIALOG_FIELDREADONLY , +.Dv BSDDIALOG_FIELDNOCOLOR , +.Dv BSDDIALOG_FIELDCURSOREND , +.Dv BSDDIALOG_FIELDEXTEND , +.Dv BSDDIALOG_FIELDSINGLEBYTE . +.Fa bottomdesc +is printed at bottom screen if the item is focused. +.Pp +.Fn bsddialog_form +can be customized by: +.Bl -column -compact +.It Fa conf.form.securech +charachter to hide the input with +.Dv BSDDIALOG_FIELDHIDDEN . +.It Fa conf.form.securembch +multibyte charachter to hide the input with +.Dv BSDDIALOG_FIELDHIDDEN , +.Fa conf.form.securech +is ignored. +.It Fa conf.form.value_wchar +the allocated +.Fa value +is a +.Em wchar_t* +string. +.El +.Pp +.Fn bsddialog_gauge +builds a dialog with a bar to show +.Fa perc . +If the file descriptor +.Fa fd +is greater or equal to 0 the dialog waits to read +.Fa sep +from it, then the first string replaces +.Fa perc +and the following strings replace +.Fa text +until the next +.Fa sep , +the loop ends reading +.Fa end . +.Pp +.Fn bsddialog_infobox +builds a dialog without buttons and returns instantly. +.Pp +.Fn bsddialog_menu +builds a dialog to select an item from a list via SPACE or ENTER. +An item is +defined like: +.Pp +.Bd -literal -offset indent -compact +struct bsddialog_menuitem { + const char *prefix; + bool on; + unsigned int depth; + const char *name; + const char *desc; + const char *bottomdesc; +}; +.Ed +.Pp +.Fa prefix , +.Fa name +and +.Fa desc +are printed at the item row. +.Fa bottomdesc +is printed at bottom screen if the item is focused. +.Fa depth +is a margin between +.Fa prefix +and +.Fa name . +At exit +.Fa on +is set to +.Dv true +if the item is selected, +.Dv false +otherwise. +.Fa items +is an array of items of +.Fa nitem +elements. +.Fa menurows +is the graphical height of the list inside the dialog, if +.Fa cols +is +.Dv BSDDIALOG_AUTOSIZE +.Fa menurows +specifies a maximum value. +if not +.Dv NULL +.Fa focusitem +is the default item index on startup and the last focused item at exit, a +negative value if no item is focused. +.Pp +.Fn bsddialog_checklist , +.Fn bsddialog_menu , +.Fn bsddialog_mixedlist +and +.Fn bsddialog_radiolist +can be customized by: +.Bl -column -compact +.It Fa conf.menu.align_left +align items to left, default center. +.It Fa conf.menu.no_desc +hide items description. +.It Fa conf.menu.no_name +hide items name, mutually exclusive with +.Fa conf.menu.no_desc . +.It Fa conf.menu.shortcut_buttons +enable shortcut keys on buttons, default on items. +.El +.Pp +.Fn bsddialog_mixedgauge +builds a dialog with a main bar with the +.Fa mainperc +percentage and +.Fa nminibars +each one with a +.Fa minilabel +and a +.Fa miniperc . +.Fa miniperc +can be: a positive value to print a bar with a percentace, a negative constant +.Dv BSDDIALOG_MG_SUCCEEDED , +.Dv BSDDIALOG_MG_FAILED , +.Dv BSDDIALOG_MG_PASSED , +.Dv BSDDIALOG_MG_COMPLETED , +.Dv BSDDIALOG_MG_CHECKED , +.Dv BSDDIALOG_MG_DONE , +.Dv BSDDIALOG_MG_SKIPPED , +.Dv BSDDIALOG_MG_INPROGRESS , +.Dv BSDDIALOG_MG_BLANK +to hide +.Fa miniperc , +.Dv BSDDIALOG_MG_NA , +.Dv BSDDIALOG_MG_PENDING +to print a descriptive string, otherwise +.Dq "UNKNOWN" +is printed. +.Pp +.Fn bsddialog_mixedlist +builds a dialog with collections of checklists, radiolists and separators. +A collection is a set defined like: +.Pp +.Bd -literal -offset indent -compact +enum bsddialog_menutype { + BSDDIALOG_CHECKLIST, + BSDDIALOG_RADIOLIST, + BSDDIALOG_SEPARATOR, +}; + +struct bsddialog_menugroup { + enum bsddialog_menutype type; + unsigned int nitems; + struct bsddialog_menuitem *items; + unsigned int min_on; /* unused for now */ +}; +.Ed +.Pp +.Fa groups +is an array of sets of +.Fa ngroups +elements. +.Fa menurows +is the graphical height size for the list. +If not +.Dv NULL , +.Fa focuslist +and +.Fa focusitem +specify the default item on startup and the last focused item at exit, could be +a negative value if no item is focused. +The dialog can be customized by +.Fa conf.menu.* , +see +.Fn bsddialog_menu . +.Pp +.Fn bsddialog_msgbox +builds a dialog with OK button. +.Pp +.Fn bsddialog_pause +builds a dialog waiting until the timeout in +.Fa seconds +expires or a button is pressed. +At exit +.Fa seconds +is set like remaining time. +.Pp +.Fn bsddialog_radiolist +builds dialogs to select at most an item from a list via the SPACE key, can be +customized by +.Fa conf.menu.* . +See +.Fn bsddialog_menu . +.Pp +.Fn bsddialog_rangebox +to select a value between +.Fa min +and +.Fa max . +.Fa value +is the default value on startup and the selected value at exit. +The current value is printed inside a bar, the keys UP, DOWN, HOME, END, PAGEUP +and PAGEDOWN can change it. +.Pp +.Fn bsddialog_textbox +opens and prints +.Fa file . +UP, DOWN, LEFT, RIGHT, HOME, END, PAGEUP and PAGEDOWN keys are available to +navigate the file, TAB changes button. +.Dq OK +button is renamed +.Dq EXIT . +.Pp +.Fn bsddialog_timebox +builds a dialog to choose a time. +.Fa hh , +.Fa mm , +and +.Fa ss +are default values on startup, selected time at exit. +.Pp +.Fn bsddialog_yesno +provides a dialog for a +.Dq Yes-No Question , +the labels on buttons are Yes and No. +.Ss Theme +The graphical properties are global to the library. +They are represented by +.Fa struct bsddialog_theme +and can be customized at runtime via the +.In bsddialog_theme.h +API. +.Pp +.Bd -literal -offset indent -compact +struct bsddialog_theme { + struct { + int color; + } screen; + struct { + int color; + unsigned int y; + unsigned int x; + } shadow; + struct { + int color; + bool delimtitle; + int titlecolor; + int lineraisecolor; + int linelowercolor; + int bottomtitlecolor; + int arrowcolor; + } dialog; + struct { + int f_prefixcolor; + int prefixcolor; + int f_selectorcolor; + int selectorcolor; + int f_namecolor; + int namecolor; + int f_desccolor; + int desccolor; + int f_shortcutcolor; + int shortcutcolor; + int bottomdesccolor; + int sepnamecolor; + int sepdesccolor; + } menu; + struct { + int f_fieldcolor; + int fieldcolor; + int readonlycolor; + int bottomdesccolor; + } form; + struct { + int f_color; + int color; + } bar; + struct { + unsigned int minmargin; + unsigned int maxmargin; + char leftdelim; + char rightdelim; + int f_delimcolor; + int delimcolor; + int f_color; + int color; + int f_shortcutcolor; + int shortcutcolor; + } button; +}; +.Ed +.Pp +A member with the +.Dq f_ +refers to focus when an element can be in selected or not selected state. +.Pp +.Fn bsddialog_color +generates and returns a color to set a +.Fa struct bsddialog_theme +color member. +An +.Fa enum bsddialog_color +can be: +.Dv BSDDIALOG_BLACK , +.Dv BSDDIALOG_RED , +.Dv BSDDIALOG_GREEN , +.Dv BSDDIALOG_YELLOW , +.Dv BSDDIALOG_BLUE , +.Dv BSDDIALOG_MAGENTA , +.Dv BSDDIALOG_CYAN , +.Dv BSDDIALOG_WHITE . +.Fa flags +is an OR value: +.Dv BSDDIALOG_BLINK , +.Dv BSDDIALOG_BOLD , +.Dv BSDDIALOG_HALFBRIGHT , +.Dv BSDDIALOG_HIGHLIGHT , +.Dv BSDDIALOG_REVERSE , +.Dv BSDDIALOG_UNDERLINE . +.Pp +.Fn bsddialog_color_attrs +sets, if not NULL, +.Fa foreground , +.Fa background , +.Fa flags , +like the properties of +.Fa color , +see +.Fn bsddialog_color . +.Pp +.Fn bsddialog_get_theme +sets +.Fa theme +like the current runtime theme. +.Pp +.Fn bsddialog_hascolors +returns +.Dv true +if the terminal provides colors, +.Dv false +otherwise. +.Pp +.Fn bsddialog_set_theme +sets +.Fa theme +like current runtime theme. +Changes take effect only for dialogs built after +the call. +.Pp +.Fn bsddialog_set_default_theme +sets a library default theme like current theme, possible values: +.Dv BSDDIALOG_THEME_BLACKWHITE , +.Dv BSDDIALOG_THEME_FLAT , +.Dv BSDDIALOG_THEME_3D . +Changes take effect only for dialogs built after the call. +.Sh RETURN VALUES +The functions return the value +.Dv BSDDIALOG_ERROR +if unsuccessful; +otherwise, depending on the pressed button, the following values can be +returned: +.Dv BSDDIALOG_OK , +.Dv BSDDIALOG_CANCEL , +.Dv BSDDIALOG_HELP , +.Dv BSDDIALOG_EXTRA , +.Dv BSDDIALOG_LEFT1 , +.Dv BSDDIALOG_LEFT2 , +.Dv BSDDIALOG_LEFT3 , +.Dv BSDDIALOG_RIGHT1 , +.Dv BSDDIALOG_RIGHT2 , +.Dv BSDDIALOG_RIGHT3 . +.Dv BSDDIALOG_YES +and +.Dv BSDDIALOG_NO +are aliases for +.Dv BSDDIALOG_OK +and +.Dv BSDDIALOG_CANCEL , +respectively. +.Pp +The functions return +.Dv BSDDIALOG_ESC +if +.Fa conf.key.enable_esc +is enabled and the ESC key is pressed. +.Pp +.Fn bsddialog_pause +returns +.Dv BSDDIALOG_TIMEOUT +if the timeout expires. +.Sh EXAMPLES +.Dq Yes-No Question +Example: +.Pp +.Bd -literal -offset indent -compact +int output; +struct bsddialog_conf conf; + +if (bsddialog_init() == BSDDIALOG_ERROR) + return (1); + +bsddialog_initconf(&conf); +conf.title = "yesno"; +output = bsddialog_yesno(&conf, "Example", 7, 25); + +bsddialog_end(); + +switch (output) { +case BSDDIALOG_YES: + printf("Yes\\n"); + break; +case BSDDIALOG_NO + printf("NO\\n"); + break; +case BSDDIALOG_ERROR: + printf("Error: %s\\n", bsddialog_geterror()); + break; +} +.Ed +.Pp +Theme Example: +.Pp +.Bd -literal -offset indent -compact +struct bsddialog_conf conf; +struct bsddialog_theme theme; + +bsddialog_init(); + +bsddialog_initconf(&conf); +bsddialog_msgbox(&conf, "Default theme", 7, 25); + +bsddialog_get_theme(&theme); +theme.screen.color = bsddialog_color(BSDDIALOG_RED, BSDDIALOG_GREEN, + BSDDIALOG_BOLD); +bsddialog_set_theme(&theme); +bsddialog_backtitle(&conf, "Red foreground and Green background"); +bsddialog_msgbox(&conf, "Change screen color", 7, 25); + +bsddialog_set_default_theme(BSDDIALOG_THEME_BLACKWHITE); +bsddialog_msgbox(&conf, "Black and White theme", 7, 25); + +bsddialog_end(); +.Ed +.Pp +Mixedlist Example: +.Pp +.Bd -literal -offset indent -compact +unsigned int i, j; +struct bsddialog_conf conf; +struct bsddialog_menuitem item; +struct bsddialog_menuitem check[2] = { + { "1", true, 0, "Name 1", "Desc 1", "Check Bottom Desc 1" }, + { "2", false, 0, "Name 2", "Desc 2", "Check Bottom Desc 2" } +}; +struct bsddialog_menuitem sep[1] = { + { "3", true, 0, "Radiolist", "(desc)", "" } +}; +struct bsddialog_menuitem radio[2] = { + { "4", true, 0, "Name 1", "Desc 1", "Radio Bottom Desc 1" }, + { "5", false, 0, "Name 2", "Desc 2", "Radio Bottom Desc 2" } +}; +struct bsddialog_menugroup group[3] = { + { BSDDIALOG_CHECKLIST, 2, check }, + { BSDDIALOG_SEPARATOR, 1, sep }, + { BSDDIALOG_RADIOLIST, 2, radio } +}; + +bsddialog_init(); +bsddialog_initconf(&conf); +bsddialog_mixedlist(&conf, "Example", 20, 30, 11, 3, group, NULL, + NULL); +bsddialog_end(); + +for (i = 0; i < 3; i++) { + for (j = 0; j < group[i].nitems; j++) { + item = group[i].items[j]; + switch (item.type) { + case BSDDIALOG_SEPARATOR: + printf("---- %s ----\\n", item.name); + break; + case BSDDIALOG_RADIOLIST: + printf(" (%c) %s\\n", + item.on ? '*' : ' ', item.name); + break; + case BSDDIALOG_CHECKLIST: + printf(" [%c] %s\\n", + item.on ? 'X' : ' ', item.name); + break; + } + } +} +.Ed +.Sh SEE ALSO +.Xr bsddialog 1 , +.Xr curses 3 +.Sh HISTORY +The +.Nm bsddialog +library first appeared in +.Fx 14.0 . +.Sh AUTHORS +.Nm bsddialog +was written by +.An Alfonso Sabato Siciliano Aq Mt asiciliano@FreeBSD.org . diff --git a/lib/bsddialog.h b/lib/bsddialog.h index 29b9b5240706..d997036b1f9f 100644 --- a/lib/bsddialog.h +++ b/lib/bsddialog.h @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021 Alfonso Sabato Siciliano + * Copyright (c) 2021-2023 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,189 +30,229 @@ #include <stdbool.h> -#define LIBBSDDIALOG_VERSION "0.0.1" - -/* Exit status */ -#define BSDDIALOG_ERROR -1 -#define BSDDIALOG_OK 0 -#define BSDDIALOG_YES BSDDIALOG_OK -#define BSDDIALOG_CANCEL 1 -#define BSDDIALOG_NO BSDDIALOG_CANCEL -#define BSDDIALOG_HELP 2 -#define BSDDIALOG_EXTRA 3 -#define BSDDIALOG_ITEM_HELP 4 -#define BSDDIALOG_TIMEOUT 5 -#define BSDDIALOG_ESC 6 -#define BSDDIALOG_GENERIC1 7 -#define BSDDIALOG_GENERIC2 8 - -/* size and position */ -#define BSDDIALOG_FULLSCREEN -1 -#define BSDDIALOG_AUTOSIZE 0 -#define BSDDIALOG_CENTER -1 +#define LIBBSDDIALOG_VERSION "1.0" + +/* Return values */ +#define BSDDIALOG_ERROR -1 +#define BSDDIALOG_OK 0 +#define BSDDIALOG_YES BSDDIALOG_OK +#define BSDDIALOG_CANCEL 1 +#define BSDDIALOG_NO BSDDIALOG_CANCEL +#define BSDDIALOG_HELP 2 +#define BSDDIALOG_EXTRA 3 +#define BSDDIALOG_TIMEOUT 4 +#define BSDDIALOG_ESC 5 +#define BSDDIALOG_LEFT1 6 +#define BSDDIALOG_LEFT2 7 +#define BSDDIALOG_LEFT3 8 +#define BSDDIALOG_RIGHT1 9 +#define BSDDIALOG_RIGHT2 10 +#define BSDDIALOG_RIGHT3 11 + +/* Size and position */ +#define BSDDIALOG_FULLSCREEN -1 +#define BSDDIALOG_AUTOSIZE 0 +#define BSDDIALOG_CENTER -1 + +/* Mixedgauge */ +#define BSDDIALOG_MG_SUCCEEDED -1 +#define BSDDIALOG_MG_FAILED -2 +#define BSDDIALOG_MG_PASSED -3 +#define BSDDIALOG_MG_COMPLETED -4 +#define BSDDIALOG_MG_CHECKED -5 +#define BSDDIALOG_MG_DONE -6 +#define BSDDIALOG_MG_SKIPPED -7 +#define BSDDIALOG_MG_INPROGRESS -8 +#define BSDDIALOG_MG_BLANK -9 +#define BSDDIALOG_MG_NA -10 +#define BSDDIALOG_MG_PENDING -11 + +/* Form */ +#define BSDDIALOG_FIELDHIDDEN 1U +#define BSDDIALOG_FIELDREADONLY 2U +#define BSDDIALOG_FIELDNOCOLOR 4U +#define BSDDIALOG_FIELDCURSOREND 8U +#define BSDDIALOG_FIELDEXTEND 16U +#define BSDDIALOG_FIELDSINGLEBYTE 32U struct bsddialog_conf { bool ascii_lines; - unsigned int aspect_ratio; unsigned int auto_minheight; unsigned int auto_minwidth; - char *bottomtitle; + unsigned int auto_topmargin; + unsigned int auto_downmargin; + const char *bottomtitle; bool clear; - char *f1_file; - char *f1_message; - int *get_height; - int *get_width; + int *get_height; + int *get_width; bool no_lines; bool shadow; unsigned int sleep; - char *title; - int y; - int x; + const char *title; + int y; + int x; + struct { + bool enable_esc; + const char *f1_file; + const char *f1_message; + } key; struct { - bool colors; + unsigned int cols_per_row; + bool escape; + unsigned int tablen; } text; struct { bool align_left; - char *default_item; bool no_desc; bool no_name; bool shortcut_buttons; } menu; struct { - int securech; - bool value_withcancel; - bool value_withextra; - bool value_withhelp; + char securech; + char *securembch; + bool value_wchar; } form; struct { + const char *format; + } date; + struct { + bool always_active; + const char *left1_label; + const char *left2_label; + const char *left3_label; bool without_ok; - char *ok_label; + const char *ok_label; bool with_extra; - char *extra_label; + const char *extra_label; bool without_cancel; - char *cancel_label; + const char *cancel_label; bool default_cancel; bool with_help; - char *help_label; - char *exit_label; - char *generic1_label; - char *generic2_label; - char *default_label; + const char *help_label; + const char *right1_label; + const char *right2_label; + const char *right3_label; + const char *default_label; } button; }; struct bsddialog_menuitem { - char *prefix; + const char *prefix; bool on; unsigned int depth; - char *name; - char *desc; - char *bottomdesc; + const char *name; + const char *desc; + const char *bottomdesc; }; -enum bsddialog_grouptype { +enum bsddialog_menutype { BSDDIALOG_CHECKLIST, BSDDIALOG_RADIOLIST, BSDDIALOG_SEPARATOR, }; struct bsddialog_menugroup { - enum bsddialog_grouptype type; + enum bsddialog_menutype type; unsigned int nitems; struct bsddialog_menuitem *items; + unsigned int min_on; /* unused for now */ }; struct bsddialog_formitem { - char *label; + const char *label; unsigned int ylabel; unsigned int xlabel; - char *init; + const char *init; unsigned int yfield; unsigned int xfield; unsigned int fieldlen; unsigned int maxvaluelen; - char *value; /* allocated memory */ -#define BSDDIALOG_FIELDHIDDEN 1U -#define BSDDIALOG_FIELDREADONLY 2U + char *value; unsigned int flags; - char *bottomdesc; + const char *bottomdesc; }; int bsddialog_init(void); +int bsddialog_init_notheme(void); +bool bsddialog_inmode(void); int bsddialog_end(void); -int bsddialog_backtitle(struct bsddialog_conf *conf, char *backtitle); +int bsddialog_backtitle(struct bsddialog_conf *conf, const char *backtitle); int bsddialog_initconf(struct bsddialog_conf *conf); -int bsddialog_clearterminal(void); +void bsddialog_clear(unsigned int y); +void bsddialog_refresh(void); const char *bsddialog_geterror(void); -/* widgets */ +/* Dialogs */ int -bsddialog_buildlist(struct bsddialog_conf *conf, char* text, int rows, int cols, - unsigned int menurows, int nitems, struct bsddialog_menuitem *items, - int *focusitem); +bsddialog_calendar(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int *year, unsigned int *month, unsigned int *day); int -bsddialog_checklist(struct bsddialog_conf *conf, char* text, int rows, int cols, - unsigned int menurows, int nitems, struct bsddialog_menuitem *items, - int *focusitem); +bsddialog_checklist(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int menurows, unsigned int nitems, + struct bsddialog_menuitem *items, int *focusitem); int -bsddialog_datebox(struct bsddialog_conf *conf, char* text, int rows, int cols, - unsigned int *yy, unsigned int *mm, unsigned int *dd); +bsddialog_datebox(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int *year, unsigned int *month, unsigned int *day); int -bsddialog_form(struct bsddialog_conf *conf, char* text, int rows, int cols, - unsigned int formheight, unsigned int nitems, - struct bsddialog_formitem *items); +bsddialog_form(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int formheight, unsigned int nitems, + struct bsddialog_formitem *items, int *focusitem); int -bsddialog_gauge(struct bsddialog_conf *conf, char* text, int rows, int cols, - unsigned int perc); +bsddialog_gauge(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int perc, int fd, const char *sep, const char *end); int -bsddialog_infobox(struct bsddialog_conf *conf, char* text, int rows, int cols); +bsddialog_infobox(struct bsddialog_conf *conf, const char *text, int rows, + int cols); int -bsddialog_menu(struct bsddialog_conf *conf, char* text, int rows, int cols, - unsigned int menurows, int nitems, struct bsddialog_menuitem *items, - int *focusitem); +bsddialog_menu(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int menurows, unsigned int nitems, + struct bsddialog_menuitem *items, int *focusitem); int -bsddialog_mixedgauge(struct bsddialog_conf *conf, char* text, int rows, - int cols, unsigned int mainperc, unsigned int nminibars, char **minilabels, - int *minipercs); +bsddialog_mixedgauge(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int mainperc, unsigned int nminibars, + const char **minilabels, int *minipercs); int -bsddialog_mixedlist(struct bsddialog_conf *conf, char* text, int rows, int cols, - unsigned int menurows, int ngroups, struct bsddialog_menugroup *groups, - int *focuslist, int *focusitem); +bsddialog_mixedlist(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int menurows, unsigned int ngroups, + struct bsddialog_menugroup *groups, int *focuslist, int *focusitem); int -bsddialog_msgbox(struct bsddialog_conf *conf, char* text, int rows, int cols); +bsddialog_msgbox(struct bsddialog_conf *conf, const char *text, int rows, + int cols); int -bsddialog_pause(struct bsddialog_conf *conf, char* text, int rows, int cols, - unsigned int sec); +bsddialog_pause(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int *seconds); int -bsddialog_radiolist(struct bsddialog_conf *conf, char* text, int rows, int cols, - unsigned int menurows, int nitems, struct bsddialog_menuitem *items, - int *focusitem); +bsddialog_radiolist(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int menurows, unsigned int nitems, + struct bsddialog_menuitem *items, int *focusitem); int -bsddialog_rangebox(struct bsddialog_conf *conf, char* text, int rows, int cols, - int min, int max, int *value); +bsddialog_rangebox(struct bsddialog_conf *conf, const char *text, int rows, + int cols, int min, int max, int *value); int -bsddialog_textbox(struct bsddialog_conf *conf, char* file, int rows, int cols); +bsddialog_textbox(struct bsddialog_conf *conf, const char *file, int rows, + int cols); int -bsddialog_timebox(struct bsddialog_conf *conf, char* text, int rows, int cols, - unsigned int *hh, unsigned int *mm, unsigned int *ss); +bsddialog_timebox(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int *hh, unsigned int *mm, unsigned int *ss); int -bsddialog_yesno(struct bsddialog_conf *conf, char* text, int rows, int cols); +bsddialog_yesno(struct bsddialog_conf *conf, const char *text, int rows, + int cols); #endif diff --git a/lib/bsddialog_progressview.h b/lib/bsddialog_progressview.h new file mode 100644 index 000000000000..5203b798bb07 --- /dev/null +++ b/lib/bsddialog_progressview.h @@ -0,0 +1,60 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021-2023 Alfonso Sabato Siciliano + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LIBBSDDIALOG_PROGRESSVIEW_H_ +#define _LIBBSDDIALOG_PROGRESSVIEW_H_ + +/* + * Undocumented API, DO NOT USE! + * Please consider this file private: it is used by bsdinstall/distextract, + * could be deleted or changed in the future. + */ + +extern bool bsddialog_interruptprogview; +extern bool bsddialog_abortprogview; +extern long long int bsddialog_total_progview; + +struct bsddialog_fileminibar { + const char *path; + const char *label; + int status; /* next if BSDDIALOG_MG_DONE or BSDDIALOG_MG_FAILED */ + long long int size; + long long int read; +}; + +struct bsddialog_progviewconf { + const char *fmtbottomstr; + unsigned int refresh; /* in seconds */ + int (*callback)(struct bsddialog_fileminibar *minibar); +}; + +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); + +#endif
\ No newline at end of file diff --git a/lib/bsddialog_theme.h b/lib/bsddialog_theme.h index d20985e66b18..2071896b61f0 100644 --- a/lib/bsddialog_theme.h +++ b/lib/bsddialog_theme.h @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021 Alfonso Sabato Siciliano + * Copyright (c) 2021-2023 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,15 +28,22 @@ #ifndef _LIBBSDDIALOG_THEME_H_ #define _LIBBSDDIALOG_THEME_H_ -/* f_ focus/active element */ +/* color flags */ +#define BSDDIALOG_BLINK 1U +#define BSDDIALOG_BOLD 2U +#define BSDDIALOG_HALFBRIGHT 4U +#define BSDDIALOG_HIGHLIGHT 8U +#define BSDDIALOG_REVERSE 16U +#define BSDDIALOG_UNDERLINE 32U + struct bsddialog_theme { struct { int color; - } terminal; + } screen; struct { int color; - unsigned int h; - unsigned int w; + unsigned int y; + unsigned int x; } shadow; struct { int color; @@ -45,49 +52,51 @@ struct bsddialog_theme { int lineraisecolor; int linelowercolor; int bottomtitlecolor; + int arrowcolor; } dialog; struct { - unsigned int hmargin; - } text; - struct { - int arrowcolor; + int f_prefixcolor; + int prefixcolor; + int f_selectorcolor; int selectorcolor; int f_namecolor; int namecolor; int f_desccolor; int desccolor; - int namesepcolor; - int descsepcolor; int f_shortcutcolor; int shortcutcolor; + int bottomdesccolor; + int sepnamecolor; + int sepdesccolor; } menu; struct { int f_fieldcolor; int fieldcolor; int readonlycolor; + int bottomdesccolor; } form; struct { int f_color; int color; } bar; struct { - unsigned int space; - int leftch; - int rightch; - int delimcolor; + unsigned int minmargin; + unsigned int maxmargin; + char leftdelim; + char rightdelim; int f_delimcolor; - int color; + int delimcolor; int f_color; - int shortcutcolor; + int color; int f_shortcutcolor; + int shortcutcolor; } button; }; enum bsddialog_default_theme { + BSDDIALOG_THEME_3D, BSDDIALOG_THEME_BLACKWHITE, - BSDDIALOG_THEME_BSDDIALOG, - BSDDIALOG_THEME_DEFAULT, - BSDDIALOG_THEME_DIALOG, + BSDDIALOG_THEME_FLAT }; enum bsddialog_color { @@ -98,18 +107,18 @@ enum bsddialog_color { BSDDIALOG_BLUE, BSDDIALOG_MAGENTA, BSDDIALOG_CYAN, - BSDDIALOG_WHITE, + BSDDIALOG_WHITE }; -#define BSDDIALOG_BOLD 1U -#define BSDDIALOG_REVERSE 2U -#define BSDDIALOG_UNDERLINE 4U - int -bsddialog_color(enum bsddialog_color background, - enum bsddialog_color foreground, unsigned int flags); +bsddialog_color(enum bsddialog_color foreground, + enum bsddialog_color background, unsigned int flags); +int +bsddialog_color_attrs(int color, enum bsddialog_color *foreground, + enum bsddialog_color *background, unsigned int *flags); int bsddialog_get_theme(struct bsddialog_theme *theme); +bool bsddialog_hascolors(void); int bsddialog_set_default_theme(enum bsddialog_default_theme theme); int bsddialog_set_theme(struct bsddialog_theme *theme); -#endif +#endif
\ No newline at end of file diff --git a/lib/datebox.c b/lib/datebox.c new file mode 100644 index 000000000000..9f26b2d08093 --- /dev/null +++ b/lib/datebox.c @@ -0,0 +1,715 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022-2023 Alfonso Sabato Siciliano + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <curses.h> +#include <stdlib.h> +#include <string.h> + +#include "bsddialog.h" +#include "bsddialog_theme.h" +#include "lib_util.h" + +/* Calendar */ +#define MIN_YEAR_CAL 0 +#define MAX_YEAR_CAL 999999999 +#define MINHCAL 13 +#define MINWCAL 36 /* 34 calendar, 1 + 1 margins */ +/* Datebox */ +#define MIN_YEAR_DATE 0 +#define MAX_YEAR_DATE 9999 +#define MINWDATE 23 /* 3 windows and their borders */ + +#define ISLEAP(year) ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) + +static int minyear; +static int maxyear; + +static const char *m[12] = { + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December" +}; + +enum operation { + UP_DAY, + DOWN_DAY, + LEFT_DAY, + RIGHT_DAY, + UP_MONTH, + DOWN_MONTH, + UP_YEAR, + DOWN_YEAR +}; + +/* private datebox item */ +struct dateitem { + enum operation up; + enum operation down; + WINDOW *win; + int width; + const char *fmt; + int *value; +}; + +static int month_days(int yy, int mm) +{ + int days; + + if (mm == 2) + days = ISLEAP(yy) ? 29 : 28; + else if (mm == 4 || mm == 6 || mm == 9 || mm == 11) + days = 30; + else + days = 31; + + return (days); +} + +static int week_day(int yy, int mm, int dd) +{ + int wd; + + dd += mm < 3 ? yy-- : yy - 2; + wd = 23*mm/9 + dd + 4 + yy/4 - yy/100 + yy/400; + wd %= 7; + + return (wd); +} + +static void +init_date(unsigned int *year, unsigned int *month, unsigned int *day, int *yy, + int *mm, int *dd) +{ + *yy = MIN(*year, (unsigned int)maxyear); + if (*yy < minyear) + *yy = minyear; + *mm = MIN(*month, 12); + if (*mm == 0) + *mm = 1; + *dd = (*day == 0) ? 1 : *day; + if(*dd > month_days(*yy, *mm)) + *dd = month_days(*yy, *mm); +} + +static void datectl(enum operation op, int *yy, int *mm, int *dd) +{ + int ndays; + + ndays = month_days(*yy, *mm); + + switch (op) { + case UP_DAY: + if (*dd > 7) + *dd -= 7; + else { + if (*mm == 1) { + *yy -= 1; + *mm = 12; + } else + *mm -= 1; + ndays = month_days(*yy, *mm); + *dd = ndays - abs(7 - *dd); + } + break; + case DOWN_DAY: + if (*dd + 7 < ndays) + *dd += 7; + else { + if (*mm == 12) { + *yy += 1; + *mm = 1; + } else + *mm += 1; + *dd = *dd + 7 - ndays; + } + break; + case LEFT_DAY: + if (*dd > 1) + *dd -= 1; + else { + if (*mm == 1) { + *yy -= 1; + *mm = 12; + } else + *mm -= 1; + *dd = month_days(*yy, *mm); + } + break; + case RIGHT_DAY: + if (*dd < ndays) + *dd += 1; + else { + if (*mm == 12) { + *yy += 1; + *mm = 1; + } else + *mm += 1; + *dd = 1; + } + break; + case UP_MONTH: + if (*mm == 1) { + *mm = 12; + *yy -= 1; + } else + *mm -= 1; + ndays = month_days(*yy, *mm); + if (*dd > ndays) + *dd = ndays; + break; + case DOWN_MONTH: + if (*mm == 12) { + *mm = 1; + *yy += 1; + } else + *mm += 1; + ndays = month_days(*yy, *mm); + if (*dd > ndays) + *dd = ndays; + break; + case UP_YEAR: + *yy -= 1; + ndays = month_days(*yy, *mm); + if (*dd > ndays) + *dd = ndays; + break; + case DOWN_YEAR: + *yy += 1; + ndays = month_days(*yy, *mm); + if (*dd > ndays) + *dd = ndays; + break; + } + + if (*yy < minyear) { + *yy = minyear; + *mm = 1; + *dd = 1; + } + if (*yy > maxyear) { + *yy = maxyear; + *mm = 12; + *dd = 31; + } +} + +static void +drawsquare(struct bsddialog_conf *conf, WINDOW *win, enum elevation elev, + const char *fmt, int value, bool focus) +{ + int h, l, w; + + getmaxyx(win, h, w); + draw_borders(conf, win, elev); + if (focus) { + l = 2 + w%2; + wattron(win, t.dialog.arrowcolor); + mvwhline(win, 0, w/2 - l/2, + conf->ascii_lines ? '^' : ACS_UARROW, l); + mvwhline(win, h-1, w/2 - l/2, + conf->ascii_lines ? 'v' : ACS_DARROW, l); + wattroff(win, t.dialog.arrowcolor); + } + + if (focus) + wattron(win, t.menu.f_namecolor); + if (strchr(fmt, 's') != NULL) + mvwprintw(win, 1, 1, fmt, m[value - 1]); + else + mvwprintw(win, 1, 1, fmt, value); + if (focus) + wattroff(win, t.menu.f_namecolor); + + wnoutrefresh(win); +} + +static void +print_calendar(struct bsddialog_conf *conf, WINDOW *win, int yy, int mm, int dd, + bool active) +{ + int ndays, i, y, x, wd, h, w; + + getmaxyx(win, h, w); + wclear(win); + draw_borders(conf, win, RAISED); + if (active) { + wattron(win, t.dialog.arrowcolor); + mvwhline(win, 0, 15, conf->ascii_lines ? '^' : ACS_UARROW, 4); + mvwhline(win, h-1, 15, conf->ascii_lines ? 'v' : ACS_DARROW, 4); + mvwvline(win, 3, 0, conf->ascii_lines ? '<' : ACS_LARROW, 3); + mvwvline(win, 3, w-1, conf->ascii_lines ? '>' : ACS_RARROW, 3); + wattroff(win, t.dialog.arrowcolor); + } + + mvwaddstr(win, 1, 5, "Sun Mon Tue Wed Thu Fri Sat"); + ndays = month_days(yy, mm); + y = 2; + wd = week_day(yy, mm, 1); + for (i = 1; i <= ndays; i++) { + x = 5 + (4 * wd); /* x has to be 6 with week number */ + wmove(win, y, x); + mvwprintw(win, y, x, "%2d", i); + if (i == dd) { + wattron(win, t.menu.f_namecolor); + mvwprintw(win, y, x, "%2d", i); + wattroff(win, t.menu.f_namecolor); + } + wd++; + if (wd > 6) { + wd = 0; + y++; + } + } + + wnoutrefresh(win); +} + +static int +calendar_redraw(struct dialog *d, WINDOW *yy_win, WINDOW *mm_win, + WINDOW *dd_win) +{ + int ycal, xcal; + + if (d->built) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ + } + if (dialog_size_position(d, MINHCAL, MINWCAL, NULL) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) + return (BSDDIALOG_ERROR); + if (d->built) + refresh(); /* Important to fix grey lines expanding screen */ + TEXTPAD(d, MINHCAL + HBUTTONS); + + ycal = d->y + d->h - 15; + xcal = d->x + d->w/2 - 17; + mvwaddstr(d->widget, d->h - 16, d->w/2 - 17, "Month"); + update_box(d->conf, mm_win, ycal, xcal, 3, 17, RAISED); + mvwaddstr(d->widget, d->h - 16, d->w/2, "Year"); + update_box(d->conf, yy_win, ycal, xcal + 17, 3, 17, RAISED); + update_box(d->conf, dd_win, ycal + 3, xcal, 9, 34, RAISED); + wnoutrefresh(d->widget); + + return (0); +} + +int +bsddialog_calendar(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int *year, unsigned int *month, unsigned int *day) +{ + bool loop, focusbuttons; + int retval, sel, yy, mm, dd; + wint_t input; + WINDOW *yy_win, *mm_win, *dd_win; + struct dialog d; + + CHECK_PTR(year); + CHECK_PTR(month); + CHECK_PTR(day); + minyear = MIN_YEAR_CAL; + maxyear = MAX_YEAR_CAL; + init_date(year, month, day, &yy, &mm, &dd); + + if (prepare_dialog(conf, text, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + set_buttons(&d, true, OK_LABEL, CANCEL_LABEL); + if ((yy_win = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW for yy"); + wbkgd(yy_win, t.dialog.color); + if ((mm_win = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW for mm"); + wbkgd(mm_win, t.dialog.color); + if ((dd_win = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW for dd"); + wbkgd(dd_win, t.dialog.color); + if (calendar_redraw(&d, yy_win, mm_win, dd_win) != 0) + return (BSDDIALOG_ERROR); + + sel = -1; + loop = focusbuttons = true; + while (loop) { + drawsquare(conf, mm_win, RAISED, "%15s", mm, sel == 0); + drawsquare(conf, yy_win, RAISED, "%15d", yy, sel == 1); + print_calendar(conf, dd_win, yy, mm, dd, sel == 2); + doupdate(); + + if (get_wch(&input) == ERR) + continue; + switch(input) { + case KEY_ENTER: + case 10: /* Enter */ + if (focusbuttons || conf->button.always_active) { + retval = BUTTONVALUE(d.bs); + loop = false; + } + break; + case 27: /* Esc */ + if (conf->key.enable_esc) { + retval = BSDDIALOG_ESC; + loop = false; + } + break; + case '\t': /* TAB */ + if (focusbuttons) { + d.bs.curr++; + if (d.bs.curr >= (int)d.bs.nbuttons) { + focusbuttons = false; + sel = 0; + d.bs.curr = conf->button.always_active ? + 0 : -1; + } + } else { + sel++; + if (sel > 2) { + focusbuttons = true; + sel = -1; + d.bs.curr = 0; + } + } + DRAW_BUTTONS(d); + break; + case KEY_RIGHT: + if (focusbuttons) { + d.bs.curr++; + if (d.bs.curr >= (int)d.bs.nbuttons) { + focusbuttons = false; + sel = 0; + d.bs.curr = conf->button.always_active ? + 0 : -1; + } + } else if (sel == 2) { + datectl(RIGHT_DAY, &yy, &mm, &dd); + } else { /* Month or Year*/ + sel++; + } + DRAW_BUTTONS(d); + break; + case KEY_LEFT: + if (focusbuttons) { + d.bs.curr--; + if (d.bs.curr < 0) { + focusbuttons = false; + sel = 2; + d.bs.curr = conf->button.always_active ? + 0 : -1; + } + } else if (sel == 2) { + datectl(LEFT_DAY, &yy, &mm, &dd); + } else if (sel == 1) { + sel = 0; + } else { /* sel = 0, Month */ + focusbuttons = true; + sel = -1; + d.bs.curr = 0; + } + DRAW_BUTTONS(d); + break; + case KEY_UP: + if (focusbuttons) { + sel = 2; + focusbuttons = false; + d.bs.curr = conf->button.always_active ? 0 : -1; + DRAW_BUTTONS(d); + } else if (sel == 0) { + datectl(UP_MONTH, &yy, &mm, &dd); + } else if (sel == 1) { + datectl(UP_YEAR, &yy, &mm, &dd); + } else { /* sel = 2 */ + datectl(UP_DAY, &yy, &mm, &dd); + } + break; + case KEY_DOWN: + if (focusbuttons) { + break; + } else if (sel == 0) { + datectl(DOWN_MONTH, &yy, &mm, &dd); + } else if (sel == 1) { + datectl(DOWN_YEAR, &yy, &mm, &dd); + } else { /* sel = 2 */ + datectl(DOWN_DAY, &yy, &mm, &dd); + } + break; + case KEY_HOME: + datectl(UP_MONTH, &yy, &mm, &dd); + break; + case KEY_END: + datectl(DOWN_MONTH, &yy, &mm, &dd); + break; + case KEY_PPAGE: + datectl(UP_YEAR, &yy, &mm, &dd); + break; + case KEY_NPAGE: + datectl(DOWN_YEAR, &yy, &mm, &dd); + break; + case KEY_F(1): + if (conf->key.f1_file == NULL && + conf->key.f1_message == NULL) + break; + if (f1help_dialog(conf) != 0) + return (BSDDIALOG_ERROR); + if (calendar_redraw(&d, yy_win, mm_win, dd_win) != 0) + return (BSDDIALOG_ERROR); + break; + case KEY_RESIZE: + if (calendar_redraw(&d, yy_win, mm_win, dd_win) != 0) + return (BSDDIALOG_ERROR); + break; + default: + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); + loop = false; + } + } + } + + *year = yy; + *month = mm; + *day = dd; + + delwin(yy_win); + delwin(mm_win); + delwin(dd_win); + end_dialog(&d); + + return (retval); +} + +static int datebox_redraw(struct dialog *d, struct dateitem *di) +{ + int y, x; + + if (d->built) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ + } + if (dialog_size_position(d, 3 /*windows*/, MINWDATE, NULL) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) + return (BSDDIALOG_ERROR); + if (d->built) + refresh(); /* Important to fix grey lines expanding screen */ + TEXTPAD(d, 3 /*windows*/ + HBUTTONS); + + y = d->y + d->h - 6; + x = (d->x + d->w / 2) - 11; + update_box(d->conf, di[0].win, y, x, 3, di[0].width, LOWERED); + mvwaddch(d->widget, d->h - 5, x - d->x + di[0].width, '/'); + x += di[0].width + 1; + update_box(d->conf, di[1].win, y, x , 3, di[1].width, LOWERED); + mvwaddch(d->widget, d->h - 5, x - d->x + di[1].width, '/'); + x += di[1].width + 1; + update_box(d->conf, di[2].win, y, x, 3, di[2].width, LOWERED); + wnoutrefresh(d->widget); + + return (0); +} + +static int +build_dateitem(const char *format, int *yy, int *mm, int *dd, + struct dateitem *dt) +{ + int i; + wchar_t *wformat; + struct dateitem init[3] = { + {UP_YEAR, DOWN_YEAR, NULL, 6, "%4d", yy}, + {UP_MONTH, DOWN_MONTH, NULL, 11, "%9s", mm}, + {LEFT_DAY, RIGHT_DAY, NULL, 4, "%02d", dd}, + }; + + for (i = 0; i < 3; i++) { + if ((init[i].win = newwin(1, 1, 1, 1)) == NULL) + RETURN_FMTERROR("Cannot build WINDOW dateitem[%d]", i); + wbkgd(init[i].win, t.dialog.color); + } + + if ((wformat = alloc_mbstows(CHECK_STR(format))) == NULL) + RETURN_ERROR("Cannot allocate conf.date.format in wchar_t*"); + if (format == NULL || wcscmp(wformat, L"d/m/y") == 0) { + dt[0] = init[2]; + dt[1] = init[1]; + dt[2] = init[0]; + } else if (wcscmp(wformat, L"m/d/y") == 0) { + dt[0] = init[1]; + dt[1] = init[2]; + dt[2] = init[0]; + } else if (wcscmp(wformat, L"y/m/d") == 0) { + dt[0] = init[0]; + dt[1] = init[1]; + dt[2] = init[2]; + } else + RETURN_FMTERROR("Invalid conf.date.format=\"%s\"", format); + free(wformat); + + return (0); +} + +int +bsddialog_datebox(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int *year, unsigned int *month, unsigned int *day) +{ + bool loop, focusbuttons; + int retval, i, sel, yy, mm, dd; + wint_t input; + struct dateitem di[3]; + struct dialog d; + + CHECK_PTR(year); + CHECK_PTR(month); + CHECK_PTR(day); + minyear = MIN_YEAR_DATE; + maxyear = MAX_YEAR_DATE; + init_date(year, month, day, &yy, &mm, &dd); + + if (prepare_dialog(conf, text, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + set_buttons(&d, true, OK_LABEL, CANCEL_LABEL); + if (build_dateitem(conf->date.format, &yy, &mm, &dd, di) != 0) + return (BSDDIALOG_ERROR); + if (datebox_redraw(&d, di) != 0) + return (BSDDIALOG_ERROR); + + sel = -1; + loop = focusbuttons = true; + while (loop) { + for (i = 0; i < 3; i++) + drawsquare(conf, di[i].win, LOWERED, di[i].fmt, + *di[i].value, sel == i); + doupdate(); + + if (get_wch(&input) == ERR) + continue; + switch(input) { + case KEY_ENTER: + case 10: /* Enter */ + if (focusbuttons || conf->button.always_active) { + retval = BUTTONVALUE(d.bs); + loop = false; + } + break; + case 27: /* Esc */ + if (conf->key.enable_esc) { + retval = BSDDIALOG_ESC; + loop = false; + } + break; + case KEY_RIGHT: + case '\t': /* TAB */ + if (focusbuttons) { + d.bs.curr++; + focusbuttons = d.bs.curr < (int)d.bs.nbuttons ? + true : false; + if (focusbuttons == false) { + sel = 0; + d.bs.curr = conf->button.always_active ? + 0 : -1; + } + } else { + sel++; + focusbuttons = sel > 2 ? true : false; + if (focusbuttons) { + d.bs.curr = 0; + } + } + DRAW_BUTTONS(d); + break; + case KEY_LEFT: + if (focusbuttons) { + d.bs.curr--; + focusbuttons = d.bs.curr < 0 ? false : true; + if (focusbuttons == false) { + sel = 2; + d.bs.curr = conf->button.always_active ? + 0 : -1; + } + } else { + sel--; + focusbuttons = sel < 0 ? true : false; + if (focusbuttons) + d.bs.curr = (int)d.bs.nbuttons - 1; + } + DRAW_BUTTONS(d); + break; + case KEY_UP: + if (focusbuttons) { + sel = 0; + focusbuttons = false; + d.bs.curr = conf->button.always_active ? 0 : -1; + DRAW_BUTTONS(d); + } else { + datectl(di[sel].up, &yy, &mm, &dd); + } + break; + case KEY_DOWN: + if (focusbuttons) + break; + datectl(di[sel].down, &yy, &mm, &dd); + break; + case KEY_F(1): + if (conf->key.f1_file == NULL && + conf->key.f1_message == NULL) + break; + if (f1help_dialog(conf) != 0) + return (BSDDIALOG_ERROR); + if (datebox_redraw(&d, di) != 0) + return (BSDDIALOG_ERROR); + break; + case KEY_RESIZE: + if (datebox_redraw(&d, di) != 0) + return (BSDDIALOG_ERROR); + break; + default: + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); + loop = false; + } + } + } + + *year = yy; + *month = mm; + *day = dd; + + for (i = 0; i < 3 ; i++) + delwin(di[i].win); + end_dialog(&d); + + return (retval); +}
\ No newline at end of file diff --git a/lib/formbox.c b/lib/formbox.c index fd497174a7ce..5e80471c6974 100644 --- a/lib/formbox.c +++ b/lib/formbox.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021 Alfonso Sabato Siciliano + * Copyright (c) 2021-2023 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,483 +25,899 @@ * SUCH DAMAGE. */ -#include <sys/param.h> - -#include <ctype.h> +#include <curses.h> +#include <limits.h> #include <stdlib.h> #include <string.h> -#ifdef PORTNCURSES -#include <ncurses/form.h> -#else -#include <form.h> -#endif - #include "bsddialog.h" -#include "lib_util.h" #include "bsddialog_theme.h" +#include "lib_util.h" -#define REDRAWFORM 14021986 /* magic number */ -#define ISFIELDHIDDEN(item) (item.flags & BSDDIALOG_FIELDHIDDEN) -#define ISFIELDREADONLY(item) (item.flags & BSDDIALOG_FIELDREADONLY) - -/* "Form": inputbox - passwordbox - form - passwordform - mixedform */ +enum field_action { + MOVE_CURSOR_BEGIN, + MOVE_CURSOR_END, + MOVE_CURSOR_RIGHT, + MOVE_CURSOR_LEFT, + DEL_LETTER +}; -extern struct bsddialog_theme t; +struct privateitem { + const char *label; /* formitem.label */ + unsigned int ylabel; /* formitem.ylabel */ + unsigned int xlabel; /* formitem.xlabel */ + unsigned int yfield; /* formitem.yfield */ + unsigned int xfield; /* formitem.xfield */ + bool secure; /* formitem.flags & BSDDIALOG_FIELDHIDDEN */ + bool readonly; /* formitem.flags & BSDDIALOG_FIELDREADONLY */ + bool fieldnocolor; /* formitem.flags & BSDDIALOG_FIELDNOCOLOR */ + bool extendfield; /* formitem.flags & BSDDIALOG_FIELDEXTEND */ + bool fieldonebyte; /* formitem.flags & BSDDIALOG_FIELDSINGLEBYTE */ + bool cursorend; /* formitem.flags & BSDDIALOG_FIELDCURSOREND */ + bool cursor; /* field cursor visibility */ + const char *bottomdesc; /* formitem.bottomdesc */ + + wchar_t *privwbuf; /* formitem.value */ + wchar_t *pubwbuf; /* string for drawitem() */ + unsigned int maxletters; /* formitem.maxvaluelen, [priv|pub]wbuf size */ + unsigned int nletters; /* letters in privwbuf and pubwbuf */ + unsigned int pos; /* pos in privwbuf and pubwbuf */ + unsigned int fieldcols; /* formitem.fieldlen */ + unsigned int xcursor; /* position in fieldcols [0 - fieldcols-1] */ + unsigned int xposdraw; /* first pubwbuf index to draw */ +}; -/* util struct for private buffer and view options */ -struct myfield { - int len; - char *buf; - int pos; - int size; - bool secure; - int securech; - char *bottomdesc; +struct privateform { + WINDOW *box; /* window to draw borders */ + WINDOW *pad; + unsigned int h; /* only to create pad */ + unsigned int w; /* only to create pad */ + unsigned int wmin; /* to refresh, w can change for FIELDEXTEND */ + unsigned int ys; /* to refresh */ + unsigned int ye; /* to refresh */ + unsigned int xs; /* to refresh */ + unsigned int xe; /* to refresh */ + unsigned int y; /* changes moving focus around items */ + unsigned int formheight; /* API formheight */ + unsigned int viewrows; /* visible rows, real formheight */ + unsigned int minviewrows; /* min viewrows, ylabel != yfield */ + wchar_t securewch; /* wide char of conf.form.secure[mb]ch */ + unsigned int nitems; /* like API nkitems */ + struct privateitem *pritems; + int sel; /* selected item in pritem, can be -1 */ + bool hasbottomdesc; /* some item has bottomdesc */ }; -#define GETMYFIELD(field) ((struct myfield*)field_userptr(field)) -#define GETMYFIELD2(form) ((struct myfield*)field_userptr(current_field(form))) -static void insertch(struct myfield *mf, int ch) +static int +build_privateform(struct bsddialog_conf*conf, unsigned int nitems, + struct bsddialog_formitem *items, struct privateform *f) { - int i; + bool insecurecursor; + int mbchsize; + unsigned int i, j, itemybeg, itemxbeg, tmp; + wchar_t *winit; + struct privateitem *item; + + /* checks */ + CHECK_ARRAY(nitems, items); + for (i = 0; i < nitems; i++) { + if (items[i].maxvaluelen == 0) + RETURN_FMTERROR("item %u [0-%u] maxvaluelen = 0", + i, nitems); + if (items[i].fieldlen == 0) + RETURN_FMTERROR("item %u [0-%u] fieldlen = 0", + i, nitems); + } + f->nitems = nitems; + + /* insecure ch */ + insecurecursor = false; + if (conf->form.securembch != NULL) { + mbchsize = mblen(conf->form.securembch, MB_LEN_MAX); + if(mbtowc(&f->securewch, conf->form.securembch, mbchsize) < 0) + RETURN_ERROR("Cannot convert securembch to wchar_t"); + insecurecursor = true; + } else if (conf->form.securech != '\0') { + f->securewch = btowc(conf->form.securech); + insecurecursor = true; + } else { + f->securewch = L' '; + } - if (mf->len == mf->size) - return; + /* alloc and set private items */ + f->pritems = malloc(f->nitems * sizeof(struct privateitem)); + if (f->pritems == NULL) + RETURN_ERROR("Cannot allocate internal form.pritems"); + f->hasbottomdesc = false; + f->h = f->w = f->minviewrows = 0; + for (i = 0; i < f->nitems; i++) { + item = &f->pritems[i]; + item->label = CHECK_STR(items[i].label); + item->ylabel = items[i].ylabel; + item->xlabel = items[i].xlabel; + item->yfield = items[i].yfield; + item->xfield = items[i].xfield; + item->secure = items[i].flags & BSDDIALOG_FIELDHIDDEN; + item->readonly = items[i].flags & BSDDIALOG_FIELDREADONLY; + item->fieldnocolor = items[i].flags & BSDDIALOG_FIELDNOCOLOR; + item->extendfield = items[i].flags & BSDDIALOG_FIELDEXTEND; + item->fieldonebyte = items[i].flags & + BSDDIALOG_FIELDSINGLEBYTE; + item->cursorend = items[i].flags & BSDDIALOG_FIELDCURSOREND; + item->bottomdesc = CHECK_STR(items[i].bottomdesc); + if (items[i].bottomdesc != NULL) + f->hasbottomdesc = true; + if (item->readonly || (item->secure && !insecurecursor)) + item->cursor = false; + else + item->cursor = true; + + item->maxletters = items[i].maxvaluelen; + item->privwbuf = calloc(item->maxletters + 1, sizeof(wchar_t)); + if (item->privwbuf == NULL) + RETURN_ERROR("Cannot allocate item private buffer"); + memset(item->privwbuf, 0, item->maxletters + 1); + item->pubwbuf = calloc(item->maxletters + 1, sizeof(wchar_t)); + if (item->pubwbuf == NULL) + RETURN_ERROR("Cannot allocate item private buffer"); + memset(item->pubwbuf, 0, item->maxletters + 1); + + if ((winit = alloc_mbstows(CHECK_STR(items[i].init))) == NULL) + RETURN_ERROR("Cannot allocate item.init in wchar_t*"); + wcsncpy(item->privwbuf, winit, item->maxletters); + wcsncpy(item->pubwbuf, winit, item->maxletters); + free(winit); + item->nletters = wcslen(item->pubwbuf); + if (item->secure) { + for (j = 0; j < item->nletters; j++) + item->pubwbuf[j] = f->securewch; + } - for (i=mf->len-1; i>=mf->pos; i--) { - mf->buf[i+1] = mf->buf[i]; + item->fieldcols = items[i].fieldlen; + item->xposdraw = 0; + item->xcursor = 0; + item->pos = 0; + + /* size and position */ + f->h = MAX(f->h, item->ylabel); + f->h = MAX(f->h, item->yfield); + f->w = MAX(f->w, item->xlabel + strcols(item->label)); + f->w = MAX(f->w, item->xfield + item->fieldcols); + if (i == 0) { + itemybeg = MIN(item->ylabel, item->yfield); + itemxbeg = MIN(item->xlabel, item->xfield); + } else { + tmp = MIN(item->ylabel, item->yfield); + itemybeg = MIN(itemybeg, tmp); + tmp = MIN(item->xlabel, item->xfield); + itemxbeg = MIN(itemxbeg, tmp); + } + tmp = abs((int)item->ylabel - (int)item->yfield); + f->minviewrows = MAX(f->minviewrows, tmp); + } + if (f->nitems > 0) { + f->h = f->h + 1 - itemybeg; + f->w -= itemxbeg; + f->minviewrows += 1; + } + f->wmin = f->w; + for (i = 0; i < f->nitems; i++) { + f->pritems[i].ylabel -= itemybeg; + f->pritems[i].yfield -= itemybeg; + f->pritems[i].xlabel -= itemxbeg; + f->pritems[i].xfield -= itemxbeg; } - mf->buf[mf->pos] = ch; - mf->pos += 1; - mf->len += 1; - mf->buf[mf->len] = '\0'; + return (0); } -static void shiftleft(struct myfield *mf) +static bool fieldctl(struct privateitem *item, enum field_action act) { - int i, last; + bool change; + int width, oldwidth, nextwidth, cols; + unsigned int i; + + change = false; + switch (act){ + case MOVE_CURSOR_BEGIN: + if (item->pos == 0 && item->xcursor == 0) + break; + /* here the cursor is changed */ + change = true; + item->pos = 0; + item->xcursor = 0; + item->xposdraw = 0; + break; + case MOVE_CURSOR_END: + while (fieldctl(item, MOVE_CURSOR_RIGHT)) + change = true; + break; + case MOVE_CURSOR_LEFT: + if (item->pos == 0) + break; + /* check redundant by item->pos == 0 because of 'while' below */ + if (item->xcursor == 0 && item->xposdraw == 0) + break; + /* here some letter to left */ + change = true; + item->pos -= 1; + width = wcwidth(item->pubwbuf[item->pos]); + if (((int)item->xcursor) - width < 0) { + item->xcursor = 0; + item->xposdraw -= 1; + } else + item->xcursor -= width; + + while (true) { + if (item->xposdraw == 0) + break; + if (item->xcursor >= item->fieldcols / 2) + break; + if (wcwidth(item->pubwbuf[item->xposdraw - 1]) + + item->xcursor + width > item->fieldcols) + break; + + item->xposdraw -= 1; + item->xcursor += + wcwidth(item->pubwbuf[item->xposdraw]); + } + break; + case DEL_LETTER: + if (item->nletters == 0) + break; + if (item->pos == item->nletters) + break; + /* here a letter under the cursor */ + change = true; + for (i = item->pos; i < item->nletters; i++) { + item->privwbuf[i] = item->privwbuf[i+1]; + item->pubwbuf[i] = item->pubwbuf[i+1]; + } + item->nletters -= 1; + item->privwbuf[i] = L'\0'; + item->pubwbuf[i] = L'\0'; + break; + case MOVE_CURSOR_RIGHT: /* used also by "insert", see handler loop */ + if (item->pos + 1 == item->maxletters) + break; + if (item->pos == item->nletters) + break; + /* here a change to right */ + change = true; + oldwidth = wcwidth(item->pubwbuf[item->pos]); + item->pos += 1; + if (item->pos == item->nletters) { /* empty column */ + nextwidth = 1; + } else { /* a letter to right */ + nextwidth = wcwidth(item->pubwbuf[item->pos]); + } + if (item->xcursor + oldwidth + nextwidth - 1 >= item->fieldcols) { + cols = nextwidth; + item->xposdraw = item->pos; + while (item->xposdraw != 0) { + cols += wcwidth(item->pubwbuf[item->xposdraw - 1]); + if (cols > (int)item->fieldcols) + break; + item->xposdraw -= 1; + } + item->xcursor = 0; + for (i = item->xposdraw; i < item->pos ; i++) + item->xcursor += wcwidth(item->pubwbuf[i]); + } + else { + item->xcursor += oldwidth; + } - for (i=mf->pos; i<mf->len; i++) { - mf->buf[i] = mf->buf[i+1]; + break; } - last = mf->len > 0 ? mf->len -1 : 0; - mf->buf[last] = '\0'; - mf->len = last; + return (change); } -static void print_bottomdesc(struct myfield *mf) +static bool insertch(struct privateitem *item, wchar_t wch, wchar_t securewch) { + int i; - move(LINES-1, 2); - clrtoeol(); - if (mf->bottomdesc != NULL) { - addstr(mf->bottomdesc); - refresh(); + if (item->nletters >= item->maxletters) + return (false); + + for (i = (int)item->nletters - 1; i >= (int)item->pos; i--) { + item->privwbuf[i+1] = item->privwbuf[i]; + item->pubwbuf[i+1] = item->pubwbuf[i]; } + + item->privwbuf[item->pos] = wch; + item->pubwbuf[item->pos] = item->secure ? securewch : wch; + item->nletters += 1; + item->privwbuf[item->nletters] = L'\0'; + item->pubwbuf[item->nletters] = L'\0'; + + return (true); +} + +static char* alloc_wstomb(wchar_t *wstr) +{ + int len, nbytes, i; + char mbch[MB_LEN_MAX], *mbstr; + + nbytes = MB_LEN_MAX; /* to ensure a null terminated string */ + len = wcslen(wstr); + for (i = 0; i < len; i++) { + wctomb(mbch, wstr[i]); + nbytes += mblen(mbch, MB_LEN_MAX); + } + if((mbstr = malloc(nbytes)) == NULL) + return (NULL); + + wcstombs(mbstr, wstr, nbytes); + + return (mbstr); } static int -form_handler(struct bsddialog_conf *conf, WINDOW *widget, int y, int cols, - struct buttons bs, WINDOW *formwin, FORM *form, FIELD **cfield, int nitems, +return_values(struct bsddialog_conf *conf, struct privateform *f, struct bsddialog_formitem *items) { - bool loop, buttupdate, informwin = true; - int i, input, output; - struct myfield *mf; - - curs_set(2); - pos_form_cursor(form); - loop = buttupdate = true; - bs.curr = -1; - form_driver(form, REQ_END_LINE); - form_driver(form, REQ_END_LINE); - mf = GETMYFIELD2(form); - print_bottomdesc(mf); - mf->pos = mf->len; - while(loop) { - if (buttupdate) { - draw_buttons(widget, y, cols, bs, !informwin); - wrefresh(widget); - buttupdate = false; + unsigned int i; + + for (i = 0; i < f->nitems; i++) { + if (conf->form.value_wchar) + items[i].value = (char*)wcsdup(f->pritems[i].privwbuf); + else + items[i].value = alloc_wstomb(f->pritems[i].privwbuf); + + if (items[i].value == NULL) + RETURN_FMTERROR( + "Cannot allocate memory for item[%d].value", i); + } + + return (0); +} + +static void set_first_with_default(struct privateform *f, int *focusitem) +{ + unsigned int i; + + f->sel = -1; + if(focusitem != NULL && *focusitem >=0 && *focusitem < (int)f->nitems) + if (f->pritems[*focusitem].readonly == false) { + f->sel = *focusitem; + return; + } + for (i = 0 ; i < f->nitems; i++) + if (f->pritems[i].readonly == false) { + f->sel = i; + break; + } +} + +static unsigned int firstitem(unsigned int nitems, struct privateitem *items) +{ + int i; + + for (i = 0; i < (int)nitems; i++) + if (items[i].readonly == false) + break; + + return (i); +} + +static unsigned int lastitem(unsigned int nitems, struct privateitem *items) +{ + int i; + + for (i = nitems - 1; i >= 0 ; i--) + if (items[i].readonly == false) + break; + + return (i); +} + +static unsigned int +previtem(unsigned int nitems, struct privateitem *items, int curritem) +{ + int i; + + for (i = curritem - 1; i >= 0; i--) + if (items[i].readonly == false) + return(i); + + for (i = nitems - 1; i > curritem - 1; i--) + if (items[i].readonly == false) + return(i); + + return (curritem); +} + +static unsigned int +nextitem(unsigned int nitems, struct privateitem *items, int curritem) +{ + int i; + + for (i = curritem + 1; i < (int)nitems; i++) + if (items[i].readonly == false) + return(i); + + for (i = 0; i < curritem; i++) + if (items[i].readonly == false) + return(i); + + return (curritem); +} + +static void redrawbuttons(struct dialog *d, bool focus, bool shortcut) +{ + int selected; + + selected = d->bs.curr; + if (focus == false) + d->bs.curr = -1; + d->bs.shortcut = shortcut; + draw_buttons(d); + d->bs.curr = selected; +} + +static void +drawitem(struct privateform *f, int idx, bool focus) +{ + int color; + unsigned int n, cols; + struct privateitem *item; + + item = &f->pritems[idx]; + + /* Label */ + wattron(f->pad, t.dialog.color); + mvwaddstr(f->pad, item->ylabel, item->xlabel, item->label); + wattroff(f->pad, t.dialog.color); + + /* Field */ + if (item->readonly) + color = t.form.readonlycolor; + else if (item->fieldnocolor) + color = t.dialog.color; + else + color = focus ? t.form.f_fieldcolor : t.form.fieldcolor; + wattron(f->pad, color); + mvwhline(f->pad, item->yfield, item->xfield, ' ', item->fieldcols); + n = 0; + cols = wcwidth(item->pubwbuf[item->xposdraw]); + while (cols <= item->fieldcols && + item->xposdraw + n < wcslen(item->pubwbuf)) { + n++; + cols += wcwidth(item->pubwbuf[item->xposdraw + n]); + + } + mvwaddnwstr(f->pad, item->yfield, item->xfield, + &item->pubwbuf[item->xposdraw], n); + wattroff(f->pad, color); + + /* Bottom Desc */ + if (f->hasbottomdesc) { + move(SCREENLINES - 1, 2); + clrtoeol(); + if (item->bottomdesc != NULL && focus) { + attron(t.form.bottomdesccolor); + addstr(item->bottomdesc); + attroff(t.form.bottomdesccolor); + refresh(); + } + } + + /* Cursor */ + curs_set((focus && item->cursor) ? 1 : 0); + wmove(f->pad, item->yfield, item->xfield + item->xcursor); +} + +/* + * Trick: draw 2 times an item switching focus. + * Problem: curses tries to optimize the rendering but sometimes it misses some + * updates or draws old stuff. libformw has a similar problem fixed by the + * same trick. + * Case 1: KEY_DC and KEY_BACKSPACE, deleted multicolumn letters are drawn + * again. It seems fixed by new items pad and prefresh(), previously WINDOW. + * Case2: some terminal, tmux and ssh does not show the cursor. + */ +#define DRAWITEM_TRICK(f, idx, focus) do { \ + drawitem(f, idx, !focus); \ + prefresh((f)->pad, (f)->y, 0, (f)->ys, (f)->xs, (f)->ye, (f)->xe); \ + drawitem(f, idx, focus); \ + prefresh((f)->pad, (f)->y, 0, (f)->ys, (f)->xs, (f)->ye, (f)->xe); \ +} while (0) + +static void update_formbox(struct bsddialog_conf *conf, struct privateform *f) +{ + int h, w; + + getmaxyx(f->box, h, w); + draw_borders(conf, f->box, LOWERED); + + if (f->viewrows < f->h) { + wattron(f->box, t.dialog.arrowcolor); + if (f->y > 0) + mvwhline(f->box, 0, (w / 2) - 2, + conf->ascii_lines ? '^' : ACS_UARROW, 5); + + if (f->y + f->viewrows < f->h) + mvwhline(f->box, h-1, (w / 2) - 2, + conf->ascii_lines ? 'v' : ACS_DARROW, 5); + wattroff(f->box, t.dialog.arrowcolor); + } +} + +static void curriteminview(struct privateform *f, struct privateitem *item) +{ + unsigned int yup, ydown; + + yup = MIN(item->ylabel, item->yfield); + ydown = MAX(item->ylabel, item->yfield); + + /* selected item in view */ + if (f->y > yup && f->y > 0) + f->y = yup; + if ((int)(f->y + f->viewrows) - 1 < (int)ydown) + f->y = ydown - f->viewrows + 1; + /* lower pad after a terminal expansion */ + if (f->y > 0 && (f->h - f->y) < f->viewrows) + f->y = f->h - f->viewrows; +} + +static int form_size_position(struct dialog *d, struct privateform *f) +{ + int htext, hform; + + if (set_widget_size(d->conf, d->rows, d->cols, &d->h, &d->w) != 0) + return (BSDDIALOG_ERROR); + + /* autosize */ + hform = (int) f->viewrows; + if (f->viewrows == BSDDIALOG_AUTOSIZE) + hform = MAX(f->h, f->minviewrows); + hform += 2; /* formborders */ + + if (set_widget_autosize(d->conf, d->rows, d->cols, &d->h, &d->w, + d->text, &htext, &d->bs, hform, f->w + 4) != 0) + return (BSDDIALOG_ERROR); + /* formheight: avoid overflow, "at most" and at least minviewrows */ + if (d->h - BORDERS - htext - HBUTTONS < 2 + (int)f->minviewrows) { + f->viewrows = f->minviewrows; /* for widget_checksize() */ + } else if (f->viewrows == BSDDIALOG_AUTOSIZE) { + f->viewrows = MIN(d->h - BORDERS - htext - HBUTTONS, hform) - 2; + f->viewrows = MAX(f->viewrows, f->minviewrows); + } else { + f->viewrows = MIN(d->h - BORDERS - htext - HBUTTONS, hform) - 2; + } + + /* checksize */ + if (f->viewrows < f->minviewrows) + RETURN_FMTERROR("formheight, current: %u needed at least %u", + f->viewrows, f->minviewrows); + if (widget_checksize(d->h, d->w, &d->bs, + 2 /* borders */ + f->minviewrows, f->w + 4) != 0) + return (BSDDIALOG_ERROR); + + if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0) + return (BSDDIALOG_ERROR); + + return (0); +} + +static int +form_redraw(struct dialog *d, struct privateform *f, bool focusinform) +{ + unsigned int i; + + if (d->built) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ + } + f->viewrows = f->formheight; + f->w = f->wmin; + if (form_size_position(d, f) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) + return (BSDDIALOG_ERROR); + if (d->built) + refresh(); /* Important to fix grey lines expanding screen */ + TEXTPAD(d, 2 /* box borders */ + f->viewrows + HBUTTONS); + + update_box(d->conf, f->box, d->y + d->h - 5 - f->viewrows, d->x + 2, + f->viewrows + 2, d->w - 4, LOWERED); + + for (i = 0; i < f->nitems; i++) { + fieldctl(&f->pritems[i], MOVE_CURSOR_BEGIN); + if (f->pritems[i].extendfield) { + f->w = d->w - 6; + f->pritems[i].fieldcols = f->w - f->pritems[i].xfield; } - wrefresh(formwin); - input = getch(); + if (f->pritems[i].cursorend) + fieldctl(&f->pritems[i], MOVE_CURSOR_END); + } + + wresize(f->pad, f->h, f->w); + for (i = 0; i < f->nitems; i++) + drawitem(f, i, false); + + f->ys = d->y + d->h - 5 - f->viewrows + 1; + f->ye = d->y + d->h - 5 ; + if ((int)f->w >= d->w - 6) { /* left */ + f->xs = d->x + 3; + f->xe = f->xs + d->w - 7; + } else { /* center */ + f->xs = d->x + 3 + (d->w - 6)/2 - f->w/2; + f->xe = f->xs + d->w - 5; + } + + if (f->sel != -1) { /* at least 1 writable item */ + redrawbuttons(d, + d->conf->button.always_active || !focusinform, + !focusinform); + wnoutrefresh(d->widget); + curriteminview(f, &f->pritems[f->sel]); + update_formbox(d->conf, f); + wnoutrefresh(f->box); + DRAWITEM_TRICK(f, f->sel, focusinform); + } else if (f->sel == -1 && f->nitems > 0) { /* all read only */ + redrawbuttons(d, true, true); + wnoutrefresh(d->widget); + update_formbox(d->conf, f); + wnoutrefresh(f->box); + DRAWITEM_TRICK(f, 0, false); /* to refresh pad*/ + } else { /* no item */ + wnoutrefresh(f->box); + } + + return (0); +} + +/* API */ +int +bsddialog_form(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int formheight, unsigned int nitems, + struct bsddialog_formitem *items, int *focusitem) +{ + bool switchfocus, changeitem, focusinform, loop; + int next, retval, wchtype; + unsigned int i; + wint_t input; + struct privateitem *item; + struct privateform form; + struct dialog d; + + if (prepare_dialog(conf, text, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + set_buttons(&d, true, OK_LABEL, CANCEL_LABEL); + + if (build_privateform(conf, nitems, items, &form) != 0) + return (BSDDIALOG_ERROR); + + if ((form.box = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW form box"); + wbkgd(form.box, t.dialog.color); + if ((form.pad = newpad(1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW form pad"); + wbkgd(form.pad, t.dialog.color); + + set_first_with_default(&form, focusitem); + if (form.sel != -1) { + focusinform = true; + form.y = 0; + item = &form.pritems[form.sel]; + } else { + item = NULL; + focusinform = false; + } + + form.formheight = formheight; + if (form_redraw(&d, &form, focusinform) != 0) + return (BSDDIALOG_ERROR); + + changeitem = switchfocus = false; + loop = true; + while (loop) { + doupdate(); + if ((wchtype = get_wch(&input)) == ERR) + continue; switch(input) { case KEY_ENTER: case 10: /* Enter */ - if (informwin) + if (focusinform && conf->button.always_active == false) break; + retval = BUTTONVALUE(d.bs); loop = false; - output = bs.value[bs.curr]; - if (output == BSDDIALOG_HELP && - conf->form.value_withhelp == false) - break; - if (output == BSDDIALOG_EXTRA && - conf->form.value_withextra == false) - break; - if (output == BSDDIALOG_CANCEL && - conf->form.value_withcancel == false) - break; - if (output == BSDDIALOG_GENERIC1 || - output == BSDDIALOG_GENERIC2) - break; - - /* BSDDIALOG_OK */ - form_driver(form, REQ_NEXT_FIELD); - form_driver(form, REQ_PREV_FIELD); - for (i=0; i<nitems; i++) { - mf = GETMYFIELD(cfield[i]); - items[i].value = strdup(mf->buf); - if (items[i].value == NULL) - RETURN_ERROR("Cannot allocate memory " - "for form value"); - } break; case 27: /* Esc */ - output = BSDDIALOG_ESC; - loop = false; + if (conf->key.enable_esc) { + retval = BSDDIALOG_ESC; + loop = false; + } break; case '\t': /* TAB */ - if (informwin) { - bs.curr = 0; - informwin = false; - curs_set(0); + if (focusinform) { + switchfocus = true; } else { - bs.curr++; - informwin = bs.curr >= (int) bs.nbuttons ? - true : false; - if (informwin) { - curs_set(2); - pos_form_cursor(form); + if (d.bs.curr + 1 < (int)d.bs.nbuttons) { + d.bs.curr++; + } else { + d.bs.curr = 0; + if (form.sel != -1) { + switchfocus = true; + } } + redrawbuttons(&d, true, true); + wnoutrefresh(d.widget); } - buttupdate = true; break; case KEY_LEFT: - if (informwin) { - form_driver(form, REQ_PREV_CHAR); - mf = GETMYFIELD2(form); - if (mf->pos > 0) - mf->pos -= 1; - } else { - if (bs.curr > 0) { - bs.curr--; - buttupdate = true; - } + if (focusinform) { + if(fieldctl(item, MOVE_CURSOR_LEFT)) + DRAWITEM_TRICK(&form, form.sel, true); + } else if (d.bs.curr > 0) { + d.bs.curr--; + redrawbuttons(&d, true, true); + wnoutrefresh(d.widget); + } else if (form.sel != -1) { + switchfocus = true; } break; case KEY_RIGHT: - if (informwin) { - mf = GETMYFIELD2(form); - if (mf->pos >= mf->len) - break; - mf->pos += 1; - form_driver(form, REQ_NEXT_CHAR); - } else { - if (bs.curr < (int) bs.nbuttons - 1) { - bs.curr++; - buttupdate = true; - } + if (focusinform) { + if(fieldctl(item, MOVE_CURSOR_RIGHT)) + DRAWITEM_TRICK(&form, form.sel, true); + } else if (d.bs.curr < (int) d.bs.nbuttons - 1) { + d.bs.curr++; + redrawbuttons(&d, true, true); + wnoutrefresh(d.widget); + } else if (form.sel != -1) { + switchfocus = true; } break; case KEY_UP: - if (nitems < 2) - break; - set_field_fore(current_field(form), t.form.fieldcolor); - set_field_back(current_field(form), t.form.fieldcolor); - form_driver(form, REQ_PREV_FIELD); - form_driver(form, REQ_END_LINE); - mf = GETMYFIELD2(form); - print_bottomdesc(mf); - mf->pos = mf->len; - set_field_fore(current_field(form), t.form.f_fieldcolor); - set_field_back(current_field(form), t.form.f_fieldcolor); + if (focusinform) { + next = previtem(form.nitems, form.pritems, + form.sel); + changeitem = form.sel != next; + } else if (form.sel != -1) { + switchfocus = true; + } break; case KEY_DOWN: - if (nitems < 2) + if (focusinform == false) break; - set_field_fore(current_field(form), t.form.fieldcolor); - set_field_back(current_field(form), t.form.fieldcolor); - form_driver(form, REQ_NEXT_FIELD); - form_driver(form, REQ_END_LINE); - mf = GETMYFIELD2(form); - print_bottomdesc(mf); - mf->pos = mf->len; - set_field_fore(current_field(form), t.form.f_fieldcolor); - set_field_back(current_field(form), t.form.f_fieldcolor); + if (form.nitems == 1) { + switchfocus = true; + } else { + next = nextitem(form.nitems, form.pritems, + form.sel); + changeitem = form.sel != next; + } + break; + case KEY_PPAGE: + if (focusinform) { + next = firstitem(form.nitems, form.pritems); + changeitem = form.sel != next; + } + break; + case KEY_NPAGE: + if (focusinform) { + next = lastitem(form.nitems, form.pritems); + changeitem = form.sel != next; + } break; case KEY_BACKSPACE: case 127: /* Backspace */ - mf = GETMYFIELD2(form); - if (mf->pos <= 0) + if (focusinform == false) break; - form_driver(form, REQ_DEL_PREV); - mf = GETMYFIELD2(form); - mf->pos -= 1; - shiftleft(mf); + if(fieldctl(item, MOVE_CURSOR_LEFT)) + if(fieldctl(item, DEL_LETTER)) + DRAWITEM_TRICK(&form, form.sel, true); break; case KEY_DC: - form_driver(form, REQ_DEL_CHAR); - mf = GETMYFIELD2(form); - if (mf->len-1 >= mf->pos) - shiftleft(mf); + if (focusinform == false) + break; + if(fieldctl(item, DEL_LETTER)) + DRAWITEM_TRICK(&form, form.sel, true); + break; + case KEY_HOME: + if (focusinform == false) + break; + if(fieldctl(item, MOVE_CURSOR_BEGIN)) + DRAWITEM_TRICK(&form, form.sel, true); + break; + case KEY_END: + if (focusinform == false) + break; + if (fieldctl(item, MOVE_CURSOR_END)) + DRAWITEM_TRICK(&form, form.sel, true); break; case KEY_F(1): - if (conf->f1_file == NULL && conf->f1_message == NULL) + if (conf->key.f1_file == NULL && + conf->key.f1_message == NULL) break; - if (f1help(conf) != 0) - return BSDDIALOG_ERROR; - /* No Break */ + curs_set(0); + if (f1help_dialog(conf) != 0) { + retval = BSDDIALOG_ERROR; + loop = false; + } + if (form_redraw(&d, &form, focusinform) != 0) + return (BSDDIALOG_ERROR); + break; case KEY_RESIZE: - output = REDRAWFORM; - loop = false; + if (form_redraw(&d, &form, focusinform) != 0) + return (BSDDIALOG_ERROR); break; default: - /* - * user input, add unicode chars to "public" buffer - */ - if (informwin) { - mf = GETMYFIELD2(form); - if (mf->secure) - form_driver(form, mf->securech); - else - form_driver(form, input); - insertch(mf, input); - } - else { - for (i = 0; i < (int) bs.nbuttons; i++) { - if (tolower(input) == - tolower((bs.label[i])[0])) { - output = bs.value[i]; - loop = false; - } + if (wchtype == KEY_CODE_YES) + break; + if (focusinform) { + if (item->fieldonebyte && wctob(input) == EOF) + break; + /* + * MOVE_CURSOR_RIGHT manages new positions + * because the cursor remains on the new letter, + * "if" and "while" update the positions. + */ + if(insertch(item, input, form.securewch)) { + fieldctl(item, MOVE_CURSOR_RIGHT); + /* + * no if(fieldctl), update always + * because it fails with maxletters. + */ + DRAWITEM_TRICK(&form, form.sel, true); + } + } else { + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); + loop = false; } } break; + } /* end switch get_wch() */ + + if (switchfocus) { + focusinform = !focusinform; + d.bs.curr = 0; + redrawbuttons(&d, + conf->button.always_active || !focusinform, + !focusinform); + wnoutrefresh(d.widget); + DRAWITEM_TRICK(&form, form.sel, focusinform); + switchfocus = false; } - } - - curs_set(0); - - return output; -} - -static void -form_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w, - char *text, int linelen, unsigned int *formheight, int nitems, - struct buttons bs) -{ - int textrow, menusize; - - textrow = text != NULL && strlen(text) > 0 ? 1 : 0; - - if (cols == BSDDIALOG_AUTOSIZE) { - *w = VBORDERS; - /* buttons size */ - *w += bs.nbuttons * bs.sizebutton; - *w += bs.nbuttons > 0 ? (bs.nbuttons-1) * t.button.space : 0; - /* line size */ - *w = MAX(*w, linelen + 3); - /* conf.auto_minwidth */ - *w = MAX(*w, (int)conf->auto_minwidth); - /* - * avoid terminal overflow, - * -1 fix false negative with big menu over the terminal and - * autosize, for example "portconfig /usr/ports/www/apache24/". - */ - *w = MIN(*w, widget_max_width(conf)-1); - } - - if (rows == BSDDIALOG_AUTOSIZE) { - *h = HBORDERS + 2 /* buttons */ + textrow; - if (*formheight == 0) { - *h += nitems + 2; - *h = MIN(*h, widget_max_height(conf)); - menusize = MIN(nitems + 2, *h - (HBORDERS + 2 + textrow)); - menusize -=2; - *formheight = menusize < 0 ? 0 : menusize; + if (changeitem) { + DRAWITEM_TRICK(&form, form.sel, false); + form.sel = next; + item = &form.pritems[form.sel]; + curriteminview(&form, item); + update_formbox(conf, &form); + wnoutrefresh(form.box); + DRAWITEM_TRICK(&form, form.sel, true); + changeitem = false; } - else /* h autosize with a fixed formheight */ - *h = *h + *formheight + 2; - - /* conf.auto_minheight */ - *h = MAX(*h, (int)conf->auto_minheight); - /* avoid terminal overflow */ - *h = MIN(*h, widget_max_height(conf)); - } - else { - if (*formheight == 0) - *formheight = MIN(rows-6-textrow, nitems); - } -} - -static int -form_checksize(int rows, int cols, char *text, int formheight, int nitems, - struct buttons bs) -{ - int mincols, textrow, formrows; - - mincols = VBORDERS; - /* buttons */ - mincols += bs.nbuttons * bs.sizebutton; - mincols += bs.nbuttons > 0 ? (bs.nbuttons-1) * t.button.space : 0; - /* line, comment to permet some cols hidden */ - /* mincols = MAX(mincols, linelen); */ - - if (cols < mincols) - RETURN_ERROR("Few cols, width < size buttons or "\ - "labels + forms"); - - textrow = text != NULL && strlen(text) > 0 ? 1 : 0; - - if (nitems > 0 && formheight == 0) - RETURN_ERROR("fields > 0 but formheight == 0, probably "\ - "terminal too small"); + } /* end while(loop) */ - formrows = nitems > 0 ? 3 : 0; - if (rows < 2 + 2 + formrows + textrow) - RETURN_ERROR("Few lines for this menus"); - - return 0; -} - -int -bsddialog_form(struct bsddialog_conf *conf, char* text, int rows, int cols, - unsigned int formheight, unsigned int nitems, - struct bsddialog_formitem *items) -{ - WINDOW *widget, *formwin, *textpad, *shadow; - int i, output, color, y, x, h, w, htextpad; - FIELD **cfield; - FORM *form; - struct buttons bs; - struct myfield *myfields; - unsigned long maxline; - - /* disable form scrolling like dialog */ - if (formheight < nitems) - formheight = nitems; - - maxline = 0; - myfields = malloc(nitems * sizeof(struct myfield)); - cfield = calloc(nitems + 1, sizeof(FIELD*)); - for (i=0; i < (int)nitems; i++) { - cfield[i] = new_field(1, items[i].fieldlen, items[i].yfield-1, - items[i].xfield-1, 0, 0); - field_opts_off(cfield[i], O_STATIC); - set_max_field(cfield[i], items[i].maxvaluelen); - set_field_buffer(cfield[i], 0, items[i].init); - - myfields[i].pos = strlen(items[i].init); - myfields[i].len = strlen(items[i].init); - myfields[i].size = items[i].maxvaluelen; - myfields[i].buf = malloc(myfields[i].size); - memset(myfields[i].buf, 0, myfields[i].size); - strcpy(myfields[i].buf, items[i].init); - myfields[i].bottomdesc = items[i].bottomdesc; - set_field_userptr(cfield[i], &myfields[i]); - - field_opts_off(cfield[i], O_AUTOSKIP); - field_opts_off(cfield[i], O_BLANK); - /* field_opts_off(field[i], O_BS_OVERLOAD); */ - - if (ISFIELDHIDDEN(items[i])) { - /* field_opts_off(field[i], O_PUBLIC); old hidden */ - myfields[i].secure = true; - myfields[i].securech = ' '; - if (conf->form.securech != '\0') - myfields[i].securech = conf->form.securech; - } - else myfields[i].secure = false; + curs_set(0); - if (ISFIELDREADONLY(items[i])) { - field_opts_off(cfield[i], O_EDIT); - field_opts_off(cfield[i], O_ACTIVE); - color = t.form.readonlycolor; - } else { - color = i == 0 ? t.form.f_fieldcolor : t.form.fieldcolor; - } - set_field_fore(cfield[i], color); - set_field_back(cfield[i], color); + if (return_values(conf, &form, items) == BSDDIALOG_ERROR) + return (BSDDIALOG_ERROR); - maxline = MAX(maxline, items[i].xlabel + strlen(items[i].label)); - maxline = MAX(maxline, items[i].xfield + items[i].fieldlen); - } - cfield[i] = NULL; + if (focusitem != NULL) + *focusitem = form.sel; - /* disable focus with 1 item (inputbox or passwordbox) */ - if (formheight == 1 && nitems == 1 && strlen(items[0].label) == 0 && - items[0].xfield == 1 ) { - set_field_fore(cfield[0], t.dialog.color); - set_field_back(cfield[0], t.dialog.color); + if (form.hasbottomdesc && conf->clear) { + move(SCREENLINES - 1, 2); + clrtoeol(); } - - get_buttons(conf, &bs, BUTTONLABEL(ok_label), BUTTONLABEL(extra_label), - BUTTONLABEL(cancel_label), BUTTONLABEL(help_label)); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return BSDDIALOG_ERROR; - form_autosize(conf, rows, cols, &h, &w, text, maxline, &formheight, - nitems, bs); - if (form_checksize(h, w, text, formheight, nitems, bs) != 0) - return BSDDIALOG_ERROR; - if (set_widget_position(conf, &y, &x, h, w) != 0) - return BSDDIALOG_ERROR; - - if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED, - &textpad, &htextpad, text, true) != 0) - return BSDDIALOG_ERROR; - - prefresh(textpad, 0, 0, y + 1, x + 1 + t.text.hmargin, - y + h - formheight, x + 1 + w - t.text.hmargin); - - formwin = new_boxed_window(conf, y + h - 3 - formheight -2, x +1, - formheight+2, w-2, LOWERED); - - form = new_form(cfield); - set_form_win(form, formwin); - /* should be formheight */ - set_form_sub(form, derwin(formwin, nitems, w-4, 1, 1)); - post_form(form); - - for (i=0; i < (int)nitems; i++) - mvwaddstr(formwin, items[i].ylabel, items[i].xlabel, items[i].label); - - wrefresh(formwin); - - do { - output = form_handler(conf, widget, h-2, w, bs, formwin, form, - cfield, nitems, items); - - if(update_widget_withtextpad(conf, shadow, widget, h, w, - RAISED, textpad, &htextpad, text, true) != 0) - return BSDDIALOG_ERROR; - - draw_buttons(widget, h-2, w, bs, true); - wrefresh(widget); - - prefresh(textpad, 0, 0, y + 1, x + 1 + t.text.hmargin, - y + h - formheight, x + 1 + w - t.text.hmargin); - - draw_borders(conf, formwin, formheight+2, w-2, LOWERED); - /* wrefresh(formwin); */ - } while (output == REDRAWFORM); - - unpost_form(form); - free_form(form); - for (i=0; i < (int)nitems; i++) { - free_field(cfield[i]); - free(myfields[i].buf); + for (i = 0; i < form.nitems; i++) { + free(form.pritems[i].privwbuf); + free(form.pritems[i].pubwbuf); } - free(cfield); - free(myfields); - - delwin(formwin); - end_widget_withtextpad(conf, widget, h, w, textpad, shadow); + delwin(form.pad); + delwin(form.box); + end_dialog(&d); - return output; + return (retval); } diff --git a/lib/infobox.c b/lib/infobox.c deleted file mode 100644 index 7752d7eec190..000000000000 --- a/lib/infobox.c +++ /dev/null @@ -1,118 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2021 Alfonso Sabato Siciliano - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/param.h> - -#ifdef PORTNCURSES -#include <ncurses/ncurses.h> -#else -#include <ncurses.h> -#endif - -#include "bsddialog.h" -#include "lib_util.h" -#include "bsddialog_theme.h" - -/* "Info": infobox */ - -#define MIN_HEIGHT 3 - -extern struct bsddialog_theme t; - -static int -infobox_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w, - char *text) -{ - int maxword, maxline, nlines; - - if (get_text_properties(conf, text, &maxword, &maxline, &nlines) != 0) - return BSDDIALOG_ERROR; - - if (cols == BSDDIALOG_AUTOSIZE) { - /* text size */ - *w = maxline + VBORDERS + t.text.hmargin * 2; - /* conf.auto_minwidth */ - *w = MAX(*w, (int)conf->auto_minwidth); - /* avoid terminal overflow */ - *w = MIN(*w, widget_max_width(conf)); - } - - if (rows == BSDDIALOG_AUTOSIZE) { - *h = MIN_HEIGHT - 1; - if (maxword > 0) - *h += MIN(nlines, (int)(*w / GET_ASPECT_RATIO(conf))); - *h = MAX(*h, MIN_HEIGHT); - /* conf.auto_minheight */ - *h = MAX(*h, (int)conf->auto_minheight); - /* avoid terminal overflow */ - *h = MIN(*h, widget_max_height(conf)); - } - - return 0; -} - -static int infobox_checksize(int rows, int cols) -{ - - if (cols < HBORDERS + 1 + (int) t.text.hmargin * 2) - RETURN_ERROR("Few cols, infobox needs at least width 3 + text "\ - "margins"); - - if (rows < 3) - RETURN_ERROR("Infobox needs at least height 3"); - - return 0; -} - -int -bsddialog_infobox(struct bsddialog_conf *conf, char* text, int rows, int cols) -{ - WINDOW *shadow, *widget, *textpad; - int y, x, h, w, htextpad; - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return BSDDIALOG_ERROR; - if (infobox_autosize(conf, rows, cols, &h, &w, text) != 0) - return BSDDIALOG_ERROR; - if (infobox_checksize(h, w) != 0) - return BSDDIALOG_ERROR; - if (set_widget_position(conf, &y, &x, h, w) != 0) - return BSDDIALOG_ERROR; - - if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED, - &textpad, &htextpad, text, false) != 0) - return BSDDIALOG_ERROR; - - pnoutrefresh(textpad, 0, 0, y+1, x+1+t.text.hmargin, y+h-2, x+w-t.text.hmargin); - - doupdate(); - - end_widget_withtextpad(conf, widget, h, w, textpad, shadow); - - return (BSDDIALOG_OK); -} - diff --git a/lib/lib_util.c b/lib/lib_util.c index b9bb29ba58cf..9cfdd6f1a075 100644 --- a/lib/lib_util.c +++ b/lib/lib_util.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021 Alfonso Sabato Siciliano + * Copyright (c) 2021-2023 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,91 +25,210 @@ * SUCH DAMAGE. */ -#include <sys/param.h> - +#include <curses.h> #include <stdlib.h> #include <string.h> #include <unistd.h> - -#ifdef PORTNCURSES -#include <ncurses/ncurses.h> -#else -#include <ncurses.h> -#endif +#include <wctype.h> #include "bsddialog.h" -#include "lib_util.h" #include "bsddialog_theme.h" +#include "lib_util.h" -extern struct bsddialog_theme t; +/* + * -1- Error and diagnostic + * + * get_error_string(); + * set_error_string(); + * set_fmt_error_string(); + * + * ---------------------------------------------------- + * -2- (Unicode) Multicolumn character strings + * + * alloc_mbstows(); + * mvwaddwch(); + * str_props(); + * strcols(); + * + * ---------------------------------------------------- + * -3- Buttons + * + * [static] buttons_min_width(); + * [static] draw_button(); + * draw_buttons(); + * set_buttons(); (to call 1 time after prepare_dialog()). + * shortcut_buttons(); + * + * ---------------------------------------------------- + * -4- (Auto) Sizing and (Auto) Position + * + * [static] widget_max_height(conf); + * [static] widget_max_width(struct bsddialog_conf *conf) + * [static] is_wtext_attr(); + * [static] text_properties(); + * [static] text_autosize(); + * [static] text_size(); + * [static] widget_min_height(conf, htext, hnotext, bool buttons); + * [static] widget_min_width(conf, wtext, minw, buttons); + * set_widget_size(); + * set_widget_autosize(); (not for all dialogs). + * widget_checksize(); (not for all dialogs). + * set_widget_position(); + * dialog_size_position(struct dialog); (not for all dialogs). + * + * ---------------------------------------------------- + * -5- (Dialog) Widget components and utils + * + * hide_dialog(struct dialog); + * f1help_dialog(conf); + * draw_borders(conf, win, elev); + * update_box(conf, win, y, x, h, w, elev); + * rtextpad(); (helper for pnoutrefresh(textpad)). + * + * ---------------------------------------------------- + * -6- Dialog init/build, update/draw, destroy + * + * end_dialog(struct dialog); + * [static] check_set_wtext_attr(); + * [static] print_string(); (word wrapping). + * [static] print_textpad(); + * draw_dialog(struct dialog); + * prepare_dialog(struct dialog); + */ -/* Error buffer */ +/* + * -1- Error and diagnostic + */ +#define ERRBUFLEN 1024 -#define ERRBUFLEN 1024 static char errorbuffer[ERRBUFLEN]; const char *get_error_string(void) { - return errorbuffer; + return (errorbuffer); } -void set_error_string(char *str) +void set_error_string(const char *str) { - strncpy(errorbuffer, str, ERRBUFLEN-1); } -/* cleaner */ -int hide_widget(int y, int x, int h, int w, bool withshadow) +void set_fmt_error_string(const char *fmt, ...) { - WINDOW *clear; + va_list arg_ptr; - /* no check: y, x, h and w are checked by the builders */ - if ((clear = newwin(h, w, y + t.shadow.h, x + t.shadow.w)) == NULL) - RETURN_ERROR("Cannot hide the widget"); - wbkgd(clear, t.terminal.color); + va_start(arg_ptr, fmt); + vsnprintf(errorbuffer, ERRBUFLEN-1, fmt, arg_ptr); + va_end(arg_ptr); +} - if (withshadow) - wrefresh(clear); +/* + * -2- (Unicode) Multicolumn character strings + */ +wchar_t* alloc_mbstows(const char *mbstring) +{ + size_t charlen, nchar; + mbstate_t mbs; + const char *pmbstring; + wchar_t *wstring; + + nchar = 1; + pmbstring = mbstring; + memset(&mbs, 0, sizeof(mbs)); + while ((charlen = mbrlen(pmbstring, MB_CUR_MAX, &mbs)) != 0 && + charlen != (size_t)-1 && charlen != (size_t)-2) { + pmbstring += charlen; + nchar++; + } - mvwin(clear, y, x); - wrefresh(clear); + if ((wstring = calloc(nchar, sizeof(wchar_t))) == NULL) + return (NULL); + mbstowcs(wstring, mbstring, nchar); - delwin(clear); + return (wstring); +} + +void mvwaddwch(WINDOW *w, int y, int x, wchar_t wch) +{ + wchar_t ws[2]; - return 0; + ws[0] = wch; + ws[1] = L'\0'; + mvwaddwstr(w, y, x, ws); } -/* F1 help */ -int f1help(struct bsddialog_conf *conf) +int str_props(const char *mbstring, unsigned int *cols, bool *has_multi_col) { - int output; - struct bsddialog_conf hconf; + bool multicol; + int w; + unsigned int ncol; + size_t charlen, mb_cur_max; + wchar_t wch; + mbstate_t mbs; + + multicol = false; + mb_cur_max = MB_CUR_MAX; + ncol = 0; + memset(&mbs, 0, sizeof(mbs)); + while ((charlen = mbrlen(mbstring, mb_cur_max, &mbs)) != 0 && + charlen != (size_t)-1 && charlen != (size_t)-2) { + if (mbtowc(&wch, mbstring, mb_cur_max) < 0) + return (-1); + w = (wch == L'\t') ? TABSIZE : wcwidth(wch); + ncol += (w < 0) ? 0 : w; + if (w > 1 && wch != L'\t') + multicol = true; + mbstring += charlen; + } - //memcpy(&hconf, conf, sizeof(struct bsddialog_conf)); - bsddialog_initconf(&hconf); - hconf.title = "HELP"; - hconf.button.ok_label = "EXIT"; - hconf.clear = true; - hconf.ascii_lines = conf->ascii_lines; - hconf.no_lines = conf->no_lines; - hconf.shadow = conf->shadow; - hconf.text.colors = conf->text.colors; + if (cols != NULL) + *cols = ncol; + if (has_multi_col != NULL) + *has_multi_col = multicol; - output = BSDDIALOG_OK; - if (conf->f1_message != NULL) - output = bsddialog_msgbox(&hconf, conf->f1_message, 0, 0); + return (0); +} - if (output != BSDDIALOG_ERROR && conf->f1_file != NULL) - output = bsddialog_textbox(&hconf, conf->f1_file, 0, 0); +unsigned int strcols(const char *mbstring) +{ + int w; + unsigned int ncol; + size_t charlen, mb_cur_max; + wchar_t wch; + mbstate_t mbs; + + mb_cur_max = MB_CUR_MAX; + ncol = 0; + memset(&mbs, 0, sizeof(mbs)); + while ((charlen = mbrlen(mbstring, mb_cur_max, &mbs)) != 0 && + charlen != (size_t)-1 && charlen != (size_t)-2) { + if (mbtowc(&wch, mbstring, mb_cur_max) < 0) + return (0); + w = (wch == L'\t') ? TABSIZE : wcwidth(wch); + ncol += (w < 0) ? 0 : w; + mbstring += charlen; + } - return (output == BSDDIALOG_ERROR ? BSDDIALOG_ERROR : 0); + return (ncol); } -/* Buttons */ -void -draw_button(WINDOW *window, int y, int x, int size, char *text, bool selected, - bool shortkey) +/* + * -3- Buttons + */ +static int buttons_min_width(struct buttons *bs) +{ + unsigned int width; + + width = bs->nbuttons * bs->sizebutton; + if (bs->nbuttons > 0) + width += (bs->nbuttons - 1) * t.button.minmargin; + + return (width); +} + +static void +draw_button(WINDOW *window, int y, int x, int size, const char *text, + wchar_t first, bool selected, bool shortcut) { int i, color_arrows, color_shortkey, color_button; @@ -124,358 +243,556 @@ draw_button(WINDOW *window, int y, int x, int size, char *text, bool selected, } wattron(window, color_arrows); - mvwaddch(window, y, x, t.button.leftch); + mvwaddch(window, y, x, t.button.leftdelim); wattroff(window, color_arrows); wattron(window, color_button); - for(i = 1; i < size - 1; i++) + for (i = 1; i < size - 1; i++) waddch(window, ' '); wattroff(window, color_button); wattron(window, color_arrows); - mvwaddch(window, y, x + i, t.button.rightch); + mvwaddch(window, y, x + i, t.button.rightdelim); wattroff(window, color_arrows); - x = x + 1 + ((size - 2 - strlen(text))/2); + x = x + 1 + ((size - 2 - strcols(text))/2); wattron(window, color_button); mvwaddstr(window, y, x, text); wattroff(window, color_button); - if (shortkey) { + if (shortcut) { wattron(window, color_shortkey); - mvwaddch(window, y, x, text[0]); + mvwaddwch(window, y, x, first); wattroff(window, color_shortkey); } } -void -draw_buttons(WINDOW *window, int y, int cols, struct buttons bs, bool shortkey) +void draw_buttons(struct dialog *d) { - int i, x, start_x; + int i, x, startx, y; + unsigned int newmargin, margin, wbuttons; + + y = d->h - 2; - start_x = bs.sizebutton * bs.nbuttons + (bs.nbuttons - 1) * t.button.space; - start_x = cols/2 - start_x/2; + newmargin = d->w - BORDERS - (d->bs.nbuttons * d->bs.sizebutton); + newmargin /= (d->bs.nbuttons + 1); + newmargin = MIN(newmargin, t.button.maxmargin); + if (newmargin == 0) { + margin = t.button.minmargin; + wbuttons = buttons_min_width(&d->bs); + } else { + margin = newmargin; + wbuttons = d->bs.nbuttons * d->bs.sizebutton; + wbuttons += (d->bs.nbuttons + 1) * margin; + } - for (i = 0; i < (int) bs.nbuttons; i++) { - x = i * (bs.sizebutton + t.button.space); - draw_button(window, y, start_x + x, bs.sizebutton, bs.label[i], - i == bs.curr, shortkey); + startx = d->w/2 - wbuttons/2 + newmargin; + for (i = 0; i < (int)d->bs.nbuttons; i++) { + x = i * (d->bs.sizebutton + margin); + draw_button(d->widget, y, startx + x, d->bs.sizebutton, + d->bs.label[i], d->bs.first[i], i == d->bs.curr, + d->bs.shortcut); } } void -get_buttons(struct bsddialog_conf *conf, struct buttons *bs, char *yesoklabel, - char *extralabel, char *nocancellabel, char *helplabel) +set_buttons(struct dialog *d, bool shortcut, const char *oklabel, + const char *cancellabel) { int i; -#define SIZEBUTTON 8 -#define DEFAULT_BUTTON_LABEL LABEL_ok_label +#define SIZEBUTTON 8 +#define DEFAULT_BUTTON_LABEL OK_LABEL #define DEFAULT_BUTTON_VALUE BSDDIALOG_OK + wchar_t first; + + d->bs.nbuttons = 0; + d->bs.curr = 0; + d->bs.sizebutton = 0; + d->bs.shortcut = shortcut; + + if (d->conf->button.left1_label != NULL) { + d->bs.label[d->bs.nbuttons] = d->conf->button.left1_label; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_LEFT1; + d->bs.nbuttons += 1; + } + + if (d->conf->button.left2_label != NULL) { + d->bs.label[d->bs.nbuttons] = d->conf->button.left2_label; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_LEFT2; + d->bs.nbuttons += 1; + } + + if (d->conf->button.left3_label != NULL) { + d->bs.label[d->bs.nbuttons] = d->conf->button.left3_label; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_LEFT3; + d->bs.nbuttons += 1; + } + if (oklabel != NULL && d->conf->button.without_ok == false) { + d->bs.label[d->bs.nbuttons] = d->conf->button.ok_label != NULL ? + d->conf->button.ok_label : oklabel; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_OK; + d->bs.nbuttons += 1; + } - bs->nbuttons = 0; - bs->curr = 0; - bs->sizebutton = 0; + if (d->conf->button.with_extra) { + d->bs.label[d->bs.nbuttons] = d->conf->button.extra_label != NULL ? + d->conf->button.extra_label : "Extra"; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_EXTRA; + d->bs.nbuttons += 1; + } - if (yesoklabel != NULL && conf->button.without_ok == false) { - bs->label[0] = yesoklabel; - bs->value[0] = BSDDIALOG_OK; - bs->nbuttons += 1; + if (cancellabel != NULL && d->conf->button.without_cancel == false) { + d->bs.label[d->bs.nbuttons] = d->conf->button.cancel_label ? + d->conf->button.cancel_label : cancellabel; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_CANCEL; + if (d->conf->button.default_cancel) + d->bs.curr = d->bs.nbuttons; + d->bs.nbuttons += 1; } - if (extralabel != NULL && conf->button.with_extra) { - bs->label[bs->nbuttons] = extralabel; - bs->value[bs->nbuttons] = BSDDIALOG_EXTRA; - bs->nbuttons += 1; + if (d->conf->button.with_help) { + d->bs.label[d->bs.nbuttons] = d->conf->button.help_label != NULL ? + d->conf->button.help_label : "Help"; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_HELP; + d->bs.nbuttons += 1; } - if (nocancellabel != NULL && conf->button.without_cancel == false) { - bs->label[bs->nbuttons] = nocancellabel; - bs->value[bs->nbuttons] = BSDDIALOG_CANCEL; - if (conf->button.default_cancel) - bs->curr = bs->nbuttons; - bs->nbuttons += 1; + if (d->conf->button.right1_label != NULL) { + d->bs.label[d->bs.nbuttons] = d->conf->button.right1_label; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_RIGHT1; + d->bs.nbuttons += 1; } - if (helplabel != NULL && conf->button.with_help) { - bs->label[bs->nbuttons] = helplabel; - bs->value[bs->nbuttons] = BSDDIALOG_HELP; - bs->nbuttons += 1; + if (d->conf->button.right2_label != NULL) { + d->bs.label[d->bs.nbuttons] = d->conf->button.right2_label; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_RIGHT2; + d->bs.nbuttons += 1; } - if (conf->button.generic1_label != NULL) { - bs->label[bs->nbuttons] = conf->button.generic1_label; - bs->value[bs->nbuttons] = BSDDIALOG_GENERIC1; - bs->nbuttons += 1; + if (d->conf->button.right3_label != NULL) { + d->bs.label[d->bs.nbuttons] = d->conf->button.right3_label; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_RIGHT3; + d->bs.nbuttons += 1; } - if (conf->button.generic2_label != NULL) { - bs->label[bs->nbuttons] = conf->button.generic2_label; - bs->value[bs->nbuttons] = BSDDIALOG_GENERIC2; - bs->nbuttons += 1; + if (d->bs.nbuttons == 0) { + d->bs.label[0] = DEFAULT_BUTTON_LABEL; + d->bs.value[0] = DEFAULT_BUTTON_VALUE; + d->bs.nbuttons = 1; } - if (bs->nbuttons == 0) { - bs->label[0] = DEFAULT_BUTTON_LABEL; - bs->value[0] = DEFAULT_BUTTON_VALUE; - bs->nbuttons = 1; + for (i = 0; i < (int)d->bs.nbuttons; i++) { + mbtowc(&first, d->bs.label[i], MB_CUR_MAX); + d->bs.first[i] = first; } - if (conf->button.default_label != NULL) { - for (i=0; i<(int)bs->nbuttons; i++) { - if (strcmp(conf->button.default_label, bs->label[i]) == 0) - bs->curr = i; + if (d->conf->button.default_label != NULL) { + for (i = 0; i < (int)d->bs.nbuttons; i++) { + if (strcmp(d->conf->button.default_label, + d->bs.label[i]) == 0) + d->bs.curr = i; } } - bs->sizebutton = MAX(SIZEBUTTON - 2, strlen(bs->label[0])); - for (i=1; i < (int) bs->nbuttons; i++) - bs->sizebutton = MAX(bs->sizebutton, strlen(bs->label[i])); - bs->sizebutton += 2; + d->bs.sizebutton = MAX(SIZEBUTTON - 2, strcols(d->bs.label[0])); + for (i = 1; i < (int)d->bs.nbuttons; i++) + d->bs.sizebutton = MAX(d->bs.sizebutton, strcols(d->bs.label[i])); + d->bs.sizebutton += 2; } -/* Text */ -static bool is_ncurses_attr(char *text) +bool shortcut_buttons(wint_t key, struct buttons *bs) { + bool match; + unsigned int i; + + match = false; + for (i = 0; i < bs->nbuttons; i++) { + if (towlower(key) == towlower(bs->first[i])) { + bs->curr = i; + match = true; + break; + } + } - if (strnlen(text, 3) < 3) - return false; + return (match); +} - if (text[0] != '\\' || text[1] != 'Z') - return false; +/* + * -4- (Auto) Sizing and (Auto) Position + */ +static int widget_max_height(struct bsddialog_conf *conf) +{ + int maxheight; + + maxheight = conf->shadow ? SCREENLINES - (int)t.shadow.y : SCREENLINES; + if (maxheight <= 0) + RETURN_ERROR("Terminal too small, screen lines - shadow <= 0"); + + if (conf->y != BSDDIALOG_CENTER && conf->auto_topmargin > 0) + RETURN_ERROR("conf.y > 0 and conf->auto_topmargin > 0"); + else if (conf->y == BSDDIALOG_CENTER) { + maxheight -= conf->auto_topmargin; + if (maxheight <= 0) + RETURN_ERROR("Terminal too small, screen lines - top " + "margins <= 0"); + } else if (conf->y > 0) { + maxheight -= conf->y; + if (maxheight <= 0) + RETURN_ERROR("Terminal too small, screen lines - " + "shadow - y <= 0"); + } - return (strchr("nbBrRuU01234567", text[2]) == NULL ? false : true); + maxheight -= conf->auto_downmargin; + if (maxheight <= 0) + RETURN_ERROR("Terminal too small, screen lines - Down margins " + "<= 0"); + + return (maxheight); } -static bool check_set_ncurses_attr(WINDOW *win, char *text) +static int widget_max_width(struct bsddialog_conf *conf) { - - if (is_ncurses_attr(text) == false) - return false; + int maxwidth; - if ((text[2] - '0') >= 0 && (text[2] - '0') < 8) { - wattron(win, bsddialog_color( text[2] - '0', COLOR_WHITE, 0)); - return true; - } + maxwidth = conf->shadow ? SCREENCOLS - (int)t.shadow.x : SCREENCOLS; + if (maxwidth <= 0) + RETURN_ERROR("Terminal too small, screen cols - shadow <= 0"); - switch (text[2]) { - case 'n': - wattrset(win, A_NORMAL); - break; - case 'b': - wattron(win, A_BOLD); - break; - case 'B': - wattroff(win, A_BOLD); - break; - case 'r': - wattron(win, A_REVERSE); - break; - case 'R': - wattroff(win, A_REVERSE); - break; - case 'u': - wattron(win, A_UNDERLINE); - break; - case 'U': - wattroff(win, A_UNDERLINE); - break; + if (conf->x > 0) { + maxwidth -= conf->x; + if (maxwidth <= 0) + RETURN_ERROR("Terminal too small, screen cols - shadow " + "- x <= 0"); } - return true; + return (maxwidth); } -static void -print_str(WINDOW *win, int *rows, int *y, int *x, int cols, char *str, bool color) +static bool is_wtext_attr(const wchar_t *wtext) { - int i, j, len, reallen; + bool att; - if(strlen(str) == 0) - return; + if (wcsnlen(wtext, 3) < 3) + return (false); + if (wtext[0] != L'\\' || wtext[1] != L'Z') + return (false); - len = reallen = strlen(str); - if (color) { - i=0; - while (i < len) { - if (is_ncurses_attr(str+i)) - reallen -= 3; - i++; - } - } + att = wcschr(L"nbBdDkKrRsSuU01234567", wtext[2]) == NULL ? false : true; - i = 0; - while (i < len) { - if (*x + reallen > cols) { - *y = (*x != 0 ? *y+1 : *y); - if (*y >= *rows) { - *rows = *y + 1; - wresize(win, *rows, cols); - } - *x = 0; - } - j = *x; - while (j < cols && i < len) { - if (color && check_set_ncurses_attr(win, str+i)) { - i += 3; - } else { - mvwaddch(win, *y, j, str[i]); - i++; - reallen--; - j++; - *x = j; - } - } - } + return (att); } -int -get_text_properties(struct bsddialog_conf *conf, char *text, int *maxword, - int *maxline, int *nlines) +#define NL -1 +#define WS -2 +#define TB -3 + +struct textproperties { + int nword; + int *words; + uint8_t *wletters; + int maxwordcols; + int maxline; + bool hasnewline; +}; + +static int +text_properties(struct bsddialog_conf *conf, const char *text, + struct textproperties *tp) { - int i, buflen, wordlen, linelen; + int i, l, currlinecols, maxwords, wtextlen, tablen, wordcols; + wchar_t *wtext; + + tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen; + + maxwords = 1024; + if ((tp->words = calloc(maxwords, sizeof(int))) == NULL) + RETURN_ERROR("Cannot alloc memory for text autosize"); + + if ((wtext = alloc_mbstows(text)) == NULL) + RETURN_ERROR("Cannot allocate/autosize text in wchar_t*"); + wtextlen = wcslen(wtext); + if ((tp->wletters = calloc(wtextlen, sizeof(uint8_t))) == NULL) + RETURN_ERROR("Cannot allocate wletters for text autosizing"); + + tp->nword = 0; + tp->maxline = 0; + tp->maxwordcols = 0; + tp->hasnewline = false; + currlinecols = 0; + wordcols = 0; + l = 0; + for (i = 0; i < wtextlen; i++) { + if (conf->text.escape && is_wtext_attr(wtext + i)) { + i += 2; /* +1 for update statement */ + continue; + } + + if (tp->nword + 1 >= maxwords) { + maxwords += 1024; + tp->words = realloc(tp->words, maxwords * sizeof(int)); + if (tp->words == NULL) + RETURN_ERROR("Cannot realloc memory for text " + "autosize"); + } + if (wcschr(L"\t\n ", wtext[i]) != NULL) { + tp->maxwordcols = MAX(wordcols, tp->maxwordcols); - buflen = strlen(text) + 1; - *maxword = 0; - wordlen = 0; - for (i=0; i < buflen; i++) { - if (text[i] == '\t' || text[i] == '\n' || text[i] == ' ' || text[i] == '\0') - if (wordlen != 0) { - *maxword = MAX(*maxword, wordlen); - wordlen = 0; - continue; + if (wordcols != 0) { + /* line */ + currlinecols += wordcols; + /* word */ + tp->words[tp->nword] = wordcols; + tp->nword += 1; + wordcols = 0; } - if (conf->text.colors && is_ncurses_attr(text + i)) - i += 3; - else - wordlen++; - } - - *maxline = linelen = 0; - *nlines = 1; - for (i=0; i < buflen; i++) { - switch (text[i]) { - case '\n': - *nlines = *nlines + 1; - case '\0': - *maxline = MAX(*maxline, linelen); - linelen = 0; - break; - default: - if (conf->text.colors && is_ncurses_attr(text + i)) - i += 3; - else - linelen++; + + switch (wtext[i]) { + case L'\t': + /* line */ + currlinecols += tablen; + /* word */ + tp->words[tp->nword] = TB; + break; + case L'\n': + /* line */ + tp->hasnewline = true; + tp->maxline = MAX(tp->maxline, currlinecols); + currlinecols = 0; + /* word */ + tp->words[tp->nword] = NL; + break; + case L' ': + /* line */ + currlinecols += 1; + /* word */ + tp->words[tp->nword] = WS; + break; + } + tp->nword += 1; + } else { + tp->wletters[l] = wcwidth(wtext[i]); + wordcols += tp->wletters[l]; + l++; } } - if (*nlines == 1 && *maxline == 0) - *nlines = 0; + /* word */ + if (wordcols != 0) { + tp->words[tp->nword] = wordcols; + tp->nword += 1; + tp->maxwordcols = MAX(wordcols, tp->maxwordcols); + } + /* line */ + tp->maxline = MAX(tp->maxline, currlinecols); - //free(buf); + free(wtext); - return 0; + return (0); } -int -print_textpad(struct bsddialog_conf *conf, WINDOW *pad, int *rows, int cols, - char *text) +static int +text_autosize(struct bsddialog_conf *conf, struct textproperties *tp, + int maxrows, int mincols, bool increasecols, int *h, int *w) { - char *string; - int i, j, x, y; - bool loop; + int i, j, x, y, z, l, line, maxwidth, tablen; - if ((string = malloc(strlen(text) + 1)) == NULL) - RETURN_ERROR("Cannot build (analyze) text"); + maxwidth = widget_max_width(conf) - BORDERS - TEXTHMARGINS; + tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen; - i = j = x = y = 0; - loop = true; - while (loop) { - string[j] = text[i]; - - if (string[j] == '\0' || string[j] == '\n' || - string[j] == '\t' || string[j] == ' ') { - if (j != 0) { - string[j] = '\0'; - print_str(pad, rows, &y, &x, cols, string, - conf->text.colors); - } - } + if (increasecols) { + mincols = MAX(mincols, tp->maxwordcols); + mincols = MAX(mincols, + (int)conf->auto_minwidth - BORDERS - TEXTHMARGINS); + mincols = MIN(mincols, maxwidth); + } - switch (text[i]) { - case '\0': - loop = false; - break; - case '\n': - j = -1; - x = 0; - y++; - break; - case '\t': - for (j=0; j<4 /*tablen*/; j++) { + while (true) { + x = 0; + y = 1; + line=0; + l = 0; + for (i = 0; i < tp->nword; i++) { + switch (tp->words[i]) { + case TB: + for (j = 0; j < tablen; j++) { + if (x >= mincols) { + x = 0; + y++; + } x++; - if (x >= cols) { + } + break; + case NL: + y++; + x = 0; + break; + case WS: + x++; + if (x >= mincols) { x = 0; y++; } + break; + default: + if (tp->words[i] + x <= mincols) { + x += tp->words[i]; + for (z = 0 ; z != tp->words[i]; l++ ) + z += tp->wletters[l]; + } else if (tp->words[i] <= mincols) { + y++; + x = tp->words[i]; + for (z = 0 ; z != tp->words[i]; l++ ) + z += tp->wletters[l]; + } else { + for (j = tp->words[i]; j > 0; ) { + y = (x == 0) ? y : y + 1; + z = 0; + while (z != j && z < mincols) { + z += tp->wletters[l]; + l++; + } + x = z; + line = MAX(line, x); + j -= z; + } + } } - j = -1; - break; - case ' ': - x++; - if (x >= cols) { - x = 0; - y++; - } - j = -1; + line = MAX(line, x); } - if (y >= *rows) { /* check for whitespaces */ - *rows = y + 1; - wresize(pad, *rows, cols); - } - - j++; - i++; + if (increasecols == false) + break; + if (mincols >= maxwidth) + break; + if (line >= y * (int)conf->text.cols_per_row && y <= maxrows) + break; + mincols++; } - free(string); + *h = (tp->nword == 0) ? 0 : y; + *w = MIN(mincols, line); /* wtext can be less than mincols */ - return 0; + return (0); } -/* autosize */ +static int +text_size(struct bsddialog_conf *conf, int rows, int cols, const char *text, + struct buttons *bs, int rowsnotext, int startwtext, int *htext, int *wtext) +{ + bool changewtext; + int wbuttons, maxhtext; + struct textproperties tp; + + wbuttons = 0; + if (bs->nbuttons > 0) + wbuttons = buttons_min_width(bs); + + /* Rows */ + if (rows == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_FULLSCREEN) { + maxhtext = widget_max_height(conf) - BORDERS - rowsnotext; + } else { /* fixed */ + maxhtext = rows - BORDERS - rowsnotext; + } + if (bs->nbuttons > 0) + maxhtext -= 2; + if (maxhtext <= 0) + maxhtext = 1; /* text_autosize() computes always htext */ + + /* Cols */ + if (cols == BSDDIALOG_AUTOSIZE) { + startwtext = MAX(startwtext, wbuttons - TEXTHMARGINS); + changewtext = true; + } else if (cols == BSDDIALOG_FULLSCREEN) { + startwtext = widget_max_width(conf) - BORDERS - TEXTHMARGINS; + changewtext = false; + } else { /* fixed */ + startwtext = cols - BORDERS - TEXTHMARGINS; + changewtext = false; + } + + if (startwtext <= 0 && changewtext) + startwtext = 1; -/* - * max y, that is from 0 to LINES - 1 - t.shadowrows, - * could not be max height but avoids problems with checksize - */ -int widget_max_height(struct bsddialog_conf *conf) + /* Sizing calculation */ + if (text_properties(conf, text, &tp) != 0) + return (BSDDIALOG_ERROR); + if (tp.nword > 0 && startwtext <= 0) + RETURN_FMTERROR("(fixed cols or fullscreen) " + "needed at least %d cols to draw text", + BORDERS + TEXTHMARGINS + 1); + if (text_autosize(conf, &tp, maxhtext, startwtext, changewtext, htext, + wtext) != 0) + return (BSDDIALOG_ERROR); + + free(tp.words); + free(tp.wletters); + + return (0); +} + +static int +widget_min_height(struct bsddialog_conf *conf, int htext, int hnotext, + bool withbuttons) { - int maxheight; + int min; + + /* dialog borders */ + min = BORDERS; + + /* text */ + min += htext; + + /* specific widget lines without text */ + min += hnotext; - if ((maxheight = conf->shadow ? LINES - 1 - t.shadow.h : LINES - 1) <= 0) - RETURN_ERROR("Terminal too small, LINES - shadow <= 0"); + /* buttons */ + if (withbuttons) + min += HBUTTONS; /* buttons and their up-border */ - if (conf->y > 0) - if ((maxheight -= conf->y) <=0) - RETURN_ERROR("Terminal too small, LINES - shadow - y <= 0"); + /* conf.auto_minheight */ + min = MAX(min, (int)conf->auto_minheight); - return maxheight; + return (min); } -/* - * max x, that is from 0 to COLS - 1 - t.shadowcols, - * * could not be max height but avoids problems with checksize - */ -int widget_max_width(struct bsddialog_conf *conf) +static int +widget_min_width(struct bsddialog_conf *conf, int wtext, int minwidget, + struct buttons *bs) + { - int maxwidth; + int min, delimtitle, wbottomtitle, wtitle; - if ((maxwidth = conf->shadow ? COLS - 1 - t.shadow.w : COLS - 1) <= 0) - RETURN_ERROR("Terminal too small, COLS - shadow <= 0"); - if (conf->x > 0) - if ((maxwidth -= conf->x) <=0) - RETURN_ERROR("Terminal too small, COLS - shadow - x <= 0"); + min = 0; - return maxwidth; + /* buttons */ + if (bs->nbuttons > 0) + min += buttons_min_width(bs); + + /* text */ + if (wtext > 0) + min = MAX(min, wtext + TEXTHMARGINS); + + /* specific widget min width */ + min = MAX(min, minwidget); + + /* title */ + if (conf->title != NULL) { + delimtitle = t.dialog.delimtitle ? 2 : 0; + wtitle = strcols(conf->title); + min = MAX(min, wtitle + 2 + delimtitle); + } + + /* bottom title */ + if (conf->bottomtitle != NULL) { + wbottomtitle = strcols(conf->bottomtitle); + min = MAX(min, wbottomtitle + 4); + } + + /* dialog borders */ + min += BORDERS; + /* conf.auto_minwidth */ + min = MAX(min, (int)conf->auto_minwidth); + + return (min); } int @@ -484,295 +801,555 @@ set_widget_size(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w) int maxheight, maxwidth; if ((maxheight = widget_max_height(conf)) == BSDDIALOG_ERROR) - return BSDDIALOG_ERROR; + return (BSDDIALOG_ERROR); if (rows == BSDDIALOG_FULLSCREEN) *h = maxheight; else if (rows < BSDDIALOG_FULLSCREEN) RETURN_ERROR("Negative (less than -1) height"); - else if (rows > BSDDIALOG_AUTOSIZE) { - if ((*h = rows) > maxheight) - RETURN_ERROR("Height too big (> terminal height - "\ - "shadow"); - } + else if (rows > BSDDIALOG_AUTOSIZE) /* fixed rows */ + *h = MIN(rows, maxheight); /* rows is at most maxheight */ /* rows == AUTOSIZE: each widget has to set its size */ if ((maxwidth = widget_max_width(conf)) == BSDDIALOG_ERROR) - return BSDDIALOG_ERROR; + return (BSDDIALOG_ERROR); if (cols == BSDDIALOG_FULLSCREEN) *w = maxwidth; else if (cols < BSDDIALOG_FULLSCREEN) RETURN_ERROR("Negative (less than -1) width"); - else if (cols > BSDDIALOG_AUTOSIZE) { - if ((*w = cols) > maxwidth) - RETURN_ERROR("Width too big (> terminal width - shadow)"); - } + else if (cols > BSDDIALOG_AUTOSIZE) /* fixed cols */ + *w = MIN(cols, maxwidth); /* cols is at most maxwidth */ /* cols == AUTOSIZE: each widget has to set its size */ - return 0; + return (0); } int -set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w) +set_widget_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, + int *w, const char *text, int *rowstext, struct buttons *bs, int hnotext, + int minw) +{ + int htext, wtext; + + if (rows == BSDDIALOG_AUTOSIZE || cols == BSDDIALOG_AUTOSIZE || + rowstext != NULL) { + if (text_size(conf, rows, cols, text, bs, hnotext, minw, + &htext, &wtext) != 0) + return (BSDDIALOG_ERROR); + if (rowstext != NULL) + *rowstext = htext; + } + + if (rows == BSDDIALOG_AUTOSIZE) { + *h = widget_min_height(conf, htext, hnotext, bs->nbuttons > 0); + *h = MIN(*h, widget_max_height(conf)); + } + + if (cols == BSDDIALOG_AUTOSIZE) { + *w = widget_min_width(conf, wtext, minw, bs); + *w = MIN(*w, widget_max_width(conf)); + } + + return (0); +} + +int widget_checksize(int h, int w, struct buttons *bs, int hnotext, int minw) { + int minheight, minwidth; + + minheight = BORDERS + hnotext; + if (bs->nbuttons > 0) + minheight += HBUTTONS; + if (h < minheight) + RETURN_FMTERROR("Current rows: %d, needed at least: %d", + h, minheight); + + minwidth = 0; + if (bs->nbuttons > 0) + minwidth = buttons_min_width(bs); + minwidth = MAX(minwidth, minw); + minwidth += BORDERS; + if (w < minwidth) + RETURN_FMTERROR("Current cols: %d, nedeed at least %d", + w, minwidth); + + return (0); +} - if (conf->y == BSDDIALOG_CENTER) - *y = LINES/2 - h/2; +int +set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w) +{ + int hshadow = conf->shadow ? (int)t.shadow.y : 0; + int wshadow = conf->shadow ? (int)t.shadow.x : 0; + + if (conf->y == BSDDIALOG_CENTER) { + *y = SCREENLINES/2 - (h + hshadow)/2; + if (*y < (int)conf->auto_topmargin) + *y = conf->auto_topmargin; + if (*y + h + hshadow > SCREENLINES - (int)conf->auto_downmargin) + *y = SCREENLINES - h - hshadow - conf->auto_downmargin; + } else if (conf->y < BSDDIALOG_CENTER) RETURN_ERROR("Negative begin y (less than -1)"); - else if (conf->y >= LINES) + else if (conf->y >= SCREENLINES) RETURN_ERROR("Begin Y under the terminal"); else *y = conf->y; - if ((*y + h + (conf->shadow ? (int) t.shadow.h : 0)) > LINES) - RETURN_ERROR("The lower of the box under the terminal "\ + if (*y + h + hshadow > SCREENLINES) + RETURN_ERROR("The lower of the box under the terminal " "(begin Y + height (+ shadow) > terminal lines)"); if (conf->x == BSDDIALOG_CENTER) - *x = COLS/2 - w/2; + *x = SCREENCOLS/2 - (w + wshadow)/2; else if (conf->x < BSDDIALOG_CENTER) RETURN_ERROR("Negative begin x (less than -1)"); - else if (conf->x >= COLS) + else if (conf->x >= SCREENCOLS) RETURN_ERROR("Begin X over the right of the terminal"); else *x = conf->x; - if ((*x + w + (conf->shadow ? (int) t.shadow.w : 0)) > COLS) - RETURN_ERROR("The right of the box over the terminal "\ + if ((*x + w + wshadow) > SCREENCOLS) + RETURN_ERROR("The right of the box over the terminal " "(begin X + width (+ shadow) > terminal cols)"); - return 0; + return (0); } -/* Widgets builders */ -void -draw_borders(struct bsddialog_conf *conf, WINDOW *win, int rows, int cols, - enum elevation elev) +int dialog_size_position(struct dialog *d, int hnotext, int minw, int *htext) { - int leftcolor, rightcolor; - int ls, rs, ts, bs, tl, tr, bl, br; - int ltee, rtee; - - ls = rs = ACS_VLINE; - ts = bs = ACS_HLINE; - tl = ACS_ULCORNER; - tr = ACS_URCORNER; - bl = ACS_LLCORNER; - br = ACS_LRCORNER; - ltee = ACS_LTEE; - rtee = ACS_RTEE; - - if (conf->no_lines == false) { - if (conf->ascii_lines) { - ls = rs = '|'; - ts = bs = '-'; - tl = tr = bl = br = ltee = rtee = '+'; - } - leftcolor = elev == RAISED ? - t.dialog.lineraisecolor : t.dialog.linelowercolor; - rightcolor = elev == RAISED ? - t.dialog.linelowercolor : t.dialog.lineraisecolor; - wattron(win, leftcolor); - wborder(win, ls, rs, ts, bs, tl, tr, bl, br); - wattroff(win, leftcolor); + if (set_widget_size(d->conf, d->rows, d->cols, &d->h, &d->w) != 0) + return (BSDDIALOG_ERROR); + if (set_widget_autosize(d->conf, d->rows, d->cols, &d->h, &d->w, + d->text, htext, &d->bs, hnotext, minw) != 0) + return (BSDDIALOG_ERROR); + if (widget_checksize(d->h, d->w, &d->bs, hnotext, minw) != 0) + return (BSDDIALOG_ERROR); + if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0) + return (BSDDIALOG_ERROR); + + return (0); +} + +/* + * -5- Widget components and utilities + */ +int hide_dialog(struct dialog *d) +{ + WINDOW *clear; + + if ((clear = newwin(d->h, d->w, d->y, d->x)) == NULL) + RETURN_ERROR("Cannot hide the widget"); + wbkgd(clear, t.screen.color); + wrefresh(clear); - wattron(win, rightcolor); - mvwaddch(win, 0, cols-1, tr); - mvwvline(win, 1, cols-1, rs, rows-2); - mvwaddch(win, rows-1, cols-1, br); - mvwhline(win, rows-1, 1, bs, cols-2); - wattroff(win, rightcolor); + if (d->conf->shadow) { + mvwin(clear, d->y + t.shadow.y, d->x + t.shadow.x); + wrefresh(clear); } + + delwin(clear); + + return (0); } -WINDOW * -new_boxed_window(struct bsddialog_conf *conf, int y, int x, int rows, int cols, - enum elevation elev) +int f1help_dialog(struct bsddialog_conf *conf) { - WINDOW *win; + int output; + struct bsddialog_conf hconf; - if ((win = newwin(rows, cols, y, x)) == NULL) { - set_error_string("Cannot build boxed window"); - return NULL; + bsddialog_initconf(&hconf); + hconf.title = "HELP"; + hconf.button.ok_label = "EXIT"; + hconf.clear = true; + hconf.ascii_lines = conf->ascii_lines; + hconf.no_lines = conf->no_lines; + hconf.shadow = conf->shadow; + hconf.text.escape = conf->text.escape; + + output = BSDDIALOG_OK; + if (conf->key.f1_message != NULL) + output = bsddialog_msgbox(&hconf, conf->key.f1_message, 0, 0); + + if (output != BSDDIALOG_ERROR && conf->key.f1_file != NULL) + output = bsddialog_textbox(&hconf, conf->key.f1_file, 0, 0); + + return (output == BSDDIALOG_ERROR ? BSDDIALOG_ERROR : 0); +} + +void draw_borders(struct bsddialog_conf *conf, WINDOW *win, enum elevation elev) +{ + int h, w; + int leftcolor, rightcolor; + int ls, rs, ts, bs, tl, tr, bl, br, ltee, rtee; + + if (conf->no_lines) + return; + + if (conf->ascii_lines) { + ls = rs = '|'; + ts = bs = '-'; + tl = tr = bl = br = ltee = rtee = '+'; + } else { + ls = rs = ACS_VLINE; + ts = bs = ACS_HLINE; + tl = ACS_ULCORNER; + tr = ACS_URCORNER; + bl = ACS_LLCORNER; + br = ACS_LRCORNER; + ltee = ACS_LTEE; + rtee = ACS_RTEE; } - wbkgd(win, t.dialog.color); + getmaxyx(win, h, w); + leftcolor = elev == RAISED ? + t.dialog.lineraisecolor : t.dialog.linelowercolor; + rightcolor = elev == RAISED ? + t.dialog.linelowercolor : t.dialog.lineraisecolor; + + wattron(win, leftcolor); + wborder(win, ls, rs, ts, bs, tl, tr, bl, br); + wattroff(win, leftcolor); + + wattron(win, rightcolor); + mvwaddch(win, 0, w-1, tr); + mvwvline(win, 1, w-1, rs, h-2); + mvwaddch(win, h-1, w-1, br); + mvwhline(win, h-1, 1, bs, w-2); + wattroff(win, rightcolor); +} - draw_borders(conf, win, rows, cols, elev); +void +update_box(struct bsddialog_conf *conf, WINDOW *win, int y, int x, int h, int w, + enum elevation elev) +{ + wclear(win); + wresize(win, h, w); + mvwin(win, y, x); + draw_borders(conf, win, elev); +} - return win; +void +rtextpad(struct dialog *d, int ytext, int xtext, int upnotext, int downnotext) +{ + pnoutrefresh(d->textpad, ytext, xtext, + d->y + BORDER + upnotext, + d->x + BORDER + TEXTHMARGIN, + d->y + d->h - 1 - downnotext - BORDER, + d->x + d->w - TEXTHMARGIN - BORDER); } /* - * `enum elevation elev` could be useless because it should be always RAISED, - * to check at the end. + * -6- Dialog init/build, update/draw, destroy */ -static int -draw_widget_withtextpad(struct bsddialog_conf *conf, WINDOW *shadow, - WINDOW *widget, int h, int w, enum elevation elev, - WINDOW *textpad, int *htextpad, char *text, bool buttons) +void end_dialog(struct dialog *d) { - int ts, ltee, rtee; - int colordelimtitle; + if (d->conf->sleep > 0) + sleep(d->conf->sleep); - ts = conf->ascii_lines ? '-' : ACS_HLINE; - ltee = conf->ascii_lines ? '+' : ACS_LTEE; - rtee = conf->ascii_lines ? '+' : ACS_RTEE; - colordelimtitle = elev == RAISED ? - t.dialog.lineraisecolor : t.dialog.linelowercolor; + delwin(d->textpad); + delwin(d->widget); + if (d->conf->shadow) + delwin(d->shadow); - if (shadow != NULL) - wnoutrefresh(shadow); + if (d->conf->clear) + hide_dialog(d); - // move / resize now or the caller? - draw_borders(conf, widget, h, w, elev); + if (d->conf->get_height != NULL) + *d->conf->get_height = d->h; + if (d->conf->get_width != NULL) + *d->conf->get_width = d->w; +} - if (conf->title != NULL) { - if (t.dialog.delimtitle && conf->no_lines == false) { - wattron(widget, colordelimtitle); - mvwaddch(widget, 0, w/2 - strlen(conf->title)/2 - 1, rtee); - wattroff(widget, colordelimtitle); - } - wattron(widget, t.dialog.titlecolor); - mvwaddstr(widget, 0, w/2 - strlen(conf->title)/2, conf->title); - wattroff(widget, t.dialog.titlecolor); - if (t.dialog.delimtitle && conf->no_lines == false) { - wattron(widget, colordelimtitle); - waddch(widget, ltee); - wattroff(widget, colordelimtitle); - } +static bool check_set_wtext_attr(WINDOW *win, wchar_t *wtext) +{ + enum bsddialog_color bg; + + if (is_wtext_attr(wtext) == false) + return (false); + + if ((wtext[2] >= L'0') && (wtext[2] <= L'7')) { + bsddialog_color_attrs(t.dialog.color, NULL, &bg, NULL); + wattron(win, bsddialog_color(wtext[2] - L'0', bg, 0)); + return (true); } - if (conf->bottomtitle != NULL) { - wattron(widget, t.dialog.bottomtitlecolor); - wmove(widget, h - 1, w/2 - strlen(conf->bottomtitle)/2 - 1); - waddch(widget, '['); - waddstr(widget, conf->bottomtitle); - waddch(widget, ']'); - wattroff(widget, t.dialog.bottomtitlecolor); + switch (wtext[2]) { + case L'n': + wattron(win, t.dialog.color); + wattrset(win, A_NORMAL); + break; + case L'b': + wattron(win, A_BOLD); + break; + case L'B': + wattroff(win, A_BOLD); + break; + case L'd': + wattron(win, A_DIM); + break; + case L'D': + wattroff(win, A_DIM); + break; + case L'k': + wattron(win, A_BLINK); + break; + case L'K': + wattroff(win, A_BLINK); + break; + case L'r': + wattron(win, A_REVERSE); + break; + case L'R': + wattroff(win, A_REVERSE); + break; + case L's': + wattron(win, A_STANDOUT); + break; + case L'S': + wattroff(win, A_STANDOUT); + break; + case L'u': + wattron(win, A_UNDERLINE); + break; + case L'U': + wattroff(win, A_UNDERLINE); + break; } - //if (textpad == NULL && text != NULL) /* no pad, text null for textbox */ - // print_text(conf, widget, 1, 2, w-3, text); + return (true); +} + +static void +print_string(WINDOW *win, int *rows, int cols, int *y, int *x, wchar_t *str, + bool color) +{ + int i, j, len, reallen, wc; + wchar_t ws[2]; + + ws[1] = L'\0'; - if (buttons && conf->no_lines == false) { - wattron(widget, t.dialog.lineraisecolor); - mvwaddch(widget, h-3, 0, ltee); - mvwhline(widget, h-3, 1, ts, w-2); - wattroff(widget, t.dialog.lineraisecolor); + len = wcslen(str); + if (color) { + reallen = 0; + i=0; + while (i < len) { + if (is_wtext_attr(str+i) == false) { + reallen += wcwidth(str[i]); + i++; + } else { + i +=3 ; + } + } + } else + reallen = wcswidth(str, len); - wattron(widget, t.dialog.linelowercolor); - mvwaddch(widget, h-3, w-1, rtee); - wattroff(widget, t.dialog.linelowercolor); + i = 0; + while (i < len) { + if (*x + reallen > cols) { + *y = (*x != 0 ? *y+1 : *y); + if (*y >= *rows) { + *rows = *y + 1; + wresize(win, *rows, cols); + } + *x = 0; + } + j = *x; + while (j < cols && i < len) { + if (color && check_set_wtext_attr(win, str+i)) { + i += 3; + } else if (j + wcwidth(str[i]) > cols) { + break; + } else { + /* inline mvwaddwch() for efficiency */ + ws[0] = str[i]; + mvwaddwstr(win, *y, j, ws); + wc = wcwidth(str[i]);; + reallen -= wc; + j += wc; + i++; + *x = j; + } + } } +} - wnoutrefresh(widget); +static int +print_textpad(struct bsddialog_conf *conf, WINDOW *pad, const char *text) +{ + bool loop; + int i, j, z, rows, cols, x, y, tablen; + wchar_t *wtext, *string; - if (textpad == NULL) - return 0; /* widget_init() ends */ + if ((wtext = alloc_mbstows(text)) == NULL) + RETURN_ERROR("Cannot allocate/print text in wchar_t*"); - if (text != NULL) /* programbox etc */ - if (print_textpad(conf, textpad, htextpad, - w - HBORDERS - t.text.hmargin * 2, text) !=0) - return BSDDIALOG_ERROR; + if ((string = calloc(wcslen(wtext) + 1, sizeof(wchar_t))) == NULL) + RETURN_ERROR("Cannot build (analyze) text"); - return 0; -} + getmaxyx(pad, rows, cols); + tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen; -/* - * `enum elevation elev` could be useless because it should be always RAISED, - * to check at the end. - */ -int -update_widget_withtextpad(struct bsddialog_conf *conf, WINDOW *shadow, - WINDOW *widget, int h, int w, enum elevation elev, - WINDOW *textpad, int *htextpad, char *text, bool buttons) -{ - int error; + i = j = x = y = 0; + loop = true; + while (loop) { + string[j] = wtext[i]; - /* nothing for now */ + if (wcschr(L"\n\t ", string[j]) != NULL || string[j] == L'\0') { + string[j] = L'\0'; + print_string(pad, &rows, cols, &y, &x, string, + conf->text.escape); + } - error = draw_widget_withtextpad(conf, shadow, widget, h, w, - elev, textpad, htextpad, text, buttons); + switch (wtext[i]) { + case L'\0': + loop = false; + break; + case L'\n': + x = 0; + y++; + j = -1; + break; + case L'\t': + for (z = 0; z < tablen; z++) { + if (x >= cols) { + x = 0; + y++; + } + x++; + } + j = -1; + break; + case L' ': + x++; + if (x >= cols) { + x = 0; + y++; + } + j = -1; + } - return error; + if (y >= rows) { + rows = y + 1; + wresize(pad, rows, cols); + } + + j++; + i++; + } + + free(wtext); + free(string); + + return (0); } -/* - * `enum elevation elev` could be useless because it should be always RAISED, - * to check at the end. - */ -int -new_widget_withtextpad(struct bsddialog_conf *conf, WINDOW **shadow, - WINDOW **widget, int y, int x, int h, int w, enum elevation elev, - WINDOW **textpad, int *htextpad, char *text, bool buttons) +int draw_dialog(struct dialog *d) { - int error; + int wtitle, wbottomtitle, ts, ltee, rtee; - if (conf->shadow) { - *shadow = newwin(h, w, y + t.shadow.h, x + t.shadow.w); - if (*shadow == NULL) - RETURN_ERROR("Cannot build shadow"); - wbkgd(*shadow, t.shadow.color); - } + ts = d->conf->ascii_lines ? '-' : ACS_HLINE; + ltee = d->conf->ascii_lines ? '+' : ACS_LTEE; + rtee = d->conf->ascii_lines ? '+' : ACS_RTEE; - if ((*widget = new_boxed_window(conf, y, x, h, w, elev)) == NULL) { - if (conf->shadow) - delwin(*shadow); - return BSDDIALOG_ERROR; + if (d->conf->shadow) { + wclear(d->shadow); + wresize(d->shadow, d->h, d->w); + mvwin(d->shadow, d->y + t.shadow.y, d->x + t.shadow.x); + wnoutrefresh(d->shadow); } - if (textpad == NULL) { /* widget_init() */ - error = draw_widget_withtextpad(conf, *shadow, *widget, h, w, - elev, NULL, NULL, text, buttons); - return error; + wclear(d->widget); + wresize(d->widget, d->h, d->w); + mvwin(d->widget, d->y, d->x); + draw_borders(d->conf, d->widget, RAISED); + + if (d->conf->title != NULL) { + if ((wtitle = strcols(d->conf->title)) < 0) + return (BSDDIALOG_ERROR); + if (t.dialog.delimtitle && d->conf->no_lines == false) { + wattron(d->widget, t.dialog.lineraisecolor); + mvwaddch(d->widget, 0, d->w/2 - wtitle/2 -1, rtee); + wattroff(d->widget, t.dialog.lineraisecolor); + } + wattron(d->widget, t.dialog.titlecolor); + mvwaddstr(d->widget, 0, d->w/2 - wtitle/2, d->conf->title); + wattroff(d->widget, t.dialog.titlecolor); + if (t.dialog.delimtitle && d->conf->no_lines == false) { + wattron(d->widget, t.dialog.lineraisecolor); + waddch(d->widget, ltee); + wattroff(d->widget, t.dialog.lineraisecolor); + } } - if (text != NULL) { /* programbox etc */ - *htextpad = 1; - *textpad = newpad(*htextpad, w - HBORDERS - t.text.hmargin * 2); - if (*textpad == NULL) { - delwin(*textpad); - if (conf->shadow) - delwin(*shadow); - RETURN_ERROR("Cannot build the pad window for text"); + if (d->bs.nbuttons > 0) { + if (d->conf->no_lines == false) { + wattron(d->widget, t.dialog.lineraisecolor); + mvwaddch(d->widget, d->h-3, 0, ltee); + mvwhline(d->widget, d->h-3, 1, ts, d->w-2); + wattroff(d->widget, t.dialog.lineraisecolor); + + wattron(d->widget, t.dialog.linelowercolor); + mvwaddch(d->widget, d->h-3, d->w-1, rtee); + wattroff(d->widget, t.dialog.linelowercolor); } - wbkgd(*textpad, t.dialog.color); + draw_buttons(d); } - error = draw_widget_withtextpad(conf, *shadow, *widget, h, w, elev, - *textpad, htextpad, text, buttons); + if (d->conf->bottomtitle != NULL) { + if ((wbottomtitle = strcols(d->conf->bottomtitle)) < 0) + return (BSDDIALOG_ERROR); + wattron(d->widget, t.dialog.bottomtitlecolor); + wmove(d->widget, d->h - 1, d->w/2 - wbottomtitle/2 - 1); + waddch(d->widget, ' '); + waddstr(d->widget, d->conf->bottomtitle); + waddch(d->widget, ' '); + wattroff(d->widget, t.dialog.bottomtitlecolor); + } - return error; -} + wnoutrefresh(d->widget); -void -end_widget_withtextpad(struct bsddialog_conf *conf, WINDOW *window, int h, int w, - WINDOW *textpad, WINDOW *shadow) -{ - int y, x; + wclear(d->textpad); + /* `infobox "" 0 2` fails but text is empty and textpad remains 1 1 */ + wresize(d->textpad, 1, d->w - BORDERS - TEXTHMARGINS); - getbegyx(window, y, x); /* for clear, add y & x to args? */ + if (print_textpad(d->conf, d->textpad, d->text) != 0) + return (BSDDIALOG_ERROR); - if (conf->sleep > 0) - sleep(conf->sleep); + d->built = true; - if (textpad != NULL) - delwin(textpad); + return (0); +} - delwin(window); +int +prepare_dialog(struct bsddialog_conf *conf, const char *text, int rows, + int cols, struct dialog *d) +{ + CHECK_PTR(conf); + + d->built = false; + d->conf = conf; + d->rows = rows; + d->cols = cols; + d->text = CHECK_STR(text); + d->bs.nbuttons = 0; + + if (d->conf->shadow) { + if ((d->shadow = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW shadow"); + wbkgd(d->shadow, t.shadow.color); + } - if (conf->shadow) - delwin(shadow); + if ((d->widget = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW widget"); + wbkgd(d->widget, t.dialog.color); - if (conf->clear) - hide_widget(y, x, h, w, shadow != NULL); + /* fake for textpad */ + if ((d->textpad = newpad(1, 1)) == NULL) + RETURN_ERROR("Cannot build the pad WINDOW for text"); + wbkgd(d->textpad, t.dialog.color); - if (conf->get_height != NULL) - *conf->get_height = h; - if (conf->get_width != NULL) - *conf->get_width = w; -} + return (0); +}
\ No newline at end of file diff --git a/lib/lib_util.h b/lib/lib_util.h index 2c552a36c6a9..1a502147c441 100644 --- a/lib/lib_util.h +++ b/lib/lib_util.h @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021 Alfonso Sabato Siciliano + * Copyright (c) 2021-2023 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,109 +28,136 @@ #ifndef _LIBBSDDIALOG_UTIL_H_ #define _LIBBSDDIALOG_UTIL_H_ -/* - * Utils to implement widgets - Internal library API - Dafult values - */ - -#define HBORDERS 2 -#define VBORDERS 2 - -/* ncurses has not a Ctrl key macro */ -#define KEY_CTRL(x) ((x) & 0x1f) +#define BORDER 1 +#define BORDERS (BORDER + BORDER) +#define TEXTHMARGIN 1 +#define TEXTHMARGINS (TEXTHMARGIN + TEXTHMARGIN) +#define HBUTTONS 2 +#define OK_LABEL "OK" +#define CANCEL_LABEL "Cancel" -/* Set default aspect ratio to 9 */ -#define GET_ASPECT_RATIO(conf) (conf->aspect_ratio > 0 ? conf->aspect_ratio : 9) +/* theme util */ +extern struct bsddialog_theme t; +extern bool hastermcolors; +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) /* debug */ -#define BSDDIALOG_DEBUG(y,x,fmt, ...) do { \ - mvprintw(y, x, fmt, __VA_ARGS__); \ - refresh(); \ +#define BSDDIALOG_DEBUG(y,x,fmt, ...) do { \ + mvprintw(y, x, fmt, __VA_ARGS__); \ + refresh(); \ } while (0) - -/* error buffer */ -const char *get_error_string(void); -void set_error_string(char *string); - -#define RETURN_ERROR(str) do { \ - set_error_string(str); \ - return BSDDIALOG_ERROR; \ +/* error and diagnostic */ +#define RETURN_ERROR(str) do { \ + set_error_string(str); \ + return (BSDDIALOG_ERROR); \ +} while (0) +#define RETURN_FMTERROR(fmt, ...) do { \ + set_fmt_error_string(fmt, __VA_ARGS__); \ + return (BSDDIALOG_ERROR); \ +} while (0) +/* check ptr */ +#define CHECK_PTR(p) do { \ + if (p == NULL) \ + RETURN_ERROR("*" #p " is NULL"); \ +} while (0) +#define CHECK_ARRAY(nitem, a) do { \ + if(nitem > 0 && a == NULL) \ + RETURN_FMTERROR(#nitem " is %d but *" #a " is NULL", nitem); \ +} while (0) +/* widget utils */ +#define TEXTPAD(d, downnotext) rtextpad(d, 0, 0, 0, downnotext) +#define SCREENLINES (getmaxy(stdscr)) +#define SCREENCOLS (getmaxx(stdscr)) +#define CHECK_STR(s) (s == NULL ? "" : s) +#define DRAW_BUTTONS(d) do { \ + draw_buttons(&d); \ + wnoutrefresh(d.widget); \ } while (0) -/* Buttons */ -#define LABEL_cancel_label "Cancel" -#define LABEL_exit_label "EXIT" -#define LABEL_extra_label "Extra" -#define LABEL_help_label "Help" -#define LABEL_ok_label "OK" -#define BUTTONLABEL(l) (conf->button.l != NULL ? conf->button.l : LABEL_ ##l) +/* internal types */ +enum elevation { RAISED, LOWERED }; -#define MAXBUTTONS 6 /* yes|ok + extra + no|cancel + help + 2 generics */ struct buttons { unsigned int nbuttons; - char *label[MAXBUTTONS]; +#define MAXBUTTONS 10 /* 3left + ok + extra + cancel + help + 3 right */ + const char *label[MAXBUTTONS]; + bool shortcut; + wchar_t first[MAXBUTTONS]; int value[MAXBUTTONS]; int curr; +#define BUTTONVALUE(bs) bs.value[bs.curr] unsigned int sizebutton; /* including left and right delimiters */ }; -void -get_buttons(struct bsddialog_conf *conf, struct buttons *bs, char *yesoklabel, - char *extralabel, char *nocancellabel, char *helplabel); +struct dialog { + bool built; /* true after the first draw_dialog() */ + struct bsddialog_conf *conf; /* Checked API conf */ + WINDOW *widget; /* Size and position refer to widget */ + int y, x; /* Current position, API conf.[y|x]: -1, >=0 */ + int rows, cols; /* API rows and cols: -1, 0, >0 */ + int h, w; /* Current height and width */ + const char *text; /* Checked API text, at least "" */ + WINDOW *textpad; /* Fake for textbox */ + struct buttons bs; /* bs.nbuttons = 0 for no buttons */ + WINDOW *shadow; +}; -void -draw_button(WINDOW *window, int y, int x, int size, char *text, bool selected, - bool shortkey); +/* error and diagnostic */ +const char *get_error_string(void); +void set_error_string(const char *string); +void set_fmt_error_string(const char *fmt, ...); -void -draw_buttons(WINDOW *window, int y, int cols, struct buttons bs, bool shortkey); +/* multicolumn character string */ +unsigned int strcols(const char *mbstring); +int str_props(const char *mbstring, unsigned int *cols, bool *has_multi_col); +void mvwaddwch(WINDOW *w, int y, int x, wchar_t wch); +wchar_t* alloc_mbstows(const char *mbstring); -/* help window with F1 key */ -int f1help(struct bsddialog_conf *conf); +/* buttons */ +void +set_buttons(struct dialog *d, bool shortcut, const char *oklabel, + const char *canclabel); +void draw_buttons(struct dialog *d); +bool shortcut_buttons(wint_t key, struct buttons *bs); -/* cleaner */ -int hide_widget(int y, int x, int h, int w, bool withshadow); +/* widget utils */ +int hide_dialog(struct dialog *d); +int f1help_dialog(struct bsddialog_conf *conf); -/* (auto) size and (auto) position */ -int -get_text_properties(struct bsddialog_conf *conf, char *text, int *maxword, - int *maxline, int *nlines); +void +draw_borders(struct bsddialog_conf *conf, WINDOW *win, enum elevation elev); -int widget_max_height(struct bsddialog_conf *conf); -int widget_max_width(struct bsddialog_conf *conf); +void +update_box(struct bsddialog_conf *conf, WINDOW *win, int y, int x, int h, int w, + enum elevation elev); -int -set_widget_size(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w); +void +rtextpad(struct dialog *d, int ytext, int xtext, int upnotext, int downnotext); +/* (auto) sizing and (auto) position */ int -set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w); +set_widget_size(struct bsddialog_conf *conf, int rows, int cols, int *h, + int *w); -/* widget builders */ int -print_textpad(struct bsddialog_conf *conf, WINDOW *pad, int *rows, int cols, - char *text); +set_widget_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, + int *w, const char *text, int *rowstext, struct buttons *bs, int hnotext, + int minw); -enum elevation { RAISED, LOWERED }; +int widget_checksize(int h, int w, struct buttons *bs, int hnotext, int minw); -void -draw_borders(struct bsddialog_conf *conf, WINDOW *win, int rows, int cols, - enum elevation elev); +int +set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w); -WINDOW * -new_boxed_window(struct bsddialog_conf *conf, int y, int x, int rows, int cols, - enum elevation elev); +int dialog_size_position(struct dialog *d, int hnotext, int minw, int *htext); -int -new_widget_withtextpad(struct bsddialog_conf *conf, WINDOW **shadow, - WINDOW **widget, int y, int x, int h, int w, enum elevation elev, - WINDOW **textpad, int *htextpad, char *text, bool buttons); +/* dialog */ +void end_dialog(struct dialog *d); +int draw_dialog(struct dialog *d); int -update_widget_withtextpad(struct bsddialog_conf *conf, WINDOW *shadow, - WINDOW *widget, int h, int w, enum elevation elev, WINDOW *textpad, - int *htextpad, char *text, bool buttons); - -void -end_widget_withtextpad(struct bsddialog_conf *conf, WINDOW *window, int h, int w, - WINDOW *textpad, WINDOW *shadow); +prepare_dialog(struct bsddialog_conf *conf, const char *text, int rows, + int cols, struct dialog *d); #endif diff --git a/lib/libbsddialog.c b/lib/libbsddialog.c index a3cbe6bf9748..755a469b126c 100644 --- a/lib/libbsddialog.c +++ b/lib/libbsddialog.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021 Alfonso Sabato Siciliano + * Copyright (c) 2021-2023 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,123 +25,128 @@ * SUCH DAMAGE. */ -#include <stdlib.h> +#include <curses.h> #include <string.h> -#include <unistd.h> - -#ifdef PORTNCURSES -#include <ncurses/ncurses.h> -#else -#include <ncurses.h> -#endif #include "bsddialog.h" -#include "lib_util.h" #include "bsddialog_theme.h" +#include "lib_util.h" -/* - * This file implements public functions not related to a specific dialog. - * utils.h/c: private functions to implement the library. - * theme.h/c: public API related to themes. - * Dialogs implementation: - * infobox.c infobox - * messgebox.c msgbox - yesno - * menubox.c buildlist - checklist - menu - mixedlist - radiolist - * formbox.c inputbox - passwordbox - form - passwordform - mixedform - * barbox.c gauge - mixedgauge - rangebox - pause - * textbox.c textbox - * timebox.c timebox - calendar - */ +#define DEFAULT_COLS_PER_ROW 10 /* Default conf.text.columns_per_row */ -extern struct bsddialog_theme t; +static bool in_bsddialog_mode = false; -int bsddialog_init(void) +int bsddialog_init_notheme(void) { - int i, j, c = 1, error = OK; + int i, j, c, error; set_error_string(""); - if(initscr() == NULL) - RETURN_ERROR("Cannot init ncurses (initscr)"); + if (initscr() == NULL) + RETURN_ERROR("Cannot init curses (initscr)"); + error = OK; error += keypad(stdscr, TRUE); nl(); error += cbreak(); error += noecho(); curs_set(0); - if(error != OK) { + if (error != OK) { bsddialog_end(); - RETURN_ERROR("Cannot init ncurses (keypad and cursor)"); + RETURN_ERROR("Cannot init curses (keypad and cursor)"); } + in_bsddialog_mode = true; + c = 1; error += start_color(); - for (i=0; i<8; i++) - for(j=0; j<8; j++) { + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) { error += init_pair(c, i, j); c++; + } } - if(error != OK) { - bsddialog_end(); - RETURN_ERROR("Cannot init ncurses (colors)"); - } - if (bsddialog_set_default_theme(BSDDIALOG_THEME_DEFAULT) != 0) { + hastermcolors = (error == OK && has_colors()) ? true : false; + + return (BSDDIALOG_OK); +} + +int bsddialog_init(void) +{ + enum bsddialog_default_theme theme; + + bsddialog_init_notheme(); + + if (bsddialog_hascolors()) + theme = BSDDIALOG_THEME_FLAT; + else + theme = BSDDIALOG_THEME_BLACKWHITE; + + if (bsddialog_set_default_theme(theme) != 0) { bsddialog_end(); + in_bsddialog_mode = false; return (BSDDIALOG_ERROR); } - return (0); + return (BSDDIALOG_OK); } int bsddialog_end(void) { - if (endwin() != OK) - RETURN_ERROR("Cannot end ncurses (endwin)"); + RETURN_ERROR("Cannot end curses (endwin)"); + in_bsddialog_mode = false; - return (0); + return (BSDDIALOG_OK); } -int bsddialog_backtitle(struct bsddialog_conf *conf, char *backtitle) +int bsddialog_backtitle(struct bsddialog_conf *conf, const char *backtitle) { + CHECK_PTR(conf); - mvaddstr(0, 1, backtitle); + move(0, 1); + clrtoeol(); + addstr(CHECK_STR(backtitle)); if (conf->no_lines != true) - mvhline(1, 1, conf->ascii_lines ? '-' : ACS_HLINE, COLS-2); + mvhline(1, 1, conf->ascii_lines ? '-' : ACS_HLINE, + SCREENCOLS - 2); refresh(); - return (0); + return (BSDDIALOG_OK); } -const char *bsddialog_geterror(void) +bool bsddialog_inmode(void) { + return (in_bsddialog_mode); +} +const char *bsddialog_geterror(void) +{ return (get_error_string()); } int bsddialog_initconf(struct bsddialog_conf *conf) { - - if (conf == NULL) - RETURN_ERROR("conf is NULL"); - if (sizeof(*conf) != sizeof(struct bsddialog_conf)) - RETURN_ERROR("Bad conf size"); + CHECK_PTR(conf); memset(conf, 0, sizeof(struct bsddialog_conf)); conf->y = BSDDIALOG_CENTER; conf->x = BSDDIALOG_CENTER; conf->shadow = true; + conf->text.cols_per_row = DEFAULT_COLS_PER_ROW; - return (0); + return (BSDDIALOG_OK); } -int bsddialog_clearterminal(void) +void bsddialog_refresh(void) { - - if (clear() != OK) - RETURN_ERROR("Cannot clear the terminal"); refresh(); - - return (0); } + +void bsddialog_clear(unsigned int y) +{ + move(y, 0); + clrtobot(); + refresh(); +}
\ No newline at end of file diff --git a/lib/menubox.c b/lib/menubox.c index 147fa66c8c27..b6213aa8f997 100644 --- a/lib/menubox.c +++ b/lib/menubox.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021 Alfonso Sabato Siciliano + * Copyright (c) 2021-2023 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,30 +25,14 @@ * SUCH DAMAGE. */ -#include <sys/param.h> - -#include <ctype.h> -#include <string.h> - -#ifdef PORTNCURSES -#include <ncurses/ncurses.h> -#else -#include <ncurses.h> -#endif +#include <curses.h> +#include <stdlib.h> #include "bsddialog.h" -#include "lib_util.h" #include "bsddialog_theme.h" - -/* "Menu": checklist - menu - mixedlist - radiolist - buildlist */ - -#define DEPTHSPACE 4 -#define MIN_HEIGHT VBORDERS + 6 /* 2 buttons 1 text 3 menu */ - -extern struct bsddialog_theme t; +#include "lib_util.h" enum menumode { - BUILDLISTMODE, CHECKLISTMODE, MENUMODE, MIXEDLISTMODE, @@ -56,1103 +40,710 @@ enum menumode { SEPARATORMODE }; -struct lineposition { - unsigned int maxsepstr; - unsigned int maxprefix; - unsigned int xselector; - unsigned int selectorlen; - unsigned int maxdepth; - unsigned int xname; - unsigned int maxname; - unsigned int xdesc; - unsigned int maxdesc; - unsigned int line; +struct privateitem { + const char *prefix; + bool on; /* menu changes, not API on */ + unsigned int depth; + const char *name; + const char *desc; + const char *bottomdesc; + int group; /* index menu in menugroup */ + int index; /* real item index inside its menu */ + enum menumode type; + wchar_t shortcut; }; -static int checkradiolist(int nitems, struct bsddialog_menuitem *items) -{ - int i, error; - - error = 0; - for (i=0; i<nitems; i++) { - if (error > 0) - items[i].on = false; +struct privatemenu { + WINDOW *box; /* only for borders */ + WINDOW *pad; /* pad for the private items */ + int ypad; /* start pad line */ + int ys, ye, xs, xe; /* pad pos */ + unsigned int xselector; /* [] */ + unsigned int xname; /* real x: xname + item.depth */ + unsigned int xdesc; /* real x: xdesc + item.depth */ + unsigned int line; /* wpad: prefix [] depth name desc */ + unsigned int apimenurows; + unsigned int menurows; /* real menurows after menu_size_position() */ + int nitems; /* total nitems (all groups * all items) */ + struct privateitem *pritems; + int sel; /* current focus item, can be -1 */ + bool hasbottomdesc; +}; - if (items[i].on == true) - error++; +static enum menumode +getmode(enum menumode mode, struct bsddialog_menugroup group) +{ + if (mode == MIXEDLISTMODE) { + if (group.type == BSDDIALOG_SEPARATOR) + mode = SEPARATORMODE; + else if (group.type == BSDDIALOG_RADIOLIST) + mode = RADIOLISTMODE; + else if (group.type == BSDDIALOG_CHECKLIST) + mode = CHECKLISTMODE; } - return (error == 0 ? 0 : -1); + return (mode); } -static int checkmenu(int nitems, struct bsddialog_menuitem *items) // useful? +static int +build_privatemenu(struct bsddialog_conf *conf, struct privatemenu *m, + enum menumode mode, unsigned int ngroups, + struct bsddialog_menugroup *groups) { - int i, error; + bool onetrue; + int i, j, abs; + unsigned int maxsepstr, maxprefix, selectorlen, maxdepth; + unsigned int maxname, maxdesc; + struct bsddialog_menuitem *item; + struct privateitem *pritem; + + /* nitems and fault checks */ + CHECK_ARRAY(ngroups, groups); + m->nitems = 0; + for (i = 0; i < (int)ngroups; i++) { + CHECK_ARRAY(groups[i].nitems, groups[i].items); + m->nitems += (int)groups[i].nitems; + } + + /* alloc and set private items */ + m->pritems = calloc(m->nitems, sizeof (struct privateitem)); + if (m->pritems == NULL) + RETURN_ERROR("Cannot allocate memory for internal menu items"); + m->hasbottomdesc = false; + abs = 0; + for (i = 0; i < (int)ngroups; i++) { + onetrue = false; + for (j = 0; j < (int)groups[i].nitems; j++) { + item = &groups[i].items[j]; + pritem = &m->pritems[abs]; + + if (getmode(mode, groups[i]) == MENUMODE) { + m->pritems[abs].on = false; + } else if (getmode(mode, groups[i]) == RADIOLISTMODE) { + m->pritems[abs].on = onetrue ? false : item->on; + if (m->pritems[abs].on) + onetrue = true; + } else { /* CHECKLISTMODE */ + m->pritems[abs].on = item->on; + } + pritem->group = i; + pritem->index = j; + pritem->type = getmode(mode, groups[i]); + + pritem->prefix = CHECK_STR(item->prefix); + pritem->depth = item->depth; + pritem->name = CHECK_STR(item->name); + pritem->desc = CHECK_STR(item->desc); + pritem->bottomdesc = CHECK_STR(item->bottomdesc); + if (item->bottomdesc != NULL) + m->hasbottomdesc = true; + + mbtowc(&pritem->shortcut, conf->menu.no_name ? + pritem->desc : pritem->name, MB_CUR_MAX); + + abs++; + } + } - error = 0; - for (i=0; i<nitems; i++) { - if (items[i].on == true) - error++; + /* positions */ + m->xselector = m->xname = m->xdesc = m->line = 0; + maxsepstr = maxprefix = selectorlen = maxdepth = maxname = maxdesc = 0; + for (i = 0; i < m->nitems; i++) { + if (m->pritems[i].type == RADIOLISTMODE || + m->pritems[i].type == CHECKLISTMODE) + selectorlen = 4; + + if (m->pritems[i].type == SEPARATORMODE) { + maxsepstr = MAX(maxsepstr, + strcols(m->pritems[i].name) + + strcols(m->pritems[i].desc)); + continue; + } - items[i].on = false; + maxprefix = MAX(maxprefix, strcols(m->pritems[i].prefix)); + maxdepth = MAX(maxdepth, m->pritems[i].depth); + maxname = MAX(maxname, strcols(m->pritems[i].name)); + maxdesc = MAX(maxdesc, strcols(m->pritems[i].desc)); } + maxname = conf->menu.no_name ? 0 : maxname; + maxdesc = conf->menu.no_desc ? 0 : maxdesc; - return (error == 0 ? 0 : -1); + m->xselector = maxprefix + (maxprefix != 0 ? 1 : 0); + m->xname = m->xselector + selectorlen; + m->xdesc = maxdepth + m->xname + maxname; + m->xdesc += (maxname != 0 ? 1 : 0); + m->line = MAX(maxsepstr + 3, m->xdesc + maxdesc); + + return (0); } static void -getfirst(int ngroups, struct bsddialog_menugroup *groups, int *abs, int *group, - int *rel) +set_return_on(struct privatemenu *m, struct bsddialog_menugroup *groups) { - int i, a; + int i; + struct privateitem *pritem; - *abs = *rel = *group = -1; - a = 0; - for (i=0; i<ngroups; i++) { - if (groups[i].type == BSDDIALOG_SEPARATOR) { - a += groups[i].nitems; + for(i = 0; i < m->nitems; i++) { + if (m->pritems[i].type == SEPARATORMODE) continue; - } - if (groups[i].nitems != 0) { - *group = i; - *abs = a; - *rel = 0; - break; - } + pritem = &m->pritems[i]; + groups[pritem->group].items[pritem->index].on = pritem->on; } } -static void -getfirst_with_default(struct bsddialog_conf *conf, int ngroups, - struct bsddialog_menugroup *groups, int *abs, int *group, int *rel) +static int getprev(struct privateitem *pritems, int abs) { - int i, j, a; - struct bsddialog_menuitem *item; + int i; - getfirst(ngroups, groups, abs, group, rel); - if (*abs < 0) - return; - - a = *abs; - - for (i=*group; i<ngroups; i++) { - if (groups[i].type == BSDDIALOG_SEPARATOR) { - a += groups[i].nitems; + for (i = abs - 1; i >= 0; i--) { + if (pritems[i].type == SEPARATORMODE) continue; - } - for (j = 0; j < (int) groups[i].nitems; j++) { - item = &groups[i].items[j]; - if (conf->menu.default_item != NULL && item->name != NULL) { - if (strcmp(item->name, conf->menu.default_item) == 0) { - *abs = a; - *group = i; - *rel = j; - return; - } - } - a++; - } + return (i); } + + return (abs); } -static void -getlast(int totnitems, int ngroups, struct bsddialog_menugroup *groups, - int *abs, int *group, int *rel) +static int getnext(int npritems, struct privateitem *pritems, int abs) { - int i, a; + int i; - a = totnitems - 1; - for (i = ngroups-1; i>=0; i--) { - if (groups[i].type == BSDDIALOG_SEPARATOR) { - a -= groups[i].nitems; + for (i = abs + 1; i < npritems; i++) { + if (pritems[i].type == SEPARATORMODE) continue; - } - if (groups[i].nitems != 0) { - *group = i; - *abs = a; - *rel = groups[i].nitems - 1; - break; - } + return (i); } + + return (abs); } -static void -getnext(int ngroups, struct bsddialog_menugroup *groups, int *abs, int *group, - int *rel) +static int +getfirst_with_default(int npritems, struct privateitem *pritems, int ngroups, + struct bsddialog_menugroup *groups, int *focusgroup, int *focusitem) { - int i, a; - - if (*abs < 0 || *group < 0 || *rel < 0) - return; - - if (*rel + 1 < (int) groups[*group].nitems) { - *rel = *rel + 1; - *abs = *abs + 1; - return; + int i, abs; + + if ((abs = getnext(npritems, pritems, -1)) < 0) + return (abs); + + if (focusgroup == NULL || focusitem == NULL) + return (abs); + if (*focusgroup < 0 || *focusgroup >= ngroups) + return (abs); + if (groups[*focusgroup].type == BSDDIALOG_SEPARATOR) + return (abs); + if (*focusitem < 0 || *focusitem >= (int)groups[*focusgroup].nitems) + return (abs); + + for (i = abs; i < npritems; i++) { + if (pritems[i].group == *focusgroup && + pritems[i].index == *focusitem) + return (i); } - if (*group + 1 > ngroups) - return; - - a = *abs; - for (i = *group + 1; i < ngroups; i++) { - if (groups[i].type == BSDDIALOG_SEPARATOR) { - a += groups[i].nitems; - continue; - } - if (groups[i].nitems != 0) { - *group = i; - *abs = a + 1; - *rel = 0; - break; - } - } + return (abs); } -static void -getfastnext(int menurows, int ngroups, struct bsddialog_menugroup *groups, - int *abs, int *group, int *rel) +static int +getfastnext(int menurows, int npritems, struct privateitem *pritems, int abs) { int a, start, i; - start = *abs; + start = abs; i = menurows; do { - a = *abs; - getnext(ngroups, groups, abs, group, rel); + a = abs; + abs = getnext(npritems, pritems, abs); i--; - } while (*abs != a && *abs < start + menurows && i > 0); -} - -static void -getprev(struct bsddialog_menugroup *groups, int *abs, int *group, int *rel) -{ - int i, a; + } while (abs != a && abs < start + menurows && i > 0); - if (*abs < 0 || *group < 0 || *rel < 0) - return; - - if (*rel > 0) { - *rel = *rel - 1; - *abs = *abs - 1; - return; - } - - if (*group - 1 < 0) - return; - - a = *abs; - for (i = *group - 1; i >= 0; i--) { - if (groups[i].type == BSDDIALOG_SEPARATOR) { - a -= (int) groups[i].nitems; - continue; - } - if (groups[i].nitems != 0) { - *group = i; - *abs = a - 1; - *rel = (int) groups[i].nitems - 1; - break; - } - } + return (abs); } -static void -getfastprev(int menurows, struct bsddialog_menugroup *groups, int *abs, - int *group, int *rel) +static int +getfastprev(int menurows, struct privateitem *pritems, int abs) { int a, start, i; - start = *abs; + start = abs; i = menurows; do { - a = *abs; - getprev(groups, abs, group, rel); + a = abs; + abs = getprev(pritems, abs); i--; - } while (*abs != a && *abs > start - menurows && i > 0); + } while (abs != a && abs > start - menurows && i > 0); + + return (abs); } -static bool -getnextshortcut(struct bsddialog_conf *conf, enum menumode mode, int ngroups, - struct bsddialog_menugroup *groups, int *abs, int *group, int *rel, - int key) +static int +getnextshortcut(int npritems, struct privateitem *pritems, int abs, wint_t key) { - int i, j, a, ch, ng, nr, na; - bool mainloop; + int i, next; - if (*abs < 0 || ngroups < 0 || *rel < 0 || mode == BUILDLISTMODE) - return false; - - na = a = -1; - mainloop = true; - for (i = 0; i < ngroups && mainloop; i++) { - if (groups[i].type == BSDDIALOG_SEPARATOR) { - a += groups[i].nitems; + next = -1; + for (i = 0; i < npritems; i++) { + if (pritems[i].type == SEPARATORMODE) continue; + if (pritems[i].shortcut == (wchar_t)key) { + if (i > abs) + return (i); + if (i < abs && next == -1) + next = i; } - for (j = 0; j < (int)groups[i].nitems; j++) { - a++; - if (a == *abs) - continue; - - if (conf->menu.no_name) - ch = groups[i].items[j].desc[0]; - else - ch = groups[i].items[j].name[0]; - - if (ch == key) { - if (a < *abs && na == -1) { - na = a; - ng = i; - nr = j; - } - if (a > *abs) { - na = a; - ng = i; - nr = j; - mainloop = false; - break; - } - } - } - } - - if (na != -1) { - *abs = na; - *group = ng; - *rel = nr; - return (true); } - return (false); + return (next != -1 ? next : abs); } -static enum menumode -getmode(enum menumode mode, struct bsddialog_menugroup group) +static void drawseparators(struct bsddialog_conf *conf, struct privatemenu *m) { + int i, linech, realw, labellen; + const char *desc, *name; - if (mode == MIXEDLISTMODE) { - if (group.type == BSDDIALOG_SEPARATOR) - mode = SEPARATORMODE; - else if (group.type == BSDDIALOG_RADIOLIST) - mode = RADIOLISTMODE; - else if (group.type == BSDDIALOG_CHECKLIST) - mode = CHECKLISTMODE; + for (i = 0; i < m->nitems; i++) { + if (m->pritems[i].type != SEPARATORMODE) + continue; + if (conf->no_lines == false) { + wattron(m->pad, t.menu.desccolor); + linech = conf->ascii_lines ? '-' : ACS_HLINE; + mvwhline(m->pad, i, 0, linech, m->line); + wattroff(m->pad, t.menu.desccolor); + } + name = m->pritems[i].name; + desc = m->pritems[i].desc; + realw = m->xe - m->xs; + labellen = strcols(name) + strcols(desc) + 1; + wmove(m->pad, i, (labellen < realw) ? realw/2 - labellen/2 : 0); + wattron(m->pad, t.menu.sepnamecolor); + waddstr(m->pad, name); + wattroff(m->pad, t.menu.sepnamecolor); + if (strcols(name) > 0 && strcols(desc) > 0) + waddch(m->pad, ' '); + wattron(m->pad, t.menu.sepdesccolor); + waddstr(m->pad, desc); + wattroff(m->pad, t.menu.sepdesccolor); } - - return mode; } static void -drawitem(struct bsddialog_conf *conf, WINDOW *pad, int y, - struct bsddialog_menuitem item, enum menumode mode, struct lineposition pos, - bool curr) +drawitem(struct bsddialog_conf *conf, struct privatemenu *m, int y, bool focus) { - int colordesc, colorname, colorshortcut, linech; - char *shortcut; + int colordesc, colorname, colorshortcut; + struct privateitem *pritem; - if (mode == SEPARATORMODE) { - if (conf->no_lines == false) { - wattron(pad, t.menu.desccolor); - linech = conf->ascii_lines ? '-' : ACS_HLINE; - mvwhline(pad, y, 0, linech, pos.line); - wattroff(pad, t.menu.desccolor); - } - wmove(pad, y, pos.line/2 - (strlen(item.name)+strlen(item.desc))/2); - wattron(pad, t.menu.namesepcolor); - waddstr(pad, item.name); - wattroff(pad, t.menu.namesepcolor); - if (strlen(item.name) > 0 && strlen(item.desc) > 0) - waddch(pad, ' '); - wattron(pad, t.menu.descsepcolor); - waddstr(pad, item.desc); - wattroff(pad, t.menu.descsepcolor); - return; - } + pritem = &m->pritems[y]; /* prefix */ - if (item.prefix != NULL && item.prefix[0] != '\0') - mvwaddstr(pad, y, 0, item.prefix); + wattron(m->pad, focus ? t.menu.f_prefixcolor : t.menu.prefixcolor); + mvwaddstr(m->pad, y, 0, pritem->prefix); + wattroff(m->pad, focus ? t.menu.f_prefixcolor : t.menu.prefixcolor); /* selector */ - wmove(pad, y, pos.xselector); - wattron(pad, t.menu.selectorcolor); - if (mode == CHECKLISTMODE) - wprintw(pad, "[%c]", item.on ? 'X' : ' '); - if (mode == RADIOLISTMODE) - wprintw(pad, "(%c)", item.on ? '*' : ' '); - wattroff(pad, t.menu.selectorcolor); + wmove(m->pad, y, m->xselector); + wattron(m->pad, focus ? t.menu.f_selectorcolor : t.menu.selectorcolor); + if (pritem->type == CHECKLISTMODE) + wprintw(m->pad, "[%c]", pritem->on ? 'X' : ' '); + if (pritem->type == RADIOLISTMODE) + wprintw(m->pad, "(%c)", pritem->on ? '*' : ' '); + wattroff(m->pad, focus ? t.menu.f_selectorcolor : t.menu.selectorcolor); /* name */ - colorname = curr ? t.menu.f_namecolor : t.menu.namecolor; - if (mode != BUILDLISTMODE && conf->menu.no_name == false) { - wattron(pad, colorname); - mvwaddstr(pad, y, pos.xname + item.depth * DEPTHSPACE, item.name); - wattroff(pad, colorname); + colorname = focus ? t.menu.f_namecolor : t.menu.namecolor; + if (conf->menu.no_name == false) { + wattron(m->pad, colorname); + mvwaddstr(m->pad, y, m->xname + pritem->depth, pritem->name); + wattroff(m->pad, colorname); } /* description */ - if (mode == BUILDLISTMODE) { - if (curr == false) - colordesc = item.on ? t.menu.namecolor : t.menu.desccolor; - else - colordesc = t.menu.f_namecolor; - } - else { - if (conf->menu.no_name) - colordesc = curr ? t.menu.f_namecolor : t.menu.namecolor; - else - colordesc = curr ? t.menu.f_desccolor : t.menu.desccolor; - } - if (mode == BUILDLISTMODE || conf->menu.no_desc == false) { - wattron(pad, colordesc); - if (conf->menu.no_name) - mvwaddstr(pad, y, pos.xname + item.depth * DEPTHSPACE, item.desc); - else - mvwaddstr(pad, y, pos.xdesc, item.desc); - wattroff(pad, colordesc); - } - - /* shortcut */ - if (mode != BUILDLISTMODE && conf->menu.shortcut_buttons == false) { - colorshortcut = curr ? t.menu.f_shortcutcolor : t.menu.shortcutcolor; - wattron(pad, colorshortcut); + if (conf->menu.no_name) + colordesc = focus ? t.menu.f_namecolor : t.menu.namecolor; + else + colordesc = focus ? t.menu.f_desccolor : t.menu.desccolor; + if (conf->menu.no_desc == false) { + wattron(m->pad, colordesc); if (conf->menu.no_name) - shortcut = item.desc; + mvwaddstr(m->pad, y, m->xname + pritem->depth, + pritem->desc); else - shortcut = item.name; - wmove(pad, y, pos.xname + item.depth * DEPTHSPACE); - if (shortcut != NULL && shortcut[0] != '\0') - waddch(pad, shortcut[0]); - wattroff(pad, colorshortcut); -} - - /* bottom description */ - move(LINES-1, 2); - clrtoeol(); - if (item.bottomdesc != NULL) { - addstr(item.bottomdesc); - refresh(); + mvwaddstr(m->pad, y, m->xdesc, pritem->desc); + wattroff(m->pad, colordesc); } -} -static void -menu_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w, - char *text, int linelen, unsigned int *menurows, int nitems, - struct buttons bs) -{ - int textrow, menusize; - - textrow = text != NULL && strlen(text) > 0 ? 1 : 0; - - if (cols == BSDDIALOG_AUTOSIZE) { - *w = VBORDERS; - /* buttons size */ - *w += bs.nbuttons * bs.sizebutton; - *w += bs.nbuttons > 0 ? (bs.nbuttons-1) * t.button.space : 0; - /* line size */ - *w = MAX(*w, linelen + 6); - /* conf.auto_minwidth */ - *w = MAX(*w, (int)conf->auto_minwidth); - /* - * avoid terminal overflow, - * -1 fix false negative with big menu over the terminal and - * autosize, for example "portconfig /usr/ports/www/apache24/". - */ - *w = MIN(*w, widget_max_width(conf)-1); + /* shortcut */ + if (conf->menu.shortcut_buttons == false) { + colorshortcut = focus ? + t.menu.f_shortcutcolor : t.menu.shortcutcolor; + wattron(m->pad, colorshortcut); + mvwaddwch(m->pad, y, m->xname + pritem->depth, pritem->shortcut); + wattroff(m->pad, colorshortcut); } - if (rows == BSDDIALOG_AUTOSIZE) { - *h = HBORDERS + 2 /* buttons */ + textrow; - - if (*menurows == 0) { - *h += nitems + 2; - *h = MIN(*h, widget_max_height(conf)); - menusize = MIN(nitems + 2, *h - (HBORDERS + 2 + textrow)); - menusize -=2; - *menurows = menusize < 0 ? 0 : menusize; + /* bottom description */ + if (m->hasbottomdesc) { + move(SCREENLINES - 1, 2); + clrtoeol(); + if (focus) { + attron(t.menu.bottomdesccolor); + addstr(pritem->bottomdesc); + attroff(t.menu.bottomdesccolor); + refresh(); } - else /* h autosize with a fixed menurows */ - *h = *h + *menurows + 2; - - /* conf.auto_minheight */ - *h = MAX(*h, (int)conf->auto_minheight); - /* avoid terminal overflow */ - *h = MIN(*h, widget_max_height(conf)); - /* avoid menurows overflow */ - /* manual: with rows=autosize menurows!=0 is maxmenurows */ - *menurows = MIN(*h - 6 - textrow, (int)*menurows); - } - else { - if (*menurows == 0) - *menurows = MIN(rows-6-textrow, nitems); } } -static int -menu_checksize(int rows, int cols, char *text, int menurows, int nitems, - struct buttons bs) +static void update_menubox(struct bsddialog_conf *conf, struct privatemenu *m) { - int mincols, textrow, menusize; + int h, w; - mincols = VBORDERS; - /* buttons */ - mincols += bs.nbuttons * bs.sizebutton; - mincols += bs.nbuttons > 0 ? (bs.nbuttons-1) * t.button.space : 0; - /* line, comment to permet some cols hidden */ - /* mincols = MAX(mincols, linelen); */ + draw_borders(conf, m->box, LOWERED); + getmaxyx(m->box, h, w); - if (cols < mincols) - RETURN_ERROR("Few cols, width < size buttons or "\ - "name+descripion of the items"); + if (m->nitems > (int)m->menurows) { + wattron(m->box, t.dialog.arrowcolor); + if (m->ypad > 0) + mvwhline(m->box, 0, 2, + conf->ascii_lines ? '^' : ACS_UARROW, 3); - textrow = text != NULL && strlen(text) > 0 ? 1 : 0; + if ((m->ypad + (int)m->menurows) < m->nitems) + mvwhline(m->box, h-1, 2, + conf->ascii_lines ? 'v' : ACS_DARROW, 3); - if (nitems > 0 && menurows == 0) - RETURN_ERROR("items > 0 but menurows == 0, probably terminal "\ - "too small"); - - menusize = nitems > 0 ? 3 : 0; - if (rows < 2 + 2 + menusize + textrow) - RETURN_ERROR("Few lines for this menus"); - - return 0; + mvwprintw(m->box, h-1, w-6, "%3d%%", + 100 * (m->ypad + m->menurows) / m->nitems); + wattroff(m->box, t.dialog.arrowcolor); + } } -/* the caller has to call prefresh(menupad, ymenupad, 0, ys, xs, ye, xe); */ -static void -update_menuwin(struct bsddialog_conf *conf, WINDOW *menuwin, int h, int w, - int totnitems, unsigned int menurows, int ymenupad) +static int menu_size_position(struct dialog *d, struct privatemenu *m) { - - draw_borders(conf, menuwin, h, w, LOWERED); - - if (totnitems > (int) menurows) { - wattron(menuwin, t.menu.arrowcolor); - - if (ymenupad > 0) - mvwprintw(menuwin, 0, 2, "^^^"); - - if ((int) (ymenupad + menurows) < totnitems) - mvwprintw(menuwin, h-1, 2, "vvv"); - - wattroff(menuwin, t.menu.arrowcolor); - - mvwprintw(menuwin, h-1, w-10, "%3d%%", - 100 * (ymenupad + menurows) / totnitems); - } + int htext, hmenu; + + if (set_widget_size(d->conf, d->rows, d->cols, &d->h, &d->w) != 0) + return (BSDDIALOG_ERROR); + + hmenu = (int)(m->menurows == BSDDIALOG_AUTOSIZE) ? + (int)m->nitems : (int)m->menurows; + hmenu += 2; /* menu borders */ + /* + * algo 1: notext = 1 (grows vertically). + * algo 2: notext = hmenu (grows horizontally, better for little term). + */ + if (set_widget_autosize(d->conf, d->rows, d->cols, &d->h, &d->w, + d->text, &htext, &d->bs, hmenu, m->line + 4) != 0) + return (BSDDIALOG_ERROR); + /* avoid menurows overflow and menurows becomes "at most menurows" */ + if (d->h - BORDERS - htext - HBUTTONS <= 2 /* menuborders */) + m->menurows = (m->nitems > 0) ? 1 : 0; /* widget_checksize() */ + else + m->menurows = MIN(d->h - BORDERS - htext - HBUTTONS, hmenu) - 2; + + /* + * no minw=linelen to avoid big menu fault, then some col can be + * hidden (example portconfig www/apache24). + */ + if (widget_checksize(d->h, d->w, &d->bs, + 2 /* border box */ + MIN(m->menurows, 1), 0) != 0) + return (BSDDIALOG_ERROR); + + if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0) + return (BSDDIALOG_ERROR); + + return (0); } -static int -do_mixedlist(struct bsddialog_conf *conf, char* text, int rows, int cols, - unsigned int menurows, enum menumode mode, int ngroups, - struct bsddialog_menugroup *groups, int *focuslist, int *focusitem) +static int mixedlist_redraw(struct dialog *d, struct privatemenu *m) { - WINDOW *shadow, *widget, *textpad, *menuwin, *menupad; - int i, j, y, x, h, w, htextpad, output, input; - int ymenupad, ys, ye, xs, xe, abs, g, rel, totnitems; - bool loop, automenurows, shortcut_buttons; - struct buttons bs; - struct bsddialog_menuitem *item; - enum menumode currmode; - struct lineposition pos = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - - shortcut_buttons = conf->menu.shortcut_buttons; - - automenurows = menurows == BSDDIALOG_AUTOSIZE ? true : false; - - totnitems = 0; - for (i=0; i < ngroups; i++) { - currmode = getmode(mode, groups[i]); - if (currmode == RADIOLISTMODE) - checkradiolist(groups[i].nitems, groups[i].items); - - if (currmode == MENUMODE) - checkmenu(groups[i].nitems, groups[i].items); - - if (currmode == RADIOLISTMODE || currmode == CHECKLISTMODE) - pos.selectorlen = 3; - - for (j=0; j < (int) groups[i].nitems; j++) { - totnitems++; - item = &groups[i].items[j]; - - if (groups[i].type == BSDDIALOG_SEPARATOR) { - pos.maxsepstr = MAX(pos.maxsepstr, - strlen(item->name) + strlen(item->desc)); - continue; - } - - pos.maxprefix = MAX(pos.maxprefix, strlen(item->prefix)); - pos.maxdepth = MAX(pos.maxdepth, item->depth); - pos.maxname = MAX(pos.maxname, strlen(item->name)); - pos.maxdesc = MAX(pos.maxdesc, strlen(item->desc)); - } - } - pos.maxname = conf->menu.no_name ? 0 : pos.maxname; - pos.maxdesc = conf->menu.no_desc ? 0 : pos.maxdesc; - pos.maxdepth *= DEPTHSPACE; - - pos.xselector = pos.maxprefix + (pos.maxprefix != 0 ? 1 : 0); - pos.xname = pos.xselector + pos.selectorlen + (pos.selectorlen > 0 ? 1 : 0); - pos.xdesc = pos.maxdepth + pos.xname + pos.maxname; - pos.xdesc += (pos.maxname != 0 ? 1 : 0); - pos.line = MAX(pos.maxsepstr + 3, pos.xdesc + pos.maxdesc); - - - get_buttons(conf, &bs, BUTTONLABEL(ok_label), BUTTONLABEL(extra_label), - BUTTONLABEL(cancel_label), BUTTONLABEL(help_label)); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return BSDDIALOG_ERROR; - menu_autosize(conf, rows, cols, &h, &w, text, pos.line, &menurows, - totnitems, bs); - if (menu_checksize(h, w, text, menurows, totnitems, bs) != 0) - return BSDDIALOG_ERROR; - if (set_widget_position(conf, &y, &x, h, w) != 0) - return BSDDIALOG_ERROR; - - if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED, - &textpad, &htextpad, text, true) != 0) - return BSDDIALOG_ERROR; - - prefresh(textpad, 0, 0, y + 1, x + 1 + t.text.hmargin, - y + h - menurows, x + 1 + w - t.text.hmargin); - - menuwin = new_boxed_window(conf, y + h - 5 - menurows, x + 2, - menurows+2, w-4, LOWERED); - - menupad = newpad(totnitems, pos.line); - wbkgd(menupad, t.dialog.color); - - ymenupad = 0; - for (i=0; i<ngroups; i++) { - currmode = getmode(mode, groups[i]); - for (j=0; j < (int) groups[i].nitems; j++) { - item = &groups[i].items[j]; - drawitem(conf, menupad, ymenupad, *item, currmode, pos, - false); - ymenupad++; - } - } - getfirst_with_default(conf, ngroups, groups, &abs, &g, &rel); - currmode = getmode(mode, groups[g]); - item = &groups[g].items[rel]; - drawitem(conf, menupad, abs, *item, currmode, pos, true); - - ys = y + h - 5 - menurows + 1; - ye = y + h - 5 ; - if (conf->menu.align_left || (int)pos.line > w - 6) { - xs = x + 3; - xe = xs + w - 7; + if (d->built) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ } - else { /* center */ - xs = x + 3 + (w-6)/2 - pos.line/2; - xe = xs + w - 5; + m->menurows = m->apimenurows; + if (menu_size_position(d, m) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) + return (BSDDIALOG_ERROR); + if (d->built) + refresh(); /* Important to fix grey lines expanding screen */ + TEXTPAD(d, 2/*bmenu*/ + m->menurows + HBUTTONS); + + /* selected item in view*/ + if (m->ypad > m->sel && m->ypad > 0) + m->ypad = m->sel; + if ((int)(m->ypad + m->menurows) <= m->sel) + m->ypad = m->sel - m->menurows + 1; + /* lower pad after a terminal expansion */ + if (m->ypad > 0 && (m->nitems - m->ypad) < (int)m->menurows) + m->ypad = m->nitems - m->menurows; + + update_box(d->conf, m->box, d->y + d->h - 5 - m->menurows, d->x + 2, + m->menurows+2, d->w-4, LOWERED); + update_menubox(d->conf, m); + wnoutrefresh(m->box); + + m->ys = d->y + d->h - 5 - m->menurows + 1; + m->ye = d->y + d->h - 5 ; + if (d->conf->menu.align_left || (int)m->line > d->w - 6) { + m->xs = d->x + 3; + m->xe = m->xs + d->w - 7; + } else { /* center */ + m->xs = d->x + 3 + (d->w-6)/2 - m->line/2; + m->xe = m->xs + d->w - 5; } + drawseparators(d->conf, m); /* uses xe - xs */ + pnoutrefresh(m->pad, m->ypad, 0, m->ys, m->xs, m->ye, m->xe); - ymenupad = 0; /* now ymenupad is pminrow for prefresh() */ - if ((int)(ymenupad + menurows) - 1 < abs) - ymenupad = abs - menurows + 1; - update_menuwin(conf, menuwin, menurows+2, w-4, totnitems, menurows, ymenupad); - wrefresh(menuwin); - prefresh(menupad, ymenupad, 0, ys, xs, ye, xe); - - draw_buttons(widget, h-2, w, bs, shortcut_buttons); - wrefresh(widget); + return (0); +} +static int +do_mixedlist(struct bsddialog_conf *conf, const char *text, int rows, int cols, + unsigned int menurows, enum menumode mode, unsigned int ngroups, + struct bsddialog_menugroup *groups, int *focuslist, int *focusitem) +{ + bool loop, changeitem; + int i, next, retval; + wint_t input; + struct privatemenu m; + struct dialog d; + + if (prepare_dialog(conf, text, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + set_buttons(&d, conf->menu.shortcut_buttons, OK_LABEL, CANCEL_LABEL); + if (d.conf->menu.no_name && d.conf->menu.no_desc) + RETURN_ERROR("Both conf.menu.no_name and conf.menu.no_desc"); + + if (build_privatemenu(conf, &m, mode, ngroups, groups) != 0) + return (BSDDIALOG_ERROR); + + if ((m.box = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW box menu"); + wbkgd(m.box, t.dialog.color); + m.pad = newpad(m.nitems, m.line); + wbkgd(m.pad, t.dialog.color); + + for (i = 0; i < m.nitems; i++) + drawitem(conf, &m, i, false); + m.sel = getfirst_with_default(m.nitems, m.pritems, ngroups, groups, + focuslist, focusitem); + if (m.sel >= 0) + drawitem(d.conf, &m, m.sel, true); + m.ypad = 0; + m.apimenurows = menurows; + if (mixedlist_redraw(&d, &m) != 0) + return (BSDDIALOG_ERROR); + + changeitem = false; loop = true; - while(loop) { - input = getch(); + while (loop) { + doupdate(); + if (get_wch(&input) == ERR) + continue; switch(input) { case KEY_ENTER: case 10: /* Enter */ - output = bs.value[bs.curr]; - if (currmode == MENUMODE) - item->on = true; + retval = BUTTONVALUE(d.bs); + if (m.sel >= 0 && m.pritems[m.sel].type == MENUMODE) + m.pritems[m.sel].on = true; loop = false; break; case 27: /* Esc */ - output = BSDDIALOG_ESC; - loop = false; + if (conf->key.enable_esc) { + retval = BSDDIALOG_ESC; + if (m.sel >= 0 && + m.pritems[m.sel].type == MENUMODE) + m.pritems[m.sel].on = true; + loop = false; + } break; case '\t': /* TAB */ - bs.curr = (bs.curr + 1) % bs.nbuttons; - draw_buttons(widget, h-2, w, bs, shortcut_buttons); - wrefresh(widget); + case KEY_RIGHT: + d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons; + DRAW_BUTTONS(d); break; case KEY_LEFT: - if (bs.curr > 0) { - bs.curr--; - draw_buttons(widget, h-2, w, bs, shortcut_buttons); - wrefresh(widget); - } - break; - case KEY_RIGHT: - if (bs.curr < (int) bs.nbuttons - 1) { - bs.curr++; - draw_buttons(widget, h-2, w, bs, shortcut_buttons); - wrefresh(widget); - } + d.bs.curr--; + if (d.bs.curr < 0) + d.bs.curr = d.bs.nbuttons - 1; + DRAW_BUTTONS(d); break; - case KEY_CTRL('E'): /* add conf->menu.extrahelpkey ? */ case KEY_F(1): - if (conf->f1_file == NULL && conf->f1_message == NULL) + if (conf->key.f1_file == NULL && + conf->key.f1_message == NULL) break; - if (f1help(conf) != 0) - return BSDDIALOG_ERROR; - /* No break! the terminal size can change */ + if (f1help_dialog(conf) != 0) + return (BSDDIALOG_ERROR); + if (mixedlist_redraw(&d, &m) != 0) + return (BSDDIALOG_ERROR); + break; case KEY_RESIZE: - hide_widget(y, x, h, w,conf->shadow); - - /* - * Unnecessary, but, when the columns decrease the - * following "refresh" seem not work - */ - refresh(); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return BSDDIALOG_ERROR; - menurows = automenurows ? 0 : menurows; - menu_autosize(conf, rows, cols, &h, &w, text, pos.line, - &menurows, totnitems, bs); - if (menu_checksize(h, w, text, menurows, totnitems, bs) != 0) - return BSDDIALOG_ERROR; - if (set_widget_position(conf, &y, &x, h, w) != 0) - return BSDDIALOG_ERROR; - - wclear(shadow); - mvwin(shadow, y + t.shadow.h, x + t.shadow.w); - wresize(shadow, h, w); - - wclear(widget); - mvwin(widget, y, x); - wresize(widget, h, w); - - htextpad = 1; - wclear(textpad); - wresize(textpad, 1, w - HBORDERS - t.text.hmargin * 2); - - if(update_widget_withtextpad(conf, shadow, widget, h, w, - RAISED, textpad, &htextpad, text, true) != 0) - return BSDDIALOG_ERROR; - - draw_buttons(widget, h-2, w, bs, shortcut_buttons); - wrefresh(widget); - - prefresh(textpad, 0, 0, y + 1, x + 1 + t.text.hmargin, - y + h - menurows, x + 1 + w - t.text.hmargin); - - wclear(menuwin); - mvwin(menuwin, y + h - 5 - menurows, x + 2); - wresize(menuwin,menurows+2, w-4); - update_menuwin(conf, menuwin, menurows+2, w-4, totnitems, - menurows, ymenupad); - wrefresh(menuwin); - - ys = y + h - 5 - menurows + 1; - ye = y + h - 5 ; - if (conf->menu.align_left || (int)pos.line > w - 6) { - xs = x + 3; - xe = xs + w - 7; - } - else { /* center */ - xs = x + 3 + (w-6)/2 - pos.line/2; - xe = xs + w - 5; - } - - if ((int)(ymenupad + menurows) - 1 < abs) - ymenupad = abs - menurows + 1; - prefresh(menupad, ymenupad, 0, ys, xs, ye, xe); - - refresh(); - + if (mixedlist_redraw(&d, &m) != 0) + return (BSDDIALOG_ERROR); break; } - if (abs < 0) + if (m.sel < 0) continue; switch(input) { case KEY_HOME: + next = getnext(m.nitems, m.pritems, -1); + changeitem = next != m.sel; + break; case KEY_UP: + next = getprev(m.pritems, m.sel); + changeitem = next != m.sel; + break; case KEY_PPAGE: - if (abs == 0) /* useless, just to save cpu refresh */ - break; - drawitem(conf, menupad, abs, *item, currmode, pos, false); - if (input == KEY_HOME) - getfirst(ngroups, groups, &abs, &g, &rel); - else if (input == KEY_UP) - getprev(groups, &abs, &g, &rel); - else /* input == KEY_PPAGE*/ - getfastprev(menurows, groups, &abs, &g, &rel); - item = &groups[g].items[rel]; - currmode= getmode(mode, groups[g]); - drawitem(conf, menupad, abs, *item, currmode, pos, true); - if (ymenupad > abs && ymenupad > 0) - ymenupad = abs; - update_menuwin(conf, menuwin, menurows+2, w-4, totnitems, - menurows, ymenupad); - wrefresh(menuwin); - prefresh(menupad, ymenupad, 0, ys, xs, ye, xe); + next = getfastprev(m.menurows, m.pritems, m.sel); + changeitem = next != m.sel; break; case KEY_END: + next = getprev(m.pritems, m.nitems); + changeitem = next != m.sel; + break; case KEY_DOWN: + next = getnext(m.nitems, m.pritems, m.sel); + changeitem = next != m.sel; + break; case KEY_NPAGE: - if (abs == totnitems -1) - break; /* useless, just to save cpu refresh */ - drawitem(conf, menupad, abs, *item, currmode, pos, false); - if (input == KEY_END) - getlast(totnitems, ngroups, groups, &abs, &g, &rel); - else if (input == KEY_DOWN) - getnext(ngroups, groups, &abs, &g, &rel); - else /* input == KEY_NPAGE*/ - getfastnext(menurows, ngroups, groups, &abs, &g, &rel); - item = &groups[g].items[rel]; - currmode= getmode(mode, groups[g]); - drawitem(conf, menupad, abs, *item, currmode, pos, true); - if ((int)(ymenupad + menurows) <= abs) - ymenupad = abs - menurows + 1; - update_menuwin(conf, menuwin, menurows+2, w-4, totnitems, - menurows, ymenupad); - wrefresh(menuwin); - prefresh(menupad, ymenupad, 0, ys, xs, ye, xe); + next = getfastnext(m.menurows, m.nitems, m.pritems, m.sel); + changeitem = next != m.sel; break; case ' ': /* Space */ - if (currmode == MENUMODE) - break; - else if (currmode == CHECKLISTMODE) - item->on = !item->on; - else { /* RADIOLISTMODE */ - for (i=0; i < (int) groups[g].nitems; i++) - if (groups[g].items[i].on == true && i != rel) { - groups[g].items[i].on = false; - drawitem(conf, menupad, - abs - rel + i, groups[g].items[i], - currmode, pos, false); + if (m.pritems[m.sel].type == MENUMODE) { + retval = BUTTONVALUE(d.bs); + m.pritems[m.sel].on = true; + loop = false; + } else if (m.pritems[m.sel].type == CHECKLISTMODE) { + m.pritems[m.sel].on = !m.pritems[m.sel].on; + } else { /* RADIOLISTMODE */ + for (i = m.sel - m.pritems[m.sel].index; + i < m.nitems && + m.pritems[i].group == m.pritems[m.sel].group; + i++) { + if (i != m.sel && m.pritems[i].on) { + m.pritems[i].on = false; + drawitem(conf, &m, i, false); } - item->on = !item->on; + } + m.pritems[m.sel].on = !m.pritems[m.sel].on; } - drawitem(conf, menupad, abs, *item, currmode, pos, true); - prefresh(menupad, ymenupad, 0, ys, xs, ye, xe); + drawitem(conf, &m, m.sel, true); + pnoutrefresh(m.pad, m.ypad, 0, m.ys, m.xs, m.ye, m.xe); + break; default: - if (shortcut_buttons) { - for (i = 0; i < (int) bs.nbuttons; i++) - if (tolower(input) == tolower((bs.label[i])[0])) { - output = bs.value[i]; - if (currmode == MENUMODE) - item->on = true; - loop = false; - } + if (conf->menu.shortcut_buttons) { + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); + if (m.pritems[m.sel].type == MENUMODE) + m.pritems[m.sel].on = true; + loop = false; + } break; } - drawitem(conf, menupad, abs, *item, currmode, pos, false); - getnextshortcut(conf, currmode, ngroups, groups, &abs, - &g, &rel, input); - item = &groups[g].items[rel]; - currmode = getmode(mode, groups[g]); - drawitem(conf, menupad, abs, *item, currmode, pos, true); - if (ymenupad > abs && ymenupad > 0) - ymenupad = abs; - if ((int)(ymenupad + menurows) <= abs) - ymenupad = abs - menurows + 1; - update_menuwin(conf, menuwin, menurows+2, w-4, totnitems, - menurows, ymenupad); - wrefresh(menuwin); - prefresh(menupad, ymenupad, 0, ys, xs, ye, xe); + /* shourtcut items */ + next = getnextshortcut(m.nitems, m.pritems, m.sel, + input); + changeitem = next != m.sel; + } /* end switch get_wch() */ + + if (changeitem) { + drawitem(conf, &m, m.sel, false); + m.sel = next; + drawitem(conf, &m, m.sel, true); + if (m.ypad > m.sel && m.ypad > 0) + m.ypad = m.sel; + if ((int)(m.ypad + m.menurows) <= m.sel) + m.ypad = m.sel - m.menurows + 1; + update_menubox(conf, &m); + wnoutrefresh(m.box); + pnoutrefresh(m.pad, m.ypad, 0, m.ys, m.xs, m.ye, m.xe); + changeitem = false; } - } + } /* end while(loop) */ + + set_return_on(&m, groups); if (focuslist != NULL) - *focuslist = g; + *focuslist = m.sel < 0 ? -1 : m.pritems[m.sel].group; if (focusitem !=NULL) - *focusitem = rel; + *focusitem = m.sel < 0 ? -1 : m.pritems[m.sel].index; - delwin(menupad); - delwin(menuwin); - end_widget_withtextpad(conf, widget, h, w, textpad, shadow); + if (m.hasbottomdesc && conf->clear) { + move(SCREENLINES - 1, 2); + clrtoeol(); + } + delwin(m.pad); + delwin(m.box); + end_dialog(&d); + free(m.pritems); - return output; + return (retval); } -/* - * API - */ - -int bsddialog_mixedlist(struct bsddialog_conf *conf, char* text, int rows, int cols, - unsigned int menurows, int ngroups, struct bsddialog_menugroup *groups, - int *focuslist, int *focusitem) +/* API */ +int +bsddialog_mixedlist(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int menurows, unsigned int ngroups, + struct bsddialog_menugroup *groups, int *focuslist, int *focusitem) { - int output; + int retval; - output = do_mixedlist(conf, text, rows, cols, menurows, MIXEDLISTMODE, + retval = do_mixedlist(conf, text, rows, cols, menurows, MIXEDLISTMODE, ngroups, groups, focuslist, focusitem); - return output; + return (retval); } int -bsddialog_checklist(struct bsddialog_conf *conf, char* text, int rows, int cols, - unsigned int menurows, int nitems, struct bsddialog_menuitem *items, - int *focusitem) +bsddialog_checklist(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int menurows, unsigned int nitems, + struct bsddialog_menuitem *items, int *focusitem) { - int output; + int retval, focuslist = 0; struct bsddialog_menugroup group = { - BSDDIALOG_CHECKLIST /* unused */, nitems, items}; + BSDDIALOG_CHECKLIST /* unused */, nitems, items, 0}; - output = do_mixedlist(conf, text, rows, cols, menurows, CHECKLISTMODE, - 1, &group, NULL, focusitem); + CHECK_ARRAY(nitems, items); /* efficiency, avoid do_mixedlist() */ + retval = do_mixedlist(conf, text, rows, cols, menurows, CHECKLISTMODE, + 1, &group, &focuslist, focusitem); - return output; + return (retval); } int -bsddialog_menu(struct bsddialog_conf *conf, char* text, int rows, int cols, - unsigned int menurows, int nitems, struct bsddialog_menuitem *items, - int *focusitem) +bsddialog_menu(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int menurows, unsigned int nitems, + struct bsddialog_menuitem *items, int *focusitem) { - int output; + int retval, focuslist = 0; struct bsddialog_menugroup group = { - BSDDIALOG_CHECKLIST /* unused */, nitems, items}; + BSDDIALOG_CHECKLIST /* unused */, nitems, items, 0}; - output = do_mixedlist(conf, text, rows, cols, menurows, MENUMODE, 1, - &group, NULL, focusitem); + CHECK_ARRAY(nitems, items); /* efficiency, avoid do_mixedlist() */ + retval = do_mixedlist(conf, text, rows, cols, menurows, MENUMODE, 1, + &group, &focuslist, focusitem); - return output; + return (retval); } int -bsddialog_radiolist(struct bsddialog_conf *conf, char* text, int rows, int cols, - unsigned int menurows, int nitems, struct bsddialog_menuitem *items, - int *focusitem) +bsddialog_radiolist(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int menurows, unsigned int nitems, + struct bsddialog_menuitem *items, int *focusitem) { - int output; + int retval, focuslist = 0; struct bsddialog_menugroup group = { - BSDDIALOG_RADIOLIST /* unused */, nitems, items}; - - output = do_mixedlist(conf, text, rows, cols, menurows, RADIOLISTMODE, - 1, &group, NULL, focusitem); - - return output; -} - -/* todo */ -static int buildlist_autosize(int rows, int cols) -{ - - if (cols == BSDDIALOG_AUTOSIZE) - RETURN_ERROR("Unimplemented cols autosize for buildlist"); - - if (rows == BSDDIALOG_AUTOSIZE) - RETURN_ERROR("Unimplemented rows autosize for buildlist"); - - return 0; -} - -/* to improve */ -static int -buildlist_checksize(int rows, int cols, char *text, int menurows, int nitems, - struct buttons bs) -{ - int mincols, textrow, menusize; - - mincols = VBORDERS; - /* buttons */ - mincols += bs.nbuttons * bs.sizebutton; - mincols += bs.nbuttons > 0 ? (bs.nbuttons-1) * t.button.space : 0; - /* line, comment to permet some cols hidden */ - /* mincols = MAX(mincols, linelen); */ - - if (cols < mincols) - RETURN_ERROR("Few cols, width < size buttons or "\ - "name+descripion of the items"); - - textrow = text != NULL && strlen(text) > 0 ? 1 : 0; - - if (nitems > 0 && menurows == 0) - RETURN_ERROR("items > 0 but menurows == 0, probably terminal "\ - "too small"); - - menusize = nitems > 0 ? 3 : 0; - if (rows < 2 + 2 + menusize + textrow) - RETURN_ERROR("Few lines for this menus"); - - return 0; -} - -int -bsddialog_buildlist(struct bsddialog_conf *conf, char* text, int rows, int cols, - unsigned int menurows, int nitems, struct bsddialog_menuitem *items, - int *focusitem) -{ - WINDOW *widget, *textpad, *leftwin, *leftpad, *rightwin, *rightpad, *shadow; - int output, i, x, y, h, w, htextpad, input; - bool loop, buttupdate, padsupdate, startleft; - int nlefts, nrights, leftwinx, rightwinx, winsy, padscols, curr; - enum side {LEFT, RIGHT} currV; - int currH; - struct buttons bs; - struct lineposition pos = {0,0,0,0,0,0,0,0,0,0}; - - startleft = false; - for (i=0; i<nitems; i++) { - pos.line = MAX(pos.line, strlen(items[i].desc)); - if (items[i].on == false) - startleft = true; - } - - get_buttons(conf, &bs, BUTTONLABEL(ok_label), BUTTONLABEL(extra_label), - BUTTONLABEL(cancel_label), BUTTONLABEL(help_label)); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return BSDDIALOG_ERROR; - if (buildlist_autosize(rows, cols) != 0) - return BSDDIALOG_ERROR; - if (buildlist_checksize(h, w, text, menurows, nitems, bs) != 0) - return BSDDIALOG_ERROR; - if (set_widget_position(conf, &y, &x, h, w) != 0) - return BSDDIALOG_ERROR; - - if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED, - &textpad, &htextpad, text, true) != 0) - return BSDDIALOG_ERROR; - - prefresh(textpad, 0, 0, y + 1, x + 1 + t.text.hmargin, - y + h - menurows, x + 1 + w - t.text.hmargin); - - winsy = y + h - 5 - menurows; - leftwinx = x+2; - leftwin = new_boxed_window(conf, winsy, leftwinx, menurows+2, (w-5)/2, - LOWERED); - rightwinx = x + w - 2 -(w-5)/2; - rightwin = new_boxed_window(conf, winsy, rightwinx, menurows+2, - (w-5)/2, LOWERED); - - wrefresh(leftwin); - wrefresh(rightwin); - - padscols = (w-5)/2 - 2; - leftpad = newpad(nitems, pos.line); - rightpad = newpad(nitems, pos.line); - wbkgd(leftpad, t.dialog.color); - wbkgd(rightpad, t.dialog.color); - - currH = 0; - currV = startleft ? LEFT : RIGHT; - loop = buttupdate = padsupdate = true; - while(loop) { - if (buttupdate) { - draw_buttons(widget, h-2, w, bs, true); - wrefresh(widget); - buttupdate = false; - } - - if (padsupdate) { - werase(leftpad); - werase(rightpad); - curr = -1; - nlefts = nrights = 0; - for (i=0; i<nitems; i++) { - if (items[i].on == false) { - if (currV == LEFT && currH == nlefts) - curr = i; - drawitem(conf, leftpad, nlefts, items[i], - BUILDLISTMODE, pos, curr == i); - nlefts++; - } else { - if (currV == RIGHT && currH == nrights) - curr = i; - drawitem(conf, rightpad, nrights, items[i], - BUILDLISTMODE, pos, curr == i); - nrights++; - } - } - prefresh(leftpad, 0, 0, winsy+1, leftwinx+1, - winsy+1+menurows, leftwinx + 1 + padscols); - prefresh(rightpad, 0, 0, winsy+1, rightwinx+1, - winsy+1+menurows, rightwinx + 1 + padscols); - padsupdate = false; - } - - input = getch(); - switch(input) { - case KEY_ENTER: - case 10: /* Enter */ - output = bs.value[bs.curr]; - loop = false; - break; - case 27: /* Esc */ - output = BSDDIALOG_ERROR; - loop = false; - break; - case '\t': /* TAB */ - bs.curr = (bs.curr + 1) % bs.nbuttons; - buttupdate = true; - break; - } - - if (nitems <= 0) - continue; - - switch(input) { - case KEY_LEFT: - if (currV == RIGHT && nrights > 0) { - currV = LEFT; - currH = 0; - padsupdate = true; - } - break; - case KEY_RIGHT: - if (currV == LEFT && nrights > 0) { - currV = RIGHT; - currH = 0; - padsupdate = true; - } - break; - case KEY_UP: - currH = (currH > 0) ? currH - 1 : 0; - padsupdate = true; - break; - case KEY_DOWN: - if (currV == LEFT) - currH = (currH < nlefts-1) ? currH +1 : currH; - else - currH = (currH < nrights-1)? currH +1 : currH; - padsupdate = true; - break; - case ' ': /* Space */ - items[curr].on = ! items[curr].on; - if (currV == LEFT) { - if (nlefts > 1) - currH = currH > 0 ? currH-1 : 0; - else { - currH = 0; - currV = RIGHT; - } - } else { - if (nrights > 1) - currH = currH > 0 ? currH-1 : 0; - else { - currH = 0; - currV = LEFT; - } - } - padsupdate = true; - break; - default: - - break; - } - } - - if(focusitem != NULL) - *focusitem = curr; + BSDDIALOG_RADIOLIST /* unused */, nitems, items, 0}; - delwin(leftpad); - delwin(leftwin); - delwin(rightpad); - delwin(rightwin); - end_widget_withtextpad(conf, widget, h, w, textpad, shadow); + CHECK_ARRAY(nitems, items); /* efficiency, avoid do_mixedlist() */ + retval = do_mixedlist(conf, text, rows, cols, menurows, RADIOLISTMODE, + 1, &group, &focuslist, focusitem); - return output; + return (retval); } diff --git a/lib/messagebox.c b/lib/messagebox.c index e2b91e1bdc5c..fc18ff8a61d2 100644 --- a/lib/messagebox.c +++ b/lib/messagebox.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021 Alfonso Sabato Siciliano + * Copyright (c) 2021-2023 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,261 +25,202 @@ * SUCH DAMAGE. */ -#include <sys/param.h> - -#include <ctype.h> -#include <string.h> - -#ifdef PORTNCURSES -#include <ncurses/ncurses.h> -#else -#include <ncurses.h> -#endif +#include <curses.h> #include "bsddialog.h" -#include "lib_util.h" #include "bsddialog_theme.h" +#include "lib_util.h" -/* "Message": msgbox - yesno */ - -#define AUTO_WIDTH (COLS / 3U) -/* - * Min height = 5: 2 up & down borders + 2 label & up border buttons + 1 line - * for text, at least 1 line is important for widget_withtextpad_init() to avoid - * "Cannot build the pad window for text". - */ -#define MIN_HEIGHT 5 - -extern struct bsddialog_theme t; +struct scroll { + int ypad; /* y scrollable pad */ + int htext; /* real h text to draw, to use with htextpad */ + int htextpad; /* h textpad, draw_dialog() set at least 1 */ + int printrows; /* h - BORDER - HBUTTONS - BORDER */ +}; -static int -message_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w, - char *text, struct buttons bs) +static void textupdate(struct dialog *d, struct scroll *s) { - int maxword, maxline, nlines, line; - - if (get_text_properties(conf, text, &maxword, &maxline, &nlines) != 0) - return BSDDIALOG_ERROR; - - if (cols == BSDDIALOG_AUTOSIZE) { - *w = VBORDERS; - /* buttons size */ - *w += bs.nbuttons * bs.sizebutton; - *w += bs.nbuttons > 0 ? (bs.nbuttons-1) * t.button.space : 0; - /* text size */ - line = MIN(maxline + VBORDERS + t.text.hmargin * 2, AUTO_WIDTH); - line = MAX(line, (int) (maxword + VBORDERS + t.text.hmargin * 2)); - *w = MAX(*w, line); - /* conf.auto_minwidth */ - *w = MAX(*w, (int)conf->auto_minwidth); - /* avoid terminal overflow */ - *w = MIN(*w, widget_max_width(conf)); - } - - if (rows == BSDDIALOG_AUTOSIZE) { - *h = MIN_HEIGHT - 1; - if (maxword > 0) - *h += MAX(nlines, (int)(*w / GET_ASPECT_RATIO(conf))); - *h = MAX(*h, MIN_HEIGHT); - /* conf.auto_minheight */ - *h = MAX(*h, (int)conf->auto_minheight); - /* avoid terminal overflow */ - *h = MIN(*h, widget_max_height(conf)); + if (s->htext > 0 && s->htextpad > s->printrows) { + wattron(d->widget, t.dialog.arrowcolor); + mvwprintw(d->widget, d->h - HBUTTONS - BORDER, + d->w - 4 - TEXTHMARGIN - BORDER, + "%3d%%", 100 * (s->ypad + s->printrows) / s->htextpad); + wattroff(d->widget, t.dialog.arrowcolor); + wnoutrefresh(d->widget); } - - return 0; -} - -static int message_checksize(int rows, int cols, struct buttons bs) -{ - int mincols; - - mincols = VBORDERS; - mincols += bs.nbuttons * bs.sizebutton; - mincols += bs.nbuttons > 0 ? (bs.nbuttons-1) * t.button.space : 0; - - if (cols < mincols) - RETURN_ERROR("Few cols, Msgbox and Yesno need at least width "\ - "for borders, buttons and spaces between buttons"); - - if (rows < MIN_HEIGHT) - RETURN_ERROR("Msgbox and Yesno need at least height 5"); - - return 0; + rtextpad(d, s->ypad, 0, 0, HBUTTONS); } -static void -buttonsupdate(WINDOW *widget, int h, int w, struct buttons bs, bool shortkey) +static int message_size_position(struct dialog *d, int *htext) { - draw_buttons(widget, h-2, w, bs, shortkey); - wnoutrefresh(widget); + int minw; + + if (set_widget_size(d->conf, d->rows, d->cols, &d->h, &d->w) != 0) + return (BSDDIALOG_ERROR); + if (set_widget_autosize(d->conf, d->rows, d->cols, &d->h, &d->w, + d->text, (*htext < 0) ? htext : NULL, &d->bs, 0, 0) != 0) + return (BSDDIALOG_ERROR); + minw = (*htext > 0) ? 1 + TEXTHMARGINS : 0 ; + if (widget_checksize(d->h, d->w, &d->bs, MIN(*htext, 1), minw) != 0) + return (BSDDIALOG_ERROR); + if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0) + return (BSDDIALOG_ERROR); + + return (0); } -static void -textupdate(WINDOW *widget, int y, int x, int h, int w, WINDOW *textpad, - int htextpad, int textrow) +static int message_draw(struct dialog *d, struct scroll *s) { + int unused; - if (htextpad > h - 4) { - mvwprintw(widget, h-3, w-6, "%3d%%", - 100 * (textrow+h-4)/ htextpad); - wnoutrefresh(widget); + if (d->built) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ } - - pnoutrefresh(textpad, textrow, 0, y+1, x+2, y+h-4, x+w-2); + if (message_size_position(d, &s->htext) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) + return (BSDDIALOG_ERROR); + if (d->built) + refresh(); /* Important to fix grey lines expanding screen */ + + s->printrows = d->h - BORDER - HBUTTONS - BORDER; + s->ypad = 0; + getmaxyx(d->textpad, s->htextpad, unused); + unused++; /* fix unused error */ + + return (0); } static int -do_widget(struct bsddialog_conf *conf, char *text, int rows, int cols, - struct buttons bs, bool shortkey) +do_message(struct bsddialog_conf *conf, const char *text, int rows, int cols, + const char *oklabel, const char *cancellabel) { - WINDOW *widget, *textpad, *shadow; bool loop; - int i, y, x, h, w, input, output, htextpad, textrow; - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return BSDDIALOG_ERROR; - if (message_autosize(conf, rows, cols, &h, &w, text, bs) != 0) - return BSDDIALOG_ERROR; - if (message_checksize(h, w, bs) != 0) - return BSDDIALOG_ERROR; - if (set_widget_position(conf, &y, &x, h, w) != 0) - return BSDDIALOG_ERROR; - - if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED, - &textpad, &htextpad, text, true) != 0) - return BSDDIALOG_ERROR; + int retval; + wint_t input; + struct scroll s; + struct dialog d; + + if (prepare_dialog(conf, text, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + set_buttons(&d, true, oklabel, cancellabel); + s.htext = -1; + if(message_draw(&d, &s) != 0) + return (BSDDIALOG_ERROR); - textrow = 0; loop = true; - buttonsupdate(widget, h, w, bs, shortkey); - textupdate(widget, y, x, h, w, textpad, htextpad, textrow); - while(loop) { + while (loop) { + textupdate(&d, &s); doupdate(); - input = getch(); + if (get_wch(&input) == ERR) + continue; switch (input) { + case KEY_ENTER: case 10: /* Enter */ - output = bs.value[bs.curr]; + retval = BUTTONVALUE(d.bs); loop = false; break; case 27: /* Esc */ - output = BSDDIALOG_ESC; - loop = false; + if (d.conf->key.enable_esc) { + retval = BSDDIALOG_ESC; + loop = false; + } break; case '\t': /* TAB */ - bs.curr = (bs.curr + 1) % bs.nbuttons; - buttonsupdate(widget, h, w, bs, shortkey); + case KEY_RIGHT: + d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons; + DRAW_BUTTONS(d); break; - case KEY_F(1): - if (conf->f1_file == NULL && conf->f1_message == NULL) - break; - if (f1help(conf) != 0) - return BSDDIALOG_ERROR; - /* No break! the terminal size can change */ - case KEY_RESIZE: - hide_widget(y, x, h, w,conf->shadow); - - /* - * Unnecessary, but, when the columns decrease the - * following "refresh" seem not work - */ - refresh(); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return BSDDIALOG_ERROR; - if (message_autosize(conf, rows, cols, &h, &w, text, bs) != 0) - return BSDDIALOG_ERROR; - if (message_checksize(h, w, bs) != 0) - return BSDDIALOG_ERROR; - if (set_widget_position(conf, &y, &x, h, w) != 0) - return BSDDIALOG_ERROR; - - wclear(shadow); - mvwin(shadow, y + t.shadow.h, x + t.shadow.w); - wresize(shadow, h, w); - - wclear(widget); - mvwin(widget, y, x); - wresize(widget, h, w); - - htextpad = 1; - wclear(textpad); - wresize(textpad, 1, w - HBORDERS - t.text.hmargin * 2); - - if(update_widget_withtextpad(conf, shadow, widget, h, w, - RAISED, textpad, &htextpad, text, true) != 0) - return BSDDIALOG_ERROR; - - buttonsupdate(widget, h, w, bs, shortkey); - textupdate(widget, y, x, h, w, textpad, htextpad, textrow); - - /* Important to fix grey lines expanding screen */ - refresh(); + case KEY_LEFT: + d.bs.curr--; + if (d.bs.curr < 0) + d.bs.curr = d.bs.nbuttons - 1; + DRAW_BUTTONS(d); break; case KEY_UP: - if (textrow == 0) - break; - textrow--; - textupdate(widget, y, x, h, w, textpad, htextpad, textrow); + if (s.ypad > 0) + s.ypad--; break; case KEY_DOWN: - if (textrow + h - 4 >= htextpad) - break; - textrow++; - textupdate(widget, y, x, h, w, textpad, htextpad, textrow); + if (s.ypad + s.printrows < s.htextpad) + s.ypad++; break; - case KEY_LEFT: - if (bs.curr > 0) { - bs.curr--; - buttonsupdate(widget, h, w, bs, shortkey); - } + case KEY_HOME: + s.ypad = 0; break; - case KEY_RIGHT: - if (bs.curr < (int) bs.nbuttons - 1) { - bs.curr++; - buttonsupdate(widget, h, w, bs, shortkey); - } + case KEY_END: + s.ypad = MAX(s.htextpad - s.printrows, 0); break; - default: - if (shortkey == false) + case KEY_PPAGE: + s.ypad = MAX(s.ypad - s.printrows, 0); + break; + case KEY_NPAGE: + s.ypad += s.printrows; + if (s.ypad + s.printrows > s.htextpad) + s.ypad = s.htextpad - s.printrows; + break; + case KEY_F(1): + if (d.conf->key.f1_file == NULL && + d.conf->key.f1_message == NULL) break; - - for (i = 0; i < (int) bs.nbuttons; i++) - if (tolower(input) == tolower((bs.label[i])[0])) { - output = bs.value[i]; - loop = false; + if (f1help_dialog(d.conf) != 0) + return (BSDDIALOG_ERROR); + if(message_draw(&d, &s) != 0) + return (BSDDIALOG_ERROR); + break; + case KEY_RESIZE: + if(message_draw(&d, &s) != 0) + return (BSDDIALOG_ERROR); + break; + default: + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); + loop = false; } } } - end_widget_withtextpad(conf, widget, h, w, textpad, shadow); + end_dialog(&d); - return output; + return (retval); } /* API */ - int -bsddialog_msgbox(struct bsddialog_conf *conf, char* text, int rows, int cols) +bsddialog_msgbox(struct bsddialog_conf *conf, const char *text, int rows, + int cols) { - struct buttons bs; - - get_buttons(conf, &bs, BUTTONLABEL(ok_label), BUTTONLABEL(extra_label), - NULL /* nocancel */, BUTTONLABEL(help_label)); - - return (do_widget(conf, text, rows, cols, bs, true)); + return (do_message(conf, text, rows, cols, OK_LABEL, NULL)); } int -bsddialog_yesno(struct bsddialog_conf *conf, char* text, int rows, int cols) +bsddialog_yesno(struct bsddialog_conf *conf, const char *text, int rows, + int cols) { - struct buttons bs; - - get_buttons(conf, &bs, - conf->button.ok_label == NULL ? "Yes" : conf->button.ok_label, - BUTTONLABEL(extra_label), - conf->button.cancel_label == NULL ? "No" : conf->button.cancel_label, - BUTTONLABEL(help_label)); + return (do_message(conf, text, rows, cols, "Yes", "No")); +} - return (do_widget(conf, text, rows, cols, bs, true)); +int +bsddialog_infobox(struct bsddialog_conf *conf, const char *text, int rows, + int cols) +{ + int htext; + struct dialog d; + + if (prepare_dialog(conf, text, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + htext = -1; + if (message_size_position(&d, &htext) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(&d) != 0) + return (BSDDIALOG_ERROR); + TEXTPAD(&d, 0); + doupdate(); + + end_dialog(&d); + + return (BSDDIALOG_OK); } diff --git a/lib/textbox.c b/lib/textbox.c index 22dd21470c3a..edd58d0a820e 100644 --- a/lib/textbox.c +++ b/lib/textbox.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021 Alfonso Sabato Siciliano + * Copyright (c) 2021-2023 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,225 +25,246 @@ * SUCH DAMAGE. */ - -#include <sys/param.h> - -#include <string.h> - -#ifdef PORTNCURSES -#include <ncurses/ncurses.h> -#else -#include <ncurses.h> -#endif +#include <curses.h> #include "bsddialog.h" -#include "lib_util.h" #include "bsddialog_theme.h" +#include "lib_util.h" -/* "Text": textbox */ - -#define BUTTON_TEXTBOX "EXIT" - -extern struct bsddialog_theme t; - -static void -textbox_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w, - int hpad, int wpad) +struct scrolltext { + WINDOW *pad; + int ypad; + int xpad; + int ys; + int ye; + int xs; + int xe; + int hpad; + int wpad; + int margin; /* 2 with multicolumn char, 0 otherwise */ + int printrows; /* d.h - BORDERS - HBUTTONS */ +}; + +static void updateborders(struct dialog *d, struct scrolltext *st) { - - if (cols == BSDDIALOG_AUTOSIZE) { - *w = VBORDERS; - /* buttons size */ - *w += strlen(BUTTON_TEXTBOX) + 2 /* text delims*/; - /* text size */ - *w = MAX(*w, wpad + VBORDERS); - /* conf.auto_minwidth */ - *w = MAX(*w, (int)conf->auto_minwidth); - /* avoid terminal overflow */ - *w = MIN(*w, widget_max_width(conf)-1); /* again -1, fix util.c */ + chtype arrowch, borderch; + + if (d->conf->no_lines) + borderch = ' '; + else if (d->conf->ascii_lines) + borderch = '|'; + else + borderch = ACS_VLINE; + + if (st->xpad > 0) { + arrowch = d->conf->ascii_lines ? '<' : ACS_LARROW; + arrowch |= t.dialog.arrowcolor; + } else { + arrowch = borderch; + arrowch |= t.dialog.lineraisecolor; } + mvwvline(d->widget, (d->h / 2) - 2, 0, arrowch, 4); + + if (st->xpad + d->w - 2 - st->margin < st->wpad) { + arrowch = d->conf->ascii_lines ? '>' : ACS_RARROW; + arrowch |= t.dialog.arrowcolor; + } else { + arrowch = borderch; + arrowch |= t.dialog.linelowercolor; + } + mvwvline(d->widget, (d->h / 2) - 2, d->w - 1, arrowch, 4); - if (rows == BSDDIALOG_AUTOSIZE) { - *h = hpad + 4; /* HBORDERS + button border */ - /* conf.auto_minheight */ - *h = MAX(*h, (int)conf->auto_minheight); - /* avoid terminal overflow */ - *h = MIN(*h, widget_max_height(conf)); + if (st->hpad > d->h - 4) { + wattron(d->widget, t.dialog.arrowcolor); + mvwprintw(d->widget, d->h - 3, d->w - 6, + "%3d%%", 100 * (st->ypad + d->h - 4) / st->hpad); + wattroff(d->widget, t.dialog.arrowcolor); } } -static int textbox_checksize(int rows, int cols, int hpad) +static int textbox_size_position(struct dialog *d, struct scrolltext *st) { - int mincols; - - mincols = VBORDERS + strlen(BUTTON_TEXTBOX) + 2 /* text delims */; - - if (cols < mincols) - RETURN_ERROR("Few cols for the textbox"); - - if (rows < 4 /* HBORDERS + button*/ + (hpad > 0 ? 1 : 0)) - RETURN_ERROR("Few rows for the textbox"); + int minw; + + if (set_widget_size(d->conf, d->rows, d->cols, &d->h, &d->w) != 0) + return (BSDDIALOG_ERROR); + if (set_widget_autosize(d->conf, d->rows, d->cols, &d->h, &d->w, + d->text, NULL, &d->bs, st->hpad, st->wpad + st->margin) != 0) + return (BSDDIALOG_ERROR); + minw = (st->wpad > 0) ? 2 /*multicolumn char*/ + st->margin : 0 ; + if (widget_checksize(d->h, d->w, &d->bs, MIN(st->hpad, 1), minw) != 0) + return (BSDDIALOG_ERROR); + if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0) + return (BSDDIALOG_ERROR); + + return (0); +} - return 0; +static int textbox_draw(struct dialog *d, struct scrolltext *st) +{ + if (d->built) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ + } + if (textbox_size_position(d, st) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) + return (BSDDIALOG_ERROR); + if (d->built) + refresh(); /* Important to fix grey lines expanding screen */ + + st->ys = d->y + 1; + st->xs = (st->margin == 0) ? d->x + 1 : d->x + 2; + st->ye = st->ys + d->h - 5; + st->xe = st->xs + d->w - 3 - st->margin; + st->ypad = st->xpad = 0; + st->printrows = d->h-4; + + return (0); } +/* API */ int -bsddialog_textbox(struct bsddialog_conf *conf, char* file, int rows, int cols) +bsddialog_textbox(struct bsddialog_conf *conf, const char *file, int rows, + int cols) { - WINDOW *widget, *pad, *shadow; - int i, input, y, x, h, w, hpad, wpad, ypad, xpad, ys, ye, xs, xe, printrows; - char buf[BUFSIZ], *exitbutt; + bool loop, has_multicol_ch; + int i, retval; + unsigned int defaulttablen, linecols; + wint_t input; + char buf[BUFSIZ]; FILE *fp; - bool loop; - int output; + struct scrolltext st; + struct dialog d; + if (file == NULL) + RETURN_ERROR("*file is NULL"); if ((fp = fopen(file, "r")) == NULL) - RETURN_ERROR("Cannot open file"); - - hpad = 1; - wpad = 1; - pad = newpad(hpad, wpad); - wbkgd(pad, t.dialog.color); + RETURN_FMTERROR("Cannot open file \"%s\"", file); + + if (prepare_dialog(conf, "" /* fake */, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + set_buttons(&d, true, "EXIT", NULL); + + defaulttablen = TABSIZE; + if (conf->text.tablen > 0) + set_tabsize(conf->text.tablen); + st.hpad = 1; + st.wpad = 1; + st.pad = newpad(st.hpad, st.wpad); + wbkgd(st.pad, t.dialog.color); + st.margin = 0; i = 0; - while(fgets(buf, BUFSIZ, fp) != NULL) { - if ((int) strlen(buf) > wpad) { - wpad = strlen(buf); - wresize(pad, hpad, wpad); + while (fgets(buf, BUFSIZ, fp) != NULL) { + if (str_props(buf, &linecols, &has_multicol_ch) != 0) + continue; + if ((int)linecols > st.wpad) { + st.wpad = linecols; + wresize(st.pad, st.hpad, st.wpad); } - if (i > hpad-1) { - hpad++; - wresize(pad, hpad, wpad); + if (i > st.hpad-1) { + st.hpad++; + wresize(st.pad, st.hpad, st.wpad); } - mvwaddstr(pad, i, 0, buf); + mvwaddstr(st.pad, i, 0, buf); i++; + if (has_multicol_ch) + st.margin = 2; } fclose(fp); + set_tabsize(defaulttablen); /* reset because it is curses global */ + + if (textbox_draw(&d, &st) != 0) + return (BSDDIALOG_ERROR); - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return BSDDIALOG_ERROR; - textbox_autosize(conf, rows, cols, &h, &w, hpad, wpad); - if (textbox_checksize(h, w, hpad) != 0) - return BSDDIALOG_ERROR; - if (set_widget_position(conf, &y, &x, h, w) != 0) - return BSDDIALOG_ERROR; - - if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED, - NULL, NULL, NULL, true) != 0) - return BSDDIALOG_ERROR; - - exitbutt = conf->button.exit_label == NULL ? BUTTON_TEXTBOX : conf->button.exit_label; - draw_button(widget, h-2, (w-2)/2 - strlen(exitbutt)/2, strlen(exitbutt)+2, - exitbutt, true, false); - - wrefresh(widget); - - ys = y + 1; - xs = x + 1; - ye = ys + h - 5; - xe = xs + w - 3; - ypad = xpad = 0; - printrows = h-4; loop = true; - while(loop) { - prefresh(pad, ypad, xpad, ys, xs, ye, xe); - input = getch(); + while (loop) { + updateborders(&d, &st); + /* + * Overflow multicolumn charchter right border: + * wnoutrefresh(widget); + * pnoutrefresh(pad, ypad, xpad, ys, xs, ye, xe); + * doupdate(); + */ + wrefresh(d.widget); + prefresh(st.pad, st.ypad, st.xpad, st.ys, st.xs, st.ye, st.xe); + if (get_wch(&input) == ERR) + continue; + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); + break; /* loop */ + } switch(input) { case KEY_ENTER: case 10: /* Enter */ - output = BSDDIALOG_OK; + retval = BSDDIALOG_OK; loop = false; break; case 27: /* Esc */ - output = BSDDIALOG_ESC; - loop = false; + if (conf->key.enable_esc) { + retval = BSDDIALOG_ESC; + loop = false; + } + break; + case '\t': /* TAB */ + d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons; + DRAW_BUTTONS(d); break; case KEY_HOME: - ypad = 0; + st.ypad = 0; break; case KEY_END: - ypad = hpad - printrows; - ypad = ypad < 0 ? 0 : ypad; + st.ypad = MAX(st.hpad - st.printrows, 0); break; case KEY_PPAGE: - ypad -= printrows; - ypad = ypad < 0 ? 0 : ypad; + st.ypad = MAX(st.ypad - st.printrows, 0); break; case KEY_NPAGE: - ypad += printrows; - ypad = ypad + printrows > hpad ? hpad - printrows : ypad; + st.ypad += st.printrows; + if (st.ypad + st.printrows > st.hpad) + st.ypad = st.hpad - st.printrows; break; case '0': - xpad = 0; + st.xpad = 0; + break; case KEY_LEFT: case 'h': - xpad = xpad > 0 ? xpad - 1 : 0; + st.xpad = MAX(st.xpad - 1, 0); break; case KEY_RIGHT: case 'l': - xpad = (xpad + w-2) < wpad-1 ? xpad + 1 : xpad; + if (st.xpad + d.w - 2 - st.margin < st.wpad) + st.xpad++; break; case KEY_UP: case 'k': - ypad = ypad > 0 ? ypad - 1 : 0; + st.ypad = MAX(st.ypad - 1, 0); break; case KEY_DOWN: case'j': - ypad = ypad + printrows <= hpad -1 ? ypad + 1 : ypad; + if (st.ypad + st.printrows <= st.hpad -1) + st.ypad++; break; case KEY_F(1): - if (conf->f1_file == NULL && conf->f1_message == NULL) + if (conf->key.f1_file == NULL && + conf->key.f1_message == NULL) break; - if (f1help(conf) != 0) - return BSDDIALOG_ERROR; - /* No break! the terminal size can change */ + if (f1help_dialog(conf) != 0) + return (BSDDIALOG_ERROR); + if (textbox_draw(&d, &st) != 0) + return (BSDDIALOG_ERROR); + break; case KEY_RESIZE: - hide_widget(y, x, h, w,conf->shadow); - - /* - * Unnecessary, but, when the columns decrease the - * following "refresh" seem not work - */ - refresh(); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return BSDDIALOG_ERROR; - textbox_autosize(conf, rows, cols, &h, &w, hpad, wpad); - if (textbox_checksize(h, w, hpad) != 0) - return BSDDIALOG_ERROR; - if (set_widget_position(conf, &y, &x, h, w) != 0) - return BSDDIALOG_ERROR; - - wclear(shadow); - mvwin(shadow, y + t.shadow.h, x + t.shadow.w); - wresize(shadow, h, w); - - wclear(widget); - mvwin(widget, y, x); - wresize(widget, h, w); - - ys = y + 1; - xs = x + 1; - ye = ys + h - 5; - xe = xs + w - 3; - ypad = xpad = 0; - printrows = h - 4; - - if(update_widget_withtextpad(conf, shadow, widget, h, w, - RAISED, NULL, NULL, NULL, true) != 0) - return BSDDIALOG_ERROR; - - draw_button(widget, h-2, (w-2)/2 - strlen(exitbutt)/2, - strlen(exitbutt)+2, exitbutt, true, false); - - wrefresh(widget); /* for button */ - - /* Important to fix grey lines expanding screen */ - refresh(); + if (textbox_draw(&d, &st) != 0) + return (BSDDIALOG_ERROR); break; } } - end_widget_withtextpad(conf, widget, h, w, pad, shadow); + delwin(st.pad); + end_dialog(&d); - return output; + return (retval); } diff --git a/lib/theme.c b/lib/theme.c index 3bcd8ee8fc53..04f85b2455fa 100644 --- a/lib/theme.c +++ b/lib/theme.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021 Alfonso Sabato Siciliano + * Copyright (c) 2021-2023 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,151 +25,124 @@ * SUCH DAMAGE. */ -#ifdef PORTNCURSES -#include <ncurses/ncurses.h> -#else -#include <ncurses.h> -#endif +#include <curses.h> #include "bsddialog.h" -#include "lib_util.h" #include "bsddialog_theme.h" +#include "lib_util.h" #define GET_COLOR(bg, fg) (COLOR_PAIR(bg * 8 + fg +1)) +#define WHITE GET_COLOR(COLOR_WHITE, COLOR_BLACK) +#define BLACK GET_COLOR(COLOR_WHITE, COLOR_BLACK) | A_REVERSE +#define NFLAGS 6 struct bsddialog_theme t; +bool hastermcolors; -static struct bsddialog_theme bsddialogtheme = { -#define bgwidget COLOR_WHITE -#define bgcurr COLOR_YELLOW - .terminal.color = GET_COLOR(COLOR_BLACK, COLOR_CYAN), - - .shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK), - .shadow.h = 1, - .shadow.w = 2, +struct flag_converter { + unsigned int public; + unsigned int private; +}; - .dialog.delimtitle = true, - .dialog.titlecolor = GET_COLOR(COLOR_YELLOW, bgwidget), - .dialog.lineraisecolor = GET_COLOR(COLOR_BLACK, bgwidget), - .dialog.linelowercolor = GET_COLOR(COLOR_BLACK, bgwidget), - .dialog.color = GET_COLOR(COLOR_BLACK, bgwidget), - .dialog.bottomtitlecolor = GET_COLOR(COLOR_BLACK, bgwidget), - - .text.hmargin = 1, - - .menu.arrowcolor = GET_COLOR(COLOR_YELLOW, bgwidget), - .menu.selectorcolor = GET_COLOR(COLOR_BLACK, bgwidget) | A_BOLD, - .menu.f_desccolor = GET_COLOR(COLOR_WHITE, bgcurr), - .menu.desccolor = GET_COLOR(COLOR_BLACK, bgwidget), - .menu.f_namecolor = GET_COLOR(COLOR_BLACK, bgcurr), - .menu.namecolor = GET_COLOR(COLOR_YELLOW, bgwidget), - .menu.namesepcolor = GET_COLOR(COLOR_YELLOW, bgwidget), - .menu.descsepcolor = GET_COLOR(COLOR_YELLOW, bgwidget), - .menu.f_shortcutcolor = GET_COLOR(COLOR_RED, bgcurr), - .menu.shortcutcolor = GET_COLOR(COLOR_RED, bgwidget), - - .form.f_fieldcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), - .form.fieldcolor = GET_COLOR(COLOR_WHITE, COLOR_CYAN), - .form.readonlycolor = GET_COLOR(COLOR_CYAN,COLOR_WHITE), - - .bar.f_color = GET_COLOR(COLOR_WHITE, COLOR_BLUE), - .bar.color = GET_COLOR(COLOR_BLUE, COLOR_WHITE), - - .button.space = 3, - .button.leftch = '[', - .button.rightch = ']', - .button.f_delimcolor = GET_COLOR(COLOR_WHITE, bgcurr), - .button.delimcolor = GET_COLOR(COLOR_BLACK, bgwidget), - .button.f_color = GET_COLOR(COLOR_WHITE, bgcurr) | A_UNDERLINE, - .button.color = GET_COLOR(COLOR_BLACK, bgwidget) | A_UNDERLINE, - .button.f_shortcutcolor = GET_COLOR(COLOR_BLACK, bgcurr) | A_UNDERLINE, - .button.shortcutcolor = GET_COLOR(COLOR_YELLOW, bgwidget) | A_UNDERLINE +static struct flag_converter flagconv[NFLAGS] = { + {BSDDIALOG_BLINK, A_BLINK}, + {BSDDIALOG_BOLD, A_BOLD}, + {BSDDIALOG_HALFBRIGHT, A_DIM}, + {BSDDIALOG_HIGHLIGHT, A_STANDOUT}, + {BSDDIALOG_REVERSE, A_REVERSE}, + {BSDDIALOG_UNDERLINE, A_UNDERLINE} }; static struct bsddialog_theme blackwhite = { -#define bk COLOR_BLACK -#define fg COLOR_WHITE - .terminal.color = GET_COLOR(fg, bk), + .screen.color = WHITE, - .shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK), - .shadow.h = 1, - .shadow.w = 2, + .shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK), + .shadow.y = 1, + .shadow.x = 2, .dialog.delimtitle = true, - .dialog.titlecolor = GET_COLOR(fg, bk), - .dialog.lineraisecolor = GET_COLOR(fg, bk), - .dialog.linelowercolor = GET_COLOR(fg, bk), - .dialog.color = GET_COLOR(fg, bk), - .dialog.bottomtitlecolor = GET_COLOR(fg, bk), - - .text.hmargin = 1, - - .menu.arrowcolor = GET_COLOR(fg, bk), - .menu.selectorcolor = GET_COLOR(fg, bk), - .menu.f_desccolor = GET_COLOR(fg, bk) | A_REVERSE, - .menu.desccolor = GET_COLOR(fg, bk), - .menu.f_namecolor = GET_COLOR(fg, bk) | A_REVERSE, - .menu.namecolor = GET_COLOR(fg, bk), - .menu.namesepcolor = GET_COLOR(fg, bk), - .menu.descsepcolor = GET_COLOR(fg, bk), - .menu.f_shortcutcolor = GET_COLOR(fg, bk) | A_UNDERLINE | A_REVERSE, - .menu.shortcutcolor = GET_COLOR(fg, bk) | A_UNDERLINE, - - .form.f_fieldcolor = GET_COLOR(fg, bk) | A_REVERSE, - .form.fieldcolor = GET_COLOR(fg, bk), - .form.readonlycolor = GET_COLOR(fg, bk), - - .bar.f_color = GET_COLOR(fg, bk) | A_REVERSE, - .bar.color = GET_COLOR(fg, bk), - - .button.space = 3, - .button.leftch = '[', - .button.rightch = ']', - .button.f_delimcolor = GET_COLOR(fg, bk), - .button.delimcolor = GET_COLOR(fg, bk), - .button.f_color = GET_COLOR(fg, bk) | A_UNDERLINE | A_REVERSE, - .button.color = GET_COLOR(fg, bk) | A_UNDERLINE, - .button.f_shortcutcolor = GET_COLOR(fg, bk) | A_UNDERLINE | A_REVERSE, - .button.shortcutcolor = GET_COLOR(fg, bk) | A_UNDERLINE + .dialog.titlecolor = WHITE, + .dialog.lineraisecolor = WHITE, + .dialog.linelowercolor = WHITE, + .dialog.color = WHITE, + .dialog.bottomtitlecolor = WHITE, + .dialog.arrowcolor = WHITE, + + .menu.f_prefixcolor = WHITE, + .menu.prefixcolor = WHITE, + .menu.f_selectorcolor = BLACK, + .menu.selectorcolor = WHITE, + .menu.f_desccolor = BLACK, + .menu.desccolor = WHITE, + .menu.f_namecolor = BLACK, + .menu.namecolor = WHITE, + .menu.f_shortcutcolor = BLACK | A_UNDERLINE, + .menu.shortcutcolor = WHITE | A_UNDERLINE, + .menu.bottomdesccolor = WHITE, + .menu.sepnamecolor = WHITE, + .menu.sepdesccolor = WHITE, + + .form.f_fieldcolor = BLACK, + .form.fieldcolor = WHITE, + .form.readonlycolor = WHITE, + .form.bottomdesccolor = WHITE, + + .bar.f_color = BLACK, + .bar.color = WHITE, + + .button.minmargin = 1, + .button.maxmargin = 5, + .button.leftdelim = '[', + .button.rightdelim = ']', + .button.f_delimcolor = WHITE, + .button.delimcolor = WHITE, + .button.f_color = BLACK, + .button.color = WHITE, + .button.f_shortcutcolor = BLACK | A_UNDERLINE | A_BOLD, + .button.shortcutcolor = WHITE | A_UNDERLINE | A_BOLD }; -static struct bsddialog_theme dialogtheme = { - .terminal.color = GET_COLOR(COLOR_CYAN, COLOR_BLUE) | A_BOLD, +static struct bsddialog_theme flat = { + .screen.color = GET_COLOR(COLOR_CYAN, COLOR_BLUE) | A_BOLD, - .shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK), - .shadow.h = 1, - .shadow.w = 2, + .shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK), + .shadow.y = 1, + .shadow.x = 2, - .dialog.delimtitle = false, + .dialog.delimtitle = true, .dialog.titlecolor = GET_COLOR(COLOR_BLUE, COLOR_WHITE) | A_BOLD, - .dialog.lineraisecolor = GET_COLOR(COLOR_WHITE, COLOR_WHITE) | A_BOLD, - .dialog.linelowercolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE) | A_BOLD, + .dialog.lineraisecolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), + .dialog.linelowercolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), .dialog.color = GET_COLOR(COLOR_BLACK, COLOR_WHITE), .dialog.bottomtitlecolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE) | A_BOLD, - - .text.hmargin = 1, - - .menu.arrowcolor = GET_COLOR(COLOR_GREEN, COLOR_WHITE), - .menu.selectorcolor = GET_COLOR(COLOR_BLACK, bgwidget) | A_BOLD, - .menu.f_desccolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE) | A_BOLD, - .menu.desccolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE) | A_BOLD, - .menu.f_namecolor = GET_COLOR(COLOR_YELLOW, COLOR_BLUE) | A_BOLD, - .menu.namecolor = GET_COLOR(COLOR_BLUE, COLOR_WHITE) | A_BOLD, - .menu.namesepcolor = GET_COLOR(COLOR_RED, COLOR_WHITE), - .menu.descsepcolor = GET_COLOR(COLOR_RED, COLOR_WHITE), - .menu.f_shortcutcolor = GET_COLOR(COLOR_RED, COLOR_BLUE) | A_BOLD, - .menu.shortcutcolor = GET_COLOR(COLOR_RED, COLOR_WHITE) | A_BOLD, - - .form.f_fieldcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE) | A_BOLD, - .form.fieldcolor = GET_COLOR(COLOR_WHITE, COLOR_CYAN) | A_BOLD, - .form.readonlycolor = GET_COLOR(COLOR_CYAN, COLOR_WHITE)| A_BOLD, + .dialog.arrowcolor = GET_COLOR(COLOR_BLUE, COLOR_WHITE), + + .menu.f_prefixcolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), + .menu.prefixcolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), + .menu.f_selectorcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), + .menu.selectorcolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), + .menu.f_desccolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), + .menu.desccolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), + .menu.f_namecolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), + .menu.namecolor = GET_COLOR(COLOR_BLUE, COLOR_WHITE), + .menu.f_shortcutcolor = GET_COLOR(COLOR_RED, COLOR_BLUE), + .menu.shortcutcolor = GET_COLOR(COLOR_RED, COLOR_WHITE), + .menu.bottomdesccolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), + .menu.sepnamecolor = GET_COLOR(COLOR_RED, COLOR_WHITE), + .menu.sepdesccolor = GET_COLOR(COLOR_RED, COLOR_WHITE), + + .form.f_fieldcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE) | A_BOLD, + .form.fieldcolor = GET_COLOR(COLOR_WHITE, COLOR_CYAN) | A_BOLD, + .form.readonlycolor = GET_COLOR(COLOR_CYAN, COLOR_WHITE)| A_BOLD, + .form.bottomdesccolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), .bar.f_color = GET_COLOR(COLOR_WHITE, COLOR_BLUE) | A_BOLD, .bar.color = GET_COLOR(COLOR_BLUE, COLOR_WHITE) | A_BOLD, - .button.space = 3, - .button.leftch = '<', - .button.rightch = '>', + .button.minmargin = 1, + .button.maxmargin = 5, + .button.leftdelim = '[', + .button.rightdelim = ']', .button.f_delimcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE) | A_BOLD, .button.delimcolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), .button.f_color = GET_COLOR(COLOR_YELLOW, COLOR_BLUE) | A_BOLD, @@ -181,41 +154,46 @@ static struct bsddialog_theme dialogtheme = { static void set_theme(struct bsddialog_theme *dst, struct bsddialog_theme *src) { + dst->screen.color = src->screen.color; + dst->shadow.color = src->shadow.color; - dst->shadow.h = src->shadow.h; - dst->shadow.w = src->shadow.w; + dst->shadow.y = src->shadow.y; + dst->shadow.x = src->shadow.x; - dst->terminal.color = src->terminal.color; dst->dialog.delimtitle = src->dialog.delimtitle; dst->dialog.titlecolor = src->dialog.titlecolor; dst->dialog.lineraisecolor = src->dialog.lineraisecolor; dst->dialog.linelowercolor = src->dialog.linelowercolor; dst->dialog.color = src->dialog.color; dst->dialog.bottomtitlecolor = src->dialog.bottomtitlecolor; + dst->dialog.arrowcolor = src->dialog.arrowcolor; - dst->text.hmargin = src->text.hmargin; - - dst->menu.arrowcolor = src->menu.arrowcolor; + dst->menu.f_prefixcolor = src->menu.f_prefixcolor; + dst->menu.prefixcolor = src->menu.prefixcolor; + dst->menu.f_selectorcolor = src->menu.f_selectorcolor; dst->menu.selectorcolor = src->menu.selectorcolor; dst->menu.f_desccolor = src->menu.f_desccolor; dst->menu.desccolor = src->menu.desccolor; dst->menu.f_namecolor = src->menu.f_namecolor; dst->menu.namecolor = src->menu.namecolor; - dst->menu.namesepcolor = src->menu.namesepcolor; - dst->menu.descsepcolor = src->menu.descsepcolor; dst->menu.f_shortcutcolor = src->menu.f_shortcutcolor; dst->menu.shortcutcolor = src->menu.shortcutcolor; + dst->menu.bottomdesccolor = src->menu.bottomdesccolor; + dst->menu.sepnamecolor = src->menu.sepnamecolor; + dst->menu.sepdesccolor = src->menu.sepdesccolor; - dst->form.f_fieldcolor = src->form.f_fieldcolor; - dst->form.fieldcolor = src->form.fieldcolor; - dst->form.readonlycolor = src->form.readonlycolor; + dst->form.f_fieldcolor = src->form.f_fieldcolor; + dst->form.fieldcolor = src->form.fieldcolor; + dst->form.readonlycolor = src->form.readonlycolor; + dst->form.bottomdesccolor = src->form.bottomdesccolor; dst->bar.f_color = src->bar.f_color; dst->bar.color = src->bar.color; - dst->button.space = src->button.space; - dst->button.leftch = src->button.leftch; - dst->button.rightch = src->button.rightch; + dst->button.minmargin = src->button.minmargin; + dst->button.maxmargin = src->button.maxmargin; + dst->button.leftdelim = src->button.leftdelim; + dst->button.rightdelim = src->button.rightdelim; dst->button.f_delimcolor = src->button.f_delimcolor; dst->button.delimcolor = src->button.delimcolor; dst->button.f_color = src->button.f_color; @@ -223,66 +201,88 @@ set_theme(struct bsddialog_theme *dst, struct bsddialog_theme *src) dst->button.f_shortcutcolor = src->button.f_shortcutcolor; dst->button.shortcutcolor = src->button.shortcutcolor; - bkgd(dst->terminal.color); - refresh(); + bkgd(dst->screen.color); } /* API */ int bsddialog_get_theme(struct bsddialog_theme *theme) { - if (theme == NULL) - RETURN_ERROR("theme is NULL"); - if (sizeof(*theme) != sizeof(struct bsddialog_theme)) - RETURN_ERROR("Bad suze struct bsddialog_theme"); - + CHECK_PTR(theme); set_theme(theme, &t); - return (0); + return (BSDDIALOG_OK); } int bsddialog_set_theme(struct bsddialog_theme *theme) { - if (theme == NULL) - RETURN_ERROR("theme is NULL"); - if (sizeof(*theme) != sizeof(struct bsddialog_theme)) - RETURN_ERROR("Bad size struct bsddialog_theme"); - + CHECK_PTR(theme); set_theme(&t, theme); + refresh(); - return (0); + return (BSDDIALOG_OK); } int bsddialog_set_default_theme(enum bsddialog_default_theme newtheme) { - - if (newtheme == BSDDIALOG_THEME_DEFAULT) { - bsddialog_set_theme(&dialogtheme); - t.dialog.lineraisecolor = t.dialog.linelowercolor; + if (newtheme == BSDDIALOG_THEME_3D) { + set_theme(&t, &flat); + t.dialog.lineraisecolor = + GET_COLOR(COLOR_WHITE, COLOR_WHITE) | A_BOLD; + t.dialog.delimtitle = false; + t.dialog.bottomtitlecolor = t.dialog.bottomtitlecolor | A_BOLD; + } else if (newtheme == BSDDIALOG_THEME_BLACKWHITE) { + set_theme(&t, &blackwhite); + } else if (newtheme == BSDDIALOG_THEME_FLAT) { + set_theme(&t, &flat); + } else { + RETURN_FMTERROR("Unknown default theme (%d), " + "to use enum bsddialog_default_theme", + newtheme); } - else if (newtheme == BSDDIALOG_THEME_BSDDIALOG) - bsddialog_set_theme(&bsddialogtheme); - else if (newtheme == BSDDIALOG_THEME_BLACKWHITE) - bsddialog_set_theme(&blackwhite); - else if (newtheme == BSDDIALOG_THEME_DIALOG) - bsddialog_set_theme(&dialogtheme); - else - RETURN_ERROR("Unknow default theme"); - - return (0); + refresh(); + + return (BSDDIALOG_OK); } int -bsddialog_color(enum bsddialog_color background, - enum bsddialog_color foreground, unsigned int flags) +bsddialog_color(enum bsddialog_color foreground, + enum bsddialog_color background, unsigned int flags) { - unsigned int cursesflags = 0; + unsigned int i, f; + + f = 0; + for (i=0; i < NFLAGS; i++) + if (flags & flagconv[i].public) + f |= flagconv[i].private; - if (flags & BSDDIALOG_BOLD) - cursesflags |= A_BOLD; - if (flags & BSDDIALOG_REVERSE) - cursesflags |= A_REVERSE; - if (flags & BSDDIALOG_UNDERLINE) - cursesflags |= A_UNDERLINE; + return (GET_COLOR(foreground, background) | f); +} - return (GET_COLOR(background, foreground) | cursesflags); +int +bsddialog_color_attrs(int color, enum bsddialog_color *foreground, + enum bsddialog_color *background, unsigned int *flags) +{ + short fg, bg; + unsigned int i, f; + + if (flags != NULL) { + f = 0; + for (i=0; i < NFLAGS; i++) + if (color & flagconv[i].private) + f |= flagconv[i].public; + *flags = f; + } + if (pair_content(PAIR_NUMBER(color), &fg, &bg) != OK) + RETURN_ERROR("Cannot get color attributes"); + if (foreground != NULL) + *foreground = fg; + if (background != NULL) + *background = bg; + + return (BSDDIALOG_OK); +} + +bool bsddialog_hascolors(void) +{ + return (hastermcolors); } diff --git a/lib/timebox.c b/lib/timebox.c index 2597cbf0c152..d683f9552b50 100644 --- a/lib/timebox.c +++ b/lib/timebox.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021 Alfonso Sabato Siciliano + * Copyright (c) 2021-2023 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,511 +25,207 @@ * SUCH DAMAGE. */ -#include <sys/param.h> - -#ifdef PORTNCURSES -#include <ncurses/ncurses.h> -#else -#include <ncurses.h> -#endif - -#include <ctype.h> -#include <string.h> +#include <curses.h> #include "bsddialog.h" -#include "lib_util.h" #include "bsddialog_theme.h" +#include "lib_util.h" -#define MINWDATE 25 /* 23 wins + 2 VBORDERS */ -#define MINWTIME 16 /*14 wins + 2 VBORDERS */ -#define MINHEIGHT 8 /* 2 for text */ - -/* "Time": timebox - datebox */ +#define MINWTIME 14 /* 3 windows and their borders */ +#define HBOX 3 +#define WBOX 4 -extern struct bsddialog_theme t; +struct clock { + unsigned int max; + unsigned int value; + WINDOW *win; +}; -static int -datetime_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, - int *w, int minw, char *text, struct buttons bs) +static void +drawsquare(struct bsddialog_conf *conf, WINDOW *win, unsigned int value, + bool focus) { - int maxword, maxline, nlines, line; - - if (get_text_properties(conf, text, &maxword, &maxline, &nlines) != 0) - return BSDDIALOG_ERROR; - - if (cols == BSDDIALOG_AUTOSIZE) { - *w = VBORDERS; - /* buttons size */ - *w += bs.nbuttons * bs.sizebutton; - *w += bs.nbuttons > 0 ? (bs.nbuttons-1) * t.button.space : 0; - /* text size */ - line = maxline + VBORDERS + t.text.hmargin * 2; - line = MAX(line, (int) (maxword + VBORDERS + t.text.hmargin * 2)); - *w = MAX(*w, line); - /* date windows */ - *w = MAX(*w, minw); - /* conf.auto_minwidth */ - *w = MAX(*w, (int)conf->auto_minwidth); - /* avoid terminal overflow */ - *w = MIN(*w, widget_max_width(conf) -1); + draw_borders(conf, win, LOWERED); + if (focus) { + wattron(win, t.dialog.arrowcolor); + mvwhline(win, 0, 1, conf->ascii_lines ? '^' : ACS_UARROW, 2); + mvwhline(win, 2, 1, conf->ascii_lines ? 'v' : ACS_DARROW, 2); + wattroff(win, t.dialog.arrowcolor); } - if (rows == BSDDIALOG_AUTOSIZE) { - *h = MINHEIGHT; - if (maxword > 0) - *h += MAX(nlines, (int)(*w / GET_ASPECT_RATIO(conf))); - /* conf.auto_minheight */ - *h = MAX(*h, (int)conf->auto_minheight); - /* avoid terminal overflow */ - *h = MIN(*h, widget_max_height(conf) -1); - } + if (focus) + wattron(win, t.menu.f_namecolor); + mvwprintw(win, 1, 1, "%02u", value); + if (focus) + wattroff(win, t.menu.f_namecolor); - return 0; + wnoutrefresh(win); } -static int -datetime_checksize(int rows, int cols, char *text, int minw, struct buttons bs) +static int timebox_redraw(struct dialog *d, struct clock *c) { - int mincols; - - mincols = VBORDERS; - mincols += bs.nbuttons * bs.sizebutton; - mincols += bs.nbuttons > 0 ? (bs.nbuttons-1) * t.button.space : 0; - mincols = MAX(minw, mincols); - - if (cols < mincols) - RETURN_ERROR("Few cols for this timebox/datebox"); - - if (rows < MINHEIGHT + (strlen(text) > 0 ? 1 : 0)) - RETURN_ERROR("Few rows for this timebox/datebox"); + int y, x; - return 0; + if (d->built) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ + } + if (dialog_size_position(d, HBOX, MINWTIME, NULL) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) + return (BSDDIALOG_ERROR); + if (d->built) + refresh(); /* Important to fix grey lines expanding screen */ + TEXTPAD(d, HBOX + HBUTTONS); + + y = d->y + d->h - BORDER - HBUTTONS - HBOX; + x = d->x + d->w/2 - 7; + update_box(d->conf, c[0].win, y, x, HBOX, WBOX, LOWERED); + mvwaddch(d->widget, d->h - 5, d->w/2 - 3, ':'); + update_box(d->conf, c[1].win, y, x += 5, HBOX, WBOX, LOWERED); + mvwaddch(d->widget, d->h - 5, d->w/2 + 2, ':'); + update_box(d->conf, c[2].win, y, x + 5, HBOX, WBOX, LOWERED); + wnoutrefresh(d->widget); /* for mvwaddch(':') */ + + return (0); } -int bsddialog_timebox(struct bsddialog_conf *conf, char* text, int rows, int cols, - unsigned int *hh, unsigned int *mm, unsigned int *ss) +/* API */ +int +bsddialog_timebox(struct bsddialog_conf *conf, const char* text, int rows, + int cols, unsigned int *hh, unsigned int *mm, unsigned int *ss) { - WINDOW *widget, *textpad, *shadow; - int i, input, output, y, x, h, w, sel, htextpad; - struct buttons bs; - bool loop; - struct myclockstruct { - unsigned int max; - unsigned int value; - WINDOW *win; - }; - - if (hh == NULL || mm == NULL || ss == NULL) - RETURN_ERROR("hh / mm / ss cannot be NULL"); - - struct myclockstruct c[3] = { + bool loop, focusbuttons; + int i, retval, sel; + wint_t input; + struct dialog d; + struct clock c[3] = { {23, *hh, NULL}, {59, *mm, NULL}, {59, *ss, NULL} }; - for (i = 0 ; i < 3; i++) { - if (c[i].value > c[i].max) - c[i].value = c[i].max; + CHECK_PTR(hh); + CHECK_PTR(mm); + CHECK_PTR(ss); + if (prepare_dialog(conf, text, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + set_buttons(&d, true, OK_LABEL, CANCEL_LABEL); + for (i=0; i<3; i++) { + if ((c[i].win = newwin(1, 1, 1, 1)) == NULL) + RETURN_FMTERROR("Cannot build WINDOW for time[%d]", i); + wbkgd(c[i].win, t.dialog.color); + c[i].value = MIN(c[i].value, c[i].max); } - - get_buttons(conf, &bs, BUTTONLABEL(ok_label), BUTTONLABEL(extra_label), - BUTTONLABEL(cancel_label), BUTTONLABEL(help_label)); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return BSDDIALOG_ERROR; - if (datetime_autosize(conf, rows, cols, &h, &w, MINWTIME, text, bs) != 0) - return BSDDIALOG_ERROR; - if (datetime_checksize(h, w, text, MINWTIME, bs) != 0) - return BSDDIALOG_ERROR; - if (set_widget_position(conf, &y, &x, h, w) != 0) - return BSDDIALOG_ERROR; - - if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED, - &textpad, &htextpad, text, true) != 0) - return BSDDIALOG_ERROR; - - draw_buttons(widget, h-2, w, bs, true); - - wrefresh(widget); - - prefresh(textpad, 0, 0, y+1, x+2, y+h-7, x+w-2); - - c[0].win = new_boxed_window(conf, y + h - 6, x + w/2 - 7, 3, 4, LOWERED); - mvwaddch(widget, h - 5, w/2 - 3, ':'); - c[1].win = new_boxed_window(conf, y + h - 6, x + w/2 - 2, 3, 4, LOWERED); - mvwaddch(widget, h - 5, w/2 + 2, ':'); - c[2].win = new_boxed_window(conf, y + h - 6, x + w/2 + 3, 3, 4, LOWERED); - - wrefresh(widget); - - sel = 0; - curs_set(2); - loop = true; - while(loop) { - for (i=0; i<3; i++) { - mvwprintw(c[i].win, 1, 1, "%2d", c[i].value); - wrefresh(c[i].win); - } - wmove(c[sel].win, 1, 2); - wrefresh(c[sel].win); - - input = getch(); + if (timebox_redraw(&d, c) != 0) + return (BSDDIALOG_ERROR); + + sel = -1; + loop = focusbuttons = true; + while (loop) { + for (i = 0; i < 3; i++) + drawsquare(conf, c[i].win, c[i].value, sel == i); + doupdate(); + if (get_wch(&input) == ERR) + continue; switch(input) { case KEY_ENTER: case 10: /* Enter */ - output = bs.value[bs.curr]; - if (output == BSDDIALOG_OK) { - *hh = c[0].value; - *mm = c[1].value; - *ss = c[2].value; + if (focusbuttons || conf->button.always_active) { + retval = BUTTONVALUE(d.bs); + loop = false; } - loop = false; break; case 27: /* Esc */ - output = BSDDIALOG_ESC; - loop = false; - break; - case '\t': /* TAB */ - sel = (sel + 1) % 3; - break; - case KEY_LEFT: - if (bs.curr > 0) { - bs.curr--; - draw_buttons(widget, h-2, w, bs, true); - wrefresh(widget); + if (conf->key.enable_esc) { + retval = BSDDIALOG_ESC; + loop = false; } break; case KEY_RIGHT: - if (bs.curr < (int) bs.nbuttons - 1) { - bs.curr++; - draw_buttons(widget, h-2, w, bs, true); - wrefresh(widget); - } - break; - case KEY_UP: - c[sel].value = c[sel].value < c[sel].max ? c[sel].value + 1 : 0; - break; - case KEY_DOWN: - c[sel].value = c[sel].value > 0 ? c[sel].value - 1 : c[sel].max; - break; - case KEY_F(1): - if (conf->f1_file == NULL && conf->f1_message == NULL) - break; - curs_set(0); - if (f1help(conf) != 0) - return BSDDIALOG_ERROR; - curs_set(2); - /* No break! the terminal size can change */ - case KEY_RESIZE: - hide_widget(y, x, h, w,conf->shadow); - - /* - * Unnecessary, but, when the columns decrease the - * following "refresh" seem not work - */ - refresh(); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return BSDDIALOG_ERROR; - if (datetime_autosize(conf, rows, cols, &h, &w, MINWTIME, text, bs) != 0) - return BSDDIALOG_ERROR; - if (datetime_checksize(h, w, text, MINWTIME, bs) != 0) - return BSDDIALOG_ERROR; - if (set_widget_position(conf, &y, &x, h, w) != 0) - return BSDDIALOG_ERROR; - - wclear(shadow); - mvwin(shadow, y + t.shadow.h, x + t.shadow.w); - wresize(shadow, h, w); - - wclear(widget); - mvwin(widget, y, x); - wresize(widget, h, w); - - htextpad = 1; - wclear(textpad); - wresize(textpad, 1, w - HBORDERS - t.text.hmargin * 2); - - if(update_widget_withtextpad(conf, shadow, widget, h, w, - RAISED, textpad, &htextpad, text, true) != 0) - return BSDDIALOG_ERROR; - - mvwaddch(widget, h - 5, w/2 - 3, ':'); - mvwaddch(widget, h - 5, w/2 + 2, ':'); - - draw_buttons(widget, h-2, w, bs, true); - - wrefresh(widget); - - prefresh(textpad, 0, 0, y+1, x+2, y+h-7, x+w-2); - - wclear(c[0].win); - mvwin(c[0].win, y + h - 6, x + w/2 - 7); - draw_borders(conf, c[0].win, 3, 4, LOWERED); - wrefresh(c[0].win); - - wclear(c[1].win); - mvwin(c[1].win, y + h - 6, x + w/2 - 2); - draw_borders(conf, c[1].win, 3, 4, LOWERED); - wrefresh(c[1].win); - - wclear(c[2].win); - mvwin(c[2].win, y + h - 6, x + w/2 + 3); - draw_borders(conf, c[2].win, 3, 4, LOWERED); - wrefresh(c[2].win); - - /* Important to avoid grey lines expanding screen */ - refresh(); - break; - default: - for (i = 0; i < (int) bs.nbuttons; i++) - if (tolower(input) == tolower((bs.label[i])[0])) { - output = bs.value[i]; - loop = false; - } - } - } - - curs_set(0); - - for (i=0; i<3; i++) - delwin(c[i].win); - end_widget_withtextpad(conf, widget, h, w, textpad, shadow); - - return output; -} - -int -bsddialog_datebox(struct bsddialog_conf *conf, char* text, int rows, int cols, - unsigned int *yy, unsigned int *mm, unsigned int *dd) -{ - WINDOW *widget, *textpad, *shadow; - int i, input, output, y, x, h, w, sel, htextpad; - struct buttons bs; - bool loop; - struct calendar { - int max; - int value; - WINDOW *win; - unsigned int x; - }; - struct month { - char *name; - unsigned int days; - }; - - if (yy == NULL || mm == NULL || dd == NULL) - RETURN_ERROR("yy / mm / dd cannot be NULL"); - - struct calendar c[3] = { - {9999, *yy, NULL, 4 }, - {12, *mm, NULL, 9 }, - {31, *dd, NULL, 2 } - }; - - struct month m[12] = { - { "January", 31 }, { "February", 28 }, { "March", 31 }, - { "April", 30 }, { "May", 31 }, { "June", 30 }, - { "July", 31 }, { "August", 31 }, { "September", 30 }, - { "October", 31 }, { "November", 30 }, { "December", 31 } - }; - -#define ISLEAF(year) ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) - - for (i = 0 ; i < 3; i++) { - if (c[i].value > c[i].max) - c[i].value = c[i].max; - if (c[i].value < 1) - c[i].value = 1; - } - c[2].max = m[c[1].value -1].days; - if (c[1].value == 2 && ISLEAF(c[0].value)) - c[2].max = 29; - if (c[2].value > c[2].max) - c[2].value = c[2].max; - - get_buttons(conf, &bs, BUTTONLABEL(ok_label), BUTTONLABEL(extra_label), - BUTTONLABEL(cancel_label), BUTTONLABEL(help_label)); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return BSDDIALOG_ERROR; - if (datetime_autosize(conf, rows, cols, &h, &w, MINWDATE, text, bs) != 0) - return BSDDIALOG_ERROR; - if (datetime_checksize(h, w, text, MINWDATE, bs) != 0) - return BSDDIALOG_ERROR; - if (set_widget_position(conf, &y, &x, h, w) != 0) - return BSDDIALOG_ERROR; - - if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED, - &textpad, &htextpad, text, true) != 0) - return BSDDIALOG_ERROR; - - draw_buttons(widget, h-2, w, bs, true); - - wrefresh(widget); - - prefresh(textpad, 0, 0, y+1, x+2, y+h-7, x+w-2); - - c[0].win = new_boxed_window(conf, y + h - 6, x + w/2 - 11, 3, 6, LOWERED); - mvwaddch(widget, h - 5, w/2 - 5, '/'); - c[1].win = new_boxed_window(conf, y + h - 6, x + w/2 - 4, 3, 11, LOWERED); - mvwaddch(widget, h - 5, w/2 + 7, '/'); - c[2].win = new_boxed_window(conf, y + h - 6, x + w/2 + 8, 3, 4, LOWERED); - - wrefresh(widget); - - sel = 2; - curs_set(2); - loop = true; - while(loop) { - mvwprintw(c[0].win, 1, 1, "%4d", c[0].value); - mvwprintw(c[1].win, 1, 1, "%9s", m[c[1].value-1].name); - mvwprintw(c[2].win, 1, 1, "%2d", c[2].value); - for (i=0; i<3; i++) { - wrefresh(c[i].win); - } - wmove(c[sel].win, 1, c[sel].x); - wrefresh(c[sel].win); - - input = getch(); - switch(input) { - case KEY_ENTER: - case 10: /* Enter */ - output = bs.value[bs.curr]; - if (output == BSDDIALOG_OK) { - *yy = c[0].value; - *mm = c[1].value; - *dd = c[2].value; - } - loop = false; - break; - case 27: /* Esc */ - output = BSDDIALOG_ESC; - loop = false; - break; case '\t': /* TAB */ - sel = (sel + 1) % 3; - break; - case KEY_LEFT: - if (bs.curr > 0) { - bs.curr--; - draw_buttons(widget, h-2, w, bs, true); - wrefresh(widget); + if (focusbuttons) { + d.bs.curr++; + focusbuttons = d.bs.curr < (int)d.bs.nbuttons ? + true : false; + if (focusbuttons == false) { + sel = 0; + d.bs.curr = + conf->button.always_active ? 0 : -1; + } + } else { + sel++; + focusbuttons = sel > 2 ? true : false; + if (focusbuttons) { + d.bs.curr = 0; + } } + DRAW_BUTTONS(d); break; - case KEY_RIGHT: - if (bs.curr < (int) bs.nbuttons - 1) { - bs.curr++; - draw_buttons(widget, h-2, w, bs, true); - wrefresh(widget); + case KEY_LEFT: + if (focusbuttons) { + d.bs.curr--; + focusbuttons = d.bs.curr < 0 ? false : true; + if (focusbuttons == false) { + sel = 2; + d.bs.curr = + conf->button.always_active ? 0 : -1; + } + } else { + sel--; + focusbuttons = sel < 0 ? true : false; + if (focusbuttons) + d.bs.curr = (int)d.bs.nbuttons - 1; } + DRAW_BUTTONS(d); break; case KEY_UP: - c[sel].value = c[sel].value > 1 ? c[sel].value - 1 : c[sel].max ; - /* if mount change */ - c[2].max = m[c[1].value -1].days; - /* if year change */ - if (c[1].value == 2 && ISLEAF(c[0].value)) - c[2].max = 29; - /* set new day */ - if (c[2].value > c[2].max) - c[2].value = c[2].max; + if (focusbuttons) { + sel = 0; + focusbuttons = false; + d.bs.curr = conf->button.always_active ? 0 : -1; + DRAW_BUTTONS(d); + } else { + c[sel].value = c[sel].value > 0 ? + c[sel].value - 1 : c[sel].max; + } break; case KEY_DOWN: - c[sel].value = c[sel].value < c[sel].max ? c[sel].value + 1 : 1; - /* if mount change */ - c[2].max = m[c[1].value -1].days; - /* if year change */ - if (c[1].value == 2 && ISLEAF(c[0].value)) - c[2].max = 29; - /* set new day */ - if (c[2].value > c[2].max) - c[2].value = c[2].max; + if (focusbuttons) + break; + c[sel].value = c[sel].value < c[sel].max ? + c[sel].value + 1 : 0; break; case KEY_F(1): - if (conf->f1_file == NULL && conf->f1_message == NULL) + if (conf->key.f1_file == NULL && + conf->key.f1_message == NULL) break; - curs_set(0); - if (f1help(conf) != 0) - return BSDDIALOG_ERROR; - curs_set(2); - /* No break! the terminal size can change */ + if (f1help_dialog(conf) != 0) + return (BSDDIALOG_ERROR); + if (timebox_redraw(&d, c) != 0) + return (BSDDIALOG_ERROR); + break; case KEY_RESIZE: - hide_widget(y, x, h, w,conf->shadow); - - /* - * Unnecessary, but, when the columns decrease the - * following "refresh" seem not work - */ - refresh(); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return BSDDIALOG_ERROR; - if (datetime_autosize(conf, rows, cols, &h, &w, MINWDATE, text, bs) != 0) - return BSDDIALOG_ERROR; - if (datetime_checksize(h, w, text, MINWDATE, bs) != 0) - return BSDDIALOG_ERROR; - if (set_widget_position(conf, &y, &x, h, w) != 0) - return BSDDIALOG_ERROR; - - wclear(shadow); - mvwin(shadow, y + t.shadow.h, x + t.shadow.w); - wresize(shadow, h, w); - - wclear(widget); - mvwin(widget, y, x); - wresize(widget, h, w); - - htextpad = 1; - wclear(textpad); - wresize(textpad, 1, w - HBORDERS - t.text.hmargin * 2); - - if(update_widget_withtextpad(conf, shadow, widget, h, w, - RAISED, textpad, &htextpad, text, true) != 0) - return BSDDIALOG_ERROR; - - mvwaddch(widget, h - 5, w/2 - 5, '/'); - mvwaddch(widget, h - 5, w/2 + 7, '/'); - - draw_buttons(widget, h-2, w, bs, true); - - wrefresh(widget); - - prefresh(textpad, 0, 0, y+1, x+2, y+h-7, x+w-2); - - wclear(c[0].win); - mvwin(c[0].win, y + h - 6, x + w/2 - 11); - draw_borders(conf, c[0].win, 3, 6, LOWERED); - wrefresh(c[0].win); - - wclear(c[1].win); - mvwin(c[1].win, y + h - 6, x + w/2 - 4); - draw_borders(conf, c[1].win, 3, 11, LOWERED); - wrefresh(c[1].win); - - wclear(c[2].win); - mvwin(c[2].win, y + h - 6, x + w/2 + 8); - draw_borders(conf, c[2].win, 3, 4, LOWERED); - wrefresh(c[2].win); - - /* Important to avoid grey lines expanding screen */ - refresh(); + if (timebox_redraw(&d, c) != 0) + return (BSDDIALOG_ERROR); break; default: - for (i = 0; i < (int) bs.nbuttons; i++) - if (tolower(input) == tolower((bs.label[i])[0])) { - output = bs.value[i]; - loop = false; + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); + loop = false; } } } - curs_set(0); + *hh = c[0].value; + *mm = c[1].value; + *ss = c[2].value; - for (i=0; i<3; i++) + for (i = 0; i < 3; i++) delwin(c[i].win); - end_widget_withtextpad(conf, widget, h, w, textpad, shadow); + end_dialog(&d); - return output; + return (retval); } diff --git a/utility/GNUmakefile b/utility/GNUmakefile new file mode 100644 index 000000000000..518ec0d912d6 --- /dev/null +++ b/utility/GNUmakefile @@ -0,0 +1,33 @@ +# PUBLIC DOMAIN - NO WARRANTY, see: +# <http://creativecommons.org/publicdomain/zero/1.0/> +# +# Written in 2021 by Alfonso Sabato Siciliano + +OUTPUT = bsddialog +SOURCES = bsddialog.c util_builders.c util_cli.c util_theme.c +OBJECTS = $(SOURCES:.c=.o) + +ifneq ($(ENABLEDEBUG),) +CFLAGS += -g +endif +CFLAGS += -D_GNU_SOURCE -Wall -Wextra -I$(LIBPATH) + +ifneq ($(DISABLERPATH),) +LDFLAGS += -ltinfo -L$(LIBPATH) -lbsddialog +else +LDFLAGS += -ltinfo -Wl,-rpath=$(LIBPATH) -L$(LIBPATH) -lbsddialog +endif + +RM = rm -f + +all : $(OUTPUT) + +$(OUTPUT): $(OBJECTS) + $(CC) $^ -o $@ $(LDFLAGS) + + +%.o: %.c + $(CC) $(CFLAGS) -c $< + +clean: + $(RM) $(OUTPUT) *.o *~ diff --git a/utility/Makefile b/utility/Makefile new file mode 100644 index 000000000000..ab51b46a25be --- /dev/null +++ b/utility/Makefile @@ -0,0 +1,33 @@ +# PUBLIC DOMAIN - NO WARRANTY, see: +# <http://creativecommons.org/publicdomain/zero/1.0/> +# +# Written in 2021 by Alfonso Sabato Siciliano + +OUTPUT = bsddialog +SOURCES = bsddialog.c util_builders.c util_cli.c util_theme.c +OBJECTS = ${SOURCES:.c=.o} + +.if defined(DEBUG) +CFLAGS += -g +.endif +CFLAGS += -I${LIBPATH} -std=gnu99 -Wall -Wextra -Werror + +.if defined(NORPATH) +LDFLAGS += -ltinfow -L${LIBPATH} -lbsddialog +.else +LDFLAGS += -ltinfow -Wl,-rpath=${LIBPATH} -L${LIBPATH} -lbsddialog +.endif + +INSTALL = install +RM = rm -f + +all : ${OUTPUT} + +${OUTPUT}: ${OBJECTS} + ${CC} ${LDFLAGS} ${OBJECTS} -o ${.PREFIX} + +.c.o: + ${CC} ${CFLAGS} -c ${.IMPSRC} -o ${.TARGET} + +clean: + ${RM} ${OUTPUT} *.o *~ *.core *.gz diff --git a/utility/bsddialog.1 b/utility/bsddialog.1 new file mode 100644 index 000000000000..653f91a3f122 --- /dev/null +++ b/utility/bsddialog.1 @@ -0,0 +1,907 @@ +.\" +.\" Copyright (c) 2021-2023 Alfonso Sabato Siciliano +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd July 25, 2023 +.Dt BSDDIALOG 1 +.Os +.Sh NAME +.Nm bsddialog +.Nd TUI dialogs +.Sh SYNOPSIS +.Nm +.Fl Fl help | Fl Fl version +.Nm +.Op Fl Fl option +.Fl Fl dialog +.Ar text +.Ar rows +.Ar cols +.Op Ar dialog-argument +.Op Fl Fl option +.Nm +\&... +.Fl Fl dialog1 +.Ar ... +.Oo Fl Fl and-dialog +.Fl Fl dialog2 +.Ar ... +.Oc ... +.Sh DESCRIPTION +The +.Nm bsddialog +utility builds Text User Interface dialogs and widgets. +.Pp +The option +.Fl Fl help +prints a brief list of features and exits. +The option +.Fl Fl version +prints the version and exits. +The option +.Fl Fl and-dialog +builds another dialog unless the previous one returns Error, ESC or Cancel. +.Pp +Each dialog accepts +.Ar text +to print a message inside, +.Ar rows +and +.Ar cols +to set height and width, +.Dv 0 +for autosize and +.Dv -1 +for fullscreen. +.Pp +The possible input from the user interface is printed to standard error at exit. +.Ss Options +The following options can change the default behavior of the utility and are +common to some dialog. +.Bl -tag -width Ds +.It Fl Fl alternate-screen +If available set alternate screen mode, see +.Xr terminfo 5 . +.It Fl Fl ascii-lines +Ascii characters to draw lines. +.It Fl Fl backtitle Ar backtitle +Screen title. +.It Fl Fl begin-x Ar x +Set dialog horizontal position, +.Dv -1 +center, +.Dv 0 +left screen. +.It Fl Fl begin-y Ar y +Set dialog vertical position, +.Dv -1 +center, +.Dv 0 +top screen. +.It Fl Fl bikeshed +Random settings. +Colors, title delimiter, button delimiter; see theme features. +Title margin. +Buttons always active or TAB to switch focus with other components, see +.Fl Fl switch-buttons . +Zero padding with time output; see +.Fl Fl time-format . +Zero padding with date output; see +.Fl Fl date-format . +User Interface date format for +.Fl Fl datebox ; +see +.Fl Fl datebox-format . +.It Fl Fl cancel-exit-code Ar retval +Set an exit code value for the +.Dq Cancel +button. +.It Fl Fl cancel-label Ar label +Label for the +.Dq Cancel +button. +.It Fl Fl clear-dialog +Hide the dialog at exit. +.It Fl Fl clear-screen +Clear the screen, wait a dialog if built. +.It Fl Fl columns-per-row Ar columns +Try to set the number of columns for a row of text with autosizing; default +.Dv 10 . +.It Fl Fl cr-wrap +Keep new line in +.Ar text +also if it constains a +.Dq \en , +see +.Fl Fl text-unchanged . +.It Fl Fl datebox-format Ar format +String to customize +.Fl Fl datebox +interface, possible values: +.Dq d/m/y , +.Dq m/y/d , +.Dq y/m/d . +.It Fl Fl date-format Ar format +String accepted by +.Xr strftime 3 +to customize the output of +.Fl Fl datebox +and +.Fl Fl calendar . +.It Fl Fl default-button Ar label +Focus on the button with +.Ar label +on startup. +.It Fl Fl default-item Ar name +Focus on the item with +.Ar name , +for Checklist, Menu, Radiolist and Treeview. +.It Fl Fl default-no +Focus on +.Dq Cancel +or +.Dq \&No +button on startup. +.It Fl Fl disable-esc +Disable ESC key to quit. +.It Fl Fl error-exit-code Ar retval +Set an exit code value for the +.Nm +errors. +.It Fl Fl esc-exit-code Ar retval +Set an exit code value for the +.Dv ESC +key. +.It Fl Fl extra-button +Add a button with +.Dq Extra +label. +.It Fl Fl extra-exit-code Ar retval +Set an exit code value for the +.Dq Extra +button. +.It Fl Fl extra-label Ar label +Set +.Ar label +for the +.Dq Extra +button. +.It Fl Fl left1-button Ar label +Add a button with +.Ar label . +.It Fl Fl left1-exit-code Ar retval +Set an exit code for +.Fl Fl left1-button . +.It Fl Fl left2-button Ar label +Add a button with +.Ar label . +.It Fl Fl left2-exit-code Ar retval +Set an exit code for +.Fl Fl left2-button . +.It Fl Fl left3-button Ar label +Add a button with +.Ar label . +.It Fl Fl left3-exit-code Ar retval +Set an exit code for +.Fl Fl left3-button . +.It Fl Fl help-button +Add a button with +.Dq Help +label. +.It Fl Fl help-exit-code Ar retval +Set an exit code value for the +.Dq Help +button. +.It Fl Fl help-label Ar label +Set +.Ar label +for +.Dq Help +button. +.It Fl Fl help-print-items +Print also the selected items or form values if the +.Dq Help +button is pressed. +.It Fl Fl help-print-name +Print the name of the focused item if the +.Dq Help +button is pressed also +with +.Fl Fl item-bottom-desc . +.It Fl Fl hfile Ar filename +Open +.Ar filename +in a Textbox if F1 key is pressed. +.It Fl Fl hline Ar string +Dialog subtitle. +.It Fl Fl hmsg Ar string +Open a Msgbox with +.Ar string +if the F1 key is pressed. +.It Fl Fl ignore +Do not exit with unknown options. +.It Fl Fl insecure +Print +.Sq * +to hide passwords while typing, white space otherwise. +.It Fl Fl item-bottom-desc +Set a help string for each item of a Checklist, Form, Menu, Mixedform, +Passwordform, Radiolist and Treeview to display at the bottom screen side. +.It Fl Fl item-depth +Specify a margin for items, available for Checklist, Menu and Radiolist. +.It Fl Fl item-prefix +Set a string to prefix each item of a Checklist, Menu, Radiolist or Treeview. +.It Fl Fl load-theme Ar file +Load theme from +.Ar file . +.It Fl Fl max-input Ar size +Maximum length of the input for +.Fl Fl inputbox +and +.Fl Fl passwordbox , +default 2048. +.It Fl Fl no-cancel +Do not show +.Dq Cancel +button. +.It Fl Fl no-descriptions +Do not display items desciption, for Checklist, Menu, Radiolist or Treeview; +mutually exclusive with +.Fl Fl no-names . +.It Fl Fl no-lines +Do not draw borders and lines. +.It Fl Fl no-names +Do not display items name, for Checklist, Menu and Radiolist; mutually exclusive +with +.Fl Fl no-descriptions . +.It Fl Fl no-ok +Do not draw +.Dq OK +button. +.It Fl Fl no-shadow +No not draw the shadow of the dialog. +.It Fl Fl ok-label Ar label +Set +.Ar label +for +.Dq OK +button. +.It Fl Fl ok-exit-code Ar retval +Set an exit code value for the +.Dq Ok +button. +.It Fl Fl normal-screen +If available set normal screen mode, see +.Xr terminfo 5 . +.It Fl Fl output-fd Ar fd +Print input from user interface to the specified file descriptor. +.It Fl Fl output-separator Ar sep +Set a sepator for the items in output, default white space. +.It Fl Fl print-maxsize +Screen size. +This option can be used without a dialog. +.It Fl Fl print-size +Print dialog height and width at exit. +.It Fl Fl print-version +Print version. +This option can be used without a dialog. +.It Fl Fl quoted +Quote items in output, default only when necessary. +.It Fl Fl right1-button Ar label +Add a button with +.Ar label . +.It Fl Fl right1-exit-code Ar retval +Set an exit code for +.Fl Fl right1-button . +.It Fl Fl right2-button Ar label +Add a button with +.Ar label . +.It Fl Fl right2-exit-code Ar retval +Set an exit code for +.Fl Fl right2-button . +.It Fl Fl right3-button Ar label +Add a button with +.Ar label . +.It Fl Fl right3-exit-code Ar retval +Set an exit code for +.Fl Fl right3-button . +.It Fl Fl save-theme Ar file +Save the current theme. +This option can be used without a dialog. +.It Fl Fl separate-output +Print selected items separated by a new line and avoid to quote. +.It Fl Fl shadow +Show a shadow for the dialog, enabled by default. +.It Fl Fl single-quoted +Use single quote for items in output. +.It Fl Fl sleep Ar secs +Wait +.Ar secs +seconds to close the dialog. +.It Fl Fl stderr +Print input from user interface to standand error, default. +.It Fl Fl stdout +Print input from user interface to standard output. +.It Fl Fl switch-buttons +Enable focus switching between buttons and input components pressing TAB, +otherwise buttons are always active and ENTER key closes the dialog. +Suitable for: +.Fl Fl form , +.Fl Fl inputbox , +.Fl Fl mixedform , +.Fl Fl passwordbox , +.Fl Fl passwordform , +.Fl Fl timebox , +.Fl Fl calendar +and +.Fl Fl datebox . +.It Fl Fl tab-escape +Replace +.Dq \et +with a tab in +.Ar text . +.It Fl Fl tab-len Ar spaces +Number of spaces to print a TAB in +.Ar text . +.It Fl Fl text-escape +Enable escapes in +.Ar text : +.Bl -column -compact +.It Dq \eZ0 +black. +.It Dq \eZ1 +red. +.It Dq \eZ2 +green. +.It Dq \eZ3 +yellow. +.It Dq \eZ4 +blue. +.It Dq \eZ5 +magenta. +.It Dq \eZ6 +cyan. +.It Dq \eZ7 +white. +.It Dq \eZb +bold. +.It Dq \eZB +disable bold. +.It Dq \eZd +Half bright. +.It Dq \eZD +disable half bright. +.It Dq \eZk +Blink. +.It Dq \eZK +disable blinking. +.It Dq \eZr +reverse foreground and background. +.It Dq \eZR +disable reverse. +.It Dq \eZs +Highlight. +.It Dq \eZS +disable highlighting. +.It Dq \eZu +underline. +.It Dq \eZU +disable underline. +.It Dq \eZn +disable each customization. +.El +.It Fl Fl text-unchanged +Disable +.Ar text +modification. +By default +.Ar text +is changed before to be printed in the dialog. +If it contains at least a +.Dq \en +each new line and TAB is converted to a space, subsequent spaces are merged. +Otherwise new line characters are preserved and a TAB becomes a space. +.It Fl Fl theme Ar theme +Set theme, possible values: +.Dq 3d , +.Dq blackwhite , +.Dq flat . +.It Fl Fl time-format Ar format +String accepted by +.Xr strftime 3 +to customize the output of +.Fl Fl timebox . +.It Fl Fl timeout-exit-code Ar retval +Set an exit code value for the +.Fl Fl pause +timeout. +.It Fl Fl title Ar title +Dialog title. +.El +.Ss Dialogs +The following dialogs are available: +.Bl -tag -width Ds +.It Fl Fl calendar Ar text Ar rows Ar cols Op Ar day Ar month Ar year +Dialog to select a date. +.It Fl Fl checklist Ar text Ar rows Ar cols Ar menurows Oo Ar name Ar desc \ +Ar status Oc ... +Checklist to select some item from a list via the SPACE key. +An item has a +.Ar name , +.Ar desc +and a default +.Ar status +specified by +.Dq on +or +.Dq off . +The names of the selected items are printed to standard error. +.Ar menurows +is the graphical height of the list, 0 for autosize. +.It Fl Fl datebox Ar text Ar rows Ar cols Op Ar day Ar month Ar year +Dialog to select a date. +.It Fl Fl form Ar text Ar rows Ar cols Ar formrows Oo Ar label Ar ylabel \ +Ar xlabel Ar init Ar yfield Ar xfield Ar fieldlen Ar maxletters Oc ... +Dialog to get a list of strings via forms. +A form has a +.Ar label +at the position +.Ar ylabel +and +.Ar xlabel , +a field to get the input at the position +.Ar yfield +and +.Ar xfield +with graphical length +.Ar fieldlen , +.Ar maxletters +is the maximum input length. +The field can be customized, if +.Ar fieldlen +is negative the field is read only and its absolute value is the field length. +If +.Ar maxletters +is 0 it is the absolute value of +.Ar fieldlen . +.Ar init +is a default value. +.Ar formrows +is the graphical height of the list, +.Dv 0 +for autosize. +.It Fl Fl gauge Ar text Ar rows Ar cols Op Ar percentage +Dialog with a bar to shows +.Ar percentage , +then it waits to read +.Dq XXX +from the standard input, then the first string replaces percentage and the +following strings replace +.Ar text +until the next +.Dq XXX , +the loop ends reading +.Dq EOF . +.It Fl Fl infobox Ar text Ar rows Ar cols +Dialog without buttons to display a message and to exit immediately. +.It Fl Fl inputbox Ar text Ar rows Ar cols Op Ar init +Dialog to get a string in input, +.Ar init +is the default value. +.It Fl Fl menu Ar text Ar rows Ar cols Ar menurows Oo Ar name desc Oc ... +Builds a menu to select an item from a list, Space key is equivalent to Enter. +An item has a +.Ar name +and a +.Ar desc . +The name of the selected item is printed to standard error. +.Ar menurows +is the graphical height of the list, 0 for autosize. +.It Fl Fl mixedform Ar text Ar rows Ar cols Ar formrows Oo Ar label Ar ylabel \ +Ar xlabel Ar init Ar yfield Ar xfield Ar fieldlen Ar maxletters Ar flag Oc ... +Dialog to get a list of strings via forms. +A form has a +.Ar label +at the position +.Ar ylabel +and +.Ar xlabel , +a field to get the input with graphical length +.Ar fieldlen +at the position +.Ar yfield +and +.Ar xfield , +.Ar maxletters +is the maximum input length, +.Ar init +is a default value, +.Ar flag +can be 0 for normal field, 1 to hide the typed characters and 2 to set the +field read only. +.Ar formrows +is the graphical height of the list, +.Dv 0 +for autosize. +.It Fl Fl mixedgauge Ar text Ar rows Ar cols Ar mainperc Oo Ar minilabel \ +Ar miniperc Oc ... +Dialog to show a main bar to represent +.Ar mainperc +from 0 to 100. +Some mini bar with a +.Ar minilabel +string and a +.Ar miniperc +with a value from 0 and 100 or negative to print a descriptive string: -1 +.Dq Succeeded , +-2 +.Dq Failed , +-3 +.Dq Passed , +-4 +.Dq Completed , +-5 +.Dq Checked , +-6 +.Dq Done , +-7 +.Dq Skipped , +-8 +.Dq \&In Progress , +-9 +to hide +.Fa miniperc , +-10 +.Dq N/A , +-11 +.Dq Pending , +otherwise +.Dq UNKNOWN . +.It Fl Fl msgbox Ar text Ar rows Ar cols +Dialog to diplay a message without the +.Dq Cancel +button. +UP, DOWN, HOME, END, PAGEUP and PAGEDOWN keys are availble to scroll the text. +.It Fl Fl passwordbox Ar text Ar rows Ar cols Op Ar init +Dialog to get a password, +.Ar init +is the default value. +.It Fl Fl passwordform Ar text Ar rows Ar cols Ar formrows Oo Ar label \ +Ar ylabel Ar xlabel Ar init Ar yfield Ar xfield Ar fieldlen Ar maxletters \ +Oc ... +Dialog to get a list of passwords, equivalent to +.Fl Fl form +except typed characters are hidden. +.It Fl Fl pause Ar text Ar rows Ar cols Ar seconds +Dialog runs until +.Ar seconds +timeout expires or a button is pressed. +.It Fl Fl radiolist Ar text Ar rows Ar cols Ar menurows Oo Ar name Ar desc \ +Ar status Oc ... +Radiolist to select at most an item from a list via the SPACE key. +An item has a +.Ar name , +.Ar desc +and a default +.Ar status +specified by +.Dq on +or +.Dq off . +The name of the selected item is printed to standard error. +.Ar menurows +is the graphical height of the list, 0 for autosize. +.It Fl Fl rangebox Ar text Ar rows Ar cols Ar min Ar max Op Ar init +Dialog to select a value between +.Ar min +and +.Ar max , +.Ar init +is the default value, the keys UP, DOWN, HOME, END, PAGEUP and PAGEDOWN can +change it. +.It Fl Fl textbox Ar file Ar rows Ar cols +Opens and prints +.Ar file . +UP, DOWN, LEFT, RIGHT, HOME, END, PAGEUP and PAGEDOWN keys are available to +navigate the file, TAB changes button. +.Dq OK +button is renamed +.Dq EXIT . +.It Fl Fl timebox Ar text Ar rows Ar cols Op Ar hour Ar min Ar sec +Dialog to select a time. +.It Fl Fl treeview Ar text Ar rows Ar cols Ar menurows Oo Ar depth Ar name \ +Ar desc Ar status Oc ... +Equivalent to Radiolist with +.Fl Fl item-depth +and +.Fl Fl no-name . +.It Fl Fl yesno Ar text Ar rows Ar cols +.Dq Yes-No Question , +.Dq OK +and +.Dq Cancel +buttons are renamed +.Dq Yes +and +.Dq \&No . +UP, DOWN, HOME, END, PAGEUP and PAGEDOWN keys are availble to scroll the text. +.El +.Sh ENVIRONMENT +The following environment variables take effect only on startup, other options +can override their setting. +.Bl -tag -width indent +.It Ev NO_COLOR +If present and not an empty string (regardless of its value) equivalent to +.Fl Fl theme Ar blackwhite . +.It Ev BSDDIALOG_ERROR , Ev BSDDIALOG_OK , Ev BSDDIALOG_CANCEL , \ +Ev BSDDIALOG_HELP , Ev BSDDIALOG_EXTRA , Ev BSDDIALOG_TIMEOUT , \ +Ev BSDDIALOG_ESC , Ev BSDDIALOG_LEFT1 , Ev BSDDIALOG_LEFT2 , \ +Ev BSDDIALOG_LEFT3 , Ev BSDDIALOG_RIGHT1 , Ev BSDDIALOG_RIGHT2 , \ +Ev BSDDIALOG_RIGHT3 +Set exit codes. +.It Ev BSDDIALOG_THEMEFILE Ar file +Equivalent to +.Fl Fl load-theme Ar file . +.El +.Sh FILES +The theme file +.Pa $HOME/.bsddialog.conf +is read on startup if exists. +.Sh EXIT STATUS +The +.Nm +utility exits 255 on unsuccessful, otherwise depending on the button or key +pressed the following values can be returned: +.Bl -column -compact +.It 0 +.Dq OK , +.Dq Yes +or +.Dq Exit +button. +.It 1 +.Dq Cancel +or +.Dq \&No +button. +.It 2 +.Dq Help +button. +.It 3 +.Dq Extra +button. +.It 4 +Timeout. +.It 5 +ESC key. +.It 6 +Left1 generic button. +.It 7 +Left2 generic button. +.It 8 +Left3 generic button. +.It 9 +Right1 generic button. +.It 10 +Right2 generic button. +.It 11 +Right2 generic button. +.El +.Sh EXAMPLES +Backtitle, title and message: +.Dl bsddialog --backtitle MESSAGE --title Msgbox --msgbox Message 0 0 +.Pp +Yes-No Question and theme: +.Dl bsddialog --theme blackwhite --yesno Question 10 30 +.Pp +Save a custom theme: +.Dl bsddialog --save-theme mytheme.txt --infobox \*qSaving theme...\*q 0 0 +.Pp +Load a custom theme: +.Dl bsddialog --load-theme mytheme.txt --infobox \*qCustom theme\*q 0 0 +.Pp +Checklist: +.Dl bsddialog --checklist Checklist 0 0 3 N1 \&D1 off N2 D2 on N3 D3 off +.Pp +Form: +.Dl bsddialog --form Form 0 0 0 L1: 0 0 X 0 4 20 25 L2: 1 0 Y 1 4 20 25 +.Pp +Multi-dialog: +.Dl bsddialog --normal-screen --begin-y 1 --yesno Continue? 0 0 \e \ +--and-dialog --begin-y 10 --infobox Yes 0 0 +.Pp +Bikeshed: +.Dl bsddialog --bikeshed --inputbox Example 0 0 +.Dl bsddialog --bikeshed --datebox Example 0 0 +.Pp +Mixedgauge: +.Dl bsddialog --sleep 3 --mixedgauge Example 10 30 60 L1 \*q -1\*q L2 30 +.Pp +Mixedgauge script: +.Bd -literal -offset indent -compact +perc=0 +while [ $perc -le 100 ] +do + bsddialog --sleep 1 --title Mixedgauge \e + --mixedgauge "\enExample...\en" 0 0 $perc \e + "Hidden" " -9" \e + "Label 1" " -4" \e + "Label 2" " -4" \e + "Label 3" $perc + + perc=`expr $perc + 20` +done +.Ed +.Pp +Gauge script: +.Bd -literal -offset indent -compact +i=1 +for c in A B C D E F G H +do + sleep 1 + echo XXX + echo "$(expr $(expr $i "*" 100) "/" 8)" + echo "[$i/8] Char: $c" + echo XXX + if [ $i -eq 8 ] + then + sleep 1 + echo EOF + fi + i=`expr $i + 1` +done | bsddialog --title Gauge --gauge "Starting..." 10 70 +.Ed +.Sh COMPATIBILITY +Outdated options are retained for compatibility, properly equivalent options are +used: +.Bd -literal -offset indent -compact +Obsolete Equivalent +--and-widget --and-dialog +--calendar <text> 2 <cols> --calendar <text> 0 <cols> +--clear --clear-screen +--colors --text-escape +--defaultno --default-no +--exit-label --ok-label +--help-status --help-print-items +--help-tags --help-print-name +--item-help --item-bottom-desc +--keep-tite --alternate-screen +--no-items --no-descriptions +--no-label --cancel-label +--no-tags --no-names +--nocancel --no-cancel +--nook --no-ok +--separator --output-separator +--yes-label --ok-label +.Ed +.Sh SEE ALSO +.Xr bsddialog 3 , +.Xr strftime 3 , +.Xr terminfo 5 +.Sh HISTORY +The +.Nm bsddialog +utility first appeared in +.Fx 14.0 . +.Sh AUTHORS +.Nm bsddialog +was written by +.An Alfonso Sabato Siciliano +.Aq Mt asiciliano@FreeBSD.org . +.Pp +.Nm bsddialog +provides also a subset of the functionality described in the +.Nm dialog +manual. +The following features were reimplemented: +.Pp +Options: +.Fl Fl and-widget , +.Fl Fl ascii-lines , +.Fl Fl backtitle , +.Fl Fl cancel-label , +.Fl Fl clear , +.Fl Fl colors , +.Fl Fl cr-wrap , +.Fl Fl date-format , +.Fl Fl defaultno , +.Fl Fl default-button , +.Fl Fl default-no , +.Fl Fl default-item , +.Fl Fl exit-label , +.Fl Fl extra-button , +.Fl Fl extra-label , +.Fl Fl help , +.Fl Fl help-button , +.Fl Fl help-label , +.Fl Fl help-status , +.Fl Fl help-tags , +.Fl Fl hfile , +.Fl Fl hline , +.Fl Fl ignore , +.Fl Fl insecure , +.Fl Fl item-help , +.Fl Fl keep-tite , +.Fl Fl max-input , +.Fl Fl no-cancel , +.Fl Fl nocancel , +.Fl Fl no-items , +.Fl Fl no-label , +.Fl Fl no-lines , +.Fl Fl no-ok , +.Fl Fl nook , +.Fl Fl no-shadow , +.Fl Fl no-tags , +.Fl Fl ok-label , +.Fl Fl output-fd , +.Fl Fl output-separator , +.Fl Fl print-maxsize , +.Fl Fl print-size , +.Fl Fl print-version , +.Fl Fl quoted , +.Fl Fl separate-output , +.Fl Fl separator , +.Fl Fl shadow , +.Fl Fl single-quoted , +.Fl Fl sleep , +.Fl Fl stderr , +.Fl Fl stdout , +.Fl Fl tab-len , +.Fl Fl time-format , +.Fl Fl title , +.Fl Fl version , +.Fl Fl yes-label . +.Pp +Dialogs: +.Fl Fl calendar , +.Fl Fl checklist , +.Fl Fl form , +.Fl Fl gauge , +.Fl Fl infobox , +.Fl Fl inputbox , +.Fl Fl menu , +.Fl Fl mixedform , +.Fl Fl mixedgauge , +.Fl Fl msgbox , +.Fl Fl passwordbox , +.Fl Fl passwordform , +.Fl Fl pause , +.Fl Fl radiolist , +.Fl Fl rangebox , +.Fl Fl textbox , +.Fl Fl timebox , +.Fl Fl treeview , +.Fl Fl yesno . +.Pp +Some feature differs in input, output, or behavior. +Compatibility is not a priority for future development. +.Sh THANKS TO +.An Baptiste Daroussin +.Aq Mt bapt@FreeBSD.org , +.An \&Ed Maste +.Aq Mt emaste@FreeBSD.org , +.An Juraj Lutter +.Aq Mt otis@FreeBSD.org +and +.An Trenton Schulz +for suggestions, help, and testing. diff --git a/utility/bsddialog.c b/utility/bsddialog.c new file mode 100644 index 000000000000..d4d1fc3e0f3a --- /dev/null +++ b/utility/bsddialog.c @@ -0,0 +1,338 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021-2023 Alfonso Sabato Siciliano + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <getopt.h> +#include <limits.h> +#include <locale.h> +#include <signal.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <term.h> + +#include <bsddialog.h> +#include <bsddialog_theme.h> + +#include "util.h" + +#define EXITCODE(retval) (exitcodes[retval + 1].value) +#define UNUSED_PAR(x) UNUSED_ ## x __attribute__((__unused__)) + +static void custom_text(struct options *opt, char *text, char *buf); + +/* Exit codes */ +struct exitcode { + const char *name; + int value; +}; + +static struct exitcode exitcodes[14] = { + { "BSDDIALOG_ERROR", 255 }, + { "BSDDIALOG_OK", 0 }, + { "BSDDIALOG_CANCEL", 1 }, + { "BSDDIALOG_HELP", 2 }, + { "BSDDIALOG_EXTRA", 3 }, + { "BSDDIALOG_TIMEOUT", 4 }, + { "BSDDIALOG_ESC", 5 }, + { "BSDDIALOG_LEFT1", 6 }, + { "BSDDIALOG_LEFT2", 7 }, + { "BSDDIALOG_LEFT3", 8 }, + { "BSDDIALOG_RIGHT1", 9 }, + { "BSDDIALOG_RIGHT2", 10 }, + { "BSDDIALOG_RIGHT3", 11 }, + { "BSDDIALOG_ITEM_HELP", 2 } /* like HELP by default */ +}; + +void set_exit_code(int lib_retval, int exitcode) +{ + exitcodes[lib_retval + 1].value = exitcode; +} + +/* Error */ +void exit_error(bool usage, const char *fmt, ...) +{ + va_list arg_ptr; + + if (bsddialog_inmode()) + bsddialog_end(); + printf("Error: "); + va_start(arg_ptr, fmt); + vprintf(fmt, arg_ptr); + va_end(arg_ptr); + printf(".\n\n"); + if (usage) { + printf("See \'bsddialog --help\' or \'man 1 bsddialog\' "); + printf("for more information.\n"); + } + + exit (EXITCODE(BSDDIALOG_ERROR)); +} + +void error_args(const char *dialog, int argc, char **argv) +{ + int i; + + if (bsddialog_inmode()) + bsddialog_end(); + printf("Error: %s unexpected argument%s:", dialog, argc > 1 ? "s" : ""); + for (i = 0; i < argc; i++) + printf(" \"%s\"", argv[i]); + printf(".\n\n"); + printf("See \'bsddialog --help\' or \'man 1 bsddialog\' "); + printf("for more information.\n"); + + exit (EXITCODE(BSDDIALOG_ERROR)); +} + +/* init */ +static void sigint_handler(int UNUSED_PAR(sig)) +{ + bsddialog_end(); + + exit(EXITCODE(BSDDIALOG_ERROR)); +} + +static void start_bsddialog_mode(void) +{ + if (bsddialog_inmode()) + return; + if (bsddialog_init() != BSDDIALOG_OK) + exit_error(false, bsddialog_geterror()); + + signal(SIGINT, sigint_handler); +} + +static void getenv_exitcodes(void) +{ + int i; + int value; + char *envvalue; + + for (i = 0; i < 10; i++) { + envvalue = getenv(exitcodes[i].name); + if (envvalue == NULL || envvalue[0] == '\0') + continue; + value = (int)strtol(envvalue, NULL, 10); + exitcodes[i].value = value; + /* ITEM_HELP follows HELP without explicit setting */ + if(i == BSDDIALOG_HELP + 1) + exitcodes[BSDDIALOG_ITEM_HELP + 1].value = value; + } +} + +/* + * bsddialog utility: TUI widgets and dialogs. + */ +int main(int argc, char *argv[argc]) +{ + bool startup; + int i, rows, cols, retval, parsed, nargc, firstoptind; + char *text, **nargv, *pn; + struct bsddialog_conf conf; + struct options opt; + + setlocale(LC_ALL, ""); + getenv_exitcodes(); + firstoptind = optind; + pn = argv[0]; + retval = BSDDIALOG_OK; + + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "--version") == 0) { + printf("Version: %s\n", LIBBSDDIALOG_VERSION); + return (BSDDIALOG_OK); + } + if (strcmp(argv[i], "--help") == 0) { + usage(); + return (BSDDIALOG_OK); + } + } + + startup = true; + while (true) { + parsed = parseargs(argc, argv, &conf, &opt); + nargc = argc - parsed; + nargv = argv + parsed; + argc = parsed - optind; + argv += optind; + + if (opt.mandatory_dialog && opt.dialogbuilder == NULL) + exit_error(true, "expected a --<dialog>"); + + if (opt.dialogbuilder == NULL && argc > 0) + error_args("(no --<dialog>)", argc, argv); + + /* --print-maxsize or --print-version */ + if (opt.mandatory_dialog == false && opt.clearscreen == false && + opt.savethemefile == NULL && opt.dialogbuilder == NULL) { + retval = BSDDIALOG_OK; + break; + } + + /* --<dialog>, --save-theme or clear-screen */ + text = NULL; /* useless inits, fix compiler warnings */ + rows = BSDDIALOG_AUTOSIZE; + cols = BSDDIALOG_AUTOSIZE; + if (opt.dialogbuilder != NULL) { + if (argc < 3) + exit_error(true, + "expected <text> <rows> <cols>"); + if ((text = strdup(argv[0])) == NULL) + exit_error(false, "cannot allocate <text>"); + if (opt.dialogbuilder != textbox_builder) + custom_text(&opt, argv[0], text); + rows = (int)strtol(argv[1], NULL, 10); + cols = (int)strtol(argv[2], NULL, 10); + argc -= 3; + argv += 3; + } + + /* bsddialog terminal mode (first iteration) */ + start_bsddialog_mode(); + + if (opt.screen_mode != NULL) { + opt.screen_mode = tigetstr(opt.screen_mode); + if (opt.screen_mode != NULL && + opt.screen_mode != (char*)-1) { + tputs(opt.screen_mode, 1, putchar); + fflush(stdout); + bsddialog_refresh(); + } + } + + /* theme */ + if (startup) + startuptheme(); + startup = false; + if ((int)opt.theme >= 0) + setdeftheme(opt.theme); + if (opt.loadthemefile != NULL) + loadtheme(opt.loadthemefile, false); + if (opt.bikeshed) + bikeshed(&conf); + if (opt.savethemefile != NULL) + savetheme(opt.savethemefile); + + /* backtitle and dialog */ + if (opt.dialogbuilder == NULL) + break; + if (opt.backtitle != NULL) + if(bsddialog_backtitle(&conf, opt.backtitle)) + exit_error(false, bsddialog_geterror()); + retval = opt.dialogbuilder(&conf, text, rows, cols, argc, argv, + &opt); + free(text); + if (retval == BSDDIALOG_ERROR) + exit_error(false, bsddialog_geterror()); + if (conf.get_height != NULL && conf.get_width != NULL) + dprintf(opt.output_fd, "DialogSize: %d, %d\n", + *conf.get_height, *conf.get_width); + if (opt.clearscreen) + bsddialog_clear(0); + opt.clearscreen = false; + /* --and-dialog ends loop with Cancel or ESC */ + if (retval == BSDDIALOG_CANCEL || retval == BSDDIALOG_ESC) + break; + argc = nargc; + argv = nargv; + if (argc <= 0) + break; + /* prepare next parseargs() call */ + argc++; + argv--; + argv[0] = pn; + optind = firstoptind; + } + + if (bsddialog_inmode()) { + /* --clear-screen can be a single option */ + if (opt.clearscreen) + bsddialog_clear(0); + bsddialog_end(); + } + /* end bsddialog terminal mode */ + + return (EXITCODE(retval)); +} + +void custom_text(struct options *opt, char *text, char *buf) +{ + bool trim, crwrap; + int i, j; + + if (strstr(text, "\\n") == NULL) { + /* "hasnl" mode */ + trim = true; + crwrap = true; + } else { + trim = false; + crwrap = opt->cr_wrap; + } + if (opt->text_unchanged) { + trim = false; + crwrap = true; + } + + i = j = 0; + while (text[i] != '\0') { + switch (text[i]) { + case '\\': + buf[j] = '\\'; + switch (text[i+1]) { + case 'n': /* implicitly in "hasnl" mode */ + buf[j] = '\n'; + i++; + if (text[i+1] == '\n') + i++; + break; + case 't': + if (opt->tab_escape) { + buf[j] = '\t'; + } else { + j++; + buf[j] = 't'; + } + i++; + break; + } + break; + case '\n': + buf[j] = crwrap ? '\n' : ' '; + break; + case '\t': + buf[j] = opt->text_unchanged ? '\t' : ' '; + break; + default: + buf[j] = text[i]; + } + i++; + if (!trim || buf[j] != ' ' || j == 0 || buf[j-1] != ' ') + j++; + } + buf[j] = '\0'; +} diff --git a/utility/util.h b/utility/util.h new file mode 100644 index 000000000000..2750c2ee6951 --- /dev/null +++ b/utility/util.h @@ -0,0 +1,125 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021-2023 Alfonso Sabato Siciliano + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _BSDDIALOG_UTIL_H_ +#define _BSDDIALOG_UTIL_H_ + +/* + * Exit codes and errors, bsddialog.c + */ +#define BSDDIALOG_ITEM_HELP 12 + +void set_exit_code(int lib_retval, int exitcode); +void exit_error(bool usage, const char *fmt, ...); +void error_args(const char *dialog, int argc, char **argv); + +/* + * Command Line, util_cli.c + */ +struct options { + /* Menus options */ + bool item_always_quote; + char *item_default; + bool item_depth; + char *item_output_sep; + bool item_output_sepnl; + bool item_prefix; + bool item_singlequote; + /* Menus and Forms options */ + bool help_print_item_name; + bool help_print_items; + bool item_bottomdesc; + /* Forms options */ + int unsigned max_input_form; + /* Date and Time options */ + char *date_fmt; + char *time_fmt; + /* General options */ + int getH; + int getW; + bool ignore; + int output_fd; + /* Text option */ + bool cr_wrap; + bool tab_escape; + bool text_unchanged; + /* Theme and Screen options*/ + char *backtitle; + bool bikeshed; + enum bsddialog_default_theme theme; + bool clearscreen; + char *loadthemefile; + char *savethemefile; + const char *screen_mode; + /* Dialog */ + bool mandatory_dialog; + const char *name; + int (*dialogbuilder)(struct bsddialog_conf *conf, char* text, int rows, + int cols, int argc, char **argv, struct options *opt); +}; + +void usage(void); +int +parseargs(int argc, char **argv, struct bsddialog_conf *conf, + struct options *opt); + +/* + * Dialogs builders, util_builders.c + */ +#define BUILDER_ARGS struct bsddialog_conf *conf, char* text, int rows, \ + int cols, int argc, char **argv, struct options *opt +int calendar_builder(BUILDER_ARGS); +int checklist_builder(BUILDER_ARGS); +int datebox_builder(BUILDER_ARGS); +int form_builder(BUILDER_ARGS); +int gauge_builder(BUILDER_ARGS); +int infobox_builder(BUILDER_ARGS); +int inputbox_builder(BUILDER_ARGS); +int menu_builder(BUILDER_ARGS); +int mixedform_builder(BUILDER_ARGS); +int mixedgauge_builder(BUILDER_ARGS); +int msgbox_builder(BUILDER_ARGS); +int passwordbox_builder(BUILDER_ARGS); +int passwordform_builder(BUILDER_ARGS); +int pause_builder(BUILDER_ARGS); +int radiolist_builder(BUILDER_ARGS); +int rangebox_builder(BUILDER_ARGS); +int textbox_builder(BUILDER_ARGS); +int timebox_builder(BUILDER_ARGS); +int treeview_builder(BUILDER_ARGS); +int yesno_builder(BUILDER_ARGS); + +/* + * Theme, util_theme.c + */ +void savetheme(const char *file); +void loadtheme(const char *file, bool compatibility); +void setdeftheme(enum bsddialog_default_theme theme); +void bikeshed(struct bsddialog_conf *conf); +void startuptheme(void); + +#endif diff --git a/utility/util_builders.c b/utility/util_builders.c new file mode 100644 index 000000000000..2dfa20cb0f86 --- /dev/null +++ b/utility/util_builders.c @@ -0,0 +1,768 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021-2023 Alfonso Sabato Siciliano + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <bsddialog.h> +#include <bsddialog_theme.h> + +#include "util.h" + +#define NO_PRINT_VALUES(rv) \ +(rv == BSDDIALOG_ERROR || rv == BSDDIALOG_CANCEL || rv == BSDDIALOG_ESC) + +/* message */ +int infobox_builder(BUILDER_ARGS) +{ + if (argc > 0) + error_args(opt->name, argc, argv); + + return (bsddialog_infobox(conf, text, rows, cols)); +} + +int msgbox_builder(BUILDER_ARGS) +{ + if (argc > 0) + error_args(opt->name, argc, argv); + + return (bsddialog_msgbox(conf, text, rows, cols)); +} + +int yesno_builder(BUILDER_ARGS) +{ + if (argc > 0) + error_args(opt->name, argc, argv); + + return (bsddialog_yesno(conf, text, rows, cols)); +} + +/* textbox */ +int textbox_builder(BUILDER_ARGS) +{ + if (argc > 0) + error_args(opt->name, argc, argv); + + return (bsddialog_textbox(conf, text, rows, cols)); +} + +/* bar */ +int gauge_builder(BUILDER_ARGS) +{ + int output; + unsigned int perc; + + perc = 0; + if (argc == 1) { + perc = (u_int)strtoul(argv[0], NULL, 10); + perc = perc > 100 ? 100 : perc; + } else if (argc > 1) { + error_args(opt->name, argc - 1, argv + 1); + } + + output = bsddialog_gauge(conf, text, rows, cols, perc, STDIN_FILENO, + "XXX", "EOF"); + + return (output); +} + +int mixedgauge_builder(BUILDER_ARGS) +{ + int output, *minipercs; + unsigned int i, mainperc, nminibars; + const char **minilabels; + + if (argc < 1) + exit_error(true, "%s missing <mainperc>", opt->name); + if (((argc-1) % 2) != 0) + exit_error(true, + "bad %s pair number [<minilabel> <miniperc>]", opt->name); + + mainperc = (u_int)strtoul(argv[0], NULL, 10); + mainperc = mainperc > 100 ? 100 : mainperc; + argc--; + argv++; + + nminibars = argc / 2; + if ((minilabels = calloc(nminibars, sizeof(char*))) == NULL) + exit_error(false, "Cannot allocate memory for minilabels"); + if ((minipercs = calloc(nminibars, sizeof(int))) == NULL) + exit_error(false, "Cannot allocate memory for minipercs"); + + for (i = 0; i < nminibars; i++) { + minilabels[i] = argv[i * 2]; + minipercs[i] = (int)strtol(argv[i * 2 + 1], NULL, 10); + } + + output = bsddialog_mixedgauge(conf, text, rows, cols, mainperc, + nminibars, minilabels, minipercs); + + return (output); +} + +int pause_builder(BUILDER_ARGS) +{ + int output; + unsigned int secs; + + if (argc == 0) + exit_error(true, "--pause missing <seconds>"); + if (argc > 1) + error_args(opt->name, argc - 1, argv + 1); + + secs = (u_int)strtoul(argv[0], NULL, 10); + output = bsddialog_pause(conf, text, rows, cols, &secs); + + return (output); +} + +int rangebox_builder(BUILDER_ARGS) +{ + int output, min, max, value; + + if (argc < 2) + exit_error(true, "--rangebox missing <min> <max> [<init>]"); + if (argc > 3) + error_args("--rangebox", argc - 3, argv + 3); + + min = (int)strtol(argv[0], NULL, 10); + max = (int)strtol(argv[1], NULL, 10); + + if (argc == 3) { + value = (int)strtol(argv[2], NULL, 10); + value = value < min ? min : value; + value = value > max ? max : value; + } else + value = min; + + output = bsddialog_rangebox(conf, text, rows, cols, min, max, &value); + if (NO_PRINT_VALUES(output) == false) + dprintf(opt->output_fd, "%d", value); + + return (output); +} + +/* date and time */ +static int date(BUILDER_ARGS) +{ + int rv; + unsigned int yy, mm, dd; + time_t cal; + struct tm *localtm; + char stringdate[1024]; + + time(&cal); + localtm = localtime(&cal); + yy = localtm->tm_year + 1900; + mm = localtm->tm_mon + 1; + dd = localtm->tm_mday; + + if (argc > 3) { + error_args(opt->name, argc - 3, argv + 3); + } else if (argc == 3) { + /* lib checks/sets max and min */ + dd = (u_int)strtoul(argv[0], NULL, 10); + mm = (u_int)strtoul(argv[1], NULL, 10); + yy = (u_int)strtoul(argv[2], NULL, 10); + } + + if (strcmp(opt->name, "--datebox") == 0) + rv = bsddialog_datebox(conf, text, rows, cols, &yy, &mm, &dd); + else + rv = bsddialog_calendar(conf, text, rows, cols, &yy, &mm, &dd); + if (NO_PRINT_VALUES(rv)) + return (rv); + + if (opt->date_fmt != NULL) { + time(&cal); + localtm = localtime(&cal); + localtm->tm_year = yy - 1900; + localtm->tm_mon = mm - 1; + localtm->tm_mday = dd; + strftime(stringdate, 1024, opt->date_fmt, localtm); + dprintf(opt->output_fd, "%s", stringdate); + } else if (opt->bikeshed && ~dd & 1) { + dprintf(opt->output_fd, "%u/%u/%u", dd, mm, yy); + } else { + dprintf(opt->output_fd, "%02u/%02u/%u", dd, mm, yy); + } + + return (rv); +} + +int calendar_builder(BUILDER_ARGS) +{ + /* Use height autosizing with rows = 2. Documented in bsddialog(1). + * + * f_dialog_calendar_size() in bsdconfig/share/dialog.subr:1352 + * computes height 2 for `dialog --calendar', called by: + * 1) f_dialog_input_expire_password() in + * bsdconfig/usermgmt/share/user_input.subr:517 and + * 2) f_dialog_input_expire_account() in + * bsdconfig/usermgmt/share/user_input.subr:660. + * + * Then use height autosizing with 2 that is min height like dialog. + */ + if (rows == 2) + rows = 0; + + return (date(conf, text, rows, cols, argc, argv, opt)); +} + +int datebox_builder(BUILDER_ARGS) +{ + return (date(conf, text, rows, cols, argc, argv, opt)); +} + +int timebox_builder(BUILDER_ARGS) +{ + int output; + unsigned int hh, mm, ss; + time_t clock; + struct tm *localtm; + char stringtime[1024]; + + time(&clock); + localtm = localtime(&clock); + hh = localtm->tm_hour; + mm = localtm->tm_min; + ss = localtm->tm_sec; + + if (argc > 3) { + error_args("--timebox", argc - 3, argv + 3); + } else if (argc == 3) { + hh = (u_int)strtoul(argv[0], NULL, 10); + mm = (u_int)strtoul(argv[1], NULL, 10); + ss = (u_int)strtoul(argv[2], NULL, 10); + } + + output = bsddialog_timebox(conf, text, rows, cols, &hh, &mm, &ss); + if (NO_PRINT_VALUES(output)) + return (output); + + if (opt->time_fmt != NULL) { + time(&clock); + localtm = localtime(&clock); + localtm->tm_hour = hh; + localtm->tm_min = mm; + localtm->tm_sec = ss; + strftime(stringtime, 1024, opt->time_fmt, localtm); + dprintf(opt->output_fd, "%s", stringtime); + } else if (opt->bikeshed && ~ss & 1) { + dprintf(opt->output_fd, "%u:%u:%u", hh, mm, ss); + } else { + dprintf(opt->output_fd, "%02u:%02u:%02u", hh, mm, ss); + } + + return (output); +} + +/* menu */ +static void +get_menu_items(int argc, char **argv, bool setprefix, bool setdepth, + bool setname, bool setdesc, bool setstatus, bool sethelp, + unsigned int *nitems, struct bsddialog_menuitem **items, int *focusitem, + struct options *opt) +{ + unsigned int i, j, sizeitem; + + *focusitem = -1; + + sizeitem = 0; + sizeitem += setprefix ? 1 : 0; + sizeitem += setdepth ? 1 : 0; + sizeitem += setname ? 1 : 0; + sizeitem += setdesc ? 1 : 0; + sizeitem += setstatus ? 1 : 0; + sizeitem += sethelp ? 1 : 0; + if ((argc % sizeitem) != 0) + exit_error(true, "%s bad arguments items number", opt->name); + + *nitems = argc / sizeitem; + *items = calloc(*nitems, sizeof(struct bsddialog_menuitem)); + if (items == NULL) + exit_error(false, "%s cannot allocate items", opt->name); + + j = 0; + for (i = 0; i < *nitems; i++) { + (*items)[i].prefix = setprefix ? argv[j++] : ""; + (*items)[i].depth = setdepth ? + (u_int)strtoul(argv[j++], NULL, 0) : 0; + (*items)[i].name = setname ? argv[j++] : ""; + (*items)[i].desc = setdesc ? argv[j++] : ""; + if (setstatus) { + if (strcasecmp(argv[j], "on") == 0) + (*items)[i].on = true; + else if (strcasecmp(argv[j], "off") == 0) + (*items)[i].on = false; + else + exit_error(true, + "\"%s\" (item %i) invalid status \"%s\"", + (*items)[i].name, i+1, argv[j]); + j++; + } else + (*items)[i].on = false; + (*items)[i].bottomdesc = sethelp ? argv[j++] : ""; + + if (opt->item_default != NULL && *focusitem == -1) + if (strcmp((*items)[i].name, opt->item_default) == 0) + *focusitem = i; + } +} + +static void +print_menu_items(int output, int nitems, struct bsddialog_menuitem *items, + int focusitem, struct options *opt) +{ + bool sep, sepbefore, sepafter, sepsecond, toquote, ismenu, ischecklist; + int i; + char quotech; + const char *focusname, *sepstr; + + ismenu = (strcmp(opt->name, "--menu") == 0) ? true : false; + ischecklist = (strcmp(opt->name, "--checklist") == 0) ? true : false; + sep = false; + quotech = opt->item_singlequote ? '\'' : '"'; + + if (NO_PRINT_VALUES(output)) + return; + + if (output == BSDDIALOG_HELP) { + dprintf(opt->output_fd, "HELP "); + + if (focusitem >= 0) { + focusname = items[focusitem].name; + if (opt->item_bottomdesc && + opt->help_print_item_name == false) + focusname = items[focusitem].bottomdesc; + + toquote = false; + if (strchr(focusname, ' ') != NULL) { + toquote = opt->item_always_quote; + if (ismenu == false && + opt->item_output_sepnl == false) + toquote = true; + } + if (toquote) { + dprintf(opt->output_fd, "%c%s%c", + quotech, focusname, quotech); + } else + dprintf(opt->output_fd, "%s", focusname); + } + + if (ismenu || opt->help_print_items == false) + return; + sep = true; + } + + sepbefore = false; + sepsecond = false; + if ((sepstr = opt->item_output_sep) == NULL) { + if (opt->item_output_sepnl) + sepstr = "\n"; + else { + sepstr = " "; + sepsecond = true; + } + } else + sepbefore = true; + + sepafter = false; + if (opt->item_output_sepnl) { + sepbefore = false; + sepafter = true; + } + + for (i = 0; i < nitems; i++) { + if (items[i].on == false) + continue; + + if (sep || sepbefore) + dprintf(opt->output_fd, "%s", sepstr); + sep = false; + if (sepsecond) + sep = true; + + toquote = false; + if (strchr(items[i].name, ' ') != NULL) { + toquote = opt->item_always_quote; + if (ischecklist && opt->item_output_sepnl == false) + toquote = true; + } + if (toquote) + dprintf(opt->output_fd, "%c%s%c", + quotech, items[i].name, quotech); + else + dprintf(opt->output_fd, "%s", items[i].name); + + if (sepafter) + dprintf(opt->output_fd, "%s", sepstr); + } +} + +int checklist_builder(BUILDER_ARGS) +{ + int output, focusitem; + unsigned int menurows, nitems; + struct bsddialog_menuitem *items; + + if (argc < 1) + exit_error(true, "--checklist missing <menurows>"); + menurows = (u_int)strtoul(argv[0], NULL, 10); + + get_menu_items(argc-1, argv+1, opt->item_prefix, opt->item_depth, true, + true, true, opt->item_bottomdesc, &nitems, &items, &focusitem, opt); + + output = bsddialog_checklist(conf, text, rows, cols, menurows, nitems, + items, &focusitem); + + print_menu_items(output, nitems, items, focusitem, opt); + free(items); + + if (output == BSDDIALOG_HELP && opt->item_bottomdesc) + output = BSDDIALOG_ITEM_HELP; + + return (output); +} + +int menu_builder(BUILDER_ARGS) +{ + int output, focusitem; + unsigned int menurows, nitems; + struct bsddialog_menuitem *items; + + if (argc < 1) + exit_error(true, "--menu missing <menurows>"); + menurows = (u_int)strtoul(argv[0], NULL, 10); + + get_menu_items(argc-1, argv+1, opt->item_prefix, opt->item_depth, true, + true, false, opt->item_bottomdesc, &nitems, &items, &focusitem, + opt); + + output = bsddialog_menu(conf, text, rows, cols, menurows, nitems, + items, &focusitem); + + print_menu_items(output, nitems, items, focusitem, opt); + free(items); + + if (output == BSDDIALOG_HELP && opt->item_bottomdesc) + output = BSDDIALOG_ITEM_HELP; + + return (output); +} + +int radiolist_builder(BUILDER_ARGS) +{ + int output, focusitem; + unsigned int menurows, nitems; + struct bsddialog_menuitem *items; + + if (argc < 1) + exit_error(true, "--radiolist missing <menurows>"); + menurows = (u_int)strtoul(argv[0], NULL, 10); + + get_menu_items(argc-1, argv+1, opt->item_prefix, opt->item_depth, true, + true, true, opt->item_bottomdesc, &nitems, &items, &focusitem, opt); + + output = bsddialog_radiolist(conf, text, rows, cols, menurows, nitems, + items, &focusitem); + + print_menu_items(output, nitems, items, focusitem, opt); + free(items); + + if (output == BSDDIALOG_HELP && opt->item_bottomdesc) + output = BSDDIALOG_ITEM_HELP; + + return (output); +} + +int treeview_builder(BUILDER_ARGS) +{ + int output, focusitem; + unsigned int menurows, nitems; + struct bsddialog_menuitem *items; + + if (argc < 1) + exit_error(true, "--treeview missing <menurows>"); + menurows = (u_int)strtoul(argv[0], NULL, 10); + + get_menu_items(argc-1, argv+1, opt->item_prefix, true, true, true, true, + opt->item_bottomdesc, &nitems, &items, &focusitem, opt); + + conf->menu.no_name = true; + conf->menu.align_left = true; + + output = bsddialog_radiolist(conf, text, rows, cols, menurows, nitems, + items, &focusitem); + + print_menu_items(output, nitems, items, focusitem, opt); + free(items); + + if (output == BSDDIALOG_HELP && opt->item_bottomdesc) + output = BSDDIALOG_ITEM_HELP; + + return (output); +} + +/* form */ +static void +print_form_items(int output, int nitems, struct bsddialog_formitem *items, + int focusitem, struct options *opt) +{ + int i; + const char *helpname; + + if (NO_PRINT_VALUES(output)) + return; + + if (output == BSDDIALOG_HELP) { + dprintf(opt->output_fd, "HELP"); + if (focusitem >= 0) { + helpname = items[focusitem].label; + if (opt->item_bottomdesc && + opt->help_print_item_name == false) + helpname = items[focusitem].bottomdesc; + dprintf(opt->output_fd, " %s", helpname); + } + if(opt->help_print_items == false) + return; + dprintf(opt->output_fd, "\n"); + } + + for (i = 0; i < nitems; i++) { + dprintf(opt->output_fd, "%s\n", items[i].value); + free(items[i].value); + } +} + +int form_builder(BUILDER_ARGS) +{ + int output, fieldlen, valuelen, focusitem; + unsigned int i, j, flags, formheight, nitems, sizeitem; + struct bsddialog_formitem *items; + + if (argc < 1) + exit_error(true, "--form missing <formheight>"); + formheight = (u_int)strtoul(argv[0], NULL, 10); + + argc--; + argv++; + sizeitem = opt->item_bottomdesc ? 9 : 8; + if (argc % sizeitem != 0) + exit_error(true, "--form bad number of arguments items"); + + nitems = argc / sizeitem; + if ((items = calloc(nitems, sizeof(struct bsddialog_formitem))) == NULL) + exit_error(false, "cannot allocate memory for form items"); + j = 0; + for (i = 0; i < nitems; i++) { + items[i].label = argv[j++]; + items[i].ylabel = (u_int)strtoul(argv[j++], NULL, 10); + items[i].xlabel = (u_int)strtoul(argv[j++], NULL, 10); + items[i].init = argv[j++]; + items[i].yfield = (u_int)strtoul(argv[j++], NULL, 10); + items[i].xfield = (u_int)strtoul(argv[j++], NULL, 10); + + fieldlen = (int)strtol(argv[j++], NULL, 10); + items[i].fieldlen = abs(fieldlen); + + valuelen = (int)strtol(argv[j++], NULL, 10); + items[i].maxvaluelen = valuelen == 0 ? abs(fieldlen) : valuelen; + + flags = (fieldlen < 0 ? BSDDIALOG_FIELDREADONLY : 0); + items[i].flags = flags; + + items[i].bottomdesc = opt->item_bottomdesc ? argv[j++] : ""; + } + + focusitem = -1; + output = bsddialog_form(conf, text, rows, cols, formheight, nitems, + items, &focusitem); + print_form_items(output, nitems, items, focusitem, opt); + free(items); + + if (output == BSDDIALOG_HELP && opt->item_bottomdesc) + output = BSDDIALOG_ITEM_HELP; + + return (output); +} + +int inputbox_builder(BUILDER_ARGS) +{ + int output; + struct bsddialog_formitem item; + + if (argc > 1) + error_args("--inputbox", argc - 1, argv + 1); + + item.label = ""; + item.ylabel = 0; + item.xlabel = 0; + item.init = argc > 0 ? argv[0] : ""; + item.yfield = 0; + item.xfield = 0; + item.fieldlen = 1; + item.maxvaluelen = opt->max_input_form; + item.flags = BSDDIALOG_FIELDNOCOLOR; + item.flags |= BSDDIALOG_FIELDCURSOREND; + item.flags |= BSDDIALOG_FIELDEXTEND; + item.bottomdesc = ""; + + output = bsddialog_form(conf, text, rows, cols, 1, 1, &item, NULL); + print_form_items(output, 1, &item, -1, opt); + + return (output); +} + +int mixedform_builder(BUILDER_ARGS) +{ + int output, focusitem; + unsigned int i, j, formheight, nitems, sizeitem; + struct bsddialog_formitem *items; + + if (argc < 1) + exit_error(true, "--mixedform missing <formheight>"); + formheight = (u_int)strtoul(argv[0], NULL, 10); + + argc--; + argv++; + sizeitem = opt->item_bottomdesc ? 10 : 9; + if (argc % sizeitem != 0) + exit_error(true, "--mixedform bad number of arguments items"); + + nitems = argc / sizeitem; + if ((items = calloc(nitems, sizeof(struct bsddialog_formitem))) == NULL) + exit_error(false, "cannot allocate memory for form items"); + j = 0; + for (i = 0; i < nitems; i++) { + items[i].label = argv[j++]; + items[i].ylabel = (u_int)strtoul(argv[j++], NULL, 10); + items[i].xlabel = (u_int)strtoul(argv[j++], NULL, 10); + items[i].init = argv[j++]; + items[i].yfield = (u_int)strtoul(argv[j++], NULL, 10); + items[i].xfield = (u_int)strtoul(argv[j++], NULL, 10); + items[i].fieldlen = (u_int)strtoul(argv[j++], NULL, 10); + items[i].maxvaluelen = (u_int)strtoul(argv[j++], NULL, 10); + items[i].flags = (u_int)strtoul(argv[j++], NULL, 10); + items[i].bottomdesc = opt->item_bottomdesc ? argv[j++] : ""; + } + + focusitem = -1; + output = bsddialog_form(conf, text, rows, cols, formheight, nitems, + items, &focusitem); + print_form_items(output, nitems, items, focusitem, opt); + free(items); + + if (output == BSDDIALOG_HELP && opt->item_bottomdesc) + output = BSDDIALOG_ITEM_HELP; + + return (output); +} + +int passwordbox_builder(BUILDER_ARGS) +{ + int output; + struct bsddialog_formitem item; + + if (argc > 1) + error_args("--passwordbox", argc - 1, argv + 1); + + item.label = ""; + item.ylabel = 0; + item.xlabel = 0; + item.init = argc > 0 ? argv[0] : ""; + item.yfield = 0; + item.xfield = 0; + item.fieldlen = 1; + item.maxvaluelen = opt->max_input_form; + item.flags = BSDDIALOG_FIELDHIDDEN; + item.flags |= BSDDIALOG_FIELDNOCOLOR; + item.flags |= BSDDIALOG_FIELDCURSOREND; + item.flags |= BSDDIALOG_FIELDEXTEND; + item.bottomdesc = ""; + + output = bsddialog_form(conf, text, rows, cols, 1, 1, &item, NULL); + print_form_items(output, 1, &item, -1, opt); + + return (output); +} + +int passwordform_builder(BUILDER_ARGS) +{ + int output, fieldlen, valuelen, focusitem; + unsigned int i, j, flags, formheight, nitems, sizeitem; + struct bsddialog_formitem *items; + + if (argc < 1) + exit_error(true, "--passwordform missing <formheight>"); + formheight = (u_int)strtoul(argv[0], NULL, 10); + + argc--; + argv++; + sizeitem = opt->item_bottomdesc ? 9 : 8; + if (argc % sizeitem != 0) + exit_error(true, "--passwordform bad arguments items number"); + + flags = BSDDIALOG_FIELDHIDDEN; + nitems = argc / sizeitem; + if ((items = calloc(nitems, sizeof(struct bsddialog_formitem))) == NULL) + exit_error(false, "cannot allocate memory for form items"); + j = 0; + for (i = 0; i < nitems; i++) { + items[i].label = argv[j++]; + items[i].ylabel = (u_int)strtoul(argv[j++], NULL, 10); + items[i].xlabel = (u_int)strtoul(argv[j++], NULL, 10); + items[i].init = argv[j++]; + items[i].yfield = (u_int)strtoul(argv[j++], NULL, 10); + items[i].xfield = (u_int)strtoul(argv[j++], NULL, 10); + + fieldlen = (int)strtol(argv[j++], NULL, 10); + items[i].fieldlen = abs(fieldlen); + + valuelen = (int)strtol(argv[j++], NULL, 10); + items[i].maxvaluelen = valuelen == 0 ? abs(fieldlen) : valuelen; + + flags |= (fieldlen < 0 ? BSDDIALOG_FIELDREADONLY : 0); + items[i].flags = flags; + + items[i].bottomdesc = opt->item_bottomdesc ? argv[j++] : ""; + } + + focusitem = -1; + output = bsddialog_form(conf, text, rows, cols, formheight, nitems, + items, &focusitem); + print_form_items(output, nitems, items, focusitem, opt); + free(items); + + if (output == BSDDIALOG_HELP && opt->item_bottomdesc) + output = BSDDIALOG_ITEM_HELP; + + return (output); +} diff --git a/utility/util_cli.c b/utility/util_cli.c new file mode 100644 index 000000000000..22bed6550fb5 --- /dev/null +++ b/utility/util_cli.c @@ -0,0 +1,841 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021-2023 Alfonso Sabato Siciliano + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/ioctl.h> + +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <bsddialog.h> +#include <bsddialog_theme.h> + +#include "util.h" + +enum OPTS { + /* Options */ + ALTERNATE_SCREEN = '?' + 1, + AND_DIALOG, + ASCII_LINES, + BACKTITLE, + BEGIN_X, + BEGIN_Y, + BIKESHED, + CANCEL_EXIT_CODE, + CANCEL_LABEL, + CLEAR_DIALOG, + CLEAR_SCREEN, + COLUMNS_PER_ROW, + CR_WRAP, + DATEBOX_FORMAT, + DATE_FORMAT, + DEFAULT_BUTTON, + DEFAULT_ITEM, + DEFAULT_NO, + DISABLE_ESC, + ERROR_EXIT_CODE, + ESC_EXIT_CODE, + EXIT_LABEL, + EXTRA_BUTTON, + EXTRA_EXIT_CODE, + EXTRA_LABEL, + HELP_BUTTON, + HELP_EXIT_CODE, + HELP_LABEL, + HELP_PRINT_ITEMS, + HELP_PRINT_NAME, + HFILE, + HLINE, + HMSG, + IGNORE, + INSECURE, + ITEM_BOTTOM_DESC, + ITEM_DEPTH, + ITEM_PREFIX, + LEFT1_BUTTON, + LEFT1_EXIT_CODE, + LEFT2_BUTTON, + LEFT2_EXIT_CODE, + LEFT3_BUTTON, + LEFT3_EXIT_CODE, + LOAD_THEME, + MAX_INPUT, + NO_CANCEL, + NO_DESCRIPTIONS, + NO_LINES, + NO_NAMES, + NO_OK, + NO_SHADOW, + NORMAL_SCREEN, + OK_EXIT_CODE, + OK_LABEL, + OUTPUT_FD, + OUTPUT_SEPARATOR, + PRINT_MAXSIZE, + PRINT_SIZE, + PRINT_VERSION, + QUOTED, + RIGHT1_BUTTON, + RIGHT1_EXIT_CODE, + RIGHT2_BUTTON, + RIGHT2_EXIT_CODE, + RIGHT3_BUTTON, + RIGHT3_EXIT_CODE, + SAVE_THEME, + SEPARATE_OUTPUT, + SHADOW, + SINGLE_QUOTED, + SLEEP, + STDERR, + STDOUT, + SWITCH_BUTTONS, + TAB_ESCAPE, + TAB_LEN, + TEXT_ESCAPE, + TEXT_UNCHANGED, + THEME, + TIMEOUT_EXIT_CODE, + TIME_FORMAT, + TITLE, + /* Dialogs */ + CALENDAR, + CHECKLIST, + DATEBOX, + FORM, + GAUGE, + INFOBOX, + INPUTBOX, + MENU, + MIXEDFORM, + MIXEDGAUGE, + MSGBOX, + PASSWORDBOX, + PASSWORDFORM, + PAUSE, + RADIOLIST, + RANGEBOX, + TEXTBOX, + TIMEBOX, + TREEVIEW, + YESNO +}; + +/* options descriptor */ +static struct option longopts[] = { + /* Options */ + {"alternate-screen", no_argument, NULL, ALTERNATE_SCREEN}, + {"and-dialog", no_argument, NULL, AND_DIALOG}, + {"and-widget", no_argument, NULL, AND_DIALOG}, + {"ascii-lines", no_argument, NULL, ASCII_LINES}, + {"backtitle", required_argument, NULL, BACKTITLE}, + {"begin-x", required_argument, NULL, BEGIN_X}, + {"begin-y", required_argument, NULL, BEGIN_Y}, + {"bikeshed", no_argument, NULL, BIKESHED}, + {"cancel-exit-code", required_argument, NULL, CANCEL_EXIT_CODE}, + {"cancel-label", required_argument, NULL, CANCEL_LABEL}, + {"clear", no_argument, NULL, CLEAR_SCREEN}, + {"clear-dialog", no_argument, NULL, CLEAR_DIALOG}, + {"clear-screen", no_argument, NULL, CLEAR_SCREEN}, + {"colors", no_argument, NULL, TEXT_ESCAPE}, + {"columns-per-row", required_argument, NULL, COLUMNS_PER_ROW}, + {"cr-wrap", no_argument, NULL, CR_WRAP}, + {"datebox-format", required_argument, NULL, DATEBOX_FORMAT}, + {"date-format", required_argument, NULL, DATE_FORMAT}, + {"defaultno", no_argument, NULL, DEFAULT_NO}, + {"default-button", required_argument, NULL, DEFAULT_BUTTON}, + {"default-item", required_argument, NULL, DEFAULT_ITEM}, + {"default-no", no_argument, NULL, DEFAULT_NO}, + {"disable-esc", no_argument, NULL, DISABLE_ESC}, + {"error-exit-code", required_argument, NULL, ERROR_EXIT_CODE}, + {"esc-exit-code", required_argument, NULL, ESC_EXIT_CODE}, + {"exit-label", required_argument, NULL, EXIT_LABEL}, + {"extra-button", no_argument, NULL, EXTRA_BUTTON}, + {"extra-exit-code", required_argument, NULL, EXTRA_EXIT_CODE}, + {"extra-label", required_argument, NULL, EXTRA_LABEL}, + {"help-button", no_argument, NULL, HELP_BUTTON}, + {"help-exit-code", required_argument, NULL, HELP_EXIT_CODE}, + {"help-label", required_argument, NULL, HELP_LABEL}, + {"help-print-items", no_argument, NULL, HELP_PRINT_ITEMS}, + {"help-print-name", no_argument, NULL, HELP_PRINT_NAME}, + {"help-status", no_argument, NULL, HELP_PRINT_ITEMS}, + {"help-tags", no_argument, NULL, HELP_PRINT_NAME}, + {"hfile", required_argument, NULL, HFILE}, + {"hline", required_argument, NULL, HLINE}, + {"hmsg", required_argument, NULL, HMSG}, + {"ignore", no_argument, NULL, IGNORE}, + {"insecure", no_argument, NULL, INSECURE}, + {"item-bottom-desc", no_argument, NULL, ITEM_BOTTOM_DESC}, + {"item-depth", no_argument, NULL, ITEM_DEPTH}, + {"item-help", no_argument, NULL, ITEM_BOTTOM_DESC}, + {"item-prefix", no_argument, NULL, ITEM_PREFIX}, + {"keep-tite", no_argument, NULL, ALTERNATE_SCREEN}, + {"left1-button", required_argument, NULL, LEFT1_BUTTON}, + {"left1-exit-code", required_argument, NULL, LEFT1_EXIT_CODE}, + {"left2-button", required_argument, NULL, LEFT2_BUTTON}, + {"left2-exit-code", required_argument, NULL, LEFT2_EXIT_CODE}, + {"left3-button", required_argument, NULL, LEFT3_BUTTON}, + {"left3-exit-code", required_argument, NULL, LEFT3_EXIT_CODE}, + {"load-theme", required_argument, NULL, LOAD_THEME}, + {"max-input", required_argument, NULL, MAX_INPUT}, + {"no-cancel", no_argument, NULL, NO_CANCEL}, + {"nocancel", no_argument, NULL, NO_CANCEL}, + {"no-descriptions", no_argument, NULL, NO_DESCRIPTIONS}, + {"no-items", no_argument, NULL, NO_DESCRIPTIONS}, + {"no-label", required_argument, NULL, CANCEL_LABEL}, + {"no-lines", no_argument, NULL, NO_LINES}, + {"no-names", no_argument, NULL, NO_NAMES}, + {"no-ok", no_argument, NULL, NO_OK}, + {"nook", no_argument, NULL, NO_OK}, + {"no-shadow", no_argument, NULL, NO_SHADOW}, + {"no-tags", no_argument, NULL, NO_NAMES}, + {"normal-screen", no_argument, NULL, NORMAL_SCREEN}, + {"ok-exit-code", required_argument, NULL, OK_EXIT_CODE}, + {"ok-label", required_argument, NULL, OK_LABEL}, + {"output-fd", required_argument, NULL, OUTPUT_FD}, + {"output-separator", required_argument, NULL, OUTPUT_SEPARATOR}, + {"print-maxsize", no_argument, NULL, PRINT_MAXSIZE}, + {"print-size", no_argument, NULL, PRINT_SIZE}, + {"print-version", no_argument, NULL, PRINT_VERSION}, + {"quoted", no_argument, NULL, QUOTED}, + {"right1-button", required_argument, NULL, RIGHT1_BUTTON}, + {"right1-exit-code", required_argument, NULL, RIGHT1_EXIT_CODE}, + {"right2-button", required_argument, NULL, RIGHT2_BUTTON}, + {"right2-exit-code", required_argument, NULL, RIGHT2_EXIT_CODE}, + {"right3-button", required_argument, NULL, RIGHT3_BUTTON}, + {"right3-exit-code", required_argument, NULL, RIGHT3_EXIT_CODE}, + {"save-theme", required_argument, NULL, SAVE_THEME}, + {"separate-output", no_argument, NULL, SEPARATE_OUTPUT}, + {"separator", required_argument, NULL, OUTPUT_SEPARATOR}, + {"shadow", no_argument, NULL, SHADOW}, + {"single-quoted", no_argument, NULL, SINGLE_QUOTED}, + {"sleep", required_argument, NULL, SLEEP}, + {"stderr", no_argument, NULL, STDERR}, + {"stdout", no_argument, NULL, STDOUT}, + {"switch-buttons", no_argument, NULL, SWITCH_BUTTONS}, + {"tab-escape", no_argument, NULL, TAB_ESCAPE}, + {"tab-len", required_argument, NULL, TAB_LEN}, + {"text-escape", no_argument, NULL, TEXT_ESCAPE}, + {"text-unchanged", no_argument, NULL, TEXT_UNCHANGED}, + {"theme", required_argument, NULL, THEME}, + {"timeout-exit-code", required_argument, NULL, TIMEOUT_EXIT_CODE}, + {"time-format", required_argument, NULL, TIME_FORMAT}, + {"title", required_argument, NULL, TITLE}, + {"yes-label", required_argument, NULL, OK_LABEL}, + /* Dialogs */ + {"calendar", no_argument, NULL, CALENDAR}, + {"checklist", no_argument, NULL, CHECKLIST}, + {"datebox", no_argument, NULL, DATEBOX}, + {"form", no_argument, NULL, FORM}, + {"gauge", no_argument, NULL, GAUGE}, + {"infobox", no_argument, NULL, INFOBOX}, + {"inputbox", no_argument, NULL, INPUTBOX}, + {"menu", no_argument, NULL, MENU}, + {"mixedform", no_argument, NULL, MIXEDFORM}, + {"mixedgauge", no_argument, NULL, MIXEDGAUGE}, + {"msgbox", no_argument, NULL, MSGBOX}, + {"passwordbox", no_argument, NULL, PASSWORDBOX}, + {"passwordform", no_argument, NULL, PASSWORDFORM}, + {"pause", no_argument, NULL, PAUSE}, + {"radiolist", no_argument, NULL, RADIOLIST}, + {"rangebox", no_argument, NULL, RANGEBOX}, + {"textbox", no_argument, NULL, TEXTBOX}, + {"timebox", no_argument, NULL, TIMEBOX}, + {"treeview", no_argument, NULL, TREEVIEW}, + {"yesno", no_argument, NULL, YESNO}, + /* END */ + { NULL, 0, NULL, 0} +}; + +void usage(void) +{ + printf("usage: bsddialog --help | --version\n"); + printf(" bsddialog [--<opt>] --<dialog> <text> <rows> <cols> " + "[<arg>] [--<opt>]\n"); + printf(" bsddialog ... --<dialog1> ... [--and-dialog --<dialog2> " + "...] ...\n"); + printf("\n"); + + printf("Options:\n"); + printf(" --alternate-screen, --ascii-lines, --backtitle <backtitle>," + " --begin-x <x>,\n --begin-y <y>, --bikeshed," + " --cancel-exit-code <retval>, --cancel-label <label>,\n" + " --clear-dialog, --clear-screen, --columns-per-row <columns>," + " --cr-wrap,\n --datebox-format d/m/y|m/d/y|y/m/d," + " --date-format <format>,\n --default-button <label>," + " --default-item <name>, --default-no, --disable-esc,\n" + " --error-exit-code <retval>, --esc-exit-code <retval>," + " --exit-label <label>,\n --extra-button," + " --extra-exit-code <retval>, --extra-label <label>,\n" + " --left1-button <label>, --left1-exit-code <retval>," + " --left2-button <label>,\n --left2-exit-code <retval>," + " --left3-button <label>, --left3-exit-code <retval>,\n" + " --help-button, --help-exit-code <retval>, --help-label <label>,\n" + " --help-print-items, --help-print-name, --hfile <file>," + " --hline <string>,\n --hmsg <string>, --ignore, --insecure," + " --item-bottom-desc, --item-depth,\n --item-prefix," + " --load-theme <file>, --max-input <size>, --no-cancel,\n" + " --no-descriptions, --no-label <label>, --no-lines, --no-names," + " --no-ok,\n --no-shadow, --normal-screen, --ok-exit-code <retval>," + " --ok-label <label>,\n --output-fd <fd>, --output-separator <sep>," + " --print-maxsize, --print-size,\n --print-version, --quoted," + " --right1-button <label>,\n --right1-exit-code <retval>," + " --right2-button <label>,\n --right2-exit-code <retval>," + " --right3-button <label>,\n --right3-exit-code <retval>," + " --save-theme <file>, --separate-output,\n --separator <sep>," + " --shadow, --single-quoted, --sleep <secs>, --stderr,\n --stdout," + " --switch-buttons, --tab-escape, --tab-len <spaces>," + " --text-escape,\n --text-unchanged, --theme 3d|blackwhite|flat," + " --timeout-exit-code <retval>,\n --time-format <format>," + " --title <title>, --yes-label <label>."); + printf("\n\n"); + + printf("Dialogs:\n"); + printf(" --calendar <text> <rows> <cols> [<dd> <mm> <yy>]\n"); + printf(" --checklist <text> <rows> <cols> <menurows> [<name> <desc> " + "on|off] ...\n"); + printf(" --datebox <text> <rows> <cols> [<dd> <mm> <yy>]\n"); + printf(" --form <text> <rows> <cols> <formrows> [<label> <ylabel> " + "<xlabel> <init> <yfield> <xfield> <fieldlen> <maxletters>] " + "...\n"); + printf(" --gauge <text> <rows> <cols> [<perc>]\n"); + printf(" --infobox <text> <rows> <cols>\n"); + printf(" --inputbox <text> <rows> <cols> [<init>]\n"); + printf(" --menu <text> <rows> <cols> <menurows> [<name> <desc>] ...\n"); + printf(" --mixedform <text> <rows> <cols> <formrows> [<label> <ylabel> " + "<xlabel> <init> <yfield> <xfield> <fieldlen> <maxletters> " + "0|1|2] ...\n"); + printf(" --mixedgauge <text> <rows> <cols> <mainperc> [<minilabel> " + "<miniperc>] ...\n"); + printf(" --msgbox <text> <rows> <cols>\n"); + printf(" --passwordbox <text> <rows> <cols> [<init>]\n"); + printf(" --passwordform <text> <rows> <cols> <formrows> [<label> " + "<ylabel> <xlabel> <init> <yfield> <xfield> <fieldlen> " + "<maxletters>] ...\n"); + printf(" --pause <text> <rows> <cols> <secs>\n"); + printf(" --radiolist <text> <rows> <cols> <menurows> [<name> <desc> " + "on|off] ...\n"); + printf(" --rangebox <text> <rows> <cols> <min> <max> [<init>]\n"); + printf(" --textbox <file> <rows> <cols>\n"); + printf(" --timebox <text> <rows> <cols> [<hh> <mm> <ss>]\n"); + printf(" --treeview <text> <rows> <cols> <menurows> [<depth> <name> " + "<desc> on|off] ...\n"); + printf(" --yesno <text> <rows> <cols>\n"); + printf("\n"); + + printf("See 'man 1 bsddialog' for more information.\n"); +} + +int +parseargs(int argc, char **argv, struct bsddialog_conf *conf, + struct options *opt) +{ + int arg, parsed, i; + struct winsize ws; + + bsddialog_initconf(conf); + conf->key.enable_esc = true; + conf->button.always_active = true; + + memset(opt, 0, sizeof(struct options)); + opt->theme = -1; + opt->output_fd = STDERR_FILENO; + opt->max_input_form = 2048; + opt->mandatory_dialog = true; + + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "--and-dialog") == 0 || + strcmp(argv[i], "--and-widget") == 0) { + argc = i + 1; + break; + } + } + parsed = argc; + while ((arg = getopt_long(argc, argv, "", longopts, NULL)) != -1) { + switch (arg) { + /* Options */ + case ALTERNATE_SCREEN: + opt->screen_mode = "smcup"; + break; + case AND_DIALOG: + if (opt->dialogbuilder == NULL) + exit_error(true,"--and-dialog without " + "previous --<dialog>"); + break; + case ASCII_LINES: + conf->ascii_lines = true; + break; + case BACKTITLE: + opt->backtitle = optarg; + if (conf->y == BSDDIALOG_CENTER) + conf->auto_topmargin = 2; + break; + case BEGIN_X: + conf->x = (int)strtol(optarg, NULL, 10); + if (conf->x < BSDDIALOG_CENTER) + exit_error(false, "--begin-x %d is < %d", + conf->x, BSDDIALOG_CENTER); + break; + case BEGIN_Y: + conf->y = (int)strtol(optarg, NULL, 10); + if (conf->y < BSDDIALOG_CENTER) + exit_error(false, "--begin-y %d is < %d", + conf->y, BSDDIALOG_CENTER); + conf->auto_topmargin = 0; + break; + case BIKESHED: + opt->bikeshed = true; + break; + case CANCEL_EXIT_CODE: + set_exit_code(BSDDIALOG_CANCEL, + (int)strtol(optarg, NULL, 10)); + break; + case CANCEL_LABEL: + conf->button.cancel_label = optarg; + break; + case CLEAR_DIALOG: + conf->clear = true; + break; + case CLEAR_SCREEN: + opt->mandatory_dialog = false; + opt->clearscreen = true; + break; + case COLUMNS_PER_ROW: + conf->text.cols_per_row = + (u_int)strtoul(optarg, NULL, 10); + break; + case CR_WRAP: + opt->cr_wrap = true; + break; + case DATEBOX_FORMAT: + if (strcasecmp(optarg, "d/m/y") == 0) + conf->date.format = "d/m/y"; + else if (strcasecmp(optarg, "m/d/y") == 0) + conf->date.format = "m/d/y"; + else if (strcasecmp(optarg, "y/m/d") == 0) + conf->date.format = "y/m/d"; + else + exit_error(true, + "date format \"%s\" is invalid", optarg); + break; + case DATE_FORMAT: + opt->date_fmt = optarg; + break; + case DEFAULT_BUTTON: + conf->button.default_label = optarg; + break; + case DEFAULT_ITEM: + opt->item_default = optarg; + break; + case DEFAULT_NO: + conf->button.default_cancel = true; + break; + case DISABLE_ESC: + conf->key.enable_esc = false; + break; + case ERROR_EXIT_CODE: + set_exit_code(BSDDIALOG_ERROR, + (int)strtol(optarg, NULL, 10)); + break; + case ESC_EXIT_CODE: + set_exit_code(BSDDIALOG_ESC, + (int)strtol(optarg, NULL, 10)); + break; + case EXIT_LABEL: + conf->button.ok_label = optarg; + break; + case EXTRA_BUTTON: + conf->button.with_extra = true; + break; + case EXTRA_EXIT_CODE: + set_exit_code(BSDDIALOG_EXTRA, + (int)strtol(optarg, NULL, 10)); + break; + case EXTRA_LABEL: + conf->button.extra_label = optarg; + break; + case HELP_BUTTON: + conf->button.with_help = true; + break; + case HELP_EXIT_CODE: + i = (int)strtol(optarg, NULL, 10); + set_exit_code(BSDDIALOG_HELP, i); + /* _TEM_HELP follows _HELP */ + set_exit_code(BSDDIALOG_ITEM_HELP, i); + break; + case HELP_LABEL: + conf->button.help_label = optarg; + break; + case HELP_PRINT_ITEMS: + opt->help_print_items = true; + break; + case HELP_PRINT_NAME: + opt->help_print_item_name = true; + break; + case HFILE: + conf->key.f1_file = optarg; + break; + case HLINE: + if (optarg[0] != '\0') + conf->bottomtitle = optarg; + break; + case HMSG: + conf->key.f1_message = optarg; + break; + case IGNORE: + opt->ignore = true; + break; + case INSECURE: + conf->form.securech = '*'; + break; + case ITEM_BOTTOM_DESC: + opt->item_bottomdesc = true; + break; + case ITEM_DEPTH: + opt->item_depth = true; + break; + case ITEM_PREFIX: + opt->item_prefix = true; + break; + case LEFT1_BUTTON: + conf->button.left1_label = optarg; + break; + case LEFT1_EXIT_CODE: + set_exit_code(BSDDIALOG_LEFT1, + (int)strtol(optarg, NULL, 10)); + break; + case LEFT2_BUTTON: + conf->button.left2_label = optarg; + break; + case LEFT2_EXIT_CODE: + set_exit_code(BSDDIALOG_LEFT2, + (int)strtol(optarg, NULL, 10)); + break; + case LEFT3_BUTTON: + conf->button.left3_label = optarg; + break; + case LEFT3_EXIT_CODE: + set_exit_code(BSDDIALOG_LEFT3, + (int)strtol(optarg, NULL, 10)); + break; + case LOAD_THEME: + opt->loadthemefile = optarg; + break; + case MAX_INPUT: + opt->max_input_form = (u_int)strtoul(optarg, NULL, 10); + break; + case NO_CANCEL: + conf->button.without_cancel = true; + break; + case NO_DESCRIPTIONS: + conf->menu.no_desc = true; + break; + case NO_LINES: + conf->no_lines = true; + break; + case NO_NAMES: + conf->menu.no_name = true; + break; + case NO_OK: + conf->button.without_ok = true; + break; + case NO_SHADOW: + conf->shadow = false; + break; + case NORMAL_SCREEN: + opt->screen_mode = "rmcup"; + break; + case OK_EXIT_CODE: + set_exit_code(BSDDIALOG_OK, + (int)strtol(optarg, NULL, 10)); + break; + case OK_LABEL: + conf->button.ok_label = optarg; + break; + case OUTPUT_FD: + opt->output_fd = (int)strtol(optarg, NULL, 10); + break; + case OUTPUT_SEPARATOR: + opt->item_output_sep = optarg; + break; + case QUOTED: + opt->item_always_quote = true; + break; + case PRINT_MAXSIZE: + opt->mandatory_dialog = false; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws); + dprintf(opt->output_fd, "MaxSize: %d, %d\n", + ws.ws_row, ws.ws_col); + break; + case PRINT_SIZE: + conf->get_height = &opt->getH; + conf->get_width = &opt->getW; + break; + case PRINT_VERSION: + opt->mandatory_dialog = false; + dprintf(opt->output_fd, "Version: %s\n", + LIBBSDDIALOG_VERSION); + break; + case RIGHT1_BUTTON: + conf->button.right1_label = optarg; + break; + case RIGHT1_EXIT_CODE: + set_exit_code(BSDDIALOG_RIGHT1, + (int)strtol(optarg, NULL, 10)); + break; + case RIGHT2_BUTTON: + conf->button.right2_label = optarg; + break; + case RIGHT2_EXIT_CODE: + set_exit_code(BSDDIALOG_RIGHT2, + (int)strtol(optarg, NULL, 10)); + break; + case RIGHT3_BUTTON: + conf->button.right3_label = optarg; + break; + case RIGHT3_EXIT_CODE: + set_exit_code(BSDDIALOG_RIGHT3, + (int)strtol(optarg, NULL, 10)); + break; + case SAVE_THEME: + opt->mandatory_dialog = false; + opt->savethemefile = optarg; + break; + case SEPARATE_OUTPUT: + opt->item_output_sepnl = true; + break; + case SHADOW: + conf->shadow = true; + break; + case SINGLE_QUOTED: + opt->item_singlequote = true; + break; + case SLEEP: + conf->sleep = (u_int)strtoul(optarg, NULL, 10); + break; + case STDERR: + opt->output_fd = STDERR_FILENO; + break; + case STDOUT: + opt->output_fd = STDOUT_FILENO; + break; + case SWITCH_BUTTONS: + conf->button.always_active = false; + break; + case TAB_ESCAPE: + opt->tab_escape = true; + break; + case TAB_LEN: + conf->text.tablen = (u_int)strtoul(optarg, NULL, 10); + break; + case TEXT_ESCAPE: + conf->text.escape = true; + break; + case TEXT_UNCHANGED: + opt->text_unchanged = true; + break; + case THEME: + if (strcasecmp(optarg, "blackwhite") == 0) + opt->theme = BSDDIALOG_THEME_BLACKWHITE; + else if (strcasecmp(optarg, "flat") == 0) + opt->theme = BSDDIALOG_THEME_FLAT; + else if (strcasecmp(optarg, "3d") == 0) + opt->theme = BSDDIALOG_THEME_3D; + else + exit_error(true, + "--theme: \"%s\" is unknown", optarg); + break; + case TIMEOUT_EXIT_CODE: + set_exit_code(BSDDIALOG_TIMEOUT, + (int)strtol(optarg, NULL, 10)); + break; + case TIME_FORMAT: + opt->time_fmt = optarg; + break; + case TITLE: + conf->title = optarg; + break; + /* Dialogs */ + case CALENDAR: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --calendar without " + "--and-dialog", opt->name); + opt->name = "--calendar"; + opt->dialogbuilder = calendar_builder; + break; + case CHECKLIST: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --checklist without " + "--and-dialog", opt->name); + opt->name = "--checklist"; + opt->dialogbuilder = checklist_builder; + conf->auto_downmargin = 1; + break; + case DATEBOX: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --datebox without " + "--and-dialog", opt->name); + opt->name = "--datebox"; + opt->dialogbuilder = datebox_builder; + break; + case FORM: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --form without " + "--and-dialog", opt->name); + opt->name = "--form"; + opt->dialogbuilder = form_builder; + conf->auto_downmargin = 1; + break; + case GAUGE: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --gauge without " + "--and-dialog", opt->name); + opt->name = "--gauge"; + opt->dialogbuilder = gauge_builder; + break; + case INFOBOX: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --infobox without " + "--and-dialog", opt->name); + opt->name = "--infobox"; + opt->dialogbuilder = infobox_builder; + break; + case INPUTBOX: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --inputbox without " + "--and-dialog", opt->name); + opt->name = "--inputbox"; + opt->dialogbuilder = inputbox_builder; + conf->auto_downmargin = 1; + break; + case MENU: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --menu without " + "--and-dialog", opt->name); + opt->name = "--menu"; + opt->dialogbuilder = menu_builder; + conf->auto_downmargin = 1; + break; + case MIXEDFORM: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --mixedform without " + "--and-dialog", opt->name); + opt->name = "--mixedform"; + opt->dialogbuilder = mixedform_builder; + conf->auto_downmargin = 1; + break; + case MIXEDGAUGE: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --mixedgauge without " + "--and-dialog", opt->name); + opt->name = "--mixedgauge"; + opt->dialogbuilder = mixedgauge_builder; + break; + case MSGBOX: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --msgbox without " + "--and-dialog", opt->name); + opt->name = "--"; + opt->dialogbuilder = msgbox_builder; + break; + case PAUSE: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --pause without " + "--and-dialog", opt->name); + opt->name = "--pause"; + opt->dialogbuilder = pause_builder; + break; + case PASSWORDBOX: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --passwordbox without " + "--and-dialog", opt->name); + opt->name = "--passwordbox"; + opt->dialogbuilder = passwordbox_builder; + conf->auto_downmargin = 1; + break; + case PASSWORDFORM: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --passwordform " + "without --and-dialog", opt->name); + opt->name = "--passwordform"; + opt->dialogbuilder = passwordform_builder; + conf->auto_downmargin = 1; + break; + case RADIOLIST: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --radiolist without " + "--and-dialog", opt->name); + opt->name = "--radiolist"; + opt->dialogbuilder = radiolist_builder; + conf->auto_downmargin = 1; + break; + case RANGEBOX: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --rangebox without " + "--and-dialog", opt->name); + opt->name = "--rangebox"; + opt->dialogbuilder = rangebox_builder; + break; + case TEXTBOX: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --textbox without " + "--and-dialog", opt->name); + opt->name = "--textbox"; + opt->dialogbuilder = textbox_builder; + break; + case TIMEBOX: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --timebox without " + "--and-dialog", opt->name); + opt->name = "--timebox"; + opt->dialogbuilder = timebox_builder; + break; + case TREEVIEW: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --treeview without " + "--and-dialog", opt->name); + opt->name = "--treeview"; + opt->dialogbuilder = treeview_builder; + conf->auto_downmargin = 1; + break; + case YESNO: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --yesno without " + "--and-dialog", opt->name); + opt->name = "--yesno"; + opt->dialogbuilder = yesno_builder; + break; + default: /* Error */ + if (opt->ignore == true) + break; + exit_error(true, "--ignore to continue"); + } + } + + return (parsed); +} diff --git a/utility/util_theme.c b/utility/util_theme.c new file mode 100644 index 000000000000..c313d743252b --- /dev/null +++ b/utility/util_theme.c @@ -0,0 +1,451 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022-2023 Alfonso Sabato Siciliano + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/time.h> + +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> + +#include <bsddialog.h> +#include <bsddialog_theme.h> + +#include "util.h" + +static struct bsddialog_theme t; +static char title[1024]; + +#define NPROPERTY 41 +#define NCOLOR 8 +#define NATTR 6 + +#define PROP_ERROR(name, error) do { \ + fclose(fp); \ + exit_error(false, "%s for \"%s\"", error, name); \ +} while (0) + +enum typeproperty { + BOOL, + CHAR, + INT, + UINT, + COLOR, + COMPAT +}; + +struct property { + const char *comment; + const char *name; + enum typeproperty type; + void *value; +}; + +struct namevalue { + const char *name; + unsigned int value; +}; + +static struct namevalue color[NCOLOR] = { + {"black", BSDDIALOG_BLACK}, + {"red", BSDDIALOG_RED}, + {"green", BSDDIALOG_GREEN}, + {"yellow", BSDDIALOG_YELLOW}, + {"blue", BSDDIALOG_BLUE}, + {"magenta", BSDDIALOG_MAGENTA}, + {"cyan", BSDDIALOG_CYAN}, + {"white", BSDDIALOG_WHITE} +}; + +static struct namevalue attr[NATTR] = { + {"bold", BSDDIALOG_BOLD}, + {"reverse", BSDDIALOG_REVERSE}, + {"underline", BSDDIALOG_UNDERLINE}, + {"blink", BSDDIALOG_BLINK}, + {"halfbright", BSDDIALOG_HALFBRIGHT}, + {"highlight", BSDDIALOG_HIGHLIGHT} +}; + +static struct property p[NPROPERTY] = { + {"\n#Terminal\n", "theme.screen.color", COLOR, &t.screen.color}, + + {"\n# Shadow\n", + "theme.shadow.color", COLOR, &t.shadow.color}, + {"# shift down right from main widget\n", + "theme.shadow.y", UINT, &t.shadow.y}, + {"", "theme.shadow.x", UINT, &t.shadow.x}, + + {"\n# Main widget\n", + "theme.dialog.color", COLOR, &t.dialog.color}, + {"", "theme.dialog.delimtitle", BOOL, &t.dialog.delimtitle}, + {"", "theme.dialog.titlecolor", COLOR, &t.dialog.titlecolor}, + {"", "theme.dialog.lineraisecolor", COLOR, &t.dialog.lineraisecolor}, + {"", "theme.dialog.linelowercolor", COLOR, &t.dialog.linelowercolor}, + {"", "theme.dialog.bottomtitlecolor", COLOR, + &t.dialog.bottomtitlecolor}, + {"", "theme.dialog.arrowcolor", COLOR, &t.dialog.arrowcolor}, + + {"\n# Menus: --checklist, --menu, --radiolist\n" + "# prefix [selector] shortcut name desc bottomdesc\n", + "theme.menu.f_prefixcolor", COLOR, &t.menu.f_prefixcolor}, + {"", "theme.menu.prefixcolor", COLOR, &t.menu.prefixcolor}, + {"", "theme.menu.f_selectorcolor", COLOR, &t.menu.f_selectorcolor}, + {"", "theme.menu.selectorcolor", COLOR, &t.menu.selectorcolor}, + {"", "theme.menu.f_namecolor", COLOR, &t.menu.f_namecolor}, + {"", "theme.menu.namecolor", COLOR, &t.menu.namecolor}, + {"", "theme.menu.f_desccolor", COLOR, &t.menu.f_desccolor}, + {"", "theme.menu.desccolor", COLOR, &t.menu.desccolor}, + {"", "theme.menu.f_shortcutcolor", COLOR, &t.menu.f_shortcutcolor}, + {"", "theme.menu.shortcutcolor", COLOR, &t.menu.shortcutcolor}, + {"", "theme.menu.bottomdesccolor", COLOR, &t.menu.bottomdesccolor}, + {"# bsddialog_menutype BSDDIALOG_SEPARATOR\n", + "theme.menu.sepnamecolor", COLOR, &t.menu.sepnamecolor}, + {"", "theme.menu.sepdesccolor", COLOR, &t.menu.sepdesccolor}, + + {"\n# Forms\n", + "theme.form.f_fieldcolor", COLOR, &t.form.f_fieldcolor}, + {"", "theme.form.fieldcolor", COLOR, &t.form.fieldcolor}, + {"", "theme.form.readonlycolor", COLOR, &t.form.readonlycolor}, + {"", "theme.form.bottomdesccolor", COLOR, &t.form.bottomdesccolor}, + + {"\n# Bar of --gauge, --mixedgauge, --pause, --rangebox\n", + "theme.bar.f_color", COLOR, &t.bar.f_color}, + {"", "theme.bar.color", COLOR, &t.bar.color}, + + {"\n# Buttons\n", + "theme.button.minmargin", UINT, &t.button.minmargin}, + {"", "theme.button.maxmargin", UINT, &t.button.maxmargin}, + {"", "theme.button.leftdelim", CHAR, &t.button.leftdelim}, + {"", "theme.button.rightdelim", CHAR, &t.button.rightdelim}, + {"", "theme.button.f_delimcolor", COLOR, &t.button.f_delimcolor}, + {"", "theme.button.delimcolor", COLOR, &t.button.delimcolor}, + {"", "theme.button.f_color", COLOR, &t.button.f_color}, + {"", "theme.button.color", COLOR, &t.button.color}, + {"", "theme.button.f_shortcutcolor", COLOR, &t.button.f_shortcutcolor}, + {"", "theme.button.shortcutcolor", COLOR, &t.button.shortcutcolor}, + + {"\n#Compatibility. Do not use, can be deleted\n", + "use_shadow", COMPAT, NULL} +}; + +void savetheme(const char *file) +{ + int i, j; + unsigned int flags; + enum bsddialog_color bg, fg; + time_t clock; + FILE *fp; + + if (bsddialog_get_theme(&t) != BSDDIALOG_OK) + exit_error(false, + "cannot save theme: %s", bsddialog_geterror()); + + if(time(&clock) < 0) + exit_error(false, "cannot save profile getting current time"); + if ((fp = fopen(file, "w")) == NULL) + exit_error(false, "cannot open %s to save profile", file); + + fprintf(fp, "### bsddialog theme - %s\n", ctime(&clock)); + + fputs("# Colors: ", fp); + fputs("black red green yellow blue magenta cyan white.\n", fp); + fputs("# Attributes: ", fp); + fputs("bold reverse underline blink halfbright highlight.\n", fp); + fputs("# f_* refers to focus for an element with selected or ", fp); + fputs("unselected state.\n\n", fp); + + fprintf(fp, "version %s\n", LIBBSDDIALOG_VERSION); + + for (i = 0; i < NPROPERTY; i++) { + if (p[i].type == COMPAT) + continue; + fprintf(fp, "%s%s", p[i].comment, p[i].name); + switch (p[i].type) { + case CHAR: + fprintf(fp, " %c\n", *((char*)p[i].value)); + break; + case INT: + fprintf(fp, " %d\n", *((int*)p[i].value)); + break; + case UINT: + fprintf(fp, " %u\n", *((unsigned int*)p[i].value)); + break; + case BOOL: + fprintf(fp, " %s\n", + *((bool*)p[i].value) ? "true" : "false"); + break; + case COLOR: + bsddialog_color_attrs(*(int*)p[i].value, &fg, &bg, + &flags); + fprintf(fp, " %s %s", color[fg].name, color[bg].name); + for (j = 0; j < NATTR; j++) + if (flags & attr[j].value) + fprintf(fp, " %s", attr[j].name); + fputs("\n", fp); + break; + case COMPAT: + /* Do not save compat property for now */ + break; + } + } + + fclose(fp); +} + +void loadtheme(const char *file, bool compatibility) +{ + bool boolvalue; + char charvalue, *value; + char line[BUFSIZ], name[BUFSIZ], c1[BUFSIZ], c2[BUFSIZ]; + int i, j, intvalue; + unsigned int uintvalue, flags; + enum bsddialog_color bg, fg; + FILE *fp; + + if (bsddialog_hascolors() == false) + return; + + if (bsddialog_get_theme(&t) != BSDDIALOG_OK) + exit_error(false, "Cannot get current theme: %s", + bsddialog_geterror()); + + if((fp = fopen(file, "r")) == NULL) + exit_error(false, "Cannot open theme \"%s\" file", file); + + while(fgets(line, BUFSIZ, fp) != NULL) { + if(line[0] == '#' || line[0] == '\n') + continue; /* superfluous, only for efficiency */ + sscanf(line, "%s", name); + value = NULL; /* useless init, fix compiler warning */ + for (i = 0; i < NPROPERTY; i++) { + if (strcmp(name, p[i].name) == 0) { + value = &line[strlen(name)]; + break; + } + } + if (i >= NPROPERTY) { + /* unknown name in property p[] */ + if (strcmp(name, "version") == 0) + continue; /* nothing for now */ + else if (compatibility) + continue; /* just ignore */ + else + PROP_ERROR(name, "Unknown theme property name"); + } + switch (p[i].type) { + case CHAR: + while (value[0] == ' ' || value[0] == '\n' || + value[0] == '\0') + value++; + if (sscanf(value, "%c", &charvalue) != 1) + PROP_ERROR(p[i].name, "Cannot get a char"); + *((int*)p[i].value) = charvalue; + break; + case INT: + if (sscanf(value, "%d", &intvalue) != 1) + PROP_ERROR(p[i].name, "Cannot get a int"); + *((int*)p[i].value) = intvalue; + break; + case UINT: + if (sscanf(value, "%u", &uintvalue) != 1) + PROP_ERROR(p[i].name, "Cannot get a uint"); + *((unsigned int*)p[i].value) = uintvalue; + break; + case BOOL: + boolvalue = (strstr(value, "true") != NULL) ? + true :false; + *((bool*)p[i].value) = boolvalue; + break; + case COLOR: + if (sscanf(value, "%s %s", c1, c2) != 2) + PROP_ERROR(p[i].name, "Cannot get 2 colors"); + /* Foreground */ + for (j = 0; j < NCOLOR ; j++) + if ((strstr(c1, color[j].name)) != NULL) + break; + if (j >= NCOLOR) + PROP_ERROR(p[i].name, "Bad foreground"); + fg = color[j].value; + /* Background */ + for (j = 0; j < NCOLOR ; j++) + if ((strstr(c2, color[j].name)) != NULL) + break; + if (j >= NCOLOR) + PROP_ERROR(p[i].name, "Bad background"); + bg = color[j].value; + /* Flags */ + flags = 0; + for (j = 0; j < NATTR; j++) + if (strstr(value, attr[j].name) != NULL) + flags |= attr[j].value; + *((int*)p[i].value) = bsddialog_color(fg, bg, flags); + break; + case COMPAT: + /* + * usr.sbin/bsdconfig/share/dialog.subr:2255 + * uses this parameter to set NO_SHADOW. + * Set t.shadow.[y|x] for compatibilty. + */ + if (strcmp(name, "use_shadow") == 0) { + if (strcasestr(value, "off") != NULL) + t.shadow.y = t.shadow.x = 0; + } + break; + } + } + + fclose(fp); + + if(bsddialog_set_theme(&t) != BSDDIALOG_OK) + exit_error(false, bsddialog_geterror()); +} + +void setdeftheme(enum bsddialog_default_theme theme) +{ + if (bsddialog_hascolors() == false) + return; + if (bsddialog_set_default_theme(theme) != BSDDIALOG_OK) + exit_error(false, bsddialog_geterror()); +} + +void startuptheme(void) +{ + bool sep; + char *env, *file, *home, path[PATH_MAX]; + + env = getenv("NO_COLOR"); + if (env != NULL && env[0] != '\0') + setdeftheme(BSDDIALOG_THEME_BLACKWHITE); + + if ((home = getenv("HOME")) != NULL) { + sep = (strcmp(home, "/") == 0) ? false : true; + + snprintf(path, PATH_MAX, "%s%s.bsddialog.conf", + home, sep ? "/" : ""); + if (access(path, F_OK) == 0) + loadtheme(path, false); + + if ((file = getenv("BSDDIALOG_COMPATRC")) != NULL) { + snprintf(path, PATH_MAX, "%s%s%s", + home, sep ? "/" : "", file); + if (access(path, F_OK) == 0) + loadtheme(path, true); + } + } + if ((file = getenv("BSDDIALOG_THEMEFILE")) != NULL) { + if (access(file, F_OK) == 0) + loadtheme(file, false); + } +} + +void bikeshed(struct bsddialog_conf *conf) +{ + int margin, i; + int colors[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + char delim[8] = {'[', '<', '(', '|', ']', '>', ')', '|'}; + enum bsddialog_color col[6]; + struct timeval tv; + + /* theme */ + if (bsddialog_get_theme(&t) != BSDDIALOG_OK) + exit_error(false, bsddialog_geterror()); + + gettimeofday(&tv, NULL); + srand(tv.tv_usec); + for (i = 0; i < 6; i++) { + do { + col[i] = rand() % 8; + } while (colors[col[i]] == 1); + colors[col[i]] = 1; + } + + t.screen.color = bsddialog_color(col[4], col[3], 0); + + t.shadow.color = bsddialog_color(col[0], col[0], 0); + t.shadow.y = 1, + t.shadow.x = 2, + + t.dialog.delimtitle = (~rand() & 1) ? true : false; + t.dialog.titlecolor = bsddialog_color(col[3], col[5], 0); + t.dialog.lineraisecolor = bsddialog_color(col[0], col[5], 0); + t.dialog.linelowercolor = bsddialog_color(col[0], col[5], 0); + t.dialog.color = bsddialog_color(col[0], col[5], 0); + t.dialog.bottomtitlecolor = bsddialog_color(col[0], col[5], 0); + t.dialog.arrowcolor = bsddialog_color(col[3], col[5], 0); + + t.menu.f_prefixcolor = bsddialog_color(col[5], col[3], 0); + t.menu.prefixcolor = bsddialog_color(col[0], col[5], 0); + t.menu.f_selectorcolor = bsddialog_color(col[5], col[3], 0); + t.menu.selectorcolor = bsddialog_color(col[0], col[5], 0); + t.menu.f_desccolor = bsddialog_color(col[5], col[3], 0); + t.menu.desccolor = bsddialog_color(col[0], col[5], 0); + t.menu.f_namecolor = bsddialog_color(col[5], col[3], 0); + t.menu.namecolor = bsddialog_color(col[3], col[5], 0); + t.menu.f_shortcutcolor = bsddialog_color(col[1], col[3], 0); + t.menu.shortcutcolor = bsddialog_color(col[1], col[5], 0); + t.menu.bottomdesccolor = bsddialog_color(col[4], col[3], 0); + t.menu.sepnamecolor = bsddialog_color(col[1], col[5], 0); + t.menu.sepdesccolor = bsddialog_color(col[1], col[5], 0); + + t.form.f_fieldcolor = bsddialog_color(col[5], col[3], 0); + t.form.fieldcolor = bsddialog_color(col[5], col[4], 0); + t.form.readonlycolor = bsddialog_color(col[4], col[5], 0); + t.form.bottomdesccolor = bsddialog_color(col[4], col[3], 0); + + t.bar.f_color = bsddialog_color(col[5], col[3], 0); + t.bar.color = bsddialog_color(col[3], col[5], 0); + + t.button.minmargin = 1, + t.button.maxmargin = 5, + i = rand() % 4; + t.button.leftdelim = delim[i]; + t.button.rightdelim = delim[i + 4]; + t.button.f_delimcolor = bsddialog_color(col[5], col[3], 0); + t.button.delimcolor = bsddialog_color(col[0], col[5], 0); + t.button.f_color = bsddialog_color(col[2], col[3], 0); + t.button.color = bsddialog_color(col[0], col[5], 0); + t.button.f_shortcutcolor = bsddialog_color(col[5], col[3], 0); + t.button.shortcutcolor = bsddialog_color(col[1], col[5], 0); + + if (bsddialog_set_theme(&t)) + exit_error(false, bsddialog_geterror()); + + /* conf */ + conf->button.always_active = (~rand() & 1) ? true : false; + if ((i = rand() % 3) != 0) /* default "d/m/y" */ + conf->date.format = (i & 1) ? "m/d/y" : "y/m/d" ; + if (conf->title != NULL) { + memset(title, 0, 1024); + margin = rand() % 5; + memset(title, ' ', margin); + strcpy(title + margin, conf->title); + memset(title + strlen(title), ' ', margin); + conf->title = title; + } +} |