diff options
author | Alfonso S. Siciliano <asiciliano@FreeBSD.org> | 2022-09-03 14:36:16 +0000 |
---|---|---|
committer | Alfonso S. Siciliano <asiciliano@FreeBSD.org> | 2022-09-03 14:36:16 +0000 |
commit | 2c9fd7655ba54e7239f528e1af9fe09662de9b03 (patch) | |
tree | fe1ff8f189880e6177658fee195a574d5ae6ebe7 | |
parent | 2f8d4418415511460bd7b3b3e532f6b328cf993f (diff) |
contrib/bsddialog: Import version 0.3vendor/bsddialog/0.3
New features overview:
* Unicode. User interface handles multi-column characters. API can
handle char* like a multibyte character string. Internally wide
characters are used for keyboard input, to adapt word wrapping and
dynamic text auto-sizing for multi-column characters.
* Forms refactoring. Complete rewrite deleting libformw dependency.
* Theme. New utility options to save and load custom theme at run-time.
* TUI navigation. Added keys to navigate input components. Changed
default focus behavior of input dialogs to be LGPL-dialog-like; a new
option can set the previous whiptail-like behavior.
See /usr/src/contrib/bsddialog/CHANGELOG '2022-08-29 Version 0.3'
for more detailed information.
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | CHANGELOG | 77 | ||||
-rw-r--r-- | GNUMakefile | 2 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | README.md | 25 | ||||
-rw-r--r-- | bsddialog.1 | 216 | ||||
-rw-r--r-- | bsddialog.c | 260 | ||||
-rwxr-xr-x | examples_library/compile | 4 | ||||
-rw-r--r-- | examples_library/datebox.c | 4 | ||||
-rw-r--r-- | examples_library/form.c | 12 | ||||
-rw-r--r-- | examples_library/formw.c | 61 | ||||
-rw-r--r-- | examples_library/timebox.c | 4 | ||||
-rwxr-xr-x | examples_utility/form.sh | 10 | ||||
-rwxr-xr-x | examples_utility/mixedform.sh | 6 | ||||
-rwxr-xr-x | examples_utility/passwordform.sh | 10 | ||||
-rw-r--r-- | lib/GNUMakefile | 6 | ||||
-rw-r--r-- | lib/Makefile | 4 | ||||
-rw-r--r-- | lib/barbox.c | 78 | ||||
-rw-r--r-- | lib/bsddialog.3 | 172 | ||||
-rw-r--r-- | lib/bsddialog.h | 20 | ||||
-rw-r--r-- | lib/bsddialog_theme.h | 17 | ||||
-rw-r--r-- | lib/formbox.c | 1145 | ||||
-rw-r--r-- | lib/infobox.c | 6 | ||||
-rw-r--r-- | lib/lib_util.c | 551 | ||||
-rw-r--r-- | lib/lib_util.h | 16 | ||||
-rw-r--r-- | lib/libbsddialog.c | 19 | ||||
-rw-r--r-- | lib/menubox.c | 155 | ||||
-rw-r--r-- | lib/messagebox.c | 63 | ||||
-rw-r--r-- | lib/textbox.c | 117 | ||||
-rw-r--r-- | lib/theme.c | 187 | ||||
-rw-r--r-- | lib/timebox.c | 190 | ||||
-rw-r--r-- | util_theme.c | 356 | ||||
-rw-r--r-- | util_theme.h | 35 |
33 files changed, 2670 insertions, 1162 deletions
diff --git a/.gitignore b/.gitignore index 62f7b594a708..10a6663fbb47 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ examples_library/checklist examples_library/datebox examples_library/form examples_library/formw +examples_library/margin examples_library/menu examples_library/mixedlist examples_library/radiolist @@ -19,6 +20,7 @@ examples_library/rangebox examples_library/sade examples_library/timebox examples_library/yesno +examples_library/u_msgbox *.gz lib/libbsddialog.so* BSDDIALOG.geany diff --git a/CHANGELOG b/CHANGELOG index 22eb3342cfe2..d6de6928c111 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,77 @@ -2022-03-02 version 0.2 +2022-08-29 Version 0.3 + + Utility: + * add: --textbox accepts options for the first button. + * add: --columns-per-row option for text autosizing. + * add: --load-theme option to read and set a custom theme at runtime. + * add: --save-theme option to save current theme. + * add: --bikeshed option for random settings. + * add: --switch-buttons enables focus switching: buttons / input + components. 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. + * 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 bsddialo_theme.h. + * add: bsddialog_hascolors() in bsddialo_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: internal segmentation fault with disabled shadow. + * fix: bsddialog_gauge() refresh new text. + * 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. @@ -31,7 +104,7 @@ * improve: "menus" colors for accessibility. -2022-01-27 version 0.1 +2022-01-27 Version 0.1 * Common-Options: --ascii-lines, --backtitle <backtitle>, --begin-x <x>, --begin-y <y>, --cancel-label <label>, --clear, --colors, --cr-wrap, diff --git a/GNUMakefile b/GNUMakefile index ad3d4f55f4a7..d57c6248d02d 100644 --- a/GNUMakefile +++ b/GNUMakefile @@ -4,7 +4,7 @@ # Written in 2021 by Alfonso Sabato Siciliano OUTPUT= bsddialog -SOURCES= bsddialog.c +SOURCES= bsddialog.c util_theme.c OBJECTS= $(SOURCES:.c=.o) LIBPATH= ./lib LIBBSDDIALOG= $(LIBPATH)/libbsddialog.so @@ -4,7 +4,7 @@ # Written in 2021 by Alfonso Sabato Siciliano OUTPUT= bsddialog -SOURCES= bsddialog.c +SOURCES= bsddialog.c util_theme.c OBJECTS= ${SOURCES:.c=.o} LIBPATH= ${.CURDIR}/lib LIBBSDDIALOG= ${LIBPATH}/libbsddialog.so diff --git a/README.md b/README.md index 3a814b38ddb4..016995b232ae 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,13 @@ -# BSDDialog 0.2 +# BSDDialog 0.3 - -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 dialogs and widgets. ## Intro Briefly: -<https://www.freebsd.org/status/report-2021-04-2021-06/#_bsddialog_tui_widgets> +<https://www.freebsd.org/status/report-2021-04-2021-06/bsddialog/> Utility: <https://alfonsosiciliano.gitlab.io/posts/2021-12-07-bsddialog.html> @@ -31,6 +30,15 @@ FreeBSD: % ./bsddialog --msgbox "Hello World!" 8 20 ``` +Linux: + +``` +% git clone https://gitlab.com/alfix/bsddialog.git +% cd bsddialog +% make -f GNUMakefile +% ./bsddialog --msgbox "Hello World!" 8 20 +``` + Output:  @@ -101,6 +109,7 @@ in the _Public Domain_ to build new projects: ``` % cd examples_library % sh compile +% ./checklist % ./datebox % ./form % ./infobox @@ -114,4 +123,10 @@ in the _Public Domain_ to build new projects: % ./timebox % ./yesno ``` + + +## TODO and Ideas + - menubar feature + - key callback + - Right-To-Left text diff --git a/bsddialog.1 b/bsddialog.1 index c87b760d2ea3..0bf8dd9dc3af 100644 --- a/bsddialog.1 +++ b/bsddialog.1 @@ -22,7 +22,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd February 3, 2022 +.Dd August 29, 2022 .Dt BSDDIALOG 1 .Os .Sh NAME @@ -74,6 +74,13 @@ Title on the top side of the screen. Dialog horizontal position, 0 is the left screen side, -1 center. .It Fl Fl begin-y Ar y Dialog vertical position, 0 is the top screen side, -1 center. +.It Fl Fl bikeshed +Random settings. +Colors. +Delimiter and margins around the title. +Buttons always active or TAB to switch focus with input components, see +.Fl Fl switch-buttons . +Zero padding with time or date output. .It Fl Fl cancel-label Ar label Label for the .Dq Cancel @@ -100,7 +107,7 @@ cyan. .It Dq \eZ7 white. .It Dq \eZr -reverse foreground and background colors. +reverse foreground and background. .It Dq \eZR disable reverse. .It Dq \eZb @@ -112,8 +119,11 @@ underline. .It Dq \eZU disable underline. .It Dq \eZn -restore to normal text. +restore normal text. .El +.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 Replace new line with a space in .Ar text . @@ -141,7 +151,7 @@ Equivalent to .Fl Fl default-no . .It Fl Fl disable-esc Disable ESC key to quit. -.It Fl Fl esc-cancelvalue +.It Fl Fl esc-return-cancel Exits with the .Dq Cancel button value if the ESC key is pressed. @@ -197,8 +207,7 @@ Do not exit with unknown options. .It Fl Fl insecure Print .Sq * -to hide passwords while typing, default space -.Sq " " . +to hide passwords while typing; whitespace otherwise. .It Fl Fl item-depth Specify a margin for items, available for Checklist, Menu and Radiolist. .It Fl Fl item-help @@ -206,9 +215,12 @@ Set a help string for each element of a Checklist, Form, Menu, Mixedform, Passwordform, Radiolist and Treeview to display at the bottom screen side. .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 input-box +.Fl Fl inputbox ans .Fl Fl passwordbox , default 2048. @@ -262,6 +274,8 @@ Print Dialog height and widget at exit. Print version. .It Fl Fl quoted Quote items in output, default only when necessary. +.It Fl Fl save-theme Ar file +Save the current theme. .It Fl Fl separate-output Separate selected items with a new line and avoid to quote. .It Fl Fl separator Ar sep @@ -279,6 +293,18 @@ seconds to close the dialog. 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 +enables 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 +and +.Fl Fl datebox . .It Fl Fl tab-len Ar spaces Number of spaces to print a TAB in .Ar text . @@ -319,7 +345,7 @@ is the graphical height of the list, 0 for autosize. .It Fl Fl datebox Ar text Ar rows Ar cols Op Ar year Ar month Ar day 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 maxvaluelen Oc ... +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 @@ -333,19 +359,21 @@ and .Ar xfield with graphical length .Ar fieldlen , -.Ar maxvaluelen +.Ar maxletters is the maximum input length. The field can be customized, if .Ar fieldlen -is 0 its length is the absolute value of -.Ar maxvaluelen , -if -.Ar maxvaluelen -is negative the field is read only, +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, has to be at least the number of forms. +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 , @@ -374,7 +402,7 @@ 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 maxvaluelen Ar flag Oc ... +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 @@ -388,7 +416,7 @@ at the position .Ar yfield and .Ar xfield , -.Ar maxvaluelen +.Ar maxletters is the maximum input length, .Ar init is a default value, @@ -396,7 +424,9 @@ is a default value, 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, has to be at least the number of forms. +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 @@ -436,7 +466,8 @@ 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 valuelen Oc ... +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. @@ -531,14 +562,39 @@ Backtitle, title and message: 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 +Bikeshed: +.Dl bsddialog --bikeshed --inputbox Example 0 0 +.Pp Mixedgauge: -.Dl bsddialog --sleep 3 --mixedgauge Example 10 30 60 L1 "\(dq" -1" L2 30 +.Dl bsddialog --sleep 3 --mixedgauge Example 10 30 60 L1 \*q -1\*q L2 30 .Pp -Form: -.Dl bsddialog --form Form 0 0 2 L1: 1 1 X 1 5 20 25 L2: 2 1 X 2 5 20 25 +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 @@ -558,22 +614,6 @@ do i=`expr $i + 1` done | bsddialog --title Gauge --gauge "Starting..." 10 70 .Ed -.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 .Sh SEE ALSO .Xr bsddialog 3 .Sh HISTORY @@ -584,7 +624,99 @@ utility first appeared in .Sh AUTHORS .Nm bsddialog was written by -.An Alfonso Sabato Siciliano Aq Mt alf.siciliano@gmail.com . -.Sh BUGS -Forms do not resize the dialog after a terminal change and do not provide -scrolling for items.
\ No newline at end of file +.An Alfonso Sabato Siciliano +.Aq Mt asiciliano@FreeBSD.org . +.Pp +.Nm bsddialog +provides a subset of the functionality described in the +.Nm dialog +manual. +The following features were reimplemented: +.Pp +Common options: +.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 max-input , +.Fl Fl no-cancel , +.Fl Fl nocancel , +.Fl Fl no-collapse , +.Fl Fl no-items , +.Fl Fl no-label , +.Fl Fl no-lines , +.Fl Fl no-nl-expand , +.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 trim , +.Fl Fl version , +.Fl Fl yes-label . +.Pp +Dialogs: +.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 +and +.An \&Ed Maste +.Aq Mt emaste@FreeBSD.org +for suggestions, help, and testing. diff --git a/bsddialog.c b/bsddialog.c index a902d751dab0..bfd6d8dbc5b7 100644 --- a/bsddialog.c +++ b/bsddialog.c @@ -39,7 +39,9 @@ #include <bsddialog.h> #include <bsddialog_theme.h> -#define BSDDIALOG_VERSION "0.2" +#include "util_theme.h" + +#define BSDDIALOG_VERSION "0.3" enum OPTS { /* Common options */ @@ -47,16 +49,18 @@ enum OPTS { BACKTITLE, BEGIN_X, BEGIN_Y, + BIKESHED, CANCEL_LABEL, CLEAR, COLORS, + COLUMNS_PER_ROW, CR_WRAP, DATE_FORMAT, DEFAULT_BUTTON, DEFAULT_ITEM, DEFAULT_NO, DISABLE_ESC, - ESC_CANCELVALUE, + ESC_RETURNCANCEL, EXIT_LABEL, EXTRA_BUTTON, EXTRA_LABEL, @@ -75,6 +79,7 @@ enum OPTS { ITEM_DEPTH, ITEM_HELP, ITEM_PREFIX, + LOAD_THEME, MAX_INPUT, NO_CANCEL, NO_COLLAPSE, @@ -91,12 +96,14 @@ enum OPTS { PRINT_SIZE, PRINT_VERSION, QUOTED, + SAVE_THEME, SEPARATE_OUTPUT, SHADOW, SINGLE_QUOTED, SLEEP, STDERR, STDOUT, + SWITCH_BUTTONS, TAB_LEN, THEME, TIME_FORMAT, @@ -136,15 +143,16 @@ static char *date_fmt_opt, *time_fmt_opt; static int unsigned max_input_form_opt; /* General options */ static int output_fd_opt; +bool bikeshed_opt; +/* Functions */ +static void sigint_handler(int sig); static void custom_text(bool cr_wrap, bool no_collapse, bool no_nl_expand, bool trim, char *text, char *buf); - -static void sigint_handler(int sig); - +static void errorexit(char *errbuf); /* Dialogs */ -#define BUILDER_ARGS struct bsddialog_conf conf, char* text, int rows, \ +#define BUILDER_ARGS struct bsddialog_conf *conf, char* text, int rows, \ int cols, int argc, char **argv, char *errbuf static int checklist_builder(BUILDER_ARGS); static int datebox_builder(BUILDER_ARGS); @@ -176,23 +184,24 @@ static void usage(void) printf("Common Options:\n"); printf("--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|flat|dialog>, " + "--begin-y <y>, --bikeshed, --cancel-label <label>, --clear, " + "--colors, --columns-per-row <columns>, --cr-wrap, " + "--date-format <format>, --default-button <label>, " + "--default-item <name>, --default-no, --disable-esc, " + "--esc-return-cancel, --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 <file>, " + "--hline <string>, --hmsg <string>, --ignore, --insecure, " + "--item-depth, --item-help, --item-prefix, --load-theme <file>, " + "--max-input <size>, --no-cancel, --no-collapse, --no-items, " + "--no-label <label>, --no-lines, --no-nl-expand, --no-ok, " + "--no-shadow, --no-tags, --ok-label <label>, --output-fd <fd>, " + "--output-separator <sep>, --print-maxsize, --print-size, " + "--print-version, --quoted, --save-theme <file>, " + "--separate-output, --separator <sep>, --shadow, --single-quoted, " + "--sleep <secs>, --stderr, --stdout, --tab-len <spaces>, " + "--switch-buttons, --theme <blackwhite|bsddialog|flat|dialog>, " "--time-format <format>, --title <title>, --trim, --version, " "--yes-label <label>.\n"); printf("\n"); @@ -202,14 +211,14 @@ static void usage(void) "<on|off>] ...\n"); printf("--datebox <text> <rows> <cols> [<yy> <mm> <dd>]\n"); printf("--form <text> <rows> <cols> <formrows> [<label> <ylabel> " - "<xlabel> <init> <yfield> <xfield> <fieldlen> <maxvaluelen>] " + "<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> <maxvaluelen> " + "<xlabel> <init> <yfield> <xfield> <fieldlen> <maxletters> " "<0|1|2>] ...\n"); printf("--mixedgauge <text> <rows> <cols> <mainperc> [<minilabel> " "<miniperc>] ...\n"); @@ -217,7 +226,7 @@ static void usage(void) printf("--passwordbox <text> <rows> <cols> [init]\n"); printf("--passwordform <text> <rows> <cols> <formrows> [<label> " "<ylabel> <xlabel> <init> <yfield> <xfield> <fieldlen> " - "<maxvaluelen>] ...\n"); + "<maxletters>] ...\n"); printf("--pause <text> <rows> <cols> <secs>\n"); printf("--radiolist <text> <rows> <cols> <menurows> [<name> <desc> " "<on|off>] ...\n"); @@ -235,11 +244,12 @@ static void usage(void) int main(int argc, char *argv[argc]) { bool cr_wrap_opt, no_collapse_opt, no_nl_expand_opt, trim_opt; - bool esc_cancelvalue_opt, ignore_opt, print_maxsize_opt;; - int input, rows, cols, output, getH, getW; + bool esc_return_cancel_opt, ignore_opt, print_maxsize_opt; + bool textfromfile; + int input, rows, cols, retval, getH, getW; int (*dialogbuilder)(BUILDER_ARGS) = NULL; enum bsddialog_default_theme theme_opt; - char *text, *backtitle_opt; + char *text, *backtitle_opt, *loadthemefile, *savethemefile; char errorbuilder[1024]; struct winsize ws; struct bsddialog_conf conf; @@ -250,7 +260,7 @@ int main(int argc, char *argv[argc]) conf.key.enable_esc = true; conf.menu.on_without_ok = true; conf.form.value_without_ok = true; - conf.form.enable_wchar = true; + conf.button.always_active = true; backtitle_opt = NULL; theme_opt = BSDDIALOG_THEME_FLAT; @@ -258,8 +268,12 @@ int main(int argc, char *argv[argc]) print_maxsize_opt = false; ignore_opt = false; cr_wrap_opt = no_collapse_opt = no_nl_expand_opt = trim_opt = false; - esc_cancelvalue_opt = false; + esc_return_cancel_opt = false; + textfromfile = false; + bikeshed_opt = false; errorbuilder[0] = '\0'; + savethemefile = NULL; + loadthemefile = NULL; item_output_sepnl_opt = item_singlequote_opt = false; item_prefix_opt = item_bottomdesc_opt = item_depth_opt = false; @@ -279,6 +293,7 @@ int main(int argc, char *argv[argc]) {"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-label", required_argument, NULL, CANCEL_LABEL}, {"clear", no_argument, NULL, CLEAR}, {"colors", no_argument, NULL, COLORS}, @@ -289,7 +304,7 @@ int main(int argc, char *argv[argc]) {"default-item", required_argument, NULL, DEFAULT_ITEM}, {"default-no", no_argument, NULL, DEFAULT_NO}, {"disable-esc", no_argument, NULL, DISABLE_ESC}, - {"esc-cancelvalue", no_argument, NULL, ESC_CANCELVALUE}, + {"esc-return-cancel",no_argument, NULL, ESC_RETURNCANCEL}, {"exit-label", required_argument, NULL, EXIT_LABEL}, {"extra-button", no_argument, NULL, EXTRA_BUTTON}, {"extra-label", required_argument, NULL, EXTRA_LABEL}, @@ -308,6 +323,7 @@ int main(int argc, char *argv[argc]) {"item-depth", no_argument, NULL, ITEM_DEPTH}, {"item-help", no_argument, NULL, ITEM_HELP}, {"item-prefix", no_argument, NULL, ITEM_PREFIX}, + {"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}, @@ -327,6 +343,8 @@ int main(int argc, char *argv[argc]) {"print-size", no_argument, NULL, PRINT_SIZE}, {"print-version", no_argument, NULL, PRINT_VERSION}, {"quoted", no_argument, NULL, QUOTED}, + {"columns-per-row", required_argument, NULL, COLUMNS_PER_ROW}, + {"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}, @@ -334,6 +352,7 @@ int main(int argc, char *argv[argc]) {"sleep", required_argument, NULL, SLEEP}, {"stderr", no_argument, NULL, STDERR}, {"stdout", no_argument, NULL, STDOUT}, + {"switch-buttons", no_argument, NULL, SWITCH_BUTTONS}, {"tab-len", required_argument, NULL, TAB_LEN}, {"theme", required_argument, NULL, THEME}, {"time-format", required_argument, NULL, TIME_FORMAT}, @@ -373,6 +392,8 @@ int main(int argc, char *argv[argc]) break; case BACKTITLE: backtitle_opt = optarg; + if (conf.y == BSDDIALOG_CENTER) + conf.auto_topmargin = 2; break; case BEGIN_X: conf.x = (int)strtol(optarg, NULL, 10); @@ -389,6 +410,10 @@ int main(int argc, char *argv[argc]) conf.y, BSDDIALOG_CENTER); return (255); } + conf.auto_topmargin = 0; + break; + case BIKESHED: + bikeshed_opt = true; break; case CANCEL_LABEL: conf.button.cancel_label = optarg; @@ -399,6 +424,10 @@ int main(int argc, char *argv[argc]) case COLORS: conf.text.highlight = true; break; + case COLUMNS_PER_ROW: + conf.text.cols_per_row = + (u_int)strtoul(optarg, NULL, 10); + break; case CR_WRAP: cr_wrap_opt = true; break; @@ -417,8 +446,8 @@ int main(int argc, char *argv[argc]) case DISABLE_ESC: conf.key.enable_esc = false; break; - case ESC_CANCELVALUE: - esc_cancelvalue_opt = true; + case ESC_RETURNCANCEL: + esc_return_cancel_opt = true; break; case EXIT_LABEL: conf.button.ok_label = optarg; @@ -454,7 +483,8 @@ int main(int argc, char *argv[argc]) conf.key.f1_file = optarg; break; case HLINE: - conf.bottomtitle = optarg; + if (optarg[0] != '\0') + conf.bottomtitle = optarg; break; case HMSG: conf.key.f1_message = optarg; @@ -474,6 +504,9 @@ int main(int argc, char *argv[argc]) case ITEM_PREFIX: item_prefix_opt = true; break; + case LOAD_THEME: + loadthemefile = optarg; + break; case MAX_INPUT: max_input_form_opt = (u_int)strtoul(optarg, NULL, 10); break; @@ -523,6 +556,9 @@ int main(int argc, char *argv[argc]) case PRINT_VERSION: printf("bsddialog version %s\n", BSDDIALOG_VERSION); break; + case SAVE_THEME: + savethemefile = optarg; + break; case SEPARATE_OUTPUT: item_output_sepnl_opt = true; break; @@ -541,6 +577,9 @@ int main(int argc, char *argv[argc]) case STDOUT: output_fd_opt = STDOUT_FILENO; break; + case SWITCH_BUTTONS: + conf.button.always_active = false; + break; case TAB_LEN: conf.text.tablen = (u_int)strtoul(optarg, NULL, 10); break; @@ -574,12 +613,14 @@ int main(int argc, char *argv[argc]) /* Dialogs */ case CHECKLIST: dialogbuilder = checklist_builder; + conf.auto_downmargin = 1; break; case DATEBOX: dialogbuilder = datebox_builder; break; case FORM: dialogbuilder = form_builder; + conf.auto_downmargin = 1; break; case GAUGE: dialogbuilder = gauge_builder; @@ -589,12 +630,15 @@ int main(int argc, char *argv[argc]) break; case INPUTBOX: dialogbuilder = inputbox_builder; + conf.auto_downmargin = 1; break; case MENU: dialogbuilder = menu_builder; + conf.auto_downmargin = 1; break; case MIXEDFORM: dialogbuilder = mixedform_builder; + conf.auto_downmargin = 1; break; case MIXEDGAUGE: dialogbuilder = mixedgauge_builder; @@ -607,24 +651,29 @@ int main(int argc, char *argv[argc]) break; case PASSWORDBOX: dialogbuilder = passwordbox_builder; + conf.auto_downmargin = 1; break; case PASSWORDFORM: dialogbuilder = passwordform_builder; + conf.auto_downmargin = 1; break; case RADIOLIST: dialogbuilder = radiolist_builder; + conf.auto_downmargin = 1; break; case RANGEBOX: dialogbuilder = rangebox_builder; break; case TEXTBOX: dialogbuilder = textbox_builder; + textfromfile = true; break; case TIMEBOX: dialogbuilder = timebox_builder; break; case TREEVIEW: dialogbuilder = treeview_builder; + conf.auto_downmargin = 1; break; case YESNO: dialogbuilder = yesno_builder; @@ -652,7 +701,7 @@ int main(int argc, char *argv[argc]) usage(); return (255); } - if (dialogbuilder == textbox_builder) + if (textfromfile) /* textbox */ text = argv[0]; else { if ((text = malloc(strlen(argv[0]) + 1)) == NULL) { @@ -670,45 +719,52 @@ int main(int argc, char *argv[argc]) /* bsddialog terminal mode */ if (bsddialog_init() != 0) { printf("Error: %s\n", bsddialog_geterror()); - return (BSDDIALOG_ERROR); + return (255); } signal(SIGINT, sigint_handler); if (theme_opt != BSDDIALOG_THEME_FLAT) - bsddialog_set_default_theme(theme_opt); + if (bsddialog_set_default_theme(theme_opt) != BSDDIALOG_OK) + errorexit(NULL); + if (loadthemefile != NULL) + if (loadtheme(loadthemefile, errorbuilder) != BSDDIALOG_OK) + errorexit(errorbuilder); + if (bikeshed_opt) + if (bikeshed(&conf, errorbuilder) != BSDDIALOG_OK) + errorexit(errorbuilder); if (backtitle_opt != NULL) - bsddialog_backtitle(&conf, backtitle_opt); + if( bsddialog_backtitle(&conf, backtitle_opt)) + errorexit(NULL); - errorbuilder[0] = '\0'; - output = BSDDIALOG_OK; - if (dialogbuilder != NULL) - output = dialogbuilder(conf, text, rows, cols, argc, argv, + if (dialogbuilder != NULL) { + retval = dialogbuilder(&conf, text, rows, cols, argc, argv, errorbuilder); + if (retval == BSDDIALOG_ERROR) + errorexit(errorbuilder); + } else + retval = BSDDIALOG_OK; - if (dialogbuilder != textbox_builder) - free(text); + if (savethemefile != NULL) + if (savetheme(savethemefile, errorbuilder, BSDDIALOG_VERSION) != + BSDDIALOG_OK) + errorexit(errorbuilder); 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()); - return (255); - } + if (textfromfile == false) + free(text); if (conf.get_height != NULL && conf.get_width != NULL) dprintf(output_fd_opt, "Dialog size: (%d - %d)\n", *conf.get_height, *conf.get_width); - if (output == BSDDIALOG_ESC && esc_cancelvalue_opt) - output = BSDDIALOG_CANCEL; + if (retval == BSDDIALOG_ESC && esc_return_cancel_opt) + retval = BSDDIALOG_CANCEL; - return (output); + return (retval); } void sigint_handler(int sig) @@ -718,6 +774,18 @@ void sigint_handler(int sig) exit(255); } +void errorexit(char *errbuf) +{ + bsddialog_end(); + + if (errbuf != NULL && errbuf[0] != '\0') + printf("Error: %s\n", errbuf); + else + printf("Error: %s\n", bsddialog_geterror()); + + exit(255); +} + void custom_text(bool cr_wrap, bool no_collapse, bool no_nl_expand, bool trim, char *text, char *buf) @@ -780,7 +848,7 @@ int gauge_builder(BUILDER_ARGS) else perc = 0; - output = bsddialog_gauge(&conf, text, rows, cols, perc, STDIN_FILENO, + output = bsddialog_gauge(conf, text, rows, cols, perc, STDIN_FILENO, "XXX"); return (output); @@ -790,7 +858,7 @@ int infobox_builder(BUILDER_ARGS) { int output; - output = bsddialog_infobox(&conf, text, rows, cols); + output = bsddialog_infobox(conf, text, rows, cols); return (output); } @@ -826,7 +894,7 @@ int mixedgauge_builder(BUILDER_ARGS) minipercs[i] = (int)strtol(argv[i * 2 + 1], NULL, 10); } - output = bsddialog_mixedgauge(&conf, text, rows, cols, mainperc, + output = bsddialog_mixedgauge(conf, text, rows, cols, mainperc, nminibars, minilabels, minipercs); return (output); @@ -836,7 +904,7 @@ int msgbox_builder(BUILDER_ARGS) { int output; - output = bsddialog_msgbox(&conf, text, rows, cols); + output = bsddialog_msgbox(conf, text, rows, cols); return (output); } @@ -852,7 +920,7 @@ int pause_builder(BUILDER_ARGS) } secs = (u_int)strtoul(argv[0], NULL, 10); - output = bsddialog_pause(&conf, text, rows, cols, secs); + output = bsddialog_pause(conf, text, rows, cols, secs); return (output); } @@ -878,7 +946,7 @@ int rangebox_builder(BUILDER_ARGS) else value = min; - output = bsddialog_rangebox(&conf, text, rows, cols, min, max, &value); + output = bsddialog_rangebox(conf, text, rows, cols, min, max, &value); dprintf(output_fd_opt, "%d", value); @@ -889,7 +957,7 @@ int textbox_builder(BUILDER_ARGS) { int output; - output = bsddialog_textbox(&conf, text, rows, cols); + output = bsddialog_textbox(conf, text, rows, cols); return (output); } @@ -898,7 +966,7 @@ int yesno_builder(BUILDER_ARGS) { int output; - output = bsddialog_yesno(&conf, text, rows, cols); + output = bsddialog_yesno(conf, text, rows, cols); return (output); } @@ -924,13 +992,11 @@ int datebox_builder(BUILDER_ARGS) dd = (u_int)strtoul(argv[2], NULL, 10); } - output = bsddialog_datebox(&conf, text, rows, cols, &yy, &mm, &dd); + output = bsddialog_datebox(conf, text, rows, cols, &yy, &mm, &dd); if (output != BSDDIALOG_OK) return (output); - if (date_fmt_opt == NULL) { - dprintf(output_fd_opt, "%u/%u/%u", yy, mm, dd); - } else { + if (date_fmt_opt != NULL) { time(&cal); localtm = localtime(&cal); localtm->tm_year = yy - 1900; @@ -938,6 +1004,10 @@ int datebox_builder(BUILDER_ARGS) localtm->tm_mday = dd; strftime(stringdate, 1024, date_fmt_opt, localtm); dprintf(output_fd_opt, "%s", stringdate); + } else if (bikeshed_opt && (dd % 2 == 0)) { + dprintf(output_fd_opt, "%u/%u/%u", yy, mm, dd); + } else { + dprintf(output_fd_opt, "%u/%02u/%02u", yy, mm, dd); } return (output); @@ -963,13 +1033,11 @@ int timebox_builder(BUILDER_ARGS) ss = (u_int)strtoul(argv[2], NULL, 10); } - output = bsddialog_timebox(&conf, text, rows, cols, &hh, &mm, &ss); + output = bsddialog_timebox(conf, text, rows, cols, &hh, &mm, &ss); if (output != BSDDIALOG_OK) return (output); - if (time_fmt_opt == NULL) { - dprintf(output_fd_opt, "%u:%u:%u", hh, mm, ss); - } else { + if (time_fmt_opt != NULL) { time(&clock); localtm = localtime(&clock); localtm->tm_hour = hh; @@ -977,6 +1045,10 @@ int timebox_builder(BUILDER_ARGS) localtm->tm_sec = ss; strftime(stringtime, 1024, time_fmt_opt, localtm); dprintf(output_fd_opt, "%s", stringtime); + } else if (bikeshed_opt && (ss % 2 == 0)) { + dprintf(output_fd_opt, "%u:%u:%u", hh, mm, ss); + } else { + dprintf(output_fd_opt, "%02u:%02u:%02u", hh, mm, ss); } return (output); @@ -1116,7 +1188,7 @@ int checklist_builder(BUILDER_ARGS) if (output != 0) return (output); - output = bsddialog_checklist(&conf, text, rows, cols, menurows, nitems, + output = bsddialog_checklist(conf, text, rows, cols, menurows, nitems, items, &focusitem); print_menu_items(output, nitems, items, focusitem); @@ -1145,7 +1217,7 @@ int menu_builder(BUILDER_ARGS) if (output != 0) return (output); - output = bsddialog_menu(&conf, text, rows, cols, menurows, nitems, + output = bsddialog_menu(conf, text, rows, cols, menurows, nitems, items, &focusitem); print_menu_items(output, nitems, items, focusitem); @@ -1174,7 +1246,7 @@ int radiolist_builder(BUILDER_ARGS) if (output != 0) return (output); - output = bsddialog_radiolist(&conf, text, rows, cols, menurows, nitems, + output = bsddialog_radiolist(conf, text, rows, cols, menurows, nitems, items, &focusitem); print_menu_items(output, nitems, items, focusitem); @@ -1203,10 +1275,10 @@ int treeview_builder(BUILDER_ARGS) if (output != 0) return (output); - conf.menu.no_name = true; - conf.menu.align_left = true; + conf->menu.no_name = true; + conf->menu.align_left = true; - output = bsddialog_radiolist(&conf, text, rows, cols, menurows, nitems, + output = bsddialog_radiolist(conf, text, rows, cols, menurows, nitems, items, &focusitem); print_menu_items(output, nitems, items, focusitem); @@ -1238,7 +1310,7 @@ print_form_items(int output, int nitems, struct bsddialog_formitem *items) return; for (i = 0; i < nitems; i++) { - dprintf(output_fd_opt, "%ls\n", (wchar_t*)items[i].value); + dprintf(output_fd_opt, "%s\n", items[i].value); free(items[i].value); } } @@ -1256,7 +1328,6 @@ int form_builder(BUILDER_ARGS) } formheight = (u_int)strtoul(argv[0], NULL, 10); - flags = 0; argc--; argv++; @@ -1279,13 +1350,13 @@ int form_builder(BUILDER_ARGS) valuelen = (int)strtol(argv[j++], NULL, 10); items[i].maxvaluelen = valuelen == 0 ? abs(fieldlen) : valuelen; - flags |= (fieldlen < 0 ? BSDDIALOG_FIELDREADONLY : 0); + flags = (fieldlen < 0 ? BSDDIALOG_FIELDREADONLY : 0); items[i].flags = flags; items[i].bottomdesc = item_bottomdesc_opt ? argv[j++] : ""; } - output = bsddialog_form(&conf, text, rows, cols, formheight, nitems, + output = bsddialog_form(conf, text, rows, cols, formheight, nitems, items); print_form_items(output, nitems, items); free(items); @@ -1302,14 +1373,16 @@ int inputbox_builder(BUILDER_ARGS) 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.yfield = 0; + item.xfield = 0; + item.fieldlen = 1; item.maxvaluelen = max_input_form_opt > 0 ? max_input_form_opt : 2048; - item.flags = 0; + item.flags = BSDDIALOG_FIELDNOCOLOR; + item.flags |= BSDDIALOG_FIELDCURSOREND; + item.flags |= BSDDIALOG_FIELDEXTEND; item.bottomdesc = ""; - output = bsddialog_form(&conf, text, rows, cols, 1, 1, &item); + output = bsddialog_form(conf, text, rows, cols, 1, 1, &item); print_form_items(output, 1, &item); return (output); @@ -1349,7 +1422,7 @@ int mixedform_builder(BUILDER_ARGS) items[i].bottomdesc = item_bottomdesc_opt ? argv[j++] : ""; } - output = bsddialog_form(&conf, text, rows, cols, formheight, nitems, + output = bsddialog_form(conf, text, rows, cols, formheight, nitems, items); print_form_items(output, nitems, items); free(items); @@ -1366,14 +1439,17 @@ int passwordbox_builder(BUILDER_ARGS) 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.yfield = 0; + item.xfield = 0; + item.fieldlen = 1; item.maxvaluelen = max_input_form_opt > 0 ? max_input_form_opt : 2048; 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); + output = bsddialog_form(conf, text, rows, cols, 1, 1, &item); print_form_items(output, 1, &item); return (output); @@ -1421,7 +1497,7 @@ int passwordform_builder(BUILDER_ARGS) items[i].bottomdesc = item_bottomdesc_opt ? argv[j++] : ""; } - output = bsddialog_form(&conf, text, rows, cols, formheight, nitems, + output = bsddialog_form(conf, text, rows, cols, formheight, nitems, items); print_form_items(output, nitems, items); free(items); diff --git a/examples_library/compile b/examples_library/compile index 3c3961d06baf..6f2c23b8ca75 100755 --- a/examples_library/compile +++ b/examples_library/compile @@ -10,7 +10,9 @@ libpath=../lib examples="menu checklist radiolist mixedlist theme infobox yesno msgbox \ - datebox form formw timebox rangebox pause" + datebox form timebox rangebox pause" + +rm -f $examples for e in $examples do diff --git a/examples_library/datebox.c b/examples_library/datebox.c index a3c8946b7f79..7f0138688d53 100644 --- a/examples_library/datebox.c +++ b/examples_library/datebox.c @@ -34,9 +34,7 @@ int main() bsddialog_initconf(&conf); conf.title = "datebox"; - output = bsddialog_datebox(&conf, - "TAB / RIGHT / LEFT to move,\nUP / DOWN to select time", 10, 35, - &yy, &mm, &dd); + output = bsddialog_datebox(&conf, "Example", 9, 35, &yy, &mm, &dd); bsddialog_end(); diff --git a/examples_library/form.c b/examples_library/form.c index 0256975f2023..56d8a8052a72 100644 --- a/examples_library/form.c +++ b/examples_library/form.c @@ -9,6 +9,7 @@ */ #include <bsddialog.h> +#include <locale.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -21,21 +22,22 @@ 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/multicolum charachters */ + 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 = '*'; output = bsddialog_form(&conf, "Example", 10, 50, 3, 3, items); - bsddialog_end(); if (output == BSDDIALOG_ERROR) { diff --git a/examples_library/formw.c b/examples_library/formw.c deleted file mode 100644 index edbeec98f2a3..000000000000 --- a/examples_library/formw.c +++ /dev/null @@ -1,61 +0,0 @@ -/*- - * 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 <locale.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#define H BSDDIALOG_FIELDHIDDEN -#define RO BSDDIALOG_FIELDREADONLY - -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"} - }; - - 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 = '*'; - conf.form.enable_wchar = true; - output = bsddialog_form(&conf, "Example", 10, 50, 3, 3, items); - - bsddialog_end(); - - if (output == BSDDIALOG_ERROR) { - printf("Error: %s", bsddialog_geterror()); - return (1); - } - - if (output == BSDDIALOG_CANCEL) { - printf("Cancel\n"); - return (0); - } - - for (i = 0; i < 3; i++) { - printf("%s \"%ls\"\n", items[i].label, (wchar_t*)items[i].value); - free(items[i].value); - } - - return (output); -}
\ No newline at end of file diff --git a/examples_library/timebox.c b/examples_library/timebox.c index 0e27f824af11..f06986423558 100644 --- a/examples_library/timebox.c +++ b/examples_library/timebox.c @@ -34,9 +34,7 @@ int main() bsddialog_initconf(&conf); conf.title = "timebox"; - output = bsddialog_timebox(&conf, - "TAB / RIGHT / LEFT to move,\nUP / DOWN to select time", 10, 35, - &hh, &mm, &ss); + output = bsddialog_timebox(&conf, "Example", 9, 35, &hh, &mm, &ss); bsddialog_end(); diff --git a/examples_utility/form.sh b/examples_utility/form.sh index bd1bac8a3939..783993d3db30 100755 --- a/examples_utility/form.sh +++ b/examples_utility/form.sh @@ -15,11 +15,11 @@ : ${BSDDIALOG_ESC=5} FORMS=$(./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 \ + 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 diff --git a/examples_utility/mixedform.sh b/examples_utility/mixedform.sh index 80b7abf93745..8a96fa8d9e72 100755 --- a/examples_utility/mixedform.sh +++ b/examples_utility/mixedform.sh @@ -16,9 +16,9 @@ FORMS=$(./bsddialog --insecure --title " mixedform " \ --mixedform "Hello World!" 12 40 3 \ - Label: 1 1 Entry 1 11 18 25 0 \ - Label: 2 1 Read-Only 2 11 18 25 2 \ - Password: 3 1 "" 3 11 18 25 1 \ + 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>&-) case $? in diff --git a/examples_utility/passwordform.sh b/examples_utility/passwordform.sh index 19b3a355b6eb..9bfe11a28645 100755 --- a/examples_utility/passwordform.sh +++ b/examples_utility/passwordform.sh @@ -16,11 +16,11 @@ FORMS=$(./bsddialog --insecure --title " passwordform " \ --passwordform "Example" 12 40 5 \ - Password1: 1 1 "" 1 12 18 25 \ - Password2: 2 1 "" 2 12 18 25 \ - Password3: 3 1 "" 3 12 18 25 \ - Password4: 4 1 "" 4 12 18 25 \ - Password5: 5 1 "" 5 12 18 25 \ + 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>&-) case $? in diff --git a/lib/GNUMakefile b/lib/GNUMakefile index 0d724b803be3..1d55d6edd60d 100644 --- a/lib/GNUMakefile +++ b/lib/GNUMakefile @@ -3,16 +3,16 @@ # # Written in 2021 by Alfonso Sabato Siciliano -VERSION = 0.2 +VERSION = 0.3 LIBRARY = bsddialog LIBRARY_SO = lib${LIBRARY:=.so} HEADERS = bsddialog.h bsddialog_theme.h bsddialog_progressview.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) -CFLAGS = -D_XOPEN_SOURCE_EXTENDED -Wall -Wextra -Wno-implicit-fallthrough \ +CFLAGS = -D_XOPEN_SOURCE_EXTENDED -D_XOPEN_SOURCE -D_GNU_SOURCE -Wall -Wextra -Wno-implicit-fallthrough \ -Werror -fpic -LDFLAGS = -lformw -lncursesw -ltinfo +LDFLAGS = -lncursesw -ltinfo LIBFLAG = -shared RM = rm -f diff --git a/lib/Makefile b/lib/Makefile index 962b059b3e03..5c535c5483f1 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -3,7 +3,7 @@ # # Written in 2021 by Alfonso Sabato Siciliano -VERSION = 0.2 +VERSION = 0.3 LIBRARY = bsddialog LIBRARY_SO = lib${LIBRARY:=.so} LIBRARY_A = lib${LIBRARY:=.a} @@ -14,7 +14,7 @@ OBJECTS = ${SOURCES:.c=.o} CFLAGS += -D_XOPEN_SOURCE_EXTENDED -fPIC -Wall -Wextra LDFLAGS += -fstack-protector-strong -shared -Wl,-x -Wl,--fatal-warnings \ -Wl,--warn-shared-textrel -Wl,-soname,${LIBRARY_SO}.${VERSION} \ - -L/usr/lib -lformw -lncursesw -ltinfow + -L/usr/lib -lncursesw -ltinfow .if defined(DEBUG) # `make -DDEBUG` diff --git a/lib/barbox.c b/lib/barbox.c index 49aa105c1de3..b6bfe32fc0aa 100644 --- a/lib/barbox.c +++ b/lib/barbox.c @@ -70,7 +70,7 @@ draw_bar(WINDOW *win, int y, int x, int barlen, int perc, bool withlabel, sprintf(labelstr, "%d", label); else sprintf(labelstr, "%3d%%", perc); - stringlen = (int)strlen(labelstr); + stringlen = (int)strlen(labelstr); /* number, always 1-byte-ch string */ wmove(win, y, x + barlen/2 - stringlen/2); for (i = 0; i < stringlen; i++) { color = (blue_x + 1 <= barlen/2 - stringlen/2 + i ) ? @@ -109,7 +109,7 @@ bar_checksize(int rows, int cols, struct buttons *bs) minwidth = 0; if (bs != NULL) /* gauge has not buttons */ - minwidth = buttons_width(*bs); + minwidth = buttons_min_width(*bs); minwidth = MAX(minwidth, MINBARWIDTH); minwidth += VBORDERS; @@ -151,16 +151,14 @@ bsddialog_gauge(struct bsddialog_conf *conf, const char *text, int rows, bar = new_boxed_window(conf, y+h-4, x+3, 3, w-6, RAISED); - mainloop = (fd < 0) ? false : true; - - if (mainloop) { + input = NULL; + if (fd >= 0) { fd2 = dup(fd); - input = fdopen(fd2, "r"); - if (input == NULL) + if ((input = fdopen(fd2, "r")) == NULL) RETURN_ERROR("Cannot build FILE* from fd"); - } else - input = NULL; + } + mainloop = true; while (mainloop) { wrefresh(widget); prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-4, @@ -168,6 +166,8 @@ bsddialog_gauge(struct bsddialog_conf *conf, const char *text, int rows, draw_borders(conf, bar, 3, w-6, RAISED); draw_bar(bar, 1, 1, w-8, perc, false, -1 /*unused*/); wrefresh(bar); + if (input == NULL) /* that is fd < 0 */ + break; while (true) { fscanf(input, "%s", inputbuf); @@ -193,10 +193,11 @@ bsddialog_gauge(struct bsddialog_conf *conf, const char *text, int rows, if (strcmp(inputbuf, sep) == 0) break; strcpy(pntext, inputbuf); - pntext += strlen(inputbuf); + pntext += strlen(inputbuf); /* end string, no strlen */ pntext[0] = ' '; pntext++; } + pntext[0] = '\0'; if (update_dialog(conf, shadow, widget, y, x, h, w, textpad, ntext, NULL, false) != 0) return (BSDDIALOG_ERROR); @@ -216,7 +217,7 @@ 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) { - int i, output, miniperc, y, x, h, w, ypad, max_minbarlen; + int i, retval, miniperc, y, x, h, w, ypad, max_minbarlen; int htextpad, htext, wtext; int colorperc, red, green; WINDOW *widget, *textpad, *bar, *shadow; @@ -240,7 +241,7 @@ do_mixedgauge(struct bsddialog_conf *conf, const char *text, int rows, int cols, max_minbarlen = 0; for (i = 0; i < (int)nminibars; i++) - max_minbarlen = MAX(max_minbarlen, (int)strlen(minilabels[i])); + max_minbarlen = MAX(max_minbarlen, (int)strcols(minilabels[i])); max_minbarlen += 3 + 16; /* seps + [...] */ max_minbarlen = MAX(max_minbarlen, MINMGBARWIDTH); /* mainbar */ @@ -267,10 +268,10 @@ do_mixedgauge(struct bsddialog_conf *conf, const char *text, int rows, int cols, if (set_widget_position(conf, &y, &x, h, w) != 0) return (BSDDIALOG_ERROR); - output = new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, + retval = new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, NULL, false); - if (output == BSDDIALOG_ERROR) - return (output); + if (retval == BSDDIALOG_ERROR) + return (retval); /* mini bars */ for (i = 0; i < (int)nminibars; i++) { @@ -281,6 +282,7 @@ do_mixedgauge(struct bsddialog_conf *conf, const char *text, int rows, int cols, if (color && (miniperc >= 0)) wattron(widget, A_BOLD); mvwaddstr(widget, i+1, 2, minilabels[i]); + if (color && (miniperc >= 0)) wattroff(widget, A_BOLD); /* perc */ if (miniperc < -11) @@ -338,12 +340,12 @@ 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 output; + int retval; - output = do_mixedgauge(conf, text, rows, cols, mainperc, nminibars, + retval = do_mixedgauge(conf, text, rows, cols, mainperc, nminibars, minilabels, minipercs, false); - return (output); + return (retval); } int @@ -352,7 +354,7 @@ bsddialog_progressview (struct bsddialog_conf *conf, const char *text, int rows, struct bsddialog_fileminibar *minibar) { bool update; - int perc, output, *minipercs; + int perc, retval, *minipercs; unsigned int i, mainperc, totaltodo; float readforsec; const char **minilabels; @@ -371,7 +373,7 @@ bsddialog_progressview (struct bsddialog_conf *conf, const char *text, int rows, } refresh = pvconf->refresh == 0 ? 0 : pvconf->refresh - 1; - output = BSDDIALOG_OK; + retval = BSDDIALOG_OK; i = 0; update = true; time(&told); @@ -384,9 +386,9 @@ bsddialog_progressview (struct bsddialog_conf *conf, const char *text, int rows, time(&tnew); if (update || tnew > told + refresh) { - output = do_mixedgauge(conf, text, rows, cols, mainperc, + retval = do_mixedgauge(conf, text, rows, cols, mainperc, nminibar, minilabels, minipercs, true); - if (output == BSDDIALOG_ERROR) + if (retval == BSDDIALOG_ERROR) return (BSDDIALOG_ERROR); move(SCREENLINES - 1, 2); @@ -421,7 +423,7 @@ bsddialog_progressview (struct bsddialog_conf *conf, const char *text, int rows, free(minilabels); free(minipercs); - return (output); + return (retval); } int @@ -430,7 +432,8 @@ bsddialog_rangebox(struct bsddialog_conf *conf, const char *text, int rows, { bool loop, buttupdate, barupdate; int y, x, h, w; - int input, currvalue, output, sizebar, bigchange, positions; + int currvalue, retval, sizebar, bigchange, positions; + wint_t input; float perc; WINDOW *widget, *textpad, *bar, *shadow; struct buttons bs; @@ -483,17 +486,18 @@ bsddialog_rangebox(struct bsddialog_conf *conf, const char *text, int rows, wrefresh(bar); } - input = getch(); + if (get_wch(&input) == ERR) + continue; switch(input) { case KEY_ENTER: case 10: /* Enter */ - output = bs.value[bs.curr]; + retval = bs.value[bs.curr]; *value = currvalue; loop = false; break; case 27: /* Esc */ if (conf->key.enable_esc) { - output = BSDDIALOG_ESC; + retval = BSDDIALOG_ESC; loop = false; } break; @@ -587,7 +591,7 @@ bsddialog_rangebox(struct bsddialog_conf *conf, const char *text, int rows, break; default: if (shortcut_buttons(input, &bs)) { - output = bs.value[bs.curr]; + retval = bs.value[bs.curr]; loop = false; } } @@ -596,7 +600,7 @@ bsddialog_rangebox(struct bsddialog_conf *conf, const char *text, int rows, delwin(bar); end_dialog(conf, shadow, widget, textpad); - return (output); + return (retval); } int @@ -604,7 +608,8 @@ bsddialog_pause(struct bsddialog_conf *conf, const char *text, int rows, int cols, unsigned int sec) { bool loop, buttupdate, barupdate; - int output, y, x, h, w, input, tout, sizebar; + int retval, y, x, h, w, tout, sizebar; + wint_t input; float perc; WINDOW *widget, *textpad, *bar, *shadow; struct buttons bs; @@ -650,11 +655,10 @@ bsddialog_pause(struct bsddialog_conf *conf, const char *text, int rows, buttupdate = false; } - input = getch(); - if (input < 0) { /* timeout */ + if (get_wch(&input) == ERR) { /* timeout */ tout--; if (tout < 0) { - output = BSDDIALOG_TIMEOUT; + retval = BSDDIALOG_TIMEOUT; break; } else { @@ -665,12 +669,12 @@ bsddialog_pause(struct bsddialog_conf *conf, const char *text, int rows, switch(input) { case KEY_ENTER: case 10: /* Enter */ - output = bs.value[bs.curr]; + retval = bs.value[bs.curr]; loop = false; break; case 27: /* Esc */ if (conf->key.enable_esc) { - output = BSDDIALOG_ESC; + retval = BSDDIALOG_ESC; loop = false; } break; @@ -731,7 +735,7 @@ bsddialog_pause(struct bsddialog_conf *conf, const char *text, int rows, break; default: if (shortcut_buttons(input, &bs)) { - output = bs.value[bs.curr]; + retval = bs.value[bs.curr]; loop = false; } } @@ -742,5 +746,5 @@ bsddialog_pause(struct bsddialog_conf *conf, const char *text, int rows, delwin(bar); end_dialog(conf, shadow, widget, textpad); - return (output); + return (retval); }
\ No newline at end of file diff --git a/lib/bsddialog.3 b/lib/bsddialog.3 index 38500b4da6ca..12db1f039d59 100644 --- a/lib/bsddialog.3 +++ b/lib/bsddialog.3 @@ -22,13 +22,14 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd February 9, 2022 +.Dd August 29, 2022 .Dt BSDDIALOG 3 .Os .Sh NAME .Nm bsddialog_backtitle , .Nm bsddialog_clearterminal , .Nm bsddialog_color , +.Nm bsddialog_color_attrs , .Nm bsddialog_checklist , .Nm bsddialog_datebox , .Nm bsddialog_end , @@ -36,8 +37,10 @@ .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_menu , .Nm bsddialog_mixedgauge , @@ -115,6 +118,8 @@ .Ft int .Fn bsddialog_init "void" .Ft int +.Fn bsddialog_init_notheme "void" +.Ft int .Fn bsddialog_initconf "struct bsddialog_conf *conf" .Ft int .Fo bsddialog_menu @@ -218,7 +223,16 @@ .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 @@ -239,6 +253,12 @@ API. restores the screen like before .Fn bsddialog_init , then it is not possible to use the library functions. +.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_error returns a string to describe the last error, it should be called after a @@ -258,10 +278,9 @@ is described later. .Pp Each .Fa char* -argument has to be a well terminated string, can be empty -.Pq Dq -but not -.Dv NULL . +argument has to be a well terminated string, it can be a multibyte character +string depending on current locale, see +.Xr setlocale 3 . .Ss Dialogs The dialogs have common arguments. .Fa text @@ -282,6 +301,8 @@ 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; @@ -298,6 +319,7 @@ struct bsddialog_conf { const char *f1_message; } key; struct { + unsigned int cols_per_row; bool highlight; unsigned int tablen; } text; @@ -309,11 +331,13 @@ struct bsddialog_conf { bool shortcut_buttons; } menu; struct { - bool enable_wchar; - int securech; + char securech; + char *securembch; + bool value_wchar; bool value_without_ok; } form; struct { + bool always_active; bool without_ok; const char *ok_label; bool with_extra; @@ -343,6 +367,23 @@ 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 subtitle at the dialog bottom side. .It Fa conf.clear @@ -382,11 +423,16 @@ file to open if F1 is pressed. message to display if F1 is pressed. .El .Pp -.Fa conf.text.highlight +.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.highlight enables highlights for .Fa text , properly the following sequences are considered escapes: -.Bl -column -compact .It Dq \eZ0 black. .It Dq \eZ1 @@ -404,7 +450,7 @@ cyan. .It Dq \eZ7 white. .It Dq \eZr -reverse colors between foreground and background. +reverse foreground and background. .It Dq \eZR disable reverse. .It Dq \eZb @@ -417,11 +463,22 @@ underline. 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 -.Fa conf.text.tablen -tab length. .Pp .Bl -column -compact +.It Fa conf.button.always_active +buttons always active, avoidind focus switch between buttons and input fields or +input boxes in +.Fn bsddialog_form , +.Fn bsddialog_datebox +and +.Fn bsddialog_timebox . .It Fa conf.button.without_ok disable OK button. .It Fa conf.button.ok_label @@ -598,8 +655,16 @@ enable shortcut keys on buttons, default on items. .El .Pp .Fn bsddialog_form -builds a dialog to display a list of items to get strings in input, an item is -defined like: +builds a dialog to display an array of +.Fa items +of +.Fa nitems +elements to get strings in input. +.Fa formrows +specifies the graphical height for the box around the items, +.Dv 0 +for autosizing. +An item is defined like: .Pp .Bd -literal -offset indent -compact struct bsddialog_formitem { @@ -621,7 +686,7 @@ struct bsddialog_formitem { .Ed .Pp .Fa label -describes the request, it is printed at the position +is a string to describe the request, it is printed at the position .Fa ylabel and .Fa xlabel . @@ -632,45 +697,43 @@ and .Fa fieldlen is its graphical width, while .Fa maxvalelen -is the maximum length of the input string, +is the maximum number of characters of the input string. .Fa init -is the default value. +is the default field value. If the OK button is pressed .Fa value -is the allocated memory with the current field string. +is the allocated memory with the current field string, its size depends on +the current locale. .Fa flags is an OR value to set the -.Dv BSDDIALOG_FIELDHIDDEN +.Dv BSDDIALOG_FIELDHIDDEN , +.Dv BSDDIALOG_FIELDREADONLY , +.Dv BSDDIALOG_FIELDNOCOLOR , +.Dv BSDDIALOG_FIELDCURSOREND , +.Dv BSDDIALOG_FIELDEXTEND and -.Dv BSDDIALOG_FIELDREADONLY +.Dv BSDDIALOG_FIELDSINGLEBYTE . flags for the field. .Fa bottomdesc is printed on the bottom side of the screen if the item is focused. -.Fa items -is an array of items of -.Fa nitems -elements, -.Fa formrows -specifies the graphical fixed height for the items list; -.Fa ylabel -and -.Fa yfield -have to be between 1 and -.Fa formrows . .Pp .Fn bsddialog_form can be customized by: .Bl -column -compact -.It Fa conf.form.enable_wchar -enables characters greater than 127 in the field, -.Fa value -is a pointer to allocated memory for a -.Em wchar_t -string. .It Fa conf.form.securech -charachter to hide the input -with +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. .It Fa conf.form.value_without_ok allocate memory and set .Fa value @@ -747,8 +810,8 @@ struct bsddialog_theme { } screen; struct { int color; - unsigned int h; - unsigned int w; + unsigned int y; + unsigned int x; } shadow; struct { int color; @@ -770,20 +833,23 @@ struct bsddialog_theme { int descsepcolor; int f_shortcutcolor; int shortcutcolor; + int bottomdesccolor; } menu; struct { int f_fieldcolor; int fieldcolor; int readonlycolor; + int bottomdesccolor; } form; struct { int f_color; int color; } bar; struct { - unsigned int hmargin; - int leftdelim; - int rightdelim; + unsigned int minmargin; + unsigned int maxmargin; + char leftdelim; + char rightdelim; int delimcolor; int f_delimcolor; int color; @@ -825,6 +891,8 @@ specifies OR-flags, possible values: .Dv BSDDIALOG_REVERSE and .Dv BSDDIALOG_UNDERLINE . +.Fn bsddialog_color_attrs +gets the properties of a color. .Pp .Fn bsddialog_set_theme sets @@ -840,6 +908,13 @@ and .Dv BSDDIALOG_THEME_DIALOG , they can be set via .Fn bsddialog_set_default_theme . +.Pp +.Fn bsddialog_hascolors +returns +.Dv true +if the terminal provides colors, +.Dv false +otherwise. .Sh RETURN VALUES The functions return the value .Dv BSDDIALOG_ERROR @@ -975,8 +1050,7 @@ for (i = 0; i < 3; i++) { .Ed .Sh SEE ALSO .Xr bsddialog 1 , -.Xr curses 3 , -.Xr ncurses 3 +.Xr curses 3 .Sh HISTORY The .Nm bsddialog @@ -985,8 +1059,4 @@ library first appeared in .Sh AUTHORS .Nm bsddialog was written by -.An Alfonso Sabato Siciliano Aq Mt alf.siciliano@gmail.com . -.Sh BUGS -.Fn bsddialog_form -does not resize the dialog after a terminal resize and does not provide -scrolling for items.
\ No newline at end of file +.An Alfonso Sabato Siciliano Aq Mt asiciliano@FreeBSD.org . diff --git a/lib/bsddialog.h b/lib/bsddialog.h index 37f9899141c0..6f13da3fa667 100644 --- a/lib/bsddialog.h +++ b/lib/bsddialog.h @@ -30,7 +30,7 @@ #include <stdbool.h> -#define LIBBSDDIALOG_VERSION "0.2" +#define LIBBSDDIALOG_VERSION "0.3" /* Exit status */ #define BSDDIALOG_ERROR -1 @@ -64,13 +64,19 @@ #define BSDDIALOG_MG_PENDING -11 /* Form */ -#define BSDDIALOG_FIELDHIDDEN 1U -#define BSDDIALOG_FIELDREADONLY 2U +#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 auto_minheight; unsigned int auto_minwidth; + unsigned int auto_topmargin; + unsigned int auto_downmargin; const char *bottomtitle; bool clear; int *get_height; @@ -87,6 +93,7 @@ struct bsddialog_conf { const char *f1_message; } key; struct { + unsigned int cols_per_row; bool highlight; unsigned int tablen; } text; @@ -98,11 +105,13 @@ struct bsddialog_conf { bool shortcut_buttons; } menu; struct { - bool enable_wchar; - int securech; + char securech; + char *securembch; + bool value_wchar; bool value_without_ok; } form; struct { + bool always_active; bool without_ok; const char *ok_label; bool with_extra; @@ -156,6 +165,7 @@ struct bsddialog_formitem { }; int bsddialog_init(void); +int bsddialog_init_notheme(void); int bsddialog_end(void); int bsddialog_backtitle(struct bsddialog_conf *conf, const char *backtitle); int bsddialog_initconf(struct bsddialog_conf *conf); diff --git a/lib/bsddialog_theme.h b/lib/bsddialog_theme.h index 89381cfe28d5..2f20d7b5a79c 100644 --- a/lib/bsddialog_theme.h +++ b/lib/bsddialog_theme.h @@ -39,8 +39,8 @@ struct bsddialog_theme { } screen; struct { int color; - unsigned int h; - unsigned int w; + unsigned int y; + unsigned int x; } shadow; struct { int color; @@ -62,20 +62,23 @@ struct bsddialog_theme { int descsepcolor; int f_shortcutcolor; int shortcutcolor; + int bottomdesccolor; } menu; struct { int f_fieldcolor; int fieldcolor; int readonlycolor; + int bottomdesccolor; } form; struct { int f_color; int color; } bar; struct { - unsigned int hmargin; - int leftdelim; - int rightdelim; + unsigned int minmargin; + unsigned int maxmargin; + char leftdelim; + char rightdelim; int delimcolor; int f_delimcolor; int color; @@ -106,7 +109,11 @@ enum bsddialog_color { int 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); diff --git a/lib/formbox.c b/lib/formbox.c index 564fa99d69a8..cd29919417be 100644 --- a/lib/formbox.c +++ b/lib/formbox.c @@ -27,454 +27,608 @@ #include <sys/param.h> -#include <ctype.h> -#include <form.h> +#include <curses.h> +#include <limits.h> #include <stdlib.h> #include <string.h> +#include <wchar.h> #include "bsddialog.h" #include "bsddialog_theme.h" #include "lib_util.h" -#define ISFIELDHIDDEN(item) (item.flags & BSDDIALOG_FIELDHIDDEN) -#define ISFIELDREADONLY(item) (item.flags & BSDDIALOG_FIELDREADONLY) -#define REDRAWFORM 19860214 /* magic number */ - -/* field_userptr for private buffer and view options */ -struct myfield { - int buflen; - wchar_t *buf; - int pos; - int maxpos; - bool secure; - int securech; - const char *bottomdesc; +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 */ }; -#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) +struct privateform { + WINDOW *border; + + 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 viewrows; /* visible rows, real formheight */ + unsigned int minviewrows; /* min viewrows, ylabel != yfield */ + + wchar_t securewch; /* wide char of conf.form.secure[mb]ch */ +}; + +enum operation { + MOVE_CURSOR_BEGIN, + MOVE_CURSOR_END, + MOVE_CURSOR_RIGHT, + MOVE_CURSOR_LEFT, + DEL_LETTER +}; + +static bool fieldctl(struct privateitem *item, enum operation op) { - int i; + bool change; + int width, oldwidth, nextwidth, cols; + unsigned int i; + + change = false; + switch (op){ + 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; - if (mf->buflen > mf->maxpos) - return; + 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->buflen; i >= mf->pos; i--) { - mf->buf[i+1] = mf->buf[i]; + break; } - mf->buf[mf->pos] = ch; - mf->pos += 1; - if (mf->pos > mf->maxpos) - mf->pos = mf->maxpos; - mf->buflen += 1; - mf->buf[mf->buflen] = '\0'; + return (change); } -static void shiftleft(struct myfield *mf) +static void +drawitem(struct privateform *form, struct privateitem *item, bool focus) { - int i, last; + int color; + unsigned int n, cols; + + /* Label */ + wattron(form->pad, t.dialog.color); + mvwaddstr(form->pad, item->ylabel, item->xlabel, item->label); + wattroff(form->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(form->pad, color); + mvwhline(form->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]); - for (i = mf->pos; i < mf->buflen -1; i++) { - mf->buf[i] = mf->buf[i+1]; } + mvwaddnwstr(form->pad, item->yfield, item->xfield, + &item->pubwbuf[item->xposdraw], n); + wattroff(form->pad, color); - last = mf->buflen > 0 ? mf->buflen -1 : 0; - mf->buf[last] = '\0'; - mf->buflen = last; -} - -static void print_bottomdesc(struct myfield *mf) -{ + /* Bottom Desc */ move(SCREENLINES - 1, 2); clrtoeol(); - if (mf->bottomdesc != NULL) { - addstr(mf->bottomdesc); + 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(form->pad, item->yfield, item->xfield + item->xcursor); + + prefresh(form->pad, form->y, 0, form->ys, form->xs, form->ye, form->xe); } -static char *w2c(wchar_t *string) +/* + * 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(form,item,focus) do { \ + drawitem(form, item, !focus); \ + drawitem(form, item, focus); \ +} while (0) + +static bool +insertch(struct privateform *form, struct privateitem *item, wchar_t wch) { - int i, len; - char *value; + int i; + + if (item->nletters >= item->maxletters) + return (false); - len = wcslen(string); - if ((value = calloc(len + 1, sizeof(char))) == NULL) - return NULL; + 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]; + } - for (i = 0; i < len; i++) - value[i] = string[i]; - value[i] = '\0'; + item->privwbuf[item->pos] = wch; + item->pubwbuf[item->pos] = item->secure ? form->securewch : wch; + item->nletters += 1; + item->privwbuf[item->nletters] = L'\0'; + item->pubwbuf[item->nletters] = L'\0'; - return value; + 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 return_values(struct bsddialog_conf *conf, int output, int nitems, - struct bsddialog_formitem *items, FORM *form, FIELD **cfield) + struct bsddialog_formitem *apiitems, struct privateitem *items) { int i; - struct myfield *mf; if (output != BSDDIALOG_OK && conf->form.value_without_ok == false) return (output); - form_driver_w(form, KEY_CODE_YES, REQ_NEXT_FIELD); - form_driver_w(form, KEY_CODE_YES, REQ_PREV_FIELD); for (i = 0; i < nitems; i++) { - mf = GETMYFIELD(cfield[i]); - if (conf->form.enable_wchar) { - items[i].value = (char*)wcsdup(mf->buf); + if (conf->form.value_wchar) { + apiitems[i].value = (char*)wcsdup(items[i].privwbuf); } else { - items[i].value = w2c(mf->buf); + apiitems[i].value = alloc_wstomb(items[i].privwbuf); } - if (items[i].value == NULL) + if (apiitems[i].value == NULL) RETURN_ERROR("Cannot allocate memory for form value"); } return (output); } -static int -form_handler(struct bsddialog_conf *conf, WINDOW *widget, struct buttons bs, - WINDOW *formwin, FORM *form, FIELD **cfield, int nitems, - struct bsddialog_formitem *items) +static unsigned int firstitem(unsigned int nitems, struct privateitem *items) { - bool loop, buttupdate, informwin; - int i, chtype, output; - wint_t input; - struct myfield *mf; + int i; - mf = GETMYFIELD2(form); - print_bottomdesc(mf); - pos_form_cursor(form); - form_driver_w(form, KEY_CODE_YES, REQ_END_LINE); - mf->pos = MIN(mf->buflen, mf->maxpos); - curs_set(1); - informwin = true; + for (i = 0; i < (int)nitems; i++) + if (items[i].readonly == false) + break; - bs.curr = -1; - buttupdate = true; + return (i); +} - loop = true; - while (loop) { - if (buttupdate) { - draw_buttons(widget, bs, !informwin); - wrefresh(widget); - buttupdate = false; - } - wrefresh(formwin); - chtype = get_wch(&input); - if (chtype != KEY_CODE_YES && input > 127 && - conf->form.enable_wchar == false) - continue; - switch(input) { - case KEY_HOME: - case KEY_PPAGE: - case KEY_END: - case KEY_NPAGE: - /* disabled keys */ - break; - case KEY_ENTER: - case 10: /* Enter */ - if (informwin) - break; - output = return_values(conf, bs.value[bs.curr], nitems, - items, form, cfield); - loop = false; - break; - case 27: /* Esc */ - if (conf->key.enable_esc) { - output = return_values(conf, BSDDIALOG_ESC, - nitems, items, form, cfield); - loop = false; - } - break; - case '\t': /* TAB */ - if (informwin) { - bs.curr = 0; - informwin = false; - curs_set(0); - } else { - bs.curr++; - informwin = bs.curr >= (int)bs.nbuttons ? - true : false; - if (informwin) { - curs_set(1); - pos_form_cursor(form); - } - } - buttupdate = true; - break; - case KEY_LEFT: - if (informwin) { - form_driver_w(form, KEY_CODE_YES, REQ_PREV_CHAR); - mf = GETMYFIELD2(form); - if (mf->pos > 0) - mf->pos -= 1; - } else { - if (bs.curr > 0) { - bs.curr--; - buttupdate = true; - } - } - break; - case KEY_RIGHT: - if (informwin) { - mf = GETMYFIELD2(form); - if (mf->pos >= mf->buflen) - break; - mf->pos += 1; - form_driver_w(form, KEY_CODE_YES, REQ_NEXT_CHAR); - } else { - if (bs.curr < (int) bs.nbuttons - 1) { - bs.curr++; - buttupdate = 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_w(form, KEY_CODE_YES, REQ_PREV_FIELD); - form_driver_w(form, KEY_CODE_YES, REQ_END_LINE); - mf = GETMYFIELD2(form); - print_bottomdesc(mf); - mf->pos = MIN(mf->buflen, mf->maxpos); - set_field_fore(current_field(form), t.form.f_fieldcolor); - set_field_back(current_field(form), t.form.f_fieldcolor); - break; - case KEY_DOWN: - if (nitems < 2) - break; - set_field_fore(current_field(form), t.form.fieldcolor); - set_field_back(current_field(form), t.form.fieldcolor); - form_driver_w(form, KEY_CODE_YES, REQ_NEXT_FIELD); - form_driver_w(form, KEY_CODE_YES, REQ_END_LINE); - mf = GETMYFIELD2(form); - print_bottomdesc(mf); - mf->pos = MIN(mf->buflen, mf->maxpos); - set_field_fore(current_field(form), t.form.f_fieldcolor); - set_field_back(current_field(form), t.form.f_fieldcolor); - break; - case KEY_BACKSPACE: - case 127: /* Backspace */ - mf = GETMYFIELD2(form); - if (mf->pos <= 0) - break; - form_driver_w(form, KEY_CODE_YES, REQ_DEL_PREV); - form_driver_w(form, KEY_CODE_YES, REQ_BEG_LINE); - mf->pos = mf->pos - 1; - for (i = 0; i < mf->pos; i++) - form_driver_w(form, KEY_CODE_YES, REQ_NEXT_CHAR); - shiftleft(mf); - break; - case KEY_DC: - form_driver_w(form, KEY_CODE_YES, REQ_DEL_CHAR); - mf = GETMYFIELD2(form); - if (mf->pos < mf->buflen) - shiftleft(mf); - break; - case KEY_F(1): - if (conf->key.f1_file == NULL && - conf->key.f1_message == NULL) - break; - if (f1help(conf) != 0) - return (BSDDIALOG_ERROR); - /* No Break */ - case KEY_RESIZE: - output = REDRAWFORM; - loop = false; - break; - default: - if (informwin) { - if (chtype == KEY_CODE_YES) - break; - mf = GETMYFIELD2(form); - if (mf->secure) - form_driver_w(form, chtype, mf->securech); - else - form_driver_w(form, chtype, input); - insertch(mf, input); - } - else { - if (shortcut_buttons(input, &bs)) { - output = return_values(conf, - bs.value[bs.curr], nitems, items, - form, cfield); - loop = false; - } - } +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; - } - } - curs_set(0); + return (i); +} - return (output); +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(WINDOW *window, struct buttons *bs, bool focus, bool shortcut) +{ + int selected; + + selected = bs->curr; + if (focus == false) + bs->curr = -1; + draw_buttons(window, *bs, shortcut); + wrefresh(window); + bs->curr = selected; } +static void +update_formborders(struct bsddialog_conf *conf, struct privateform *form) +{ + int h, w; + + getmaxyx(form->border, h, w); + draw_borders(conf, form->border, h, w, LOWERED); + + if (form->viewrows < form->h) { + wattron(form->border, t.dialog.arrowcolor); + if (form->y > 0) + mvwhline(form->border, 0, (w / 2) - 2, + conf->ascii_lines ? '^' : ACS_UARROW, 5); + + if (form->y + form->viewrows < form->h) + mvwhline(form->border, h-1, (w / 2) - 2, + conf->ascii_lines ? 'v' : ACS_DARROW, 5); + wattroff(form->border, t.dialog.arrowcolor); + wrefresh(form->border); + } +} + +/* use menu autosizing, linelen = form.w, nitems = form.h */ static int -form_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w, - const char *text, int linelen, unsigned int *formheight, int nitems, +menu_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w, + const char *text, int linelen, unsigned int *menurows, int nitems, struct buttons bs) { - int htext, wtext, menusize; - - if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) { - if (text_size(conf, rows, cols, text, &bs, *formheight + 2, - linelen + 2, &htext, &wtext) != 0) + int htext, wtext, menusize, notext; + + notext = 2; + if (*menurows == BSDDIALOG_AUTOSIZE) { + /* algo 1): grows vertically */ + /* notext = 1; */ + /* algo 2): grows horizontally, better with little screens */ + notext += nitems; + notext = MIN(notext, widget_max_height(conf) - HBORDERS - 3); + } else + notext += *menurows; + + /* cols autosize, rows autosize, rows fullscreen, menu particularity */ + if (cols == BSDDIALOG_AUTOSIZE || rows <= BSDDIALOG_AUTOSIZE) { + if (text_size(conf, rows, cols, text, &bs, notext, linelen + 4, + &htext, &wtext) != 0) return (BSDDIALOG_ERROR); } if (cols == BSDDIALOG_AUTOSIZE) - *w = widget_min_width(conf, wtext, linelen + 2, &bs); + *w = widget_min_width(conf, wtext, linelen + 4, &bs); if (rows == BSDDIALOG_AUTOSIZE) { - if (*formheight == 0) { + if (*menurows == 0) { menusize = widget_max_height(conf) - HBORDERS - 2 /*buttons*/ - htext; menusize = MIN(menusize, nitems + 2); - *formheight = menusize - 2 < 0 ? 0 : menusize - 2; + *menurows = menusize - 2 < 0 ? 0 : menusize - 2; } - else /* h autosize with fixed formheight */ - menusize = *formheight + 2; + else /* h autosize with fixed menurows */ + menusize = *menurows + 2; *h = widget_min_height(conf, htext, menusize, true); + /* + * avoid menurows overflow and + * with rows=AUTOSIZE menurows!=0 becomes max-menurows + */ + *menurows = MIN(*h - 6 - htext, (int)*menurows); } else { - if (*formheight == 0) - *formheight = MIN(rows-6-htext, nitems); + if (*menurows == 0) { + if (*h - 6 - htext <= 0) + *menurows = 0; /* form_checksize() will check */ + else + *menurows = MIN(*h-6-htext, nitems); + } } return (0); } static int -form_checksize(int rows, int cols, const char *text, int formheight, int nitems, - unsigned int linelen, struct buttons bs) +form_checksize(int rows, int cols, const char *text, struct privateform *form, + int nitems, struct buttons bs) { - int mincols, textrow, formrows; + int mincols, textrow, menusize; + /* cols */ mincols = VBORDERS; - /* buttons */ - mincols += buttons_width(bs); - mincols = MAX(mincols, (int)linelen + 4); + mincols += buttons_min_width(bs); + mincols = MAX(mincols, (int)form->w + 6); if (cols < mincols) - RETURN_ERROR("Few cols, width < size buttons or " - "forms (label + field)"); - - textrow = text != NULL && strlen(text) > 0 ? 1 : 0; + RETURN_ERROR("Form width, cols < buttons or xlabels/xfields"); - if (nitems > 0 && formheight == 0) - RETURN_ERROR("fields > 0 but formheight == 0, probably " + /* rows */ + if (nitems > 0 && form->viewrows == 0) + RETURN_ERROR("items > 0 but viewrows == 0, if formheight = 0 " "terminal too small"); - formrows = nitems > 0 ? 3 : 0; - if (rows < 2 + 2 + formrows + textrow) - RETURN_ERROR("Few lines for this menus"); + if (form->viewrows < form->minviewrows) + RETURN_ERROR("Few formheight rows, if formheight = 0 terminal " + "too small"); + + textrow = text != NULL && text[0] != '\0' ? 1 : 0; + menusize = nitems > 0 ? 3 : 0; + if (rows < 2 + 2 + menusize + textrow) + RETURN_ERROR("Few lines for this form"); return (0); } +static void curriteminview(struct privateform *form, struct privateitem *item) +{ + unsigned int yup, ydown; + + yup = MIN(item->ylabel, item->yfield); + ydown = MAX(item->ylabel, item->yfield); + + if (form->y > yup && form->y > 0) + form->y = yup; + if ((int)(form->y + form->viewrows) - 1 < (int)ydown) + form->y = ydown - form->viewrows + 1; +} + +/* 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) + struct bsddialog_formitem *apiitems) { - int i, output, color, y, x, h, w; - unsigned long j, maxline, mybufsize; + bool switchfocus, changeitem, focusinform, insecurecursor, loop; + int curritem, mbchsize, next, retval, y, x, h, w, wchtype; + unsigned int i, j, itemybeg, itemxbeg, tmp; + wchar_t *winit; + wint_t input; + WINDOW *widget, *textpad, *shadow; + struct privateitem *items, *item; struct buttons bs; - struct myfield *myfields; - FIELD **cfield; - FORM *form; - WINDOW *widget, *formwin, *textpad, *shadow; - - /* disable form scrolling */ - if (formheight < nitems) - formheight = nitems; + struct privateform form; - for (i = 0; i < (int)nitems; i++) { - if (items[i].maxvaluelen == 0) + for (i = 0; i < nitems; i++) { + if (apiitems[i].maxvaluelen == 0) RETURN_ERROR("maxvaluelen cannot be zero"); - if (items[i].fieldlen == 0) + if (apiitems[i].fieldlen == 0) RETURN_ERROR("fieldlen cannot be zero"); - if (items[i].fieldlen > items[i].maxvaluelen) - RETURN_ERROR("fieldlen cannot be > maxvaluelen"); } - 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); - /* setlocale() should handle set_field_buffer() */ - set_field_buffer(cfield[i], 0, items[i].init); - - mybufsize = (items[i].maxvaluelen + 1) * sizeof(wchar_t); - myfields[i].buf = malloc(mybufsize); - memset(myfields[i].buf, 0, mybufsize); - for (j = 0; j < items[i].maxvaluelen && j < strlen(items[i].init); - j++) - myfields[i].buf[j] = items[i].init[j]; - - myfields[i].buflen = wcslen(myfields[i].buf); - - myfields[i].maxpos = items[i].maxvaluelen -1; - myfields[i].pos = MIN(myfields[i].buflen, myfields[i].maxpos); - - 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); - - if (ISFIELDHIDDEN(items[i])) { - myfields[i].secure = true; - myfields[i].securech = ' '; - if (conf->form.securech != '\0') - myfields[i].securech = conf->form.securech; - } + insecurecursor = false; + if (conf->form.securembch != NULL) { + mbchsize = mblen(conf->form.securembch, MB_LEN_MAX); + if(mbtowc(&form.securewch, conf->form.securembch, mbchsize) < 0) + RETURN_ERROR("Cannot convert securembch to wchar_t"); + insecurecursor = true; + } else if (conf->form.securech != '\0') { + form.securewch = btowc(conf->form.securech); + insecurecursor = true; + } else { + form.securewch = L' '; + } + + if ((items = malloc(nitems * sizeof(struct privateitem))) == NULL) + RETURN_ERROR("Cannot allocate internal items"); + form.h = form.w = form.minviewrows = 0; + for (i = 0; i < nitems; i++) { + item = &items[i]; + item->label = apiitems[i].label; + item->ylabel = apiitems[i].ylabel; + item->xlabel = apiitems[i].xlabel; + item->yfield = apiitems[i].yfield; + item->xfield = apiitems[i].xfield; + item->secure = apiitems[i].flags & BSDDIALOG_FIELDHIDDEN; + item->readonly = apiitems[i].flags & BSDDIALOG_FIELDREADONLY; + item->fieldnocolor = apiitems[i].flags & BSDDIALOG_FIELDNOCOLOR; + item->extendfield = apiitems[i].flags & BSDDIALOG_FIELDEXTEND; + item->fieldonebyte = apiitems[i].flags & + BSDDIALOG_FIELDSINGLEBYTE; + item->cursorend = apiitems[i].flags & BSDDIALOG_FIELDCURSOREND; + item->bottomdesc = apiitems[i].bottomdesc; + if (item->readonly || (item->secure && !insecurecursor)) + item->cursor = false; else - myfields[i].secure = false; + item->cursor = true; + + item->maxletters = apiitems[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(apiitems[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] = form.securewch; + } - if (ISFIELDREADONLY(items[i])) { - field_opts_off(cfield[i], O_EDIT); - field_opts_off(cfield[i], O_ACTIVE); - color = t.form.readonlycolor; + item->fieldcols = apiitems[i].fieldlen; + item->xposdraw = 0; + item->xcursor = 0; + item->pos = 0; + + form.h = MAX(form.h, items[i].ylabel); + form.h = MAX(form.h, items[i].yfield); + form.w = MAX(form.w, items[i].xlabel + strcols(items[i].label)); + form.w = MAX(form.w, items[i].xfield + items[i].fieldcols); + if (i == 0) { + itemybeg = MIN(items[i].ylabel, items[i].yfield); + itemxbeg = MIN(items[i].xlabel, items[i].xfield); } else { - color = i == 0 ? t.form.f_fieldcolor : t.form.fieldcolor; + tmp = MIN(items[i].ylabel, items[i].yfield); + itemybeg = MIN(itemybeg, tmp); + tmp = MIN(items[i].xlabel, items[i].xfield); + itemxbeg = MIN(itemxbeg, tmp); } - set_field_fore(cfield[i], color); - set_field_back(cfield[i], color); - - maxline = MAX(maxline, items[i].xlabel + strlen(items[i].label)); - maxline = MAX(maxline, items[i].xfield + items[i].fieldlen - 1); + tmp = abs((int)items[i].ylabel - (int)items[i].yfield); + form.minviewrows = MAX(form.minviewrows, tmp); } - cfield[i] = NULL; - - /* 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 (nitems > 0) { + form.h = form.h + 1 - itemybeg; + form.w -= itemxbeg; + form.minviewrows += 1; + } + form.wmin = form.w; + for (i = 0; i < nitems; i++) { + items[i].ylabel -= itemybeg; + items[i].yfield -= itemybeg; + items[i].xlabel -= itemxbeg; + items[i].xfield -= itemxbeg; } get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL); + form.viewrows = formheight; if (set_widget_size(conf, rows, cols, &h, &w) != 0) return (BSDDIALOG_ERROR); - if (form_autosize(conf, rows, cols, &h, &w, text, maxline, &formheight, - nitems, bs) != 0) + if (menu_autosize(conf, rows, cols, &h, &w, text, form.w, + &form.viewrows, form.h, bs) != 0) return (BSDDIALOG_ERROR); - if (form_checksize(h, w, text, formheight, nitems, maxline, bs) != 0) + if (form_checksize(h, w, text, &form, nitems, bs) != 0) return (BSDDIALOG_ERROR); if (set_widget_position(conf, &y, &x, h, w) != 0) return (BSDDIALOG_ERROR); @@ -483,55 +637,308 @@ bsddialog_form(struct bsddialog_conf *conf, const char *text, int rows, true) != 0) return (BSDDIALOG_ERROR); + doupdate(); + prefresh(textpad, 0, 0, y + 1, x + 1 + TEXTHMARGIN, - y + h - formheight, x + 1 + w - TEXTHMARGIN); + y + h - form.viewrows, x + 1 + w - TEXTHMARGIN); - formwin = new_boxed_window(conf, y + h - 3 - formheight -2, x +1, - formheight+2, w-2, LOWERED); + form.border = new_boxed_window(conf, y + h - 5 - form.viewrows, x + 2, + form.viewrows + 2, w - 4, 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 < nitems; i++) { + if (items[i].extendfield) { + form.w = w - 6; + items[i].fieldcols = form.w - items[i].xfield; + } + if (items[i].cursorend) + fieldctl(item, MOVE_CURSOR_END); + } - for (i = 0; i < (int)nitems; i++) - mvwaddstr(formwin, items[i].ylabel, items[i].xlabel, - items[i].label); + form.pad = newpad(form.h, form.w); + wbkgd(form.pad, t.dialog.color); + + form.ys = y + h - 5 - form.viewrows + 1; + form.ye = y + h - 5 ; + if ((int)form.w >= w - 6) { /* left */ + form.xs = x + 3; + form.xe = form.xs + w - 7; + } else { /* center */ + form.xs = x + 3 + (w-6)/2 - form.w/2; + form.xe = form.xs + w - 5; + } + + curritem = -1; + for (i=0 ; i < nitems; i++) { + DRAWITEM_TRICK(&form, &items[i], false); + if (curritem == -1 && items[i].readonly == false) + curritem = i; + } + if (curritem != -1) { + focusinform = true; + redrawbuttons(widget, &bs, conf->button.always_active, false); + form.y = 0; + item = &items[curritem]; + curriteminview(&form, item); + update_formborders(conf, &form); + wrefresh(form.border); + DRAWITEM_TRICK(&form, item, true); + } else { + item = NULL; + focusinform = false; + wrefresh(form.border); + } - wrefresh(formwin); + changeitem = switchfocus = false; + loop = true; + while (loop) { + if ((wchtype = get_wch(&input)) == ERR) + continue; + switch(input) { + case KEY_ENTER: + case 10: /* Enter */ + if (focusinform && conf->button.always_active == false) + break; + retval = return_values(conf, bs.value[bs.curr], + nitems, apiitems, items); + loop = false; + break; + case 27: /* Esc */ + if (conf->key.enable_esc) { + retval = return_values(conf, BSDDIALOG_ESC, + nitems, apiitems, items); + loop = false; + } + break; + case '\t': /* TAB */ + if (focusinform) { + switchfocus = true; + } else { + if (bs.curr + 1 < (int)bs.nbuttons) { + bs.curr++; + } else { + bs.curr = 0; + if (curritem != -1) { + switchfocus = true; + } + } + draw_buttons(widget, bs, true); + wrefresh(widget); + } + break; + case KEY_LEFT: + if (focusinform) { + if(fieldctl(item, MOVE_CURSOR_LEFT)) + DRAWITEM_TRICK(&form, item, true); + } else if (bs.curr > 0) { + bs.curr--; + draw_buttons(widget, bs, true); + wrefresh(widget); + } else if (curritem != -1) { + switchfocus = true; + } + break; + case KEY_RIGHT: + if (focusinform) { + if(fieldctl(item, MOVE_CURSOR_RIGHT)) + DRAWITEM_TRICK(&form, item, true); + } else if (bs.curr < (int) bs.nbuttons - 1) { + bs.curr++; + draw_buttons(widget, bs, true); + wrefresh(widget); + } else if (curritem != -1) { + switchfocus = true; + } + break; + case KEY_UP: + if (focusinform) { + next = previtem(nitems, items, curritem); + changeitem = curritem != next; + } else if (curritem != -1) { + switchfocus = true; + } + break; + case KEY_DOWN: + if (focusinform == false) + break; + if (nitems == 1) { + switchfocus = true; + } else { + next = nextitem(nitems, items, curritem); + changeitem = curritem != next; + } + break; + case KEY_PPAGE: + if (focusinform) { + next = firstitem(nitems, items); + changeitem = curritem != next; + } + break; + case KEY_NPAGE: + if (focusinform) { + next = lastitem(nitems, items); + changeitem = curritem != next; + } + break; + case KEY_BACKSPACE: + case 127: /* Backspace */ + if (focusinform == false) + break; + if(fieldctl(item, MOVE_CURSOR_LEFT)) + if(fieldctl(item, DEL_LETTER)) + DRAWITEM_TRICK(&form, item, true); + break; + case KEY_DC: + if (focusinform == false) + break; + if(fieldctl(item, DEL_LETTER)) + DRAWITEM_TRICK(&form, item, true); + break; + case KEY_HOME: + if (focusinform == false) + break; + if(fieldctl(item, MOVE_CURSOR_BEGIN)) + DRAWITEM_TRICK(&form, item, true); + break; + case KEY_END: + if (focusinform == false) + break; + if (fieldctl(item, MOVE_CURSOR_END)) + DRAWITEM_TRICK(&form, item, true); + break; + case KEY_F(1): + if (conf->key.f1_file == NULL && + conf->key.f1_message == NULL) + break; + curs_set(0); + if (f1help(conf) != 0) { + retval = BSDDIALOG_ERROR; + loop = false; + } + /* No break, screen size can change */ + case KEY_RESIZE: + /* Important for decreasing screen */ + hide_widget(y, x, h, w, conf->shadow); + refresh(); - do { - output = form_handler(conf, widget, bs, formwin, form, cfield, - nitems, items); + form.viewrows = formheight; + form.w = form.wmin; + if (set_widget_size(conf, rows, cols, &h, &w) != 0) + return (BSDDIALOG_ERROR); + if (menu_autosize(conf, rows, cols, &h, &w, text, form.w, + &form.viewrows, form.h, bs) != 0) + return (BSDDIALOG_ERROR); + if (form_checksize(h, w, text, &form, nitems, bs) != 0) + return (BSDDIALOG_ERROR); + if (set_widget_position(conf, &y, &x, h, w) != 0) + return (BSDDIALOG_ERROR); - if (update_dialog(conf, shadow, widget, y, x, h, w, textpad, - text, &bs, true) != 0) + if (update_dialog(conf, shadow, widget, y, x, h, w, + textpad, text, &bs, true) != 0) return (BSDDIALOG_ERROR); - doupdate(); - wrefresh(widget); + doupdate(); - prefresh(textpad, 0, 0, y + 1, x + 1 + TEXTHMARGIN, - y + h - formheight, x + 1 + w - TEXTHMARGIN); + prefresh(textpad, 0, 0, y + 1, x + 1 + TEXTHMARGIN, + y + h - form.viewrows, x + 1 + w - TEXTHMARGIN); - draw_borders(conf, formwin, formheight+2, w-2, LOWERED); - wrefresh(formwin); + wclear(form.border); + mvwin(form.border, y + h - 5 - form.viewrows, x + 2); + wresize(form.border, form.viewrows + 2, w - 4); - refresh(); - } while (output == REDRAWFORM); + for (i = 0; i < nitems; i++) { + fieldctl(&items[i], MOVE_CURSOR_BEGIN); + if (items[i].extendfield) { + form.w = w - 6; + items[i].fieldcols = + form.w - items[i].xfield; + } + if (items[i].cursorend) + fieldctl(&items[i], MOVE_CURSOR_END); + } - unpost_form(form); - free_form(form); - for (i = 0; i < (int)nitems; i++) { - free_field(cfield[i]); - free(myfields[i].buf); - } - free(cfield); - free(myfields); + form.ys = y + h - 5 - form.viewrows + 1; + form.ye = y + h - 5 ; + if ((int)form.w >= w - 6) { /* left */ + form.xs = x + 3; + form.xe = form.xs + w - 7; + } else { /* center */ + form.xs = x + 3 + (w-6)/2 - form.w/2; + form.xe = form.xs + w - 5; + } + + if (curritem != -1) { + redrawbuttons(widget, &bs, + conf->button.always_active || !focusinform, + !focusinform); + curriteminview(&form, item); + update_formborders(conf, &form); + wrefresh(form.border); + /* drawitem just to prefresh() pad */ + DRAWITEM_TRICK(&form, item, focusinform); + } else { + wrefresh(form.border); + } + break; + default: + 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(&form, item, input)) { + fieldctl(item, MOVE_CURSOR_RIGHT); + /* + * no if(fieldctl), update always + * because it fails with maxletters. + */ + DRAWITEM_TRICK(&form, item, true); + } + } else { + if (shortcut_buttons(input, &bs)) { + retval = return_values(conf, + bs.value[bs.curr], nitems, apiitems, + items); + loop = false; + } + } + break; + } /* end switch handler */ + + if (switchfocus) { + focusinform = !focusinform; + bs.curr = 0; + redrawbuttons(widget, &bs, + conf->button.always_active || !focusinform, + !focusinform); + DRAWITEM_TRICK(&form, item, focusinform); + switchfocus = false; + } + + if (changeitem) { + DRAWITEM_TRICK(&form, item, false); + curritem = next; + item = &items[curritem]; + curriteminview(&form, item); + update_formborders(conf, &form); + DRAWITEM_TRICK(&form, item, true); + changeitem = false; + } + } /* end while handler */ + + curs_set(0); - delwin(formwin); + delwin(form.pad); + delwin(form.border); + for (i = 0; i < nitems; i++) { + free(items[i].privwbuf); + free(items[i].pubwbuf); + } end_dialog(conf, shadow, widget, textpad); - return (output); + return (retval); } diff --git a/lib/infobox.c b/lib/infobox.c index 5a6b7c2fd692..c8a0b6e90c8e 100644 --- a/lib/infobox.c +++ b/lib/infobox.c @@ -39,13 +39,13 @@ infobox_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int htext, wtext; if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) { - if (text_size(conf, rows, cols, text, NULL, 0, SCREENCOLS/2, - &htext, &wtext) != 0) + if (text_size(conf, rows, cols, text, NULL, 0, 1, &htext, + &wtext) != 0) return (BSDDIALOG_ERROR); } if (cols == BSDDIALOG_AUTOSIZE) - *w = widget_min_width(conf, wtext, 0, NULL); + *w = widget_min_width(conf, wtext, TEXTHMARGINS + 1, NULL); if (rows == BSDDIALOG_AUTOSIZE) *h = widget_min_height(conf, htext, 0, false); diff --git a/lib/lib_util.c b/lib/lib_util.c index 4c2ab76bc592..fcdf4c3d8769 100644 --- a/lib/lib_util.c +++ b/lib/lib_util.c @@ -32,13 +32,14 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <wchar.h> +#include <wctype.h> #include "bsddialog.h" #include "bsddialog_theme.h" #include "lib_util.h" -#define TABLEN 4 /* Default tab len */ -#define ERRBUFLEN 1024 /* Error buffer */ +#define ERRBUFLEN 1024 /* Error buffer len */ /* Error */ static char errorbuffer[ERRBUFLEN]; @@ -53,12 +54,101 @@ void set_error_string(const char *str) strncpy(errorbuffer, str, ERRBUFLEN-1); } +/* Unicode */ +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++; + } + + if ((wstring = calloc(nchar, sizeof(wchar_t))) == NULL) + return (NULL); + mbstowcs(wstring, mbstring, nchar); + + return (wstring); +} + +void mvwaddwch(WINDOW *w, int y, int x, wchar_t wch) +{ + wchar_t ws[2]; + + ws[0] = wch; + ws[1] = L'\0'; + mvwaddwstr(w, y, x, ws); + +} + +int str_props(const char *mbstring, unsigned int *cols, bool *has_multi_col) +{ + 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; + } + + if (cols != NULL) + *cols = ncol; + if (has_multi_col != NULL) + *has_multi_col = multicol; + + return (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 (ncol); +} + /* Clear */ int hide_widget(int y, int x, int h, int w, bool withshadow) { WINDOW *clear; - if ((clear = newwin(h, w, y + t.shadow.h, x + t.shadow.w)) == NULL) + if ((clear = newwin(h, w, y + t.shadow.y, x + t.shadow.x)) == NULL) RETURN_ERROR("Cannot hide the widget"); wbkgd(clear, t.screen.color); @@ -101,7 +191,7 @@ int f1help(struct bsddialog_conf *conf) /* Buttons */ static void draw_button(WINDOW *window, int y, int x, int size, const char *text, - bool selected, bool shortcut) + wchar_t first, bool selected, bool shortcut) { int i, color_arrows, color_shortkey, color_button; @@ -126,14 +216,14 @@ draw_button(WINDOW *window, int y, int x, int size, const char *text, 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 (shortcut) { wattron(window, color_shortkey); - mvwaddch(window, y, x, text[0]); + mvwaddwch(window, y, x, first); wattroff(window, color_shortkey); } } @@ -142,16 +232,28 @@ void draw_buttons(WINDOW *window, struct buttons bs, bool shortcut) { int i, x, startx, y, rows, cols; + unsigned int newmargin, margin, wbuttons; getmaxyx(window, rows, cols); y = rows - 2; - startx = cols/2 - buttons_width(bs)/2; + newmargin = cols - VBORDERS - (bs.nbuttons * bs.sizebutton); + newmargin /= (bs.nbuttons + 1); + newmargin = MIN(newmargin, t.button.maxmargin); + if (newmargin == 0) { + margin = t.button.minmargin; + wbuttons = buttons_min_width(bs); + } else { + margin = newmargin; + wbuttons = bs.nbuttons * bs.sizebutton; + wbuttons += (bs.nbuttons + 1) * margin; + } + startx = (cols)/2 - wbuttons/2 + newmargin; for (i = 0; i < (int)bs.nbuttons; i++) { - x = i * (bs.sizebutton + t.button.hmargin); + x = i * (bs.sizebutton + margin); draw_button(window, y, startx + x, bs.sizebutton, bs.label[i], - i == bs.curr, shortcut); + bs.first[i], i == bs.curr, shortcut); } } @@ -163,6 +265,7 @@ get_buttons(struct bsddialog_conf *conf, struct buttons *bs, #define SIZEBUTTON 8 #define DEFAULT_BUTTON_LABEL BUTTON_OK_LABEL #define DEFAULT_BUTTON_VALUE BSDDIALOG_OK + wchar_t first; bs->nbuttons = 0; bs->curr = 0; @@ -216,6 +319,11 @@ get_buttons(struct bsddialog_conf *conf, struct buttons *bs, bs->nbuttons = 1; } + for (i = 0; i < (int)bs->nbuttons; i++) { + mbtowc(&first, bs->label[i], MB_CUR_MAX); + bs->first[i] = first; + } + if (conf->button.default_label != NULL) { for (i = 0; i < (int)bs->nbuttons; i++) { if (strcmp(conf->button.default_label, @@ -224,31 +332,31 @@ get_buttons(struct bsddialog_conf *conf, struct buttons *bs, } } - bs->sizebutton = MAX(SIZEBUTTON - 2, strlen(bs->label[0])); + bs->sizebutton = MAX(SIZEBUTTON - 2, strcols(bs->label[0])); for (i = 1; i < (int)bs->nbuttons; i++) - bs->sizebutton = MAX(bs->sizebutton, strlen(bs->label[i])); + bs->sizebutton = MAX(bs->sizebutton, strcols(bs->label[i])); bs->sizebutton += 2; } -int buttons_width(struct buttons bs) +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.hmargin; + width += (bs.nbuttons - 1) * t.button.minmargin; return (width); } -bool shortcut_buttons(int key, struct buttons *bs) +bool shortcut_buttons(wint_t key, struct buttons *bs) { bool match; unsigned int i; match = false; for (i = 0; i < bs->nbuttons; i++) { - if (tolower(key) == tolower(bs->label[i][0])) { + if (towlower(key) == towlower(bs->first[i])) { bs->curr = i; match = true; break; @@ -259,48 +367,51 @@ bool shortcut_buttons(int key, struct buttons *bs) } /* Text */ -static bool is_text_attr(const char *text) +static bool is_wtext_attr(const wchar_t *wtext) { - if (strnlen(text, 3) < 3) + if (wcsnlen(wtext, 3) < 3) return (false); - if (text[0] != '\\' || text[1] != 'Z') + if (wtext[0] != L'\\' || wtext[1] != L'Z') return (false); - return (strchr("nbBrRuU01234567", text[2]) == NULL ? false : true); + return (wcschr(L"nbBrRuU01234567", wtext[2]) == NULL ? false : true); } -static bool check_set_text_attr(WINDOW *win, char *text) +static bool check_set_wtext_attr(WINDOW *win, wchar_t *wtext) { - if (is_text_attr(text) == false) + enum bsddialog_color bg; + + if (is_wtext_attr(wtext) == false) return (false); - if ((text[2] - '0') >= 0 && (text[2] - '0') < 8) { - wattron(win, bsddialog_color(text[2] - '0', COLOR_WHITE, 0)); + if ((wtext[2] - L'0') >= 0 && (wtext[2] - L'0') < 8) { + bsddialog_color_attrs(t.dialog.color, NULL, &bg, NULL); + wattron(win, bsddialog_color(wtext[2] - L'0', bg, 0)); return (true); } - switch (text[2]) { - case 'n': + switch (wtext[2]) { + case L'n': wattron(win, t.dialog.color); wattrset(win, A_NORMAL); break; - case 'b': + case L'b': wattron(win, A_BOLD); break; - case 'B': + case L'B': wattroff(win, A_BOLD); break; - case 'r': + case L'r': wattron(win, A_REVERSE); break; - case 'R': + case L'R': wattroff(win, A_REVERSE); break; - case 'u': + case L'u': wattron(win, A_UNDERLINE); break; - case 'U': + case L'U': wattroff(win, A_UNDERLINE); break; } @@ -308,21 +419,27 @@ static bool check_set_text_attr(WINDOW *win, char *text) return (true); } +/* Word Wrapping */ static void -print_string(WINDOW *win, int *rows, int cols, int *y, int *x, char *str, +print_string(WINDOW *win, int *rows, int cols, int *y, int *x, wchar_t *str, bool color) { - int i, j, len, reallen; + int i, j, len, reallen, wc; + wchar_t ws[2]; + + ws[1] = L'\0'; - len = reallen = strlen(str); + len = wcslen(str); if (color) { + reallen = 0; i=0; while (i < len) { - if (is_text_attr(str+i)) - reallen -= 3; + if (is_wtext_attr(str+i) == false) + reallen += wcwidth(str[i]); i++; } - } + } else + reallen = wcswidth(str, len); i = 0; while (i < len) { @@ -336,13 +453,18 @@ print_string(WINDOW *win, int *rows, int cols, int *y, int *x, char *str, } j = *x; while (j < cols && i < len) { - if (color && check_set_text_attr(win, str+i)) { + if (color && check_set_wtext_attr(win, str+i)) { i += 3; + } else if (j + wcwidth(str[i]) > cols) { + break; } else { - mvwaddch(win, *y, j, str[i]); + /* inline mvwaddwch() for efficiency */ + ws[0] = str[i]; + mvwaddwstr(win, *y, j, ws); + wc = wcwidth(str[i]);; + reallen -= wc; + j += wc; i++; - reallen--; - j++; *x = j; } } @@ -354,35 +476,38 @@ print_textpad(struct bsddialog_conf *conf, WINDOW *pad, const char *text) { bool loop; int i, j, z, rows, cols, x, y, tablen; - char *string; + wchar_t *wtext, *string; + + if ((wtext = alloc_mbstows(text)) == NULL) + RETURN_ERROR("Cannot allocate/print text in wchar_t*"); - if ((string = malloc(strlen(text) + 1)) == NULL) + if ((string = calloc(wcslen(wtext) + 1, sizeof(wchar_t))) == NULL) RETURN_ERROR("Cannot build (analyze) text"); getmaxyx(pad, rows, cols); - tablen = (conf->text.tablen == 0) ? TABLEN : (int)conf->text.tablen; + tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen; i = j = x = y = 0; loop = true; while (loop) { - string[j] = text[i]; + string[j] = wtext[i]; - if (strchr("\n\t ", string[j]) != NULL || string[j] == '\0') { - string[j] = '\0'; + 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.highlight); } - switch (text[i]) { - case '\0': + switch (wtext[i]) { + case L'\0': loop = false; break; - case '\n': + case L'\n': x = 0; y++; j = -1; break; - case '\t': + case L'\t': for (z = 0; z < tablen; z++) { if (x >= cols) { x = 0; @@ -392,7 +517,7 @@ print_textpad(struct bsddialog_conf *conf, WINDOW *pad, const char *text) } j = -1; break; - case ' ': + case L' ': x++; if (x >= cols) { x = 0; @@ -410,78 +535,133 @@ print_textpad(struct bsddialog_conf *conf, WINDOW *pad, const char *text) i++; } + free(wtext); free(string); return (0); } -/* Autosize */ +/* Text Autosize */ +#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_autosize(struct bsddialog_conf *conf, const char *text, int maxrows, - int mincols, bool increasecols, int *h, int *w) +text_properties(struct bsddialog_conf *conf, const char *text, + struct textproperties *tp) { - int i, j, z, x, y; - int tablen, wordlen, maxwordlen, nword, maxwords, line, maxwidth; - int *words; -#define NL -1 -#define WS -2 + int i, l, currlinecols, maxwords, wtextlen, tablen, wordcols; + wchar_t *wtext; + + tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen; maxwords = 1024; - if ((words = calloc(maxwords, sizeof(int))) == NULL) + if ((tp->words = calloc(maxwords, sizeof(int))) == NULL) RETURN_ERROR("Cannot alloc memory for text autosize"); - tablen = (conf->text.tablen == 0) ? TABLEN : (int)conf->text.tablen; - maxwidth = widget_max_width(conf) - HBORDERS - TEXTHMARGINS; - - nword = 0; - wordlen = 0; - maxwordlen = 0; - i=0; - while (true) { - if (conf->text.highlight && is_text_attr(text + i)) { - i += 3; + 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.highlight && is_wtext_attr(wtext + i)) { + i += 2; /* +1 for update statement */ continue; } - if (nword + tablen >= maxwords) { + if (tp->nword + 1 >= maxwords) { maxwords += 1024; - if (realloc(words, maxwords * sizeof(int)) == NULL) + tp->words = realloc(tp->words, maxwords * sizeof(int)); + if (tp->words == NULL) RETURN_ERROR("Cannot realloc memory for text " "autosize"); } - if (text[i] == '\0') { - words[nword] = wordlen; - maxwordlen = MAX(wordlen, maxwordlen); - break; - } - - if (strchr("\t\n ", text[i]) != NULL) { - maxwordlen = MAX(wordlen, maxwordlen); + if (wcschr(L"\t\n ", wtext[i]) != NULL) { + tp->maxwordcols = MAX(wordcols, tp->maxwordcols); - if (wordlen != 0) { - words[nword] = wordlen; - nword++; - wordlen = 0; + if (wordcols != 0) { + /* line */ + currlinecols += wordcols; + /* word */ + tp->words[tp->nword] = wordcols; + tp->nword += 1; + wordcols = 0; } - if (text[i] == '\t') { - for (j = 0; j < tablen; j++) - words[nword + j] = 1; - nword += tablen; - } else { - words[nword] = text[i] == '\n' ? NL : WS; - nword++; + 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++; } - else - wordlen++; - - i++; } + /* 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(wtext); + + return (0); +} + + +static int +text_autosize(struct bsddialog_conf *conf, struct textproperties *tp, + int maxrows, int mincols, bool increasecols, int *h, int *w) +{ + int i, j, x, y, z, l, line, maxwidth, tablen; + + maxwidth = widget_max_width(conf) - HBORDERS - TEXTHMARGINS; + tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen; if (increasecols) { - mincols = MAX(mincols, maxwordlen); + mincols = MAX(mincols, tp->maxwordcols); mincols = MAX(mincols, (int)conf->auto_minwidth - HBORDERS - TEXTHMARGINS); mincols = MIN(mincols, maxwidth); @@ -491,26 +671,50 @@ text_autosize(struct bsddialog_conf *conf, const char *text, int maxrows, x = 0; y = 1; line=0; - for (i = 0; i <= nword; i++) { - if (words[i] == NL) { + 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++; + } + break; + case NL: y++; x = 0; - } - else if (words[i] == WS) { + break; + case WS: x++; if (x >= mincols) { x = 0; y++; } - } - else { - if (words[i] + x <= mincols) - x += words[i]; - else { - for (z = words[i]; z > 0; ) { - y++; - x = MIN(mincols, z); - z -= x; + 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; } } } @@ -519,16 +723,16 @@ text_autosize(struct bsddialog_conf *conf, const char *text, int maxrows, if (increasecols == false) break; - if (y <= maxrows || mincols >= maxwidth) + if (mincols >= maxwidth) + break; + if (line >= y * (int)conf->text.cols_per_row && y <= maxrows) break; mincols++; } - *h = (nword == 0 && words[0] == 0) ? 0 : y; + *h = (tp->nword == 0) ? 0 : y; *w = MIN(mincols, line); /* wtext can be less than mincols */ - free(words); - return (0); } @@ -536,13 +740,26 @@ 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) { - int wbuttons, maxhtext; bool changewtext; + int wbuttons, maxhtext; + struct textproperties tp; wbuttons = 0; if (bs != NULL) - wbuttons = buttons_width(*bs); + wbuttons = buttons_min_width(*bs); + + /* Rows */ + if (rows == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_FULLSCREEN) { + maxhtext = widget_max_height(conf) - VBORDERS - rowsnotext; + } else { /* fixed */ + maxhtext = rows - VBORDERS - rowsnotext; + } + if (bs != NULL) + maxhtext -= 2; + if (maxhtext <= 0) + maxhtext = 1; /* text_autosize() computes always htext */ + /* Cols */ if (cols == BSDDIALOG_AUTOSIZE) { startwtext = MAX(startwtext, wbuttons - TEXTHMARGINS); changewtext = true; @@ -554,45 +771,52 @@ text_size(struct bsddialog_conf *conf, int rows, int cols, const char *text, changewtext = false; } - if (rows == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_FULLSCREEN) { - maxhtext = widget_max_height(conf) - VBORDERS - rowsnotext; - if (bs != NULL) - maxhtext -= 2; - } else { /* fixed */ - maxhtext = rows - VBORDERS - rowsnotext; - if (bs != NULL) - maxhtext -= 2; - } - if (startwtext <= 0 && changewtext) startwtext = 1; - if (maxhtext <= 0 || startwtext <= 0) { - *htext = *wtext = 0; - return (0); - } + if (startwtext <= 0) + RETURN_ERROR("Fullscreen or fixed cols to print text <=0"); - if (text_autosize(conf, text, maxhtext, startwtext, changewtext, - htext, wtext) != 0) + /* Sizing calculation */ + if (text_properties(conf, text, &tp) != 0) + return (BSDDIALOG_ERROR); + if (text_autosize(conf, &tp, maxhtext, startwtext, changewtext, htext, + wtext) != 0) return (BSDDIALOG_ERROR); + free(tp.words); + free(tp.wletters); + return (0); } +/* Widget size and position */ int widget_max_height(struct bsddialog_conf *conf) { int maxheight; - maxheight = conf->shadow ? SCREENLINES - (int)t.shadow.h : SCREENLINES; + maxheight = conf->shadow ? SCREENLINES - (int)t.shadow.y : SCREENLINES; if (maxheight <= 0) RETURN_ERROR("Terminal too small, screen lines - shadow <= 0"); - if (conf->y > 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"); } + maxheight -= conf->auto_downmargin; + if (maxheight <= 0) + RETURN_ERROR("Terminal too small, screen lines - Down margins " + "<= 0"); + return (maxheight); } @@ -600,7 +824,7 @@ int widget_max_width(struct bsddialog_conf *conf) { int maxwidth; - maxwidth = conf->shadow ? SCREENCOLS - (int)t.shadow.w : SCREENCOLS; + maxwidth = conf->shadow ? SCREENCOLS - (int)t.shadow.x : SCREENCOLS; if (maxwidth <= 0) RETURN_ERROR("Terminal too small, screen cols - shadow <= 0"); @@ -647,13 +871,13 @@ widget_min_width(struct bsddialog_conf *conf, int wtext, int minwidget, struct buttons *bs) { - int min, delimtitle; + int min, delimtitle, wbottomtitle, wtitle; min = 0; /* buttons */ if (bs != NULL) - min += buttons_width(*bs); + min += buttons_min_width(*bs); /* text */ if (wtext > 0) @@ -665,12 +889,15 @@ widget_min_width(struct bsddialog_conf *conf, int wtext, int minwidget, /* title */ if (conf->title != NULL) { delimtitle = t.dialog.delimtitle ? 2 : 0; - min = MAX(min, (int)strlen(conf->title) + 2 + delimtitle); + wtitle = strcols(conf->title); + min = MAX(min, wtitle + 2 + delimtitle); } /* bottom title */ - if (conf->bottomtitle != NULL) - min = MAX(min, (int)strlen(conf->bottomtitle) + 4); + if (conf->bottomtitle != NULL) { + wbottomtitle = strcols(conf->bottomtitle); + min = MAX(min, wbottomtitle + 4); + } /* dialog borders */ min += VBORDERS; @@ -721,8 +948,16 @@ set_widget_size(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w) int set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w) { - if (conf->y == BSDDIALOG_CENTER) - *y = SCREENLINES/2 - (h + t.shadow.h)/2; + 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 >= SCREENLINES) @@ -730,13 +965,13 @@ set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w) else *y = conf->y; - if ((*y + h + (conf->shadow ? (int) t.shadow.h : 0)) > SCREENLINES) + 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 = SCREENCOLS/2 - (w + t.shadow.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 >= SCREENCOLS) @@ -744,14 +979,14 @@ set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w) else *x = conf->x; - if ((*x + w + (conf->shadow ? (int) t.shadow.w : 0)) > SCREENCOLS) + if ((*x + w + wshadow) > SCREENCOLS) RETURN_ERROR("The right of the box over the terminal " "(begin X + width (+ shadow) > terminal cols)"); return (0); } -/* Widgets builders */ +/* Widgets build, update, destroy */ void draw_borders(struct bsddialog_conf *conf, WINDOW *win, int rows, int cols, enum elevation elev) @@ -815,7 +1050,7 @@ static int draw_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget, WINDOW *textpad, const char *text, struct buttons *bs, bool shortcutbuttons) { - int h, w, ts, ltee, rtee; + int h, w, wtitle, wbottomtitle, ts, ltee, rtee; ts = conf->ascii_lines ? '-' : ACS_HLINE; ltee = conf->ascii_lines ? '+' : ACS_LTEE; @@ -823,19 +1058,21 @@ draw_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget, getmaxyx(widget, h, w); - if (shadow != NULL) + if (conf->shadow) wnoutrefresh(shadow); draw_borders(conf, widget, h, w, RAISED); if (conf->title != NULL) { + if ((wtitle = strcols(conf->title)) < 0) + return (BSDDIALOG_ERROR); if (t.dialog.delimtitle && conf->no_lines == false) { wattron(widget, t.dialog.lineraisecolor); - mvwaddch(widget, 0, w/2-strlen(conf->title)/2-1, rtee); + mvwaddch(widget, 0, w/2 - wtitle/2 -1, rtee); wattroff(widget, t.dialog.lineraisecolor); } wattron(widget, t.dialog.titlecolor); - mvwaddstr(widget, 0, w/2 - strlen(conf->title)/2, conf->title); + mvwaddstr(widget, 0, w/2 - wtitle/2, conf->title); wattroff(widget, t.dialog.titlecolor); if (t.dialog.delimtitle && conf->no_lines == false) { wattron(widget, t.dialog.lineraisecolor); @@ -859,8 +1096,10 @@ draw_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget, } if (conf->bottomtitle != NULL) { + if ((wbottomtitle = strcols(conf->bottomtitle)) < 0) + return (BSDDIALOG_ERROR); wattron(widget, t.dialog.bottomtitlecolor); - wmove(widget, h - 1, w/2 - strlen(conf->bottomtitle)/2 - 1); + wmove(widget, h - 1, w/2 - wbottomtitle/2 - 1); waddch(widget, ' '); waddstr(widget, conf->bottomtitle); waddch(widget, ' '); @@ -883,9 +1122,9 @@ update_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget, { int error; - if (shadow != NULL) { + if (conf->shadow) { wclear(shadow); - mvwin(shadow, y + t.shadow.h, x + t.shadow.w); + mvwin(shadow, y + t.shadow.y, x + t.shadow.x); wresize(shadow, h, w); } @@ -912,7 +1151,7 @@ new_dialog(struct bsddialog_conf *conf, WINDOW **shadow, WINDOW **widget, int y, int error; if (conf->shadow) { - *shadow = newwin(h, w, y + t.shadow.h, x + t.shadow.w); + *shadow = newwin(h, w, y + t.shadow.y, x + t.shadow.x); if (*shadow == NULL) RETURN_ERROR("Cannot build shadow"); wbkgd(*shadow, t.shadow.color); @@ -962,10 +1201,10 @@ end_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget, delwin(shadow); if (conf->clear) - hide_widget(y, x, h, w, shadow != NULL); + hide_widget(y, x, h, w, conf->shadow); if (conf->get_height != NULL) *conf->get_height = h; if (conf->get_width != NULL) *conf->get_width = w; -}
\ No newline at end of file +} diff --git a/lib/lib_util.h b/lib/lib_util.h index 6ebc73cf1055..238d3fda4675 100644 --- a/lib/lib_util.h +++ b/lib/lib_util.h @@ -33,8 +33,9 @@ #define TEXTHMARGIN 1 #define TEXTHMARGINS (TEXTHMARGIN + TEXTHMARGIN) -/* current theme */ +/* theme utils */ extern struct bsddialog_theme t; +extern bool hastermcolors; /* debug */ #define BSDDIALOG_DEBUG(y,x,fmt, ...) do { \ @@ -42,6 +43,12 @@ extern struct bsddialog_theme t; refresh(); \ } while (0) +/* unicode */ +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); + /* error buffer */ const char *get_error_string(void); void set_error_string(const char *string); @@ -56,6 +63,7 @@ struct buttons { unsigned int nbuttons; #define MAXBUTTONS 6 /* ok + extra + cancel + help + 2 generics */ const char *label[MAXBUTTONS]; + wchar_t first[MAXBUTTONS]; int value[MAXBUTTONS]; int curr; unsigned int sizebutton; /* including left and right delimiters */ @@ -70,8 +78,8 @@ get_buttons(struct bsddialog_conf *conf, struct buttons *bs, void draw_buttons(WINDOW *window, struct buttons bs, bool shortcut); -int buttons_width(struct buttons bs); -bool shortcut_buttons(int key, struct buttons *bs); +int buttons_min_width(struct buttons bs); +bool shortcut_buttons(wint_t key, struct buttons *bs); /* help window with F1 key */ int f1help(struct bsddialog_conf *conf); @@ -130,4 +138,4 @@ void end_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget, WINDOW *textpad); -#endif
\ No newline at end of file +#endif diff --git a/lib/libbsddialog.c b/lib/libbsddialog.c index 761bdc3efa77..1a8188b0e7d3 100644 --- a/lib/libbsddialog.c +++ b/lib/libbsddialog.c @@ -34,10 +34,11 @@ #include "bsddialog_theme.h" #include "lib_util.h" -int bsddialog_init(void) +#define COLSPERROW 10 /* Default conf.text.columns_per_row */ + +int bsddialog_init_notheme(void) { int i, j, c, error; - enum bsddialog_default_theme theme; set_error_string(""); @@ -64,7 +65,18 @@ int bsddialog_init(void) } } - if (error == OK && has_colors()) + 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; @@ -113,6 +125,7 @@ int bsddialog_initconf(struct bsddialog_conf *conf) conf->y = BSDDIALOG_CENTER; conf->x = BSDDIALOG_CENTER; conf->shadow = true; + conf->text.cols_per_row = COLSPERROW; return (BSDDIALOG_OK); } diff --git a/lib/menubox.c b/lib/menubox.c index 22ed15e6e7a0..28b9c8a0923b 100644 --- a/lib/menubox.c +++ b/lib/menubox.c @@ -177,9 +177,10 @@ getfastprev(int menurows, struct privateitem *pritems, int abs) static int getnextshortcut(struct bsddialog_conf *conf, int npritems, - struct privateitem *pritems, int abs, int key) + struct privateitem *pritems, int abs, wint_t key) { - int i, ch, next; + int i, next; + wchar_t wch; next = -1; for (i = 0; i < npritems; i++) { @@ -187,11 +188,11 @@ getnextshortcut(struct bsddialog_conf *conf, int npritems, continue; if (conf->menu.no_name) - ch = pritems[i].item->desc[0]; + mbtowc(&wch, pritems[i].item->desc, MB_CUR_MAX); else - ch = pritems[i].item->name[0]; + mbtowc(&wch, pritems[i].item->name, MB_CUR_MAX); - if (ch == key) { + if (wch == (wchar_t)key) { if (i > abs) return (i); @@ -236,12 +237,12 @@ drawseparators(struct bsddialog_conf *conf, WINDOW *pad, int linelen, } name = pritems[i].item->name; desc = pritems[i].item->desc; - labellen = strlen(name) + strlen(desc) + 1; + labellen = strcols(name) + strcols(desc) + 1; wmove(pad, i, labellen < linelen ? linelen/2 - labellen/2 : 0); wattron(pad, t.menu.namesepcolor); waddstr(pad, name); wattroff(pad, t.menu.namesepcolor); - if (strlen(name) > 0 && strlen(desc) > 0) + if (strcols(name) > 0 && strcols(desc) > 0) waddch(pad, ' '); wattron(pad, t.menu.descsepcolor); waddstr(pad, desc); @@ -254,7 +255,7 @@ drawitem(struct bsddialog_conf *conf, WINDOW *pad, int y, struct lineposition pos, struct privateitem *pritem, bool focus) { int colordesc, colorname, colorshortcut; - const char *shortcut; + wchar_t shortcut; struct bsddialog_menuitem *item; item = pritem->item; @@ -303,24 +304,47 @@ drawitem(struct bsddialog_conf *conf, WINDOW *pad, int y, wattron(pad, colorshortcut); if (conf->menu.no_name) - shortcut = item->desc; + mbtowc(&shortcut, item->desc, MB_CUR_MAX); else - shortcut = item->name; - wmove(pad, y, pos.xname + item->depth * DEPTH); - if (shortcut != NULL && shortcut[0] != '\0') - waddch(pad, shortcut[0]); - wattroff(pad, colorshortcut); + mbtowc(&shortcut, item->name, MB_CUR_MAX); + mvwaddwch(pad, y, pos.xname + item->depth * DEPTH, shortcut); + wattroff(pad, colorshortcut); } /* bottom description */ move(SCREENLINES - 1, 2); clrtoeol(); if (item->bottomdesc != NULL && focus) { + attron(t.menu.bottomdesccolor); addstr(item->bottomdesc); + attroff(t.menu.bottomdesccolor); refresh(); } } +/* 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) +{ + draw_borders(conf, menuwin, h, w, LOWERED); + + if (totnitems > (int)menurows) { + wattron(menuwin, t.dialog.arrowcolor); + if (ymenupad > 0) + mvwhline(menuwin, 0, 2, + conf->ascii_lines ? '^' : ACS_UARROW, 3); + + if ((ymenupad + (int)menurows) < totnitems) + mvwhline(menuwin, h-1, 2, + conf->ascii_lines ? 'v' : ACS_DARROW, 3); + + mvwprintw(menuwin, h-1, w-6, "%3d%%", + 100 * (ymenupad + menurows) / totnitems); + wattroff(menuwin, t.dialog.arrowcolor); + } +} + static int menu_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w, const char *text, int linelen, unsigned int *menurows, int nitems, @@ -365,8 +389,12 @@ menu_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w, */ *menurows = MIN(*h - 6 - htext, (int)*menurows); } else { - if (*menurows == 0) - *menurows = MIN(*h-6-htext, nitems); + if (*menurows == 0) { + if (*h - 6 - htext <= 0) + *menurows = 0; /* menu_checksize() will check */ + else + *menurows = MIN(*h-6-htext, nitems); + } } return (0); @@ -380,8 +408,7 @@ menu_checksize(int rows, int cols, const char *text, int menurows, int nitems, mincols = VBORDERS; /* buttons */ - mincols += buttons_width(bs); - + mincols += buttons_min_width(bs); /* * linelen check, comment to allow some hidden col otherwise portconfig * could not show big menus like www/apache24 @@ -392,11 +419,11 @@ menu_checksize(int rows, int cols, const char *text, int menurows, int nitems, RETURN_ERROR("Few cols, width < size buttons or " "name + descripion of the items"); - textrow = text != NULL && strlen(text) > 0 ? 1 : 0; + textrow = text != NULL && text[0] != '\0' ? 1 : 0; if (nitems > 0 && menurows == 0) - RETURN_ERROR("items > 0 but menurows == 0, probably terminal " - "too small"); + RETURN_ERROR("items > 0 but menurows == 0, if menurows = 0 " + "terminal too small"); menusize = nitems > 0 ? 3 : 0; if (rows < 2 + 2 + menusize + textrow) @@ -405,37 +432,15 @@ menu_checksize(int rows, int cols, const char *text, int menurows, int nitems, return (0); } -/* 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) -{ - draw_borders(conf, menuwin, h, w, LOWERED); - - if (totnitems > (int)menurows) { - wattron(menuwin, t.dialog.arrowcolor); - - if (ymenupad > 0) - mvwprintw(menuwin, 0, 2, "^^^"); - - if ((ymenupad + (int)menurows) < totnitems) - mvwprintw(menuwin, h-1, 2, "vvv"); - - wattroff(menuwin, t.dialog.arrowcolor); - - mvwprintw(menuwin, h-1, w-10, "%3d%%", - 100 * (ymenupad + menurows) / totnitems); - } -} - 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, onetrue, movefocus, automenurows, shortcut_butts; - int i, j, y, x, h, w, output, input; + int i, j, y, x, h, w, retval; int ymenupad, ys, ye, xs, xe, abs, next, totnitems; + wint_t input; WINDOW *shadow, *widget, *textpad, *menuwin, *menupad; struct buttons bs; struct lineposition pos = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -458,14 +463,14 @@ do_mixedlist(struct bsddialog_conf *conf, const char *text, int rows, int cols, if (groups[i].type == BSDDIALOG_SEPARATOR) { pos.maxsepstr = MAX(pos.maxsepstr, - strlen(item->name) + strlen(item->desc)); + strcols(item->name) + strcols(item->desc)); continue; } - pos.maxprefix = MAX(pos.maxprefix,strlen(item->prefix)); + pos.maxprefix = MAX(pos.maxprefix,strcols(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 = MAX(pos.maxname, strcols(item->name)); + pos.maxdesc = MAX(pos.maxdesc, strcols(item->desc)); } } pos.maxname = conf->menu.no_name ? 0 : pos.maxname; @@ -479,7 +484,6 @@ do_mixedlist(struct bsddialog_conf *conf, const char *text, int rows, int cols, pos.xdesc += (pos.maxname != 0 ? 1 : 0); pos.line = MAX(pos.maxsepstr + 3, pos.xdesc + pos.maxdesc); - get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL); if (set_widget_size(conf, rows, cols, &h, &w) != 0) @@ -498,11 +502,11 @@ do_mixedlist(struct bsddialog_conf *conf, const char *text, int rows, int cols, doupdate(); - prefresh(textpad, 0, 0, y + 1, x + 1 + TEXTHMARGIN, - y + h - menurows, x + 1 + w - TEXTHMARGIN); + prefresh(textpad, 0, 0, y + 1, x + 1 + TEXTHMARGIN, y + h - menurows, + x + 1 + w - TEXTHMARGIN); menuwin = new_boxed_window(conf, y + h - 5 - menurows, x + 2, - menurows+2, w-4, LOWERED); + menurows + 2, w - 4, LOWERED); menupad = newpad(totnitems, pos.line); wbkgd(menupad, t.dialog.color); @@ -562,22 +566,23 @@ do_mixedlist(struct bsddialog_conf *conf, const char *text, int rows, int cols, movefocus = false; loop = true; while (loop) { - input = getch(); + if (get_wch(&input) == ERR) + continue; switch(input) { case KEY_ENTER: case 10: /* Enter */ - output = bs.value[bs.curr]; + retval = bs.value[bs.curr]; if (abs >= 0 && pritems[abs].type == MENUMODE) pritems[abs].on = true; - set_on_output(conf, output, ngroups, groups, pritems); + set_on_output(conf, retval, ngroups, groups, pritems); loop = false; break; case 27: /* Esc */ if (conf->key.enable_esc) { - output = BSDDIALOG_ESC; + retval = BSDDIALOG_ESC; if (abs >= 0 && pritems[abs].type == MENUMODE) pritems[abs].on = true; - set_on_output(conf, output, ngroups, groups, + set_on_output(conf, retval, ngroups, groups, pritems); loop = false; } @@ -714,10 +719,10 @@ do_mixedlist(struct bsddialog_conf *conf, const char *text, int rows, int cols, default: if (shortcut_butts) { if (shortcut_buttons(input, &bs)) { - output = bs.value[bs.curr]; + retval = bs.value[bs.curr]; if (pritems[abs].type == MENUMODE) pritems[abs].on = true; - set_on_output(conf, output, ngroups, + set_on_output(conf, retval, ngroups, groups, pritems); loop = false; } @@ -728,7 +733,7 @@ do_mixedlist(struct bsddialog_conf *conf, const char *text, int rows, int cols, next = getnextshortcut(conf, totnitems, pritems, abs, input); movefocus = next != abs; - } + } /* end switch handler */ if (movefocus) { drawitem(conf, menupad, abs, pos, &pritems[abs], false); @@ -744,7 +749,7 @@ do_mixedlist(struct bsddialog_conf *conf, const char *text, int rows, int cols, prefresh(menupad, ymenupad, 0, ys, xs, ye, xe); movefocus = false; } - } + } /* end while handler */ if (focuslist != NULL) *focuslist = abs < 0 ? -1 : pritems[abs].group; @@ -756,7 +761,7 @@ do_mixedlist(struct bsddialog_conf *conf, const char *text, int rows, int cols, end_dialog(conf, shadow, widget, textpad); free(pritems); - return (output); + return (retval); } /* API */ @@ -765,12 +770,12 @@ 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 @@ -778,14 +783,14 @@ 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, focuslist = 0; + int retval, focuslist = 0; struct bsddialog_menugroup group = { BSDDIALOG_CHECKLIST /* unused */, nitems, items}; - output = do_mixedlist(conf, text, rows, cols, menurows, CHECKLISTMODE, + retval = do_mixedlist(conf, text, rows, cols, menurows, CHECKLISTMODE, 1, &group, &focuslist, focusitem); - return (output); + return (retval); } int @@ -793,14 +798,14 @@ 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, focuslist = 0; + int retval, focuslist = 0; struct bsddialog_menugroup group = { BSDDIALOG_CHECKLIST /* unused */, nitems, items}; - output = do_mixedlist(conf, text, rows, cols, menurows, MENUMODE, 1, + retval = do_mixedlist(conf, text, rows, cols, menurows, MENUMODE, 1, &group, &focuslist, focusitem); - return (output); + return (retval); } int @@ -808,12 +813,12 @@ 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, focuslist = 0; + int retval, focuslist = 0; struct bsddialog_menugroup group = { BSDDIALOG_RADIOLIST /* unused */, nitems, items}; - output = do_mixedlist(conf, text, rows, cols, menurows, RADIOLISTMODE, + 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 24b34ccbce97..06753be20c3d 100644 --- a/lib/messagebox.c +++ b/lib/messagebox.c @@ -31,18 +31,22 @@ #include <string.h> #include "bsddialog.h" +#include "bsddialog_theme.h" #include "lib_util.h" static int message_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, - int *w, const char *text, struct buttons bs) + int *w, const char *text, bool *hastext, struct buttons bs) { int htext, wtext; - if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) { - if (text_size(conf, rows, cols, text, &bs, 0, SCREENCOLS/2, - &htext, &wtext) != 0) + if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE || + hastext != NULL) { + if (text_size(conf, rows, cols, text, &bs, 0, 1, &htext, + &wtext) != 0) return (BSDDIALOG_ERROR); + if (hastext != NULL) + *hastext = htext > 0 ? true : false; } if (cols == BSDDIALOG_AUTOSIZE) @@ -54,34 +58,40 @@ message_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, return (0); } -static int message_checksize(int rows, int cols, struct buttons bs) +static int +message_checksize(int rows, int cols, bool hastext, struct buttons bs) { int mincols; mincols = VBORDERS; - mincols += buttons_width(bs); + mincols += buttons_min_width(bs); if (cols < mincols) RETURN_ERROR("Few cols, Msgbox and Yesno need at least width " "for borders, buttons and spaces between buttons"); - if (rows < HBORDERS + 2 /*buttons*/) - RETURN_ERROR("Msgbox and Yesno need at least height 4"); + if (rows < HBORDERS + 2 /* buttons */) + RETURN_ERROR("Msgbox and Yesno need at least 4 rows"); + if (hastext && rows < HBORDERS + 2 /*buttons*/ + 1 /* text row */) + RETURN_ERROR("Msgbox and Yesno with text need at least 5 rows"); return (0); } static void -textupdate(WINDOW *widget, WINDOW *textpad, int htextpad, int ytextpad) +textupdate(WINDOW *widget, WINDOW *textpad, int htextpad, int ytextpad, + bool hastext) { int y, x, h, w; getbegyx(widget, y, x); getmaxyx(widget, h, w); - if (htextpad > h - 4) { + if (hastext && htextpad > h - 4) { + wattron(widget, t.dialog.arrowcolor); mvwprintw(widget, h-3, w-6, "%3d%%", 100 * (ytextpad+h-4)/ htextpad); + wattroff(widget, t.dialog.arrowcolor); wnoutrefresh(widget); } @@ -92,15 +102,16 @@ static int do_message(struct bsddialog_conf *conf, const char *text, int rows, int cols, struct buttons bs) { - bool loop; - int y, x, h, w, input, output, ytextpad, htextpad, unused; + bool hastext, loop; + int y, x, h, w, retval, ytextpad, htextpad, unused; WINDOW *widget, *textpad, *shadow; + wint_t input; if (set_widget_size(conf, rows, cols, &h, &w) != 0) return (BSDDIALOG_ERROR); - if (message_autosize(conf, rows, cols, &h, &w, text, bs) != 0) + if (message_autosize(conf, rows, cols, &h, &w, text, &hastext, bs) != 0) return (BSDDIALOG_ERROR); - if (message_checksize(h, w, bs) != 0) + if (message_checksize(h, w, hastext, bs) != 0) return (BSDDIALOG_ERROR); if (set_widget_position(conf, &y, &x, h, w) != 0) return (BSDDIALOG_ERROR); @@ -112,20 +123,21 @@ do_message(struct bsddialog_conf *conf, const char *text, int rows, int cols, ytextpad = 0; getmaxyx(textpad, htextpad, unused); unused++; /* fix unused error */ - textupdate(widget, textpad, htextpad, ytextpad); + textupdate(widget, textpad, htextpad, ytextpad, hastext); loop = true; while (loop) { doupdate(); - input = getch(); + if (get_wch(&input) == ERR) + continue; switch (input) { case KEY_ENTER: case 10: /* Enter */ - output = bs.value[bs.curr]; + retval = bs.value[bs.curr]; loop = false; break; case 27: /* Esc */ if (conf->key.enable_esc) { - output = BSDDIALOG_ESC; + retval = BSDDIALOG_ESC; loop = false; } break; @@ -163,9 +175,9 @@ do_message(struct bsddialog_conf *conf, const char *text, int rows, int cols, if (set_widget_size(conf, rows, cols, &h, &w) != 0) return (BSDDIALOG_ERROR); if (message_autosize(conf, rows, cols, &h, &w, text, - bs) != 0) + NULL, bs) != 0) return (BSDDIALOG_ERROR); - if (message_checksize(h, w, bs) != 0) + if (message_checksize(h, w, hastext, bs) != 0) return (BSDDIALOG_ERROR); if (set_widget_position(conf, &y, &x, h, w) != 0) return (BSDDIALOG_ERROR); @@ -175,7 +187,8 @@ do_message(struct bsddialog_conf *conf, const char *text, int rows, int cols, return (BSDDIALOG_ERROR); getmaxyx(textpad, htextpad, unused); - textupdate(widget, textpad, htextpad, ytextpad); + ytextpad = 0; + textupdate(widget, textpad, htextpad, ytextpad, hastext); /* Important to fix grey lines expanding screen */ refresh(); @@ -184,17 +197,17 @@ do_message(struct bsddialog_conf *conf, const char *text, int rows, int cols, if (ytextpad == 0) break; ytextpad--; - textupdate(widget, textpad, htextpad, ytextpad); + textupdate(widget, textpad, htextpad, ytextpad, hastext); break; case KEY_DOWN: if (ytextpad + h - 4 >= htextpad) break; ytextpad++; - textupdate(widget, textpad, htextpad, ytextpad); + textupdate(widget, textpad, htextpad, ytextpad, hastext); break; default: if (shortcut_buttons(input, &bs)) { - output = bs.value[bs.curr]; + retval = bs.value[bs.curr]; loop = false; } } @@ -202,7 +215,7 @@ do_message(struct bsddialog_conf *conf, const char *text, int rows, int cols, end_dialog(conf, shadow, widget, textpad); - return (output); + return (retval); } /* API */ diff --git a/lib/textbox.c b/lib/textbox.c index 69eff7c0bc2e..0c6d53a8cad0 100644 --- a/lib/textbox.c +++ b/lib/textbox.c @@ -28,18 +28,61 @@ #include <sys/param.h> #include <curses.h> +#include <stdlib.h> #include <string.h> +#include <wchar.h> #include "bsddialog.h" #include "bsddialog_theme.h" #include "lib_util.h" static void +updateborders(struct bsddialog_conf *conf, WINDOW *widget, int padmargin, + int hpad, int wpad, int ypad, int xpad) +{ + int h, w; + chtype arrowch, borderch; + + getmaxyx(widget, h, w); + + if (conf->no_lines) + borderch = ' '; + else if (conf->ascii_lines) + borderch = '|'; + else + borderch = ACS_VLINE; + + if (xpad > 0) { + arrowch = conf->ascii_lines ? '<' : ACS_LARROW; + arrowch |= A_ATTRIBUTES & t.dialog.arrowcolor; + } else { + arrowch = borderch; + arrowch |= A_ATTRIBUTES & t.dialog.lineraisecolor; + } + mvwvline(widget, (h / 2) - 2, 0, arrowch, 4); + + if (xpad + w-2-padmargin < wpad) { + arrowch = conf->ascii_lines ? '>' : ACS_RARROW; + arrowch |= A_ATTRIBUTES & t.dialog.arrowcolor; + } else { + arrowch = borderch; + arrowch |= A_ATTRIBUTES & t.dialog.linelowercolor; + } + mvwvline(widget, (h / 2) - 2, w - 1, arrowch, 4); + + if (hpad > h - 4) { + wattron(widget, t.dialog.arrowcolor); + mvwprintw(widget, h-3, w-6, "%3d%%", 100 * (ypad+h-4)/ hpad); + wattroff(widget, t.dialog.arrowcolor); + } +} + +static void textbox_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, - int *w, int hpad, int wpad, struct buttons bs) + int *w, int hpad, int wpad, int padmargin, struct buttons bs) { if (cols == BSDDIALOG_AUTOSIZE) - *w = widget_min_width(conf, 0, wpad, &bs); + *w = widget_min_width(conf, 0, wpad + padmargin, &bs); if (rows == BSDDIALOG_AUTOSIZE) *h = widget_min_height(conf, 0, hpad, true); @@ -50,7 +93,8 @@ textbox_checksize(int rows, int cols, int hpad, struct buttons bs) { int mincols; - mincols = VBORDERS + bs.sizebutton; + mincols = VBORDERS; + mincols += buttons_min_width(bs); if (cols < mincols) RETURN_ERROR("Few cols for the textbox"); @@ -66,9 +110,11 @@ int bsddialog_textbox(struct bsddialog_conf *conf, const char* file, int rows, int cols) { - bool loop; - int i, output, input; - int y, x, h, w, hpad, wpad, ypad, xpad, ys, ye, xs, xe, printrows; + bool loop, has_multi_col; + int i, retval, y, x, h, w; + int hpad, wpad, ypad, xpad, ys, ye, xs, xe, padmargin, printrows; + unsigned int defaulttablen, linecols; + wint_t input; char buf[BUFSIZ]; FILE *fp; struct buttons bs; @@ -77,14 +123,19 @@ bsddialog_textbox(struct bsddialog_conf *conf, const char* file, int rows, if ((fp = fopen(file, "r")) == NULL) RETURN_ERROR("Cannot open file"); + defaulttablen = TABSIZE; + set_tabsize((conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen); hpad = 1; wpad = 1; pad = newpad(hpad, wpad); wbkgd(pad, t.dialog.color); + padmargin = 0; i = 0; while (fgets(buf, BUFSIZ, fp) != NULL) { - if ((int) strlen(buf) > wpad) { - wpad = strlen(buf); + if (str_props(buf, &linecols, &has_multi_col) != 0) + continue; + if ((int)linecols > wpad) { + wpad = linecols; wresize(pad, hpad, wpad); } if (i > hpad-1) { @@ -93,20 +144,19 @@ bsddialog_textbox(struct bsddialog_conf *conf, const char* file, int rows, } mvwaddstr(pad, i, 0, buf); i++; + if (has_multi_col) + padmargin = 2; } fclose(fp); + set_tabsize(defaulttablen); - bs.nbuttons = 1; - bs.label[0] = "EXIT"; - if (conf->button.ok_label != NULL) - bs.label[0] = conf->button.ok_label; - bs.value[0] = BSDDIALOG_OK; + get_buttons(conf, &bs, "EXIT", NULL); bs.curr = 0; - bs.sizebutton = strlen(bs.label[0]) + 2; + bs.nbuttons = 1; if (set_widget_size(conf, rows, cols, &h, &w) != 0) return (BSDDIALOG_ERROR); - textbox_autosize(conf, rows, cols, &h, &w, hpad, wpad, bs); + textbox_autosize(conf, rows, cols, &h, &w, hpad, wpad, padmargin, bs); if (textbox_checksize(h, w, hpad, bs) != 0) return (BSDDIALOG_ERROR); if (set_widget_position(conf, &y, &x, h, w) != 0) @@ -117,26 +167,33 @@ bsddialog_textbox(struct bsddialog_conf *conf, const char* file, int rows, return (BSDDIALOG_ERROR); ys = y + 1; - xs = x + 1; + xs = (padmargin == 0) ? x + 1 : x + 2; ye = ys + h - 5; - xe = xs + w - 3; + xe = xs + w - 3 - padmargin; ypad = xpad = 0; printrows = h-4; loop = true; while (loop) { - wnoutrefresh(widget); - pnoutrefresh(pad, ypad, xpad, ys, xs, ye, xe); - doupdate(); - input = getch(); + updateborders(conf, widget, padmargin, hpad, wpad, ypad, xpad); + /* + * Overflow multicolumn charchter right border: + * wnoutrefresh(widget); + * pnoutrefresh(pad, ypad, xpad, ys, xs, ye, xe); + * doupdate(); + */ + wrefresh(widget); + prefresh(pad, ypad, xpad, ys, xs, ye, xe); + if (get_wch(&input) == ERR) + continue; switch(input) { case KEY_ENTER: case 10: /* Enter */ - output = BSDDIALOG_OK; + retval = BSDDIALOG_OK; loop = false; break; case 27: /* Esc */ if (conf->key.enable_esc) { - output = BSDDIALOG_ESC; + retval = BSDDIALOG_ESC; loop = false; } break; @@ -164,7 +221,7 @@ bsddialog_textbox(struct bsddialog_conf *conf, const char* file, int rows, break; case KEY_RIGHT: case 'l': - xpad = (xpad + w-2) < wpad-1 ? xpad + 1 : xpad; + xpad = (xpad + w-2-padmargin) < wpad ? xpad + 1 : xpad; break; case KEY_UP: case 'k': @@ -189,16 +246,16 @@ bsddialog_textbox(struct bsddialog_conf *conf, const char* file, int rows, if (set_widget_size(conf, rows, cols, &h, &w) != 0) return (BSDDIALOG_ERROR); textbox_autosize(conf, rows, cols, &h, &w, hpad, wpad, - bs); + padmargin, bs); if (textbox_checksize(h, w, hpad, bs) != 0) return (BSDDIALOG_ERROR); if (set_widget_position(conf, &y, &x, h, w) != 0) return (BSDDIALOG_ERROR); ys = y + 1; - xs = x + 1; + xs = (padmargin == 0) ? x + 1 : x + 2; ye = ys + h - 5; - xe = xs + w - 3; + xe = xs + w - 3 - padmargin; ypad = xpad = 0; printrows = h - 4; @@ -211,7 +268,7 @@ bsddialog_textbox(struct bsddialog_conf *conf, const char* file, int rows, break; default: if (shortcut_buttons(input, &bs)) { - output = bs.value[bs.curr]; + retval = bs.value[bs.curr]; loop = false; } } @@ -219,5 +276,5 @@ bsddialog_textbox(struct bsddialog_conf *conf, const char* file, int rows, end_dialog(conf, shadow, widget, pad); - return (output); -}
\ No newline at end of file + return (retval); +} diff --git a/lib/theme.c b/lib/theme.c index 20b1e35428dd..40310d58cda4 100644 --- a/lib/theme.c +++ b/lib/theme.c @@ -34,51 +34,53 @@ #define GET_COLOR(bg, fg) (COLOR_PAIR(bg * 8 + fg +1)) struct bsddialog_theme t; +bool hastermcolors; static struct bsddialog_theme bsddialogtheme = { -#define bgwidget COLOR_WHITE -#define bgcurr COLOR_YELLOW .screen.color = GET_COLOR(COLOR_BLACK, COLOR_CYAN), .shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK), - .shadow.h = 1, - .shadow.w = 2, + .shadow.y = 1, + .shadow.x = 2, .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), - .dialog.arrowcolor = GET_COLOR(COLOR_YELLOW, bgwidget), - - .menu.f_selectorcolor = GET_COLOR(COLOR_BLACK, bgcurr), - .menu.selectorcolor = GET_COLOR(COLOR_BLACK, bgwidget), - .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_BLACK, 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.hmargin = 3, - .button.leftdelim = '[', - .button.rightdelim = ']', - .button.f_delimcolor = GET_COLOR(COLOR_BLACK, bgwidget), - .button.delimcolor = GET_COLOR(COLOR_BLACK, bgwidget), - .button.f_color = GET_COLOR(COLOR_BLACK, bgcurr) | A_UNDERLINE, - .button.color = GET_COLOR(COLOR_BLACK, bgwidget) | A_UNDERLINE, - .button.f_shortcutcolor = GET_COLOR(COLOR_RED, bgcurr) | A_UNDERLINE, - .button.shortcutcolor = GET_COLOR(COLOR_RED, bgwidget) | A_UNDERLINE + .dialog.titlecolor = GET_COLOR(COLOR_YELLOW, COLOR_WHITE), + .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), + .dialog.arrowcolor = GET_COLOR(COLOR_YELLOW, COLOR_WHITE), + + .menu.f_selectorcolor = GET_COLOR(COLOR_BLACK, COLOR_YELLOW), + .menu.selectorcolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), + .menu.f_desccolor = GET_COLOR(COLOR_WHITE, COLOR_YELLOW), + .menu.desccolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), + .menu.f_namecolor = GET_COLOR(COLOR_BLACK, COLOR_YELLOW), + .menu.namecolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), + .menu.namesepcolor = GET_COLOR(COLOR_YELLOW, COLOR_WHITE), + .menu.descsepcolor = GET_COLOR(COLOR_YELLOW, COLOR_WHITE), + .menu.f_shortcutcolor = GET_COLOR(COLOR_RED, COLOR_YELLOW), + .menu.shortcutcolor = GET_COLOR(COLOR_RED, COLOR_WHITE), + .menu.bottomdesccolor = GET_COLOR(COLOR_WHITE, COLOR_CYAN), + + .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), + .form.bottomdesccolor = GET_COLOR(COLOR_WHITE, COLOR_CYAN), + + .bar.f_color = GET_COLOR(COLOR_BLACK, COLOR_YELLOW), + .bar.color = GET_COLOR(COLOR_BLACK, COLOR_WHITE), + + .button.minmargin = 1, + .button.maxmargin = 5, + .button.leftdelim = '[', + .button.rightdelim = ']', + .button.f_delimcolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), + .button.delimcolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), + .button.f_color = GET_COLOR(COLOR_BLACK, COLOR_YELLOW), + .button.color = GET_COLOR(COLOR_BLACK, COLOR_WHITE), + .button.f_shortcutcolor = GET_COLOR(COLOR_RED, COLOR_YELLOW), + .button.shortcutcolor = GET_COLOR(COLOR_RED, COLOR_WHITE) }; static struct bsddialog_theme blackwhite = { @@ -87,8 +89,8 @@ static struct bsddialog_theme blackwhite = { .screen.color = GET_COLOR(fg, bk), .shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK), - .shadow.h = 1, - .shadow.w = 2, + .shadow.y = 1, + .shadow.x = 2, .dialog.delimtitle = true, .dialog.titlecolor = GET_COLOR(fg, bk), @@ -108,15 +110,18 @@ static struct bsddialog_theme blackwhite = { .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, + .menu.bottomdesccolor = GET_COLOR(fg, bk), - .form.f_fieldcolor = GET_COLOR(fg, bk) | A_REVERSE, - .form.fieldcolor = GET_COLOR(fg, bk), - .form.readonlycolor = GET_COLOR(fg, bk), + .form.f_fieldcolor = GET_COLOR(fg, bk) | A_REVERSE, + .form.fieldcolor = GET_COLOR(fg, bk), + .form.readonlycolor = GET_COLOR(fg, bk), + .form.bottomdesccolor = GET_COLOR(fg, bk), .bar.f_color = GET_COLOR(fg, bk) | A_REVERSE, .bar.color = GET_COLOR(fg, bk), - .button.hmargin = 3, + .button.minmargin = 1, + .button.maxmargin = 5, .button.leftdelim = '[', .button.rightdelim = ']', .button.f_delimcolor = GET_COLOR(fg, bk), @@ -128,11 +133,11 @@ static struct bsddialog_theme blackwhite = { }; static struct bsddialog_theme dialogtheme = { - .screen.color = GET_COLOR(COLOR_CYAN, COLOR_BLUE) | A_BOLD, + .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.y = 1, + .shadow.x = 2, .dialog.delimtitle = false, .dialog.titlecolor = GET_COLOR(COLOR_BLUE, COLOR_WHITE) | A_BOLD, @@ -140,27 +145,30 @@ static struct bsddialog_theme dialogtheme = { .dialog.linelowercolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE) | A_BOLD, .dialog.color = GET_COLOR(COLOR_BLACK, COLOR_WHITE), .dialog.bottomtitlecolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE) | A_BOLD, - .dialog.arrowcolor = GET_COLOR(COLOR_GREEN, COLOR_WHITE), - - .menu.f_selectorcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), - .menu.selectorcolor = GET_COLOR(COLOR_BLACK, bgwidget), - .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.namesepcolor = GET_COLOR(COLOR_RED, COLOR_WHITE), - .menu.descsepcolor = GET_COLOR(COLOR_RED, COLOR_WHITE), - .menu.f_shortcutcolor = GET_COLOR(COLOR_RED, COLOR_BLUE), - .menu.shortcutcolor = 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, + .dialog.arrowcolor = GET_COLOR(COLOR_BLUE, 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.namesepcolor = GET_COLOR(COLOR_RED, COLOR_WHITE), + .menu.descsepcolor = GET_COLOR(COLOR_RED, 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), + + .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.hmargin = 3, + .button.minmargin = 1, + .button.maxmargin = 5, .button.leftdelim = '<', .button.rightdelim = '>', .button.f_delimcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE) | A_BOLD, @@ -177,8 +185,8 @@ 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->dialog.delimtitle = src->dialog.delimtitle; dst->dialog.titlecolor = src->dialog.titlecolor; @@ -198,15 +206,18 @@ set_theme(struct bsddialog_theme *dst, struct bsddialog_theme *src) 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->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.hmargin = src->button.hmargin; + 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; @@ -230,7 +241,7 @@ int bsddialog_get_theme(struct bsddialog_theme *theme) set_theme(theme, &t); - return (0); + return (BSDDIALOG_OK); } int bsddialog_set_theme(struct bsddialog_theme *theme) @@ -242,7 +253,7 @@ int bsddialog_set_theme(struct bsddialog_theme *theme) set_theme(&t, theme); - return (0); + return (BSDDIALOG_OK); } int bsddialog_set_default_theme(enum bsddialog_default_theme newtheme) @@ -254,6 +265,7 @@ int bsddialog_set_default_theme(enum bsddialog_default_theme newtheme) t.dialog.delimtitle = true; t.button.leftdelim = '['; t.button.rightdelim = ']'; + t.dialog.bottomtitlecolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE); } else if (newtheme == BSDDIALOG_THEME_BSDDIALOG) bsddialog_set_theme(&bsddialogtheme); @@ -264,7 +276,7 @@ int bsddialog_set_default_theme(enum bsddialog_default_theme newtheme) else RETURN_ERROR("Unknow default theme"); - return (0); + return (BSDDIALOG_OK); } int @@ -282,3 +294,32 @@ bsddialog_color(enum bsddialog_color foreground, return (GET_COLOR(foreground, background) | cursesflags); } + +int +bsddialog_color_attrs(int color, enum bsddialog_color *foreground, + enum bsddialog_color *background, unsigned int *flags) +{ + unsigned int flag; + short f, b; + + flag = 0U; + flag |= (color & A_BOLD) ? BSDDIALOG_BOLD : 0U; + flag |= (color & A_REVERSE) ? BSDDIALOG_REVERSE : 0U; + flag |= (color & A_UNDERLINE) ? BSDDIALOG_UNDERLINE : 0U; + if (flags != NULL) + *flags = flag; + + if (pair_content(PAIR_NUMBER(color), &f, &b) != OK) + RETURN_ERROR("Cannot get color attributes"); + if (foreground != NULL) + *foreground = f; + if (background != NULL) + *background = b; + + return (BSDDIALOG_OK); +} + +bool bsddialog_hascolors(void) +{ + return hastermcolors; +}
\ No newline at end of file diff --git a/lib/timebox.c b/lib/timebox.c index 7d69666c32b8..529563b49bca 100644 --- a/lib/timebox.c +++ b/lib/timebox.c @@ -32,11 +32,42 @@ #include <string.h> #include "bsddialog.h" +#include "bsddialog_theme.h" #include "lib_util.h" #define MINWDATE 23 /* 3 windows and their borders */ #define MINWTIME 14 /* 3 windows and their borders */ +static void +drawquare(struct bsddialog_conf *conf, WINDOW *win, const char *fmt, + const void *value, bool focus) +{ + int h, l, w; + + getmaxyx(win, h, w); + draw_borders(conf, win, h, w, LOWERED); + 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, (const char*)value); + else + mvwprintw(win, 1, 1, fmt, *((const int*)value)); + if (focus) + wattroff(win, t.menu.f_namecolor); + + wrefresh(win); +} + static int datetime_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w, int minw, const char *text, struct buttons bs) @@ -50,7 +81,7 @@ datetime_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, } if (cols == BSDDIALOG_AUTOSIZE) - *w = widget_min_width(conf, htext,minw, &bs); + *w = widget_min_width(conf, wtext, minw, &bs); if (rows == BSDDIALOG_AUTOSIZE) *h = widget_min_height(conf, htext, 3 /* windows */, true); @@ -64,7 +95,7 @@ datetime_checksize(int rows, int cols, int minw, struct buttons bs) int mincols; mincols = VBORDERS; - mincols += buttons_width(bs); + mincols += buttons_min_width(bs); mincols = MAX(minw, mincols); if (cols < mincols) @@ -81,7 +112,8 @@ bsddialog_timebox(struct bsddialog_conf *conf, const char* text, int rows, int cols, unsigned int *hh, unsigned int *mm, unsigned int *ss) { bool loop, focusbuttons; - int i, input, output, y, x, h, w, sel; + int i, retval, y, x, h, w, sel; + wint_t input; WINDOW *widget, *textpad, *shadow; struct buttons bs; struct myclockstruct { @@ -131,30 +163,26 @@ bsddialog_timebox(struct bsddialog_conf *conf, const char* text, int rows, wrefresh(widget); + sel = -1; loop = focusbuttons = true; while (loop) { - for (i = 0; i < 3; i++) { - mvwprintw(c[i].win, 1, 1, "%2d", c[i].value); - wrefresh(c[i].win); - } - - if (focusbuttons == false) { - wmove(c[sel].win, 1, 2); - wrefresh(c[sel].win); - } + for (i = 0; i < 3; i++) + drawquare(conf, c[i].win, "%02d", &c[i].value, + sel == i); - input = getch(); + if (get_wch(&input) == ERR) + continue; switch(input) { case KEY_ENTER: case 10: /* Enter */ - if (focusbuttons == false) - break; - output = bs.value[bs.curr]; - loop = false; + if (focusbuttons || conf->button.always_active) { + retval = bs.value[bs.curr]; + loop = false; + } break; case 27: /* Esc */ if (conf->key.enable_esc) { - output = BSDDIALOG_ESC; + retval = BSDDIALOG_ESC; loop = false; } break; @@ -165,14 +193,13 @@ bsddialog_timebox(struct bsddialog_conf *conf, const char* text, int rows, focusbuttons = bs.curr < (int)bs.nbuttons ? true : false; if (focusbuttons == false) { - curs_set(1); sel = 0; + bs.curr = conf->button.always_active ? 0 : -1; } } else { sel++; focusbuttons = sel > 2 ? true : false; if (focusbuttons) { - curs_set(0); bs.curr = 0; } } @@ -184,25 +211,28 @@ bsddialog_timebox(struct bsddialog_conf *conf, const char* text, int rows, bs.curr--; focusbuttons = bs.curr < 0 ? false : true; if (focusbuttons == false) { - curs_set(1); sel = 2; + bs.curr = conf->button.always_active ? 0 : -1; } } else { sel--; focusbuttons = sel < 0 ? true : false; - if (focusbuttons) { - curs_set(0); + if (focusbuttons) bs.curr = (int)bs.nbuttons - 1; - } } draw_buttons(widget, bs, true); wrefresh(widget); break; case KEY_UP: - if (focusbuttons) - break; - c[sel].value = c[sel].value > 0 ? + if (focusbuttons) { + sel = 0; + focusbuttons = false; + bs.curr = conf->button.always_active ? 0 : -1; + draw_buttons(widget, bs, true); + wrefresh(widget); + } else { c[sel].value = c[sel].value > 0 ? c[sel].value - 1 : c[sel].max; + } break; case KEY_DOWN: if (focusbuttons) @@ -214,10 +244,8 @@ bsddialog_timebox(struct bsddialog_conf *conf, const char* text, int rows, if (conf->key.f1_file == NULL && conf->key.f1_message == NULL) break; - curs_set(0); if (f1help(conf) != 0) return (BSDDIALOG_ERROR); - curs_set(1); /* No break, screen size can change */ case KEY_RESIZE: /* Important for decreasing screen */ @@ -248,43 +276,33 @@ bsddialog_timebox(struct bsddialog_conf *conf, const char* text, int rows, 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: if (shortcut_buttons(input, &bs)) { - output = bs.value[bs.curr]; + retval = bs.value[bs.curr]; loop = false; } } } - if (output == BSDDIALOG_OK) { + if (retval == BSDDIALOG_OK) { *hh = c[0].value; *mm = c[1].value; *ss = c[2].value; } - curs_set(0); - for (i = 0; i < 3; i++) delwin(c[i].win); end_dialog(conf, shadow, widget, textpad); - return (output); + return (retval); } int @@ -292,7 +310,8 @@ bsddialog_datebox(struct bsddialog_conf *conf, const char *text, int rows, int cols, unsigned int *yy, unsigned int *mm, unsigned int *dd) { bool loop, focusbuttons; - int i, input, output, y, x, h, w, sel; + int i, retval, y, x, h, w, sel; + wint_t input; WINDOW *widget, *textpad, *shadow; struct buttons bs; struct calendar { @@ -363,31 +382,27 @@ bsddialog_datebox(struct bsddialog_conf *conf, const char *text, int rows, wrefresh(widget); + sel = -1; loop = focusbuttons = 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); - } - if (focusbuttons == false) { - wmove(c[sel].win, 1, c[sel].x); - wrefresh(c[sel].win); - } + drawquare(conf, c[0].win, "%4d", &c[0].value, sel == 0); + drawquare(conf, c[1].win, "%9s", m[c[1].value-1].name, + sel == 1); + drawquare(conf, c[2].win, "%02d", &c[2].value, sel == 2); - input = getch(); + if (get_wch(&input) == ERR) + continue; switch(input) { case KEY_ENTER: case 10: /* Enter */ - if (focusbuttons == false) - break; - output = bs.value[bs.curr]; - loop = false; + if (focusbuttons || conf->button.always_active) { + retval = bs.value[bs.curr]; + loop = false; + } break; case 27: /* Esc */ if (conf->key.enable_esc) { - output = BSDDIALOG_ESC; + retval = BSDDIALOG_ESC; loop = false; } break; @@ -398,14 +413,13 @@ bsddialog_datebox(struct bsddialog_conf *conf, const char *text, int rows, focusbuttons = bs.curr < (int)bs.nbuttons ? true : false; if (focusbuttons == false) { - curs_set(1); sel = 0; + bs.curr = conf->button.always_active ? 0 : -1; } } else { sel++; focusbuttons = sel > 2 ? true : false; if (focusbuttons) { - curs_set(0); bs.curr = 0; } } @@ -417,33 +431,37 @@ bsddialog_datebox(struct bsddialog_conf *conf, const char *text, int rows, bs.curr--; focusbuttons = bs.curr < 0 ? false : true; if (focusbuttons == false) { - curs_set(1); sel = 2; + bs.curr = conf->button.always_active ? 0 : -1; } } else { sel--; focusbuttons = sel < 0 ? true : false; - if (focusbuttons) { - curs_set(0); + if (focusbuttons) bs.curr = (int)bs.nbuttons - 1; - } } draw_buttons(widget, bs, true); wrefresh(widget); break; case KEY_UP: - if (focusbuttons) - break; - 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; + bs.curr = conf->button.always_active ? 0 : -1; + draw_buttons(widget, bs, true); + wrefresh(widget); + } else { + 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; + } break; case KEY_DOWN: if (focusbuttons) @@ -463,10 +481,8 @@ bsddialog_datebox(struct bsddialog_conf *conf, const char *text, int rows, if (conf->key.f1_file == NULL && conf->key.f1_message == NULL) break; - curs_set(0); if (f1help(conf) != 0) return (BSDDIALOG_ERROR); - curs_set(1); /* No break, screen size can change */ case KEY_RESIZE: /* Important for decreasing screen */ @@ -496,41 +512,31 @@ bsddialog_datebox(struct bsddialog_conf *conf, const char *text, int rows, 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(); break; default: if (shortcut_buttons(input, &bs)) { - output = bs.value[bs.curr]; + retval = bs.value[bs.curr]; loop = false; } } } - if (output == BSDDIALOG_OK) { + if (retval == BSDDIALOG_OK) { *yy = c[0].value; *mm = c[1].value; *dd = c[2].value; } - curs_set(0); - for (i = 0; i < 3; i++) delwin(c[i].win); end_dialog(conf, shadow, widget, textpad); - return (output); + return (retval); } diff --git a/util_theme.c b/util_theme.c new file mode 100644 index 000000000000..e003e2c3d05f --- /dev/null +++ b/util_theme.c @@ -0,0 +1,356 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022 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 <bsddialog.h> +#include <bsddialog_theme.h> + +#include "util_theme.h" + +static struct bsddialog_theme t; +static char title[1024]; + +enum typeprop { + BOOL, + CHAR, + INT, + UINT, + COLOR +}; + +struct property { + const char* name; + enum typeprop type; + void *value; +}; + +#define NPROPERTY 38 +static struct property p[NPROPERTY] = { + { "theme.screen.color", COLOR, &t.screen.color }, + + { "theme.shadow.color", COLOR, &t.shadow.color }, + { "theme.shadow.y", UINT, &t.shadow.y }, + { "theme.shadow.x", UINT, &t.shadow.x }, + + { "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 }, + + { "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.namesepcolor", COLOR, &t.menu.namesepcolor}, + { "theme.menu.descsepcolor", COLOR, &t.menu.descsepcolor}, + { "theme.menu.f_shortcutcolor", COLOR, &t.menu.f_shortcutcolor}, + { "theme.menu.shortcutcolor", COLOR, &t.menu.shortcutcolor}, + { "theme.menu.bottomdesccolor", COLOR, &t.menu.bottomdesccolor}, + + { "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}, + + { "theme.bar.f_color", COLOR, &t.bar.f_color}, + { "theme.bar.color", COLOR, &t.bar.color}, + + { "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.delimcolor", COLOR, &t.button.delimcolor}, + { "theme.button.f_delimcolor", COLOR, &t.button.f_delimcolor}, + { "theme.button.color", COLOR, &t.button.color}, + { "theme.button.f_color", COLOR, &t.button.f_color}, + { "theme.button.shortcutcolor", COLOR, &t.button.shortcutcolor}, + { "theme.button.f_shortcutcolor", COLOR, &t.button.f_shortcutcolor} +}; + +static char *color[8] = { + "black", + "red", + "green", + "yellow", + "blue", + "magenta", + "cyan", + "white" +}; + +int savetheme(const char *file, char *errbuf, const char *version) +{ + int i; + unsigned int flags; + enum bsddialog_color bg, fg; + time_t clock; + FILE *fp; + + if (bsddialog_get_theme(&t) != BSDDIALOG_OK) { + sprintf(errbuf, "Cannot save theme: %s", bsddialog_geterror()); + return (BSDDIALOG_ERROR); + } + + if(time(&clock) < 0) { + sprintf(errbuf, "Cannot save profile (getting current time)"); + return (BSDDIALOG_ERROR); + } + + if ((fp = fopen(file, "w")) == NULL) { + sprintf(errbuf, "Cannot open %s to save profile", file); + return (BSDDIALOG_ERROR); + } + + fprintf(fp, "### bsddialog theme - %s", ctime(&clock)); + fputs("# Refer to bsddialog(3) manual for theme.* properties\n", fp); + fprintf(fp, "version %s\n", version); + + for (i = 0; i < NPROPERTY; i++) { + switch (p[i].type) { + case CHAR: + fprintf(fp, "%s %c\n", p[i].name, *((char*)p[i].value)); + break; + case INT: + fprintf(fp, "%s %d\n", p[i].name, *((int*)p[i].value)); + break; + case UINT: + fprintf(fp, "%s %u\n", p[i].name, + *((unsigned int*)p[i].value)); + break; + case BOOL: + fprintf(fp, "%s %s\n", p[i].name, + *((bool*)p[i].value) ? "true" : "false"); + break; + case COLOR: + bsddialog_color_attrs(*(int*)p[i].value, &fg, &bg, + &flags); + fprintf(fp, "%s %s %s%s%s%s\n", + p[i].name, color[fg], color[bg], + flags & BSDDIALOG_BOLD ? " bold" : "", + flags & BSDDIALOG_REVERSE ? " reverse" : "", + flags & BSDDIALOG_UNDERLINE ? " underline" : ""); + break; + } + } + + fclose(fp); + + return (BSDDIALOG_OK); +} + +int loadtheme(const char *file, char *errbuf) +{ + bool boolvalue; + char charvalue, *value; + char line[BUFSIZ], name[BUFSIZ], c1[BUFSIZ], c2[BUFSIZ]; + int i, j, intvalue, flags; + unsigned int uintvalue; + enum bsddialog_color bg, fg; + FILE *fp; + + if (bsddialog_get_theme(&t) != BSDDIALOG_OK) { + sprintf(errbuf, "Cannot get current theme: %s", + bsddialog_geterror()); + return (BSDDIALOG_ERROR); + } + + if((fp = fopen(file, "r")) == NULL) { + sprintf(errbuf, "Cannot open theme \"%s\"", file); + return (BSDDIALOG_ERROR); + } + +#define RETURN_ERROR(name, error) do { \ + sprintf(errbuf, "%s for \"%s\"", error, name); \ + fclose(fp); \ + return (BSDDIALOG_ERROR); \ +} while (0) + + while(fgets(line, BUFSIZ, fp) != NULL) { + if(line[0] == '#' || line[0] == '\n') + continue; /* superfluous, only for efficiency */ + sscanf(line, "%s", name); + for (i = 0; i < NPROPERTY; i++) { + if (strcmp(name, p[i].name) == 0) { + value = &line[strlen(name)]; + break; + } + } + if (i >= NPROPERTY) { + if (strcmp(name, "version") == 0) + continue; + RETURN_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) + RETURN_ERROR(p[i].name, "Cannot get a char"); + *((int*)p[i].value) = charvalue; + break; + case INT: + if (sscanf(value, "%d", &intvalue) != 1) + RETURN_ERROR(p[i].name, "Cannot get a int"); + *((int*)p[i].value) = intvalue; + break; + case UINT: + if (sscanf(value, "%u", &uintvalue) != 1) + RETURN_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) + RETURN_ERROR(p[i].name, "Cannot get 2 colors"); + /* Foreground */ + for (j = 0; j < 8 ; j++) + if ((strstr(c1, color[j])) != NULL) + break; + if ((fg = j) > 7) + RETURN_ERROR(p[i].name, "Bad foreground"); + /* Background */ + for (j = 0; j < 8 ; j++) + if ((value = strstr(c2, color[j])) != NULL) + break; + if ((bg = j) > 7) + RETURN_ERROR(p[i].name, "Bad background"); + /* Flags */ + flags = 0; + if (strstr(value, "bold") != NULL) + flags |= BSDDIALOG_BOLD; + if (strstr(value, "reverse") != NULL) + flags |= BSDDIALOG_REVERSE; + if (strstr(value, "underline") != NULL) + flags |= BSDDIALOG_UNDERLINE; + *((int*)p[i].value) = bsddialog_color(fg, bg, flags); + break; + } + } + + fclose(fp); + + bsddialog_set_theme(&t); + + return (BSDDIALOG_OK); +} + +int bikeshed(struct bsddialog_conf *conf, char *errbuf) +{ + int margin, i; + int colors[8] = {0, 0, 0, 0 ,0 ,0 , 0, 0}; + enum bsddialog_color col[6]; + time_t clock; + + time(&clock); + srand(clock); + + /* theme */ + if (bsddialog_get_theme(&t) != BSDDIALOG_OK) + return (BSDDIALOG_ERROR); + + 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() % 2 == 0) ? 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_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.namesepcolor = bsddialog_color(col[1], col[5], 0); + t.menu.descsepcolor = bsddialog_color(col[1], 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.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, + t.button.leftdelim = '[', + t.button.rightdelim = ']', + 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)) + return (BSDDIALOG_ERROR); + + /* conf */ + conf->button.always_active = (rand() % 2 == 0) ? true : false; + 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; + } + + return (BSDDIALOG_OK); +} diff --git a/util_theme.h b/util_theme.h new file mode 100644 index 000000000000..fd9b32db1204 --- /dev/null +++ b/util_theme.h @@ -0,0 +1,35 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022 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_UTILITY_THEME_H_ +#define _BSDDIALOG_UTILITY_THEME_H_ + +int savetheme(const char *file, char *errbuf, const char *version); +int loadtheme(const char *file, char *errbuf); +int bikeshed(struct bsddialog_conf *conf, char *errbuf); + +#endif |