1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-10-18 02:19:39 +00:00

bsddialog: Import version 1.0

This commit is contained in:
Baptiste Daroussin 2023-10-04 17:43:22 +02:00
parent 9f24fda5a8
commit e56a937c3e
65 changed files with 6919 additions and 6216 deletions

2
.gitignore vendored
View File

@ -12,8 +12,10 @@ examples_library/calendar
examples_library/checklist
examples_library/datebox
examples_library/form
examples_library/gauge
examples_library/infobox
examples_library/menu
examples_library/mixedgauge
examples_library/mixedlist
examples_library/msgbox
examples_library/pause

194
CHANGELOG
View File

@ -1,3 +1,173 @@
2023-08-01 Version 1.0
Utility:
* add: comments to --save-theme output file.
* add: blink, halfbright, highlight to --save-theme and --load-theme.
* add: theme.menu.[f_]prefixcolor to --save-theme and --load-theme.
* add: --datebox-format <d/m/y|m/d/y|y/m/d> to set --datebox UI.
* add: --help-print-items (--help-status becomes alias).
* add: --text-escape (--colors becomes alias).
* add: new escapes \Zd, \Zk, \Zs, \ZD, \ZK, \ZS, for --text-escape.
* add: env NO_COLOR, to set blackwhite theme.
* add: $HOME/.bsddialog.conf startup theme file.
* add: env BSDDIALOG_THEMEFILE startup theme file.
* add: --left1-button <label>.
* add: --left2-button <label>.
* add: --left3-button <label>.
* add: --right1-button <label>.
* add: --right2-button <label>.
* add: --right3-button <label>.
* add: dynamic exit codes.
- add: --error-exit-code.
- add: --ok-exit-code.
- add: --cancel-exit-code.
- add: --help-exit-code.
- add: --extra-exit-code.
- add: --timeout-exit-code.
- add: --esc-exit-code.
- add: --left1-exit-code.
- add: --left2-exit-code.
- add: --left3-exit-code.
- add: --right1-exit-code.
- add: --right2-exit-code.
- add: --right3-exit-code.
- add: env BSDDIALOG_ERROR.
- add: env BSDDIALOG_OK.
- add: env BSDDIALOG_CANCEL.
- add: env BSDDIALOG_HELP.
- add: env BSDDIALOG_EXTRA.
- add: env BSDDIALOG_TIMEOUT.
- add: env BSDDIALOG_ESC.
- add: env BSDDIALOG_LEFT1.
- add: env BSDDIALOG_LEFT2.
- add: env BSDDIALOG_LEFT3.
- add: env BSDDIALOG_RIGHT1.
- add: env BSDDIALOG_RIGHT2.
- add: env BSDDIALOG_RIGHT3.
* add: undocumented envs for bsdconfig(8) compatibility.
- env BSDDIALOG_COMPATRC for use_shadow setting.
- env BSDDIALOG_ITEM_HELP to add/set exit code.
* change: rename themes --theme <3d|blackwhite|flat>.
* change: --no-names and --no-descriptions mutually exclusive (via lia).
* change: quote only checklist output items if necessary. Previously
also radiolist item.
* change: dialogs with user input print always values except with ERROR,
ESC, Cancel. Previously the situation was quite heterogeneous.
* improve: DIAGNOSTIC messages adding fmt string errors.
* improve: disable theme setting (opt and env) with no-color terminals.
* improve: menus on|off status (strcasecmp, diagnostic, real off check).
* improve: --bikeshed with button delimiter and --date-format.
* improve: --textbox accepts button options.
* improve: Forms with Help button.
- print "HELP" (like menus).
- accept --help-list-items.
- accept --help-print-name.
* fix: --load-theme attributes.
* fix: --clear-screen with --and-dialog.
* delete: --theme <bsddialog> (partially implemented).
* delete: --esc-return-cancel (replaced by new env and option).
* delete: --generic-button1 (replaced by --right1-button).
* delete: --generic-button2 (replaced by --right2-button).
* refactor: modularize in more files (main, cli, builders, theme).
Library:
* add: bsddialog_inmode().
* add: bsddialog_clear(y) for utility --clear-screen.
* add: bsddialog_refresh() for utility terminal mode options.
* add: conf.date.format="d/m/y"|"m/d/y"|"y/m/d" to customize
bsddialog_datebox() UI (boxes) with a date format.
* add: 'const char *end' to bsddialog_gauge().
* add: draw focus on the shortcut-key-selected button at exit.
* add: escapes for conf.text.highlight \Zd, \Zk, \Zs, \ZD, \ZK, \ZS.
* add: other theme flags.
- BSDDIALOG_BLINK.
- BSDDIALOG_HALFBRIGHT.
- BSDDIALOG_HIGHLIGHT.
* add: generic buttons.
- conf.button.left1_label, BSDDIALOG_LEFT1 return value.
- conf.button.left2_label, BSDDIALOG_LEFT2 return value.
- conf.button.left3_label, BSDDIALOG_LEFT3 return value.
- conf.button.right1.label, BSDDIALOG_RIGHT1 return value.
- conf.button.right2.label, BSDDIALOG_RIGHT2 return value.
- conf.button.right3.label, BSDDIALOG_RIGHT3 return value.
* add: unused bsddialog_menugroup.min_on for future features.
* add: theme.menu.f_prefixcolor and theme.menu.prefixcolor.
* improve: check (when possible) API pointers.
* improve: circolar buttons with left and right keys for msgbox, yesno,
menus, rangebox and pause.
* improve: bsddialog_textbox() handles conf.buttons.
* improve: bsddialog_datebox() a box change affects the others as well.
* improve: bsddialog_geterror() with fmt strings.
* change: API NULL strings handled like "", except gauge *sep and *end.
* change: menus and form less restrictive with text, hide text with
little screens (same behavior as other dialogs).
* change: mixedgauge BSDDIALOG_MG_BLANK does not draw minibar but prints
minilabel. The change allows mixedgauge to add sections. To restore
the previous behavior setting minilabel to "".
* change: check/set bsddialog_gauge() perc max 100.
* change: check/set bsddialog_mixedgauge() mainperc max 100.
* change: conf.menu.no_name and conf.menu.no_desc mutually exclusive.
* change: bsddialog_pause() sec -> *sec to know remaining time at exit.
* change: add *focusitem to bsddialog_form() like menus.
* change: "pointer" values are always set except when BSDDIALOG_ERROR
occurs. Examples *yy/*mm/*ss, rangebox *value.
- delete conf.menu.on_without_ok.
- delete conf.form.value_without_ok.
* rename: conf.text.highlight -> conf.text.escape.
* rename: theme.menu.namesepcolor -> theme.menu.sepnamecolor.
* rename: theme.menu.descsepcolor -> theme.menu.sepdesccolor.
* fix: bsddialog_pause() elevation bar after resize.
* fix: bsddialog_textbox() key '0'.
* fix: timebox.c checksize (boxes width).
* fix: extend menurows after shrink and enlarge.
* fix: menu pad and form pad "re-expansion" after shrink and enlarge.
* fix: shadow top-left corner (trick wresize() before wmove()).
* fix: increment bsddialog_total_progview size for more general use.
* delete: BSDDIALOG_THEME_BSDDIALOG (partially implemented).
* delete: conf.button.generic1_label (for new conf.button.right1_label).
* delete: BSDDIALOG_GENERIC1 return value (new BSDDIALOG_RIGHT1).
* delete: conf.button.generic2_label (for new conf.button.right2_label).
* delete: BSDDIALOG_GENERIC2 return value (new BSDDIALOG_RIGHT2).
* delete: bsddialog_clearterminal(), replaced by bsddialog_clear(y).
* refactor: internal implementation.
- add: internal structures to represent components.
- merge: (when possible) dialogs autosize.
- merge: (when possible) dialogs checksize.
- merge: for each dialog "build" with "update" -> <dialog>_redraw().
- merge: new_dialog() with update_dialog() -> draw_dialog().
- merge: infobox.c with messagebox.c (delete infobox.c)
- merge: bsddialog_datebox() + bsddialog_calendar() -> datebox.c
- change: flat and blackwhite real themes, 3d adapted from flat.
- improve: replace wrefresh() -> wnoutrefresh()/doupdate().
- improve: replace prefresh() -> pnoutrefresh()/doupdate().
- improve: menu split code to build private items.
- improve: form split code to build private items.
- delete: -Wno-implicit-fallthrough.
2023-06-12 Version 0.4.2
Library:
* fix: compile error with aarch64-gcc12 for "\Z[0-7]" check;
https://gitlab.com/alfix/bsddialog/-/issues/5.
* fix: BSDDIALOG_FIELDCURSOREND with multiple items
(warning aarch64-gcc12).
2023-01-02 Version 0.4.1
Utility:
* fix: default space separator menus output, except if --separator " ".
* rename: GNUMakefile to GNUmakefile to simplify linux build.
Thanks to https://gitlab.com/alfix/bsddialog/-/merge_requests/2.
Library:
* fix: t.dialog.linelowercolor no bold-black, some terminal draws grey.
* fix: text wrapping (actual string length) with --colors.
* rename: GNUMakefile to GNUmakefile to simplify linux build.
Thanks to https://gitlab.com/alfix/bsddialog/-/merge_requests/2.
2022-09-24 Version 0.4
Utility:
@ -6,19 +176,19 @@
* add: --keep-tite as --alternate-screen alias.
* add: --and-dialog to build other dialogs.
* add: --and-widget as --and-dialog alias.
* add: --no-names (--no-tags becames its alias).
* add: --no-descriptions (--no-items becames its alias).
* add: --help-print-name (--help-tags becames its alias).
* add: --item-bottom-desc (--item-help becames its alias).
* add: --no-names (--no-tags becomes alias).
* add: --no-descriptions (--no-items becomes alias).
* add: --help-print-name (--help-tags becomes alias).
* add: --item-bottom-desc (--item-help becomes alias).
* add: --cr-wrap (was partially implemented) to keep '\n' with "\n".
* add: --text-unchanged to avoid default modification.
* add: --tab-escape enables "\t" in text.
* add: --tab-escape to enable "\t" in text.
* add: --clear-screen to clear the screen.
* add: --clear-dialog to clear the dialog (was --clear).
* add: --calendar dialog to select a date.
* add: DIAGNOSTICS messages for bad arguments number.
* add: DIAGNOSTICS messages for missing and unexpected options.
* change: --clear becames alias for --clear-screen.
* change: --clear becomes alias for --clear-screen.
* change: --print-maxsize format output.
* change: --menu, --radiolist, --checklist and --treeview output.
- no printed items with Cancel or ESC.
@ -41,9 +211,9 @@
* add: bsddialog_menu() SPACE key (equivalent to ENTER).
* add: bsddialog_calendar() to select a date.
* change: rename enum bsddialog_grouptype -> enum bsddialog_menutype.
* change: fixed-menurows becames at most menurows (depending on text).
* change: fixed-rows becames at most rows, min(rows, screenH - shadow).
* change: fixed-cols becames at most cols, min(cols, screenW - shadow).
* change: fixed-menurows becomes at most menurows (depending on text).
* change: fixed-rows becomes at most rows, min(rows, screenH - shadow).
* change: fixed-cols becomes at most cols, min(cols, screenW - shadow).
* delete: undocumented internal bsddialog_menuitem.depth factor (was 2).
@ -55,9 +225,9 @@
* add: --load-theme to read and set a custom theme at runtime.
* add: --save-theme to save current theme.
* add: --bikeshed for random settings.
* add: --switch-buttons enables focus switching: buttons / input
components. Available for: --form, --inputbox, --mixedform,
--passwordform, --passwordbox, --timebox and --datebox.
* add: --switch-buttons to enable buttons/input widgets focus switching.
Available for: --form, --inputbox, --mixedform, --passwordform,
--passwordbox, --timebox and --datebox.
* change: rename --esc-cancelvalue to --esc-return-cancel.
* change: form field value is printed like multibyte charachter string,
previously widechar string.

View File

@ -1,28 +0,0 @@
# PUBLIC DOMAIN - NO WARRANTY, see:
# <http://creativecommons.org/publicdomain/zero/1.0/>
#
# Written in 2021 by Alfonso Sabato Siciliano
OUTPUT= bsddialog
SOURCES= bsddialog.c util_theme.c
OBJECTS= $(SOURCES:.c=.o)
LIBPATH= ./lib
LIBBSDDIALOG= $(LIBPATH)/libbsddialog.so
CFLAGS= -Wall -Wextra -I$(LIBPATH)
LDFLAGS= -ltinfo -Wl,-rpath=$(LIBPATH) -L$(LIBPATH) -lbsddialog
RM = rm -f
all : $(OUTPUT)
$(OUTPUT): $(LIBBSDDIALOG) $(OBJECTS)
$(CC) $^ -o $@ $(LDFLAGS)
${LIBBSDDIALOG}:
make -C ${LIBPATH} -f GNUMakefile
%.o: %.c $(LIBBSDDIALOG)
$(CC) $(CFLAGS) -c $<
clean:
make -C ${LIBPATH} -f GNUMakefile clean
$(RM) $(OUTPUT) *.o *~

View File

@ -1,6 +1,6 @@
BSD 2-Clause License
Copyright (c) 2021-2022, Alfonso Sabato Siciliano
Copyright (c) 2021-2023, Alfonso Sabato Siciliano
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View File

@ -1,50 +1,38 @@
# PUBLIC DOMAIN - NO WARRANTY, see:
# <http://creativecommons.org/publicdomain/zero/1.0/>
#
# Written in 2021 by Alfonso Sabato Siciliano
# Written in 2023 by Alfonso Sabato Siciliano
OUTPUT= bsddialog
SOURCES= bsddialog.c util_theme.c
OBJECTS= ${SOURCES:.c=.o}
LIBPATH= ${.CURDIR}/lib
LIBBSDDIALOG= ${LIBPATH}/libbsddialog.so
OUTPUT = bsddialog
export VERSION=1.0
.CURDIR ?= ${CURDIR}
LIBPATH = ${.CURDIR}/lib
LIBBSDDIALOG = ${LIBPATH}/libbsddialog.so
UTILITYPATH = ${.CURDIR}/utility
CFLAGS+= -I${LIBPATH} -std=gnu99 -Wall -Wextra -Werror
# `make -DDEBUG`
.if defined(DEBUG)
CFLAGS= -g -Wall -I${LIBPATH}
LIBDEBUG= -DDEBUG
.endif
LDFLAGS+= -ltinfow -Wl,-rpath=${LIBPATH} -L${LIBPATH} -lbsddialog
BINDIR= /usr/local/bin
MAN= ${OUTPUT}.1
GZIP= gzip -cn
MANDIR= /usr/local/share/man/man1
INSTALL= install
RM= rm -f
LN = ln -s -f
### cli options ###
# port/pkg Makefile: 'MAKE_ARGS = -DNORPATH'
NORPATH ?=
export DISABLERPATH=${NORPATH}
# `make -DDEBUG`
# `gmake DEBUG=1`
DEBUG ?=
export ENABLEDEBUG=${DEBUG}
all : ${OUTPUT}
${OUTPUT}: ${LIBBSDDIALOG} ${OBJECTS}
${CC} ${LDFLAGS} ${OBJECTS} -o ${.PREFIX}
${OUTPUT}: ${LIBBSDDIALOG}
${MAKE} -C ${UTILITYPATH} LIBPATH=${LIBPATH}
${LN} ${UTILITYPATH}/${OUTPUT} ${.CURDIR}/${OUTPUT}
${LIBBSDDIALOG}:
make -C ${LIBPATH} ${LIBDEBUG}
.c.o:
${CC} ${CFLAGS} -c ${.IMPSRC} -o ${.TARGET}
install:
${INSTALL} -s -m 555 ${OUTPUT} ${BINDIR}
${GZIP} ${MAN} > ${MAN}.gz
${INSTALL} -m 444 ${MAN}.gz ${MANDIR}
unistall:
${RM} ${BINDIR}/${OUTPUT}
${RM} ${MANDIR}/${MAN}.gz
${MAKE} -C ${LIBPATH}
clean:
make -C ${LIBPATH} clean
${RM} ${OUTPUT} *.o *~ *.core ${MAN}.gz
${MAKE} -C ${LIBPATH} clean
${MAKE} -C ${UTILITYPATH} clean
${RM} ${OUTPUT} *.core

View File

@ -1,13 +1,17 @@
# BSDDialog 0.4
# BSDDialog 1.0
This project provides **bsddialog** and **libbsddialog**, an utility
and a library to build scripts and tools with TUI dialogs and widgets.
## Demo
[Screenshots](https://www.flickr.com/photos/alfonsosiciliano/albums/72157720215006074).
## Getting Started
FreeBSD:
FreeBSD and Linux:
```
% git clone https://gitlab.com/alfix/bsddialog.git
@ -16,15 +20,6 @@ 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:
![screenshot](screenshot.png)
@ -60,6 +55,7 @@ in the _Public Domain_ to build new projects:
```
% sh ./examples_utility/calendar.sh
% sh ./examples_utility/checklist.sh
% sh ./examples_utility/datebox.sh
% sh ./examples_utility/form.sh
% sh ./examples_utility/gauge.sh
% sh ./examples_utility/infobox.sh
@ -72,6 +68,7 @@ in the _Public Domain_ to build new projects:
% sh ./examples_utility/passwordform.sh
% sh ./examples_utility/pause.sh
% sh ./examples_utility/radiolist.sh
% sh ./examples_utility/rangebox.sh
% sh ./examples_utility/timebox.sh
% sh ./examples_utility/yesno.sh
```
@ -100,8 +97,10 @@ in the _Public Domain_ to build new projects:
% ./checklist
% ./datebox
% ./form
% ./gauge
% ./infobox
% ./menu
% ./mixedgauge
% ./mixedlist
% ./msgbox
% ./pause
@ -114,7 +113,19 @@ in the _Public Domain_ to build new projects:
## TODO and Ideas
- menubar feature
- key callback
- Right-To-Left text
- some terminal does not hide the cursor, move it bottom-right before to getch.
- refactor backtitle: multiline, conf.backtitle, WINDOW \*dialog.backtitle.
- refactor bottomdesc: WINDOW \*dialog.bottomdesc -> fix expandig screen.
- accessibility https://wiki.freebsd.org/Accessibility/Wishlist/Base
- add bool conf.menu.depthlines.
- implement custom getopt\_long().
- refactor/redesign gauge().
- improve grey lines expanding terminal (maybe redrawwin() in hide\_dialog()).
- more restrictive strtol() and strtoul().
- implement global buttons handler.
- add/move external tutorial.
- implement menutype.min_on.

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,6 @@
#include <bsddialog.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
int main()
@ -31,25 +30,16 @@ int main()
printf("Error: %s\n", bsddialog_geterror());
return (1);
}
bsddialog_initconf(&conf);
conf.title = "calendar";
output = bsddialog_calendar(&conf, "Example", 18, 40, &yy, &mm, &dd);
bsddialog_end();
switch (output) {
case BSDDIALOG_OK:
printf("Date: %u/%u/%u", yy, mm, dd);
break;
case BSDDIALOG_CANCEL:
printf("Cancel");
break;
case BSDDIALOG_ERROR:
printf("Error: %s", bsddialog_geterror());
break;
if (output == BSDDIALOG_ERROR) {
printf("Error: %s\n", bsddialog_geterror());
return (1);
}
printf("\n");
return (output);
printf("Date: %u/%u/%u\n", yy, mm, dd);
return (0);
}

View File

@ -10,7 +10,6 @@
#include <bsddialog.h>
#include <stdio.h>
#include <string.h>
int main()
{
@ -28,27 +27,19 @@ int main()
printf("Error: %s\n", bsddialog_geterror());
return (1);
}
bsddialog_initconf(&conf);
conf.title = "checklist";
output = bsddialog_checklist(&conf, "Example", 15, 30, 5, 5, items,
NULL);
bsddialog_end();
if (output == BSDDIALOG_ERROR) {
printf("Error: %s\n", bsddialog_geterror());
return (1);
}
if (output == BSDDIALOG_CANCEL) {
printf("Cancel\n");
return (0);
}
printf("Checklist:\n");
for (i = 0; i < 5; i++)
printf(" [%c] %s\n", items[i].on ? 'X' : ' ', items[i].name);
return (output);
return (0);
}

View File

@ -10,7 +10,7 @@
libpath=../lib
examples="menu checklist radiolist mixedlist theme infobox yesno msgbox \
datebox form timebox rangebox pause calendar"
datebox form timebox rangebox pause calendar gauge mixedgauge"
rm -f $examples

View File

@ -10,7 +10,6 @@
#include <bsddialog.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
int main()
@ -31,25 +30,15 @@ int main()
printf("Error: %s\n", bsddialog_geterror());
return (1);
}
bsddialog_initconf(&conf);
conf.title = "datebox";
output = bsddialog_datebox(&conf, "Example", 9, 35, &yy, &mm, &dd);
bsddialog_end();
switch (output) {
case BSDDIALOG_OK:
printf("Date: %u/%u/%u", yy, mm, dd);
break;
case BSDDIALOG_CANCEL:
printf("Cancel");
break;
case BSDDIALOG_ERROR:
printf("Error: %s", bsddialog_geterror());
break;
if (output == BSDDIALOG_ERROR) {
printf("Error: %s\n", bsddialog_geterror());
return (1);
}
printf("\n");
printf("Date: %u/%u/%u\n", yy, mm, dd);
return (output);
return (0);
}

View File

@ -12,7 +12,6 @@
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define H BSDDIALOG_FIELDHIDDEN
#define RO BSDDIALOG_FIELDREADONLY
@ -37,23 +36,17 @@ int main()
bsddialog_initconf(&conf);
conf.title = "form";
conf.form.securech = '*';
output = bsddialog_form(&conf, "Example", 10, 50, 3, 3, items);
output = bsddialog_form(&conf, "Example", 10, 50, 3, 3, items, NULL);
bsddialog_end();
if (output == BSDDIALOG_ERROR) {
printf("Error: %s", bsddialog_geterror());
return (1);
}
if (output == BSDDIALOG_CANCEL) {
printf("Cancel\n");
return (0);
}
for (i = 0; i < 3; i++) {
printf("%s \"%s\"\n", items[i].label, items[i].value);
free(items[i].value);
}
return (output);
return (0);
}

57
examples_library/gauge.c Normal file
View File

@ -0,0 +1,57 @@
/*-
* SPDX-License-Identifier: CC0-1.0
*
* Written in 2023 by Alfonso Sabato Siciliano.
* To the extent possible under law, the author has dedicated all copyright
* and related and neighboring rights to this software to the public domain
* worldwide. This software is distributed without any warranty, see:
* <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
#include <bsddialog.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static void sender(int fd)
{
int i;
for (i = 1; i <= 10; i++) {
sleep(1);
dprintf(fd, "SEP\n");
dprintf(fd, "%d\n", i * 10);
dprintf(fd, "In Progress... [%d / 10]\n", i);
dprintf(fd, "SEP\n");
}
sleep(1);
dprintf(fd, "EOF\n");
}
int main()
{
int rv, fd[2];
struct bsddialog_conf conf;
/* add checks and sync */
pipe(fd);
if (fork() == 0) {
close(fd[0]);
sender(fd[1]);
exit (0);
}
close(fd[1]);
if (bsddialog_init() == BSDDIALOG_ERROR) {
printf("Error: %s\n", bsddialog_geterror());
return (1);
}
bsddialog_initconf(&conf);
conf.title = "gauge";
rv = bsddialog_gauge(&conf, "Example", 7, 30, 0, fd[0], "SEP", "EOF");
bsddialog_end();
if(rv == BSDDIALOG_ERROR)
printf("Error: %s\n", bsddialog_geterror());
return (0);
}

View File

@ -10,7 +10,6 @@
#include <bsddialog.h>
#include <stdio.h>
#include <string.h>
int main()
{
@ -21,18 +20,15 @@ int main()
printf("Error: %s\n", bsddialog_geterror());
return (1);
}
bsddialog_initconf(&conf);
conf.title = "infobox";
conf.sleep = 3;
output = bsddialog_infobox(&conf, "Example\n(3 seconds)", 7, 20);
bsddialog_end();
if (output == BSDDIALOG_ERROR) {
printf("Error: %s\n", bsddialog_geterror());
return (1);
}
return (output);
return (0);
}

View File

@ -10,7 +10,6 @@
#include <bsddialog.h>
#include <stdio.h>
#include <string.h>
int main()
{
@ -28,26 +27,18 @@ int main()
printf("Error: %s\n", bsddialog_geterror());
return (1);
}
bsddialog_initconf(&conf);
conf.title = "menu";
output = bsddialog_menu(&conf, "Example", 15, 30, 5, 5, items, NULL);
bsddialog_end();
if (output == BSDDIALOG_ERROR) {
printf("Error: %s\n", bsddialog_geterror());
return (1);
}
if (output == BSDDIALOG_CANCEL) {
printf("Cancel\n");
return (0);
}
printf("Menu:\n");
for (i = 0; i < 5; i++)
printf(" [%c] %s\n", items[i].on ? 'X' : ' ', items[i].name);
return (output);
return (0);
}

View File

@ -0,0 +1,79 @@
/*-
* SPDX-License-Identifier: CC0-1.0
*
* Written in 2023 by Alfonso Sabato Siciliano.
* To the extent possible under law, the author has dedicated all copyright
* and related and neighboring rights to this software to the public domain
* worldwide. This software is distributed without any warranty, see:
* <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
#include <bsddialog.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#define NMINIBAR 13
static const char *minilabels[NMINIBAR] = {
"Label 1",
"Label 2",
"Label 3",
"Label 4",
"Label 5",
"Label 6",
"Label 7",
"Label 8",
"Label 9",
"Label 10",
"Label 11",
"Label X",
"Label Y",
};
static int minipercs[NMINIBAR] = {
BSDDIALOG_MG_SUCCEEDED,
BSDDIALOG_MG_FAILED,
BSDDIALOG_MG_PASSED,
BSDDIALOG_MG_COMPLETED,
BSDDIALOG_MG_CHECKED,
BSDDIALOG_MG_DONE,
BSDDIALOG_MG_SKIPPED,
BSDDIALOG_MG_INPROGRESS,
BSDDIALOG_MG_BLANK,
BSDDIALOG_MG_NA,
BSDDIALOG_MG_PENDING,
67,
0,
};
static void exit_error()
{
if (bsddialog_inmode())
bsddialog_end();
printf("Error: %s\n", bsddialog_geterror());
exit (1);
}
int main()
{
int retval, i;
struct bsddialog_conf conf;
if (bsddialog_init() == BSDDIALOG_ERROR)
exit_error();
bsddialog_initconf(&conf);
conf.title = "mixedgauge";
for (i = 0; i <= 10; i++) {
minipercs[11] += 3;
minipercs[12] = i * 10;
retval= bsddialog_mixedgauge(&conf, "Example", 20, 40,
50 + i * 5, NMINIBAR, minilabels, minipercs);
if(retval == BSDDIALOG_ERROR)
exit_error();
sleep(1);
}
bsddialog_end();
return (0);
}

View File

@ -10,7 +10,6 @@
#include <bsddialog.h>
#include <stdio.h>
#include <string.h>
int main()
{
@ -40,34 +39,26 @@ int main()
{ "", false, 0, "Name 5", "Desc 5", "Bottom Desc 5" }
};
struct bsddialog_menugroup group[4] = {
{ BSDDIALOG_SEPARATOR, 1, sep1 },
{ BSDDIALOG_CHECKLIST, 5, check },
{ BSDDIALOG_SEPARATOR, 2, sep2 },
{ BSDDIALOG_RADIOLIST, 5, radio }
{ BSDDIALOG_SEPARATOR, 1, sep1, 0 },
{ BSDDIALOG_CHECKLIST, 5, check, 0 },
{ BSDDIALOG_SEPARATOR, 2, sep2, 0 },
{ BSDDIALOG_RADIOLIST, 5, radio, 0 }
};
if (bsddialog_init() == BSDDIALOG_ERROR) {
printf("Error: %s\n", bsddialog_geterror());
return (1);
}
bsddialog_initconf(&conf);
conf.title = "mixedlist";
output = bsddialog_mixedlist(&conf, "Example", 20, 0, 13, 4, group,
NULL, NULL);
bsddialog_end();
if (output == BSDDIALOG_ERROR) {
printf("Error: %s\n", bsddialog_geterror());
return (1);
}
if (output == BSDDIALOG_CANCEL) {
printf("Cancel\n");
return (0);
}
printf("Mixedlist:\n");
for (i = 0; i < 4; i++) {
for (j = 0; j < group[i].nitems; j++) {
@ -83,5 +74,5 @@ int main()
}
}
return (output);
}
return (0);
}

View File

@ -10,7 +10,6 @@
#include <bsddialog.h>
#include <stdio.h>
#include <string.h>
int main()
{
@ -21,21 +20,19 @@ int main()
printf("Error: %s\n", bsddialog_geterror());
return (1);
}
bsddialog_initconf(&conf);
conf.title = "msgbox";
output = bsddialog_msgbox(&conf, "Example", 7, 20);
bsddialog_end();
switch (output) {
case BSDDIALOG_ERROR:
printf("Error %s\n", bsddialog_geterror());
break;
return (1);
case BSDDIALOG_OK:
printf("OK\n");
printf("[OK]\n");
break;
}
return (output);
return (0);
}

View File

@ -10,38 +10,37 @@
#include <bsddialog.h>
#include <stdio.h>
#include <string.h>
int main()
{
int output;
unsigned int sec;
struct bsddialog_conf conf;
if (bsddialog_init() == BSDDIALOG_ERROR) {
printf("Error: %s\n", bsddialog_geterror());
return (1);
}
bsddialog_initconf(&conf);
conf.title = "pause";
output = bsddialog_pause(&conf, "Example", 8, 50, 10);
sec = 10;
output = bsddialog_pause(&conf, "Example", 8, 50, &sec);
bsddialog_end();
switch (output) {
case BSDDIALOG_OK:
printf("OK\n");
break;
case BSDDIALOG_CANCEL:
printf("Cancel\n");
break;
case BSDDIALOG_ERROR:
printf("Error: %s\n", bsddialog_geterror());
return (1);
case BSDDIALOG_OK:
printf("[OK] remaining time: %u\n", sec);
break;
case BSDDIALOG_CANCEL:
printf("[Cancel] remaining time: %u\n", sec);
break;
case BSDDIALOG_TIMEOUT:
printf("Timeout\n");
break;
}
return (output);
}
return (0);
}

View File

@ -10,7 +10,6 @@
#include <bsddialog.h>
#include <stdio.h>
#include <string.h>
int main()
{
@ -28,26 +27,19 @@ int main()
printf("Error: %s\n", bsddialog_geterror());
return (1);
}
bsddialog_initconf(&conf);
conf.title = "radiolist";
output = bsddialog_radiolist(&conf, "Example", 15, 30, 5, 5, items, NULL);
output = bsddialog_radiolist(&conf, "Example", 15, 30, 5, 5, items,
NULL);
bsddialog_end();
if (output == BSDDIALOG_ERROR) {
printf("Error: %s\n", bsddialog_geterror());
return (1);
}
if (output == BSDDIALOG_CANCEL) {
printf("Cancel\n");
return (0);
}
printf("Radiolist:\n");
for (i = 0; i < 5; i++)
printf(" (%c) %s\n", items[i].on ? '*' : ' ', items[i].name);
return (output);
return (0);
}

View File

@ -10,7 +10,6 @@
#include <bsddialog.h>
#include <stdio.h>
#include <string.h>
int main()
{
@ -21,20 +20,16 @@ int main()
printf("Error: %s\n", bsddialog_geterror());
return (1);
}
bsddialog_initconf(&conf);
conf.title = "rangebox";
value = 5;
output = bsddialog_rangebox(&conf, "Example", 8, 50, 0, 10, &value);
bsddialog_end();
if (output == BSDDIALOG_ERROR) {
printf("Error: %s\n", bsddialog_geterror());
return (1);
}
printf("Value: %d\n", value);
return (output);
return (0);
}

View File

@ -11,64 +11,54 @@
#include <bsddialog.h>
#include <bsddialog_theme.h>
#include <stdio.h>
#include <string.h>
int main()
{
int output, focusitem;
struct bsddialog_conf conf;
enum bsddialog_default_theme theme;
struct bsddialog_menuitem items[5] = {
{"", false, 0, "Flat", "dialog-like",
"BSDDIALOG_THEME_FLAT" },
{"", false, 0, "Dialog", "dialog clone",
"BSDDIALOG_THEME_DIALOG" },
{"", false, 0, "BSDDialog", "new theme",
"BSDDIALOG_THEME_BSDDIALOG" },
{"", false, 0, "BlackWhite","black and white",
"BSDDIALOG_THEME_BLACKWHITE" },
{"", false, 0, "Quit", "Exit", "Quit or Cancel to exit" }
struct bsddialog_menuitem items[4] = {
{"", false, 0, "Flat", "default flat theme",
"enum bsddialog_default_theme BSDDIALOG_THEME_FLAT" },
{"", false, 0, "3D", "pseudo 3D theme",
"enum bsddialog_default_theme BSDDIALOG_THEME_3D" },
{"", false, 0, "BlackWhite","black and white theme",
"enum bsddialog_default_theme BSDDIALOG_THEME_BLACKWHITE" },
{"", false, 0, "Quit", "Exit", "Quit, Cancel or ESC to exit" }
};
if (bsddialog_init() == BSDDIALOG_ERROR) {
printf("Error: %s\n", bsddialog_geterror());
return (1);
}
bsddialog_initconf(&conf);
conf.ascii_lines = true;
bsddialog_backtitle(&conf, "Theme Example");
bsddialog_initconf(&conf);
conf.key.enable_esc = true;
conf.title = " Theme ";
focusitem = -1;
while (true) {
output = bsddialog_menu(&conf, "Choose theme", 15, 45, 5, 5,
output = bsddialog_menu(&conf, "Choose theme", 15, 45, 4, 4,
items, &focusitem);
if (output != BSDDIALOG_OK || items[4].on)
if (output != BSDDIALOG_OK || items[3].on)
break;
if (items[0].on) {
theme = BSDDIALOG_THEME_FLAT;
focusitem = 0;
}
else if (items[1].on) {
theme = BSDDIALOG_THEME_DIALOG;
} else if (items[1].on) {
theme = BSDDIALOG_THEME_3D;
focusitem = 1;
}
else if (items[2].on) {
theme = BSDDIALOG_THEME_BSDDIALOG;
} else if (items[2].on) {
theme = BSDDIALOG_THEME_BLACKWHITE;
focusitem = 2;
}
else if (items[3].on) {
theme = BSDDIALOG_THEME_BLACKWHITE;
focusitem = 3;
}
bsddialog_set_default_theme(theme);
}
bsddialog_end();
return (output);
return (0);
}

View File

@ -10,7 +10,6 @@
#include <bsddialog.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
int main()
@ -31,24 +30,15 @@ int main()
printf("Error: %s\n", bsddialog_geterror());
return (1);
}
bsddialog_initconf(&conf);
conf.title = "timebox";
output = bsddialog_timebox(&conf, "Example", 9, 35, &hh, &mm, &ss);
bsddialog_end();
switch (output) {
case BSDDIALOG_OK:
printf("Time: [%u:%u:%u]\n", hh, mm, ss);
break;
case BSDDIALOG_CANCEL:
printf("Cancel\n");
break;
case BSDDIALOG_ERROR:
if (output == BSDDIALOG_ERROR) {
printf("Error: %s\n", bsddialog_geterror());
break;
return (1);
}
printf("Time: %u:%u:%u\n", hh, mm, ss);
return (output);
return (0);
}

View File

@ -10,7 +10,6 @@
#include <bsddialog.h>
#include <stdio.h>
#include <string.h>
int main()
{
@ -21,24 +20,22 @@ int main()
printf("Error: %s\n", bsddialog_geterror());
return (1);
}
bsddialog_initconf(&conf);
conf.title = "yesno";
output = bsddialog_yesno(&conf, "Example", 7, 25);
bsddialog_end();
switch (output) {
case BSDDIALOG_ERROR:
printf("Error %s\n", bsddialog_geterror());
break;
return (1);
case BSDDIALOG_YES:
printf("YES\n");
printf("[YES]\n");
break;
case BSDDIALOG_NO:
printf("NO\n");
printf("[NO]\n");
break;
}
return (output);
return (0);
}

View File

@ -15,11 +15,11 @@
: ${BSDDIALOG_ESC=5}
ITEMS=$(./bsddialog --title " checklist " --checklist "Hello World!" 15 30 5 \
"Tag 1" "DESC 1 xyz" on \
"Tag 2" "DESC 2 xyz" off \
"Tag 3" "DESC 3 xyz" on \
"Tag 4" "DESC 4 xyz" off \
"Tag 5" "DESC 5 xyz" on \
"1 Name" "DESC 1 xyz" on \
"2 Name" "DESC 2 xyz" off \
"3 Name" "DESC 3 xyz" on \
"4 Name" "DESC 4 xyz" off \
"5 Name" "DESC 5 xyz" on \
3>&1 1>&2 2>&3 3>&-)
case $? in
@ -27,12 +27,12 @@ case $? in
exit 1
;;
$BSDDIALOG_ESC )
echo "[ESC] focus: $ITEMS"
echo "[ESC]"
;;
$BSDDIALOG_CANCEL )
echo "[Cancel] focus: $ITEMS"
echo "[Cancel]"
;;
$BSDDIALOG_OK )
echo "[OK] Selected: $ITEMS"
echo "[OK] $ITEMS"
;;
esac

34
examples_utility/datebox.sh Executable file
View File

@ -0,0 +1,34 @@
#!/bin/sh
#-
# SPDX-License-Identifier: CC0-1.0
#
# Written in 2023 by Alfonso Sabato Siciliano.
#
# To the extent possible under law, the author has dedicated all copyright
# and related and neighboring rights to this software to the public domain
# worldwide. This software is distributed without any warranty, see:
# <http://creativecommons.org/publicdomain/zero/1.0/>.
: ${BSDDIALOG_ERROR=255}
: ${BSDDIALOG_OK=0}
: ${BSDDIALOG_CANCEL=1}
: ${BSDDIALOG_ESC=5}
DATE=$(./bsddialog --title " datebox " --date-format "%x" \
--datebox "Hello World!" 9 30 \
3>&1 1>&2 2>&3 3>&-)
case $? in
$BSDDIALOG_ERROR )
exit 1
;;
$BSDDIALOG_ESC )
echo "[ESC]"
;;
$BSDDIALOG_CANCEL )
echo "[Cancel]"
;;
$BSDDIALOG_OK )
echo "[OK] $DATE"
;;
esac

View File

@ -33,8 +33,6 @@ case $? in
echo "[Cancel]"
;;
$BSDDIALOG_OK )
echo "[OK]"
echo "[OK] $FORMS"
;;
esac
echo "$FORMS"

View File

@ -9,4 +9,4 @@
# worldwide. This software is distributed without any warranty, see:
# <http://creativecommons.org/publicdomain/zero/1.0/>.
./bsddialog --sleep 3 --title " infobox " --infobox "Hello World!\n3 secs" 6 20
./bsddialog --normal-screen --title " infobox " --infobox "Hello World!" 6 20

View File

@ -28,9 +28,6 @@ case $? in
echo "[Cancel]"
;;
$BSDDIALOG_OK )
echo "[OK]"
echo "[OK] $FORM"
;;
esac
echo "$FORM"

View File

@ -15,10 +15,11 @@
: ${BSDDIALOG_ESC=5}
ITEM=$(./bsddialog --title " menu " --menu "Hello World!" 15 30 5 \
"Tag 1" "DESC 1 xyz" \
"Tag 2" "DESC 2 xyz" \
"Tag 3" "DESC 3 xyz" \
"Tag 4" "DESC 4 xyz" \
"1 Name" "DESC 1 xyz" \
"2 Name" "DESC 2 xyz" \
"3 Name" "DESC 3 xyz" \
"4 Name" "DESC 4 xyz" \
"5 Name" "DESC 5 xyz" \
3>&1 1>&2 2>&3 3>&-)
case $? in
@ -26,10 +27,10 @@ case $? in
exit 1
;;
$BSDDIALOG_ESC )
echo "[ESC] $ITEM"
echo "[ESC]"
;;
$BSDDIALOG_CANCEL )
echo "[Cancel] $ITEM"
echo "[Cancel]"
;;
$BSDDIALOG_OK )
echo "[OK] $ITEM"

View File

@ -32,8 +32,6 @@ case $? in
echo "[Cancel]"
;;
$BSDDIALOG_OK )
echo "[OK]"
echo "[OK] $FORMS"
;;
esac
echo "$FORMS"

View File

@ -10,11 +10,11 @@
# <http://creativecommons.org/publicdomain/zero/1.0/>.
perc=0
mainperc=50
while [ $perc -le 100 ]
do
./bsddialog --sleep 1 --title " mixedgauge " \
--mixedgauge "Example..." 20 45 $perc \
"(Hidden)" " -9" \
--mixedgauge "Example..." 20 45 $mainperc \
"Label 1" " -1" \
"Label 2" " -2" \
"Label 3" " -3" \
@ -23,9 +23,11 @@ do
"Label 6" " -6" \
"Label 7" " -7" \
"Label 8" " -8" \
"Label 9" " -10" \
"Label 10" " -11" \
"Label 9" " -9" \
"Label 10" " -10" \
"Label 11" " -11" \
"Label X" $perc
perc=`expr $perc + 20`
mainperc=`expr $mainperc + 10`
done

View File

@ -29,8 +29,6 @@ case $? in
echo "[Cancel]"
;;
$BSDDIALOG_OK )
echo "[OK]"
echo "[OK] $FORM"
;;
esac
echo "$FORM"

View File

@ -34,8 +34,6 @@ case $? in
echo "[Cancel]"
;;
$BSDDIALOG_OK )
echo "[OK]"
echo "[OK] $FORMS"
;;
esac
echo "$FORMS"

View File

@ -15,11 +15,11 @@
: ${BSDDIALOG_ESC=5}
ITEM=$(./bsddialog --title " radiolist " --radiolist "Hello World!" 15 30 5 \
"Tag 1" "DESC 1 xyz" off \
"Tag 2" "DESC 2 xyz" off \
"Tag 3" "DESC 3 xyz" on \
"Tag 4" "DESC 4 xyz" off \
"Tag 5" "DESC 5 xyz" off \
"1 Name" "DESC 1 xyz" off \
"2 Name" "DESC 2 xyz" off \
"3 Name" "DESC 3 xyz" on \
"4 Name" "DESC 4 xyz" off \
"5 Name" "DESC 5 xyz" off \
3>&1 1>&2 2>&3 3>&-)
case $? in
@ -27,14 +27,12 @@ case $? in
exit 1
;;
$BSDDIALOG_ESC )
echo "[ESC] focus "
echo "[ESC]"
;;
$BSDDIALOG_CANCEL )
echo "[Cancel] focus "
echo "[Cancel]"
;;
$BSDDIALOG_OK )
echo "[OK]"
echo "[OK] $ITEM"
;;
esac
echo "$ITEM"

View File

@ -0,0 +1,33 @@
#!/bin/sh
#-
# SPDX-License-Identifier: CC0-1.0
#
# Written in 2023 by Alfonso Sabato Siciliano.
#
# To the extent possible under law, the author has dedicated all copyright
# and related and neighboring rights to this software to the public domain
# worldwide. This software is distributed without any warranty, see:
# <http://creativecommons.org/publicdomain/zero/1.0/>.
: ${BSDDIALOG_ERROR=255}
: ${BSDDIALOG_OK=0}
: ${BSDDIALOG_CANCEL=1}
: ${BSDDIALOG_ESC=5}
VALUE=$(./bsddialog --title " rangebox " --rangebox "Hello World!" 7 35 0 10 5 \
3>&1 1>&2 2>&3 3>&-)
case $? in
$BSDDIALOG_ERROR )
exit 1
;;
$BSDDIALOG_ESC )
echo "[ESC]"
;;
$BSDDIALOG_CANCEL )
echo "[Cancel]"
;;
$BSDDIALOG_OK )
echo "[OK] Value: $VALUE"
;;
esac

View File

@ -3,16 +3,19 @@
#
# Written in 2021 by Alfonso Sabato Siciliano
VERSION = 0.4
LIBRARY = bsddialog
LIBRARY_SO = lib${LIBRARY:=.so}
HEADERS = bsddialog.h bsddialog_theme.h bsddialog_progressview.h
SOURCES = barbox.c calendarbox.c formbox.c infobox.c libbsddialog.c \
lib_util.c menubox.c messagebox.c textbox.c theme.c timebox.c
SOURCES = barbox.c datebox.c formbox.c libbsddialog.c lib_util.c \
menubox.c messagebox.c textbox.c theme.c timebox.c
OBJECTS = $(SOURCES:.c=.o)
CFLAGS = -D_XOPEN_SOURCE_EXTENDED -D_XOPEN_SOURCE -D_GNU_SOURCE -Wall -Wextra \
-Wno-implicit-fallthrough -Werror -fpic
LDFLAGS = -lncursesw -ltinfo
ifneq ($(ENABLEDEBUG),)
CFLAGS += -g
endif
CFLAGS += -D_XOPEN_SOURCE_EXTENDED -D_XOPEN_SOURCE -D_GNU_SOURCE \
-Wall -Wextra -Werror -fpic
LDFLAGS += -lncursesw -ltinfo
LIBFLAG = -shared
RM = rm -f

View File

@ -3,39 +3,27 @@
#
# Written in 2021 by Alfonso Sabato Siciliano
VERSION = 0.4
LIBRARY = bsddialog
LIBRARY_SO = lib${LIBRARY:=.so}
LIBRARY_A = lib${LIBRARY:=.a}
HEADERS = bsddialog.h bsddialog_theme.h bsddialog_progressview.h
SOURCES = barbox.c calendarbox.c formbox.c infobox.c libbsddialog.c \
lib_util.c menubox.c messagebox.c textbox.c theme.c timebox.c
SOURCES = barbox.c datebox.c formbox.c libbsddialog.c lib_util.c \
menubox.c messagebox.c textbox.c theme.c timebox.c
OBJECTS = ${SOURCES:.c=.o}
CFLAGS += -D_XOPEN_SOURCE_EXTENDED -fPIC -Wall -Wextra
.if defined(DEBUG)
CFLAGS += -g
.endif
CFLAGS += -D_XOPEN_SOURCE_EXTENDED -fPIC -Wall -Wextra -std=gnu99 \
-fstack-protector-strong
LDFLAGS += -fstack-protector-strong -shared -Wl,-x -Wl,--fatal-warnings \
-Wl,--warn-shared-textrel -Wl,-soname,${LIBRARY_SO}.${VERSION} \
-L/usr/lib -lncursesw -ltinfow
.if defined(DEBUG)
# `make -DDEBUG`
CFLAGS = -g -D_XOPEN_SOURCE_EXTENDED -fPIC -Wall -Wextra
.else
CFLAGS += -std=gnu99 -fstack-protector-strong
.endif
LOCALBASE = /usr/local
LN = ln -s -f
RM = rm -f
CP = cp
GZIP = gzip -cn
LDCONFIG = /sbin/ldconfig -m
MAN = ${OUTPUT}.3
GZIP = gzip -cn
MANDIR = ${LOCALBASE}/share/man/man3
INSTALL = install
RM = rm -f
all : man ${LIBRARY}
all : ${LIBRARY}
${LIBRARY}: ${LIBRARY_SO} ${LIBRARY_A}
@ -52,24 +40,5 @@ ${LIBRARY_A}: ${OBJECTS}
.c.o:
${CC} ${CFLAGS} -c ${.IMPSRC} -o ${.TARGET}
man:
${GZIP} ${LIBRARY}.3 > ${LIBRARY}.3.gz
clean:
${RM} ${LIBRARY_SO}* *.o *~ *.gz ${LIBRARY_A}
install:
${INSTALL} -m 644 ${HEADERS} ${LOCALBASE}/include
${INSTALL} -m 644 -s ${LIBRARY_SO}.${VERSION} ${LOCALBASE}/lib/
${INSTALL} -l rs ${LOCALBASE}/lib/${LIBRARY_SO}.${VERSION} ${LOCALBASE}/lib/${LIBRARY_SO}
${INSTALL} -m 644 ${LIBRARY_A} ${LOCALBASE}/lib
${LDCONFIG} ${LOCALBASE}/lib
${INSTALL} -m 644 ${LIBRARY}.3.gz ${MANDIR}
unistall:
${RM} ${LOCALBASE}/include/${LIBRARY}*.h
${RM} ${LOCALBASE}/lib/${LIBRARY_SO}
${RM} ${LOCALBASE}/lib/${LIBRARY_SO}.${VERSION}
${LDCONFIG} ${LOCALBASE}/lib
${RM} ${MANDIR}/${LIBRARY}.3.gz

View File

@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2022 Alfonso Sabato Siciliano
* Copyright (c) 2021-2023 Alfonso Sabato Siciliano
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -25,9 +25,6 @@
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <ctype.h>
#include <curses.h>
#include <stdlib.h>
#include <string.h>
@ -39,139 +36,138 @@
#include "bsddialog_theme.h"
#include "lib_util.h"
#define BARPADDING 2
#define MINBARLEN 15
#define MINBARWIDTH (2 + 2 * BARPADDING + MINBARLEN)
#define MINMGBARLEN 18
#define MINMGBARWIDTH (2 + 2 * BARPADDING + MINMGBARLEN)
#define BARPADDING 2 /* widget border | BARPADDING | box bar */
#define BOXBORDERS 2
#define MIN_WBAR 15
#define MIN_WBOX (BARPADDING + BOXBORDERS + MIN_WBAR + BARPADDING)
#define MIN_WMGBAR 18
#define MIN_WMGBOX (BARPADDING + BOXBORDERS + MIN_WMGBAR + BARPADDING)
#define HBOX 3
#define WBOX(d) ((d)->w - BORDERS - BARPADDING - BARPADDING)
#define WBAR(d) (WBOX(d) - BOXBORDERS)
bool bsddialog_interruptprogview;
bool bsddialog_abortprogview;
int bsddialog_total_progview;
long long int bsddialog_total_progview;
static void
draw_bar(WINDOW *win, int y, int x, int barlen, int perc, bool withlabel,
int label)
static const char states[12][14] = {
" Succeeded ", /* -1 */
" Failed ", /* -2 */
" Passed ", /* -3 */
" Completed ", /* -4 */
" Checked ", /* -5 */
" Done ", /* -6 */
" Skipped ", /* -7 */
" In Progress ", /* -8 */
"(blank) ", /* -9 */
" N/A ", /* -10 */
" Pending ", /* -11 */
" UNKNOWN ", /* < -11, no API */
};
struct bar {
bool toupdate;
WINDOW *win;
int y; /* bar y in win */
int x; /* bar x in win */
int w; /* width in win */
int perc; /* barlen = (w * perc) / 100 */
const char* fmt; /* format for label */
int label; /* rangebox and pause perc!=label */
};
static void draw_bar(struct bar *b)
{
int i, blue_x, color, stringlen;
char labelstr[128];
int barlen, xlabel;
chtype ch;
char label[128];
blue_x = perc > 0 ? (perc * barlen) / 100 : -1;
barlen = b->perc > 0 ? (b->perc * b->w) / 100 : 0;
wmove(win, y, x);
for (i = 0; i < barlen; i++) {
color = (i <= blue_x) ? t.bar.f_color : t.bar.color;
wattron(win, color);
waddch(win, ' ');
wattroff(win, color);
}
ch = ' ' | t.bar.f_color;
mvwhline(b->win, b->y, b->x, ch, barlen);
ch = ' ' | t.bar.color;
mvwhline(b->win, b->y, b->x + barlen, ch, b->w - barlen);
if (withlabel)
sprintf(labelstr, "%d", label);
else
sprintf(labelstr, "%3d%%", perc);
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 ) ?
t.bar.color : t.bar.f_color;
wattron(win, color);
waddch(win, labelstr[i]);
wattroff(win, color);
}
sprintf(label, b->fmt, b->label);
xlabel = b->x + b->w/2 - (int)strlen(label)/2; /* 1-byte-char string */
wattron(b->win, t.bar.color); /* x+barlen < xlabel */
mvwaddstr(b->win, b->y, xlabel, label);
wattroff(b->win, t.bar.color);
wattron(b->win, t.bar.f_color); /* x+barlen >= xlabel */
mvwaddnstr(b->win, b->y, xlabel, label, MAX((b->x+barlen) - xlabel, 0));
wattroff(b->win, t.bar.f_color);
if (b->toupdate)
wnoutrefresh(b->win);
b->toupdate = false;
}
static int
bar_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w,
const char *text, struct buttons *bs)
static void update_barbox(struct dialog *d, struct bar *b, bool buttons)
{
int htext, wtext;
int y;
if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) {
if (text_size(conf, rows, cols, text, bs, 3, MINBARWIDTH,
&htext, &wtext) != 0)
return (BSDDIALOG_ERROR);
}
if (cols == BSDDIALOG_AUTOSIZE)
*w = widget_min_width(conf, wtext, MINBARWIDTH, bs);
if (rows == BSDDIALOG_AUTOSIZE)
*h = widget_min_height(conf, htext, 3 /* bar */, bs != NULL);
return (0);
}
static int
bar_checksize(int rows, int cols, struct buttons *bs)
{
int minheight, minwidth;
minwidth = 0;
if (bs != NULL) /* gauge has not buttons */
minwidth = buttons_min_width(*bs);
minwidth = MAX(minwidth, MINBARWIDTH);
minwidth += VBORDERS;
if (cols < minwidth)
RETURN_ERROR("Few cols to draw bar and/or buttons");
minheight = HBORDERS + 3;
if (bs != NULL)
minheight += 2;
if (rows < minheight)
RETURN_ERROR("Few rows to draw bar");
return (0);
y = d->y + d->h - BORDER - HBOX;
if (buttons)
y -= HBUTTONS;
update_box(d->conf, b->win, y, d->x + BORDER + BARPADDING, HBOX,
WBOX(d), RAISED);
}
int
bsddialog_gauge(struct bsddialog_conf *conf, const char *text, int rows,
int cols, unsigned int perc, int fd, const char *sep)
int cols, unsigned int perc, int fd, const char *sep, const char *end)
{
bool mainloop;
int y, x, h, w, fd2;
int fd2;
FILE *input;
WINDOW *widget, *textpad, *bar, *shadow;
char inputbuf[2048], ntext[2048], *pntext;
struct bar b;
struct dialog d;
if (set_widget_size(conf, rows, cols, &h, &w) != 0)
if (prepare_dialog(conf, text, rows, cols, &d) != 0)
return (BSDDIALOG_ERROR);
if (bar_autosize(conf, rows, cols, &h, &w, text, NULL) != 0)
return (BSDDIALOG_ERROR);
if (bar_checksize(h, w, NULL) != 0)
return (BSDDIALOG_ERROR);
if (set_widget_position(conf, &y, &x, h, w) != 0)
return (BSDDIALOG_ERROR);
if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, NULL,
false) != 0)
return (BSDDIALOG_ERROR);
bar = new_boxed_window(conf, y+h-4, x+3, 3, w-6, RAISED);
if ((b.win = newwin(1, 1, 1, 1)) == NULL)
RETURN_ERROR("Cannot build WINDOW bar");
b.y = b.x = 1;
b.fmt = "%3d%%";
input = NULL;
if (fd >= 0) {
CHECK_PTR(sep);
CHECK_PTR(end);
fd2 = dup(fd);
if ((input = fdopen(fd2, "r")) == NULL)
RETURN_ERROR("Cannot build FILE* from fd");
RETURN_FMTERROR("Cannot build FILE* from fd %d", fd);
}
perc = MIN(perc, 100);
mainloop = true;
while (mainloop) {
wrefresh(widget);
prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-4,
x+w-1-TEXTHMARGIN);
draw_borders(conf, bar, 3, w-6, RAISED);
draw_bar(bar, 1, 1, w-8, perc, false, -1 /*unused*/);
wrefresh(bar);
if (d.built) {
hide_dialog(&d);
refresh(); /* Important for decreasing screen */
}
if (dialog_size_position(&d, HBOX, MIN_WBOX, NULL) != 0)
return (BSDDIALOG_ERROR);
if (draw_dialog(&d))
return (BSDDIALOG_ERROR);
if (d.built)
refresh(); /* fix grey lines expanding screen */
TEXTPAD(&d, HBOX);
update_barbox(&d, &b, false);
b.w = WBAR(&d);
b.perc = b.label = perc;
b.toupdate = true;
draw_bar(&b);
doupdate();
if (input == NULL) /* that is fd < 0 */
break;
while (true) {
fscanf(input, "%s", inputbuf);
if (strcmp(inputbuf,"EOF") == 0) {
if (strcmp(inputbuf, end) == 0) {
mainloop = false;
break;
}
@ -181,12 +177,12 @@ bsddialog_gauge(struct bsddialog_conf *conf, const char *text, int rows,
if (mainloop == false)
break;
fscanf(input, "%d", &perc);
perc = perc > 100 ? 100 : perc;
perc = MIN(perc, 100);
pntext = &ntext[0];
ntext[0] = '\0';
while (true) {
fscanf(input, "%s", inputbuf);
if (strcmp(inputbuf,"EOF") == 0) {
if (strcmp(inputbuf, end) == 0) {
mainloop = false;
break;
}
@ -198,15 +194,13 @@ bsddialog_gauge(struct bsddialog_conf *conf, const char *text, int rows,
pntext++;
}
pntext[0] = '\0';
if (update_dialog(conf, shadow, widget, y, x, h, w, textpad,
ntext, NULL, false) != 0)
return (BSDDIALOG_ERROR);
d.text = ntext;
}
if (input != NULL)
fclose(input);
delwin(bar);
end_dialog(conf, shadow, widget, textpad);
delwin(b.win);
end_dialog(&d);
return (BSDDIALOG_OK);
}
@ -217,120 +211,95 @@ 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, retval, miniperc, y, x, h, w, ypad, max_minbarlen;
int htextpad, htext, wtext;
int colorperc, red, green;
WINDOW *widget, *textpad, *bar, *shadow;
char states[12][14] = {
" Succeeded ", /* -1 */
" Failed ", /* -2 */
" Passed ", /* -3 */
" Completed ", /* -4 */
" Checked ", /* -5 */
" Done ", /* -6 */
" Skipped ", /* -7 */
" In Progress ", /* -8 */
"(blank) ", /* -9 */
" N/A ", /* -10 */
" Pending ", /* -11 */
" UNKNOWN ", /* < -11, no API */
};
int i, miniperc, max_minibarlen;
int ystext, htext;
int minicolor, red, green;
struct bar b;
struct dialog d;
CHECK_ARRAY(nminibars, minilabels);
CHECK_ARRAY(nminibars, minipercs);
red = bsddialog_color(BSDDIALOG_WHITE,BSDDIALOG_RED, BSDDIALOG_BOLD);
green = bsddialog_color(BSDDIALOG_WHITE,BSDDIALOG_GREEN,BSDDIALOG_BOLD);
max_minbarlen = 0;
max_minibarlen = 0;
for (i = 0; i < (int)nminibars; i++)
max_minbarlen = MAX(max_minbarlen, (int)strcols(minilabels[i]));
max_minbarlen += 3 + 16; /* seps + [...] */
max_minbarlen = MAX(max_minbarlen, MINMGBARWIDTH); /* mainbar */
max_minibarlen = MAX(max_minibarlen,
(int)strcols(CHECK_STR(minilabels[i])));
max_minibarlen += 3 + 16; /* seps + [...] */
max_minibarlen = MAX(max_minibarlen, MIN_WMGBOX); /* mainbar */
if (set_widget_size(conf, rows, cols, &h, &w) != 0)
if (prepare_dialog(conf, text, rows, cols, &d) != 0)
return (BSDDIALOG_ERROR);
/* mixedgauge autosize */
if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) {
if (text_size(conf, rows, cols, text, NULL, nminibars + 3,
max_minbarlen, &htext, &wtext) != 0)
return (BSDDIALOG_ERROR);
}
if (cols == BSDDIALOG_AUTOSIZE)
w = widget_min_width(conf, wtext, max_minbarlen, NULL);
if (rows == BSDDIALOG_AUTOSIZE)
h = widget_min_height(conf, htext, nminibars + 3, false);
/* mixedgauge checksize */
if (w < max_minbarlen + 2)
RETURN_ERROR("Few cols for this mixedgauge");
if (h < 5 + (int)nminibars)
RETURN_ERROR("Few rows for this mixedgauge");
if (set_widget_position(conf, &y, &x, h, w) != 0)
if (dialog_size_position(&d, nminibars + HBOX, max_minibarlen,
&htext) != 0)
return (BSDDIALOG_ERROR);
if (draw_dialog(&d) != 0)
return (BSDDIALOG_ERROR);
retval = new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text,
NULL, false);
if (retval == BSDDIALOG_ERROR)
return (retval);
/* mini bars */
b.win = d.widget;
b.x = 1 + d.w - 2 - 15;
b.w = 13;
b.fmt = "%3d%%";
b.toupdate = false;
for (i = 0; i < (int)nminibars; i++) {
miniperc = minipercs[i];
/* label */
if (color && miniperc >= 0)
wattron(d.widget, A_BOLD);
mvwaddstr(d.widget, i+1, 2, CHECK_STR(minilabels[i]));
if (color && miniperc >= 0)
wattroff(d.widget, A_BOLD);
/* perc */
if (miniperc == BSDDIALOG_MG_BLANK)
continue;
/* label */
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)
mvwaddstr(widget, i+1, w-2-15, states[11]);
else if (miniperc < 0) {
mvwaddstr(widget, i+1, w-2-15, "[ ]");
colorperc = -1;
mvwaddstr(d.widget, i+1, d.w-2-15, "[ ]");
if (miniperc >= 0) {
b.y = i + 1;
b.perc = b.label = MIN(miniperc, 100);
draw_bar(&b);
} else { /* miniperc < 0 */
if (miniperc < BSDDIALOG_MG_PENDING)
miniperc = -12; /* UNKNOWN */
minicolor = t.dialog.color;
if (color && miniperc == BSDDIALOG_MG_FAILED)
colorperc = red;
if (color && miniperc == BSDDIALOG_MG_DONE)
colorperc = green;
if (colorperc != -1)
wattron(widget, colorperc);
minicolor = red;
else if (color && miniperc == BSDDIALOG_MG_DONE)
minicolor = green;
wattron(d.widget, minicolor);
miniperc = abs(miniperc + 1);
mvwaddstr(widget, i+1, 1+w-2-15, states[miniperc]);
if (colorperc != -1)
wattroff(widget, colorperc);
}
else { /* miniperc >= 0 */
if (miniperc > 100)
miniperc = 100;
mvwaddstr(widget, i+1, w-2-15, "[ ]");
draw_bar(widget, i+1, 1+w-2-15, 13, miniperc, false,
-1 /*unused*/);
mvwaddstr(d.widget, i+1, 1+d.w-2-15, states[miniperc]);
wattroff(d.widget, minicolor);
}
}
wnoutrefresh(d.widget);
wrefresh(widget);
getmaxyx(textpad, htextpad, i /* unused */);
ypad = y + h - 4 - htextpad;
ypad = ypad < y+(int)nminibars ? y+(int)nminibars : ypad;
prefresh(textpad, 0, 0, ypad, x+2, y+h-4, x+w-2);
/* text */
ystext = MAX(d.h - BORDERS - htext - HBOX, (int)nminibars);
rtextpad(&d, 0, 0, ystext, HBOX);
/* main bar */
bar = new_boxed_window(conf, y+h -4, x+3, 3, w-6, RAISED);
if ((b.win = newwin(1, 1, 1, 1)) == NULL)
RETURN_ERROR("Cannot build WINDOW bar");
update_barbox(&d, &b, false);
wattron(b.win, t.bar.color);
mvwaddstr(b.win, 0, 2, "Overall Progress");
wattroff(b.win, t.bar.color);
draw_bar(bar, 1, 1, w-8, mainperc, false, -1 /*unused*/);
b.y = b.x = 1;
b.w = WBAR(&d);
b.fmt = "%3d%%";
b.perc = b.label = MIN(mainperc, 100);
b.toupdate = true;
draw_bar(&b);
wattron(bar, t.bar.color);
mvwaddstr(bar, 0, 2, "Overall Progress");
wattroff(bar, t.bar.color);
doupdate();
/* getch(); to test with "alternate mode" */
wrefresh(bar);
/* getch(); alternate mode (port devel/ncurses) shows nothing */
delwin(bar);
end_dialog(conf, shadow, widget, textpad);
delwin(b.win);
end_dialog(&d);
return (BSDDIALOG_OK);
}
@ -414,7 +383,8 @@ bsddialog_progressview (struct bsddialog_conf *conf, const char *text, int rows,
minipercs[i] = BSDDIALOG_MG_DONE;
update = true;
i++;
} else if (minibar[i].status == BSDDIALOG_MG_FAILED || perc < 0) {
} else if (minibar[i].status == BSDDIALOG_MG_FAILED ||
perc < 0) {
minipercs[i] = BSDDIALOG_MG_FAILED;
update = true;
} else /* perc >= 0 */
@ -426,73 +396,73 @@ bsddialog_progressview (struct bsddialog_conf *conf, const char *text, int rows,
return (retval);
}
static int rangebox_redraw(struct dialog *d, struct bar *b, int *bigchange)
{
if (d->built) {
hide_dialog(d);
refresh(); /* Important for decreasing screen */
}
if (dialog_size_position(d, HBOX, MIN_WBOX, NULL) != 0)
return (BSDDIALOG_ERROR);
if (draw_dialog(d) != 0)
return (BSDDIALOG_ERROR);
if (d->built)
refresh(); /* Important to fix grey lines expanding screen */
TEXTPAD(d, HBOX + HBUTTONS);
b->w = WBAR(d);
*bigchange = MAX(1, b->w / 10);
update_barbox(d, b, true);
b->toupdate = true;
return (0);
}
int
bsddialog_rangebox(struct bsddialog_conf *conf, const char *text, int rows,
int cols, int min, int max, int *value)
{
bool loop, buttupdate, barupdate;
int y, x, h, w;
int currvalue, retval, sizebar, bigchange, positions;
bool loop;
int currvalue, retval, bigchange, positions;
wint_t input;
float perc;
WINDOW *widget, *textpad, *bar, *shadow;
struct buttons bs;
if (value == NULL)
RETURN_ERROR("*value cannot be NULL");
struct bar b;
struct dialog d;
CHECK_PTR(value);
if (min >= max)
RETURN_ERROR("min >= max");
RETURN_FMTERROR("min (%d) >= max (%d)", min, max);
if (*value < min)
RETURN_FMTERROR("value (%d) < min (%d)", *value, min);
if (*value > max)
RETURN_FMTERROR("value (%d) > max (%d)", *value, max);
currvalue = *value;
positions = max - min + 1;
get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL);
if (set_widget_size(conf, rows, cols, &h, &w) != 0)
if (prepare_dialog(conf, text, rows, cols, &d) != 0)
return (BSDDIALOG_ERROR);
if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0)
return (BSDDIALOG_ERROR);
if (bar_checksize(h, w, &bs) != 0)
return (BSDDIALOG_ERROR);
if (set_widget_position(conf, &y, &x, h, w) != 0)
set_buttons(&d, true, OK_LABEL, CANCEL_LABEL);
if ((b.win = newwin(1, 1, 1, 1)) == NULL)
RETURN_ERROR("Cannot build WINDOW bar");
b.y = b.x = 1;
b.fmt = "%d";
if (rangebox_redraw(&d, &b, &bigchange) != 0)
return (BSDDIALOG_ERROR);
if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs,
true) != 0)
return (BSDDIALOG_ERROR);
doupdate();
prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-7, x+w-1-TEXTHMARGIN);
sizebar = w - HBORDERS - (2 * BARPADDING) - 2;
bigchange = MAX(1, sizebar/10);
bar = new_boxed_window(conf, y + h - 6, x + 1 + BARPADDING, 3,
sizebar + 2, RAISED);
loop = buttupdate = barupdate = true;
loop = true;
while (loop) {
if (buttupdate) {
draw_buttons(widget, bs, true);
wrefresh(widget);
buttupdate = false;
if (b.toupdate) {
b.perc = ((float)(currvalue - min)*100) / (positions-1);
b.label = currvalue;
draw_bar(&b);
}
if (barupdate) {
perc = ((float)(currvalue - min)*100) / (positions-1);
draw_bar(bar, 1, 1, sizebar, perc, true, currvalue);
barupdate = false;
wrefresh(bar);
}
doupdate();
if (get_wch(&input) == ERR)
continue;
switch(input) {
case KEY_ENTER:
case 10: /* Enter */
retval = bs.value[bs.curr];
*value = currvalue;
retval = BUTTONVALUE(d.bs);
loop = false;
break;
case 27: /* Esc */
@ -502,159 +472,132 @@ bsddialog_rangebox(struct bsddialog_conf *conf, const char *text, int rows,
}
break;
case '\t': /* TAB */
bs.curr = (bs.curr + 1) % bs.nbuttons;
buttupdate = true;
case KEY_RIGHT:
d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons;
DRAW_BUTTONS(d);
break;
case KEY_LEFT:
if (bs.curr > 0) {
bs.curr--;
buttupdate = true;
}
break;
case KEY_RIGHT:
if (bs.curr < (int) bs.nbuttons - 1) {
bs.curr++;
buttupdate = true;
}
d.bs.curr--;
if (d.bs.curr < 0)
d.bs.curr = d.bs.nbuttons - 1;
DRAW_BUTTONS(d);
break;
case KEY_HOME:
currvalue = max;
barupdate = true;
b.toupdate = true;
break;
case KEY_END:
currvalue = min;
barupdate = true;
b.toupdate = true;
break;
case KEY_NPAGE:
currvalue -= bigchange;
if (currvalue < min)
currvalue = min;
barupdate = true;
b.toupdate = true;
break;
case KEY_PPAGE:
currvalue += bigchange;
if (currvalue > max)
currvalue = max;
barupdate = true;
b.toupdate = true;
break;
case KEY_UP:
if (currvalue < max) {
currvalue++;
barupdate = true;
b.toupdate = true;
}
break;
case KEY_DOWN:
if (currvalue > min) {
currvalue--;
barupdate = true;
b.toupdate = true;
}
break;
case KEY_F(1):
if (conf->key.f1_file == NULL &&
conf->key.f1_message == NULL)
break;
if (f1help(conf) != 0)
if (f1help_dialog(conf) != 0)
return (BSDDIALOG_ERROR);
/* No break, screen size can change */
if (rangebox_redraw(&d, &b, &bigchange) != 0)
return (BSDDIALOG_ERROR);
break;
case KEY_RESIZE:
/* Important for decreasing screen */
hide_widget(y, x, h, w, conf->shadow);
refresh();
if (set_widget_size(conf, rows, cols, &h, &w) != 0)
if (rangebox_redraw(&d, &b, &bigchange) != 0)
return (BSDDIALOG_ERROR);
if (bar_autosize(conf, rows, cols, &h, &w, text,
&bs) != 0)
return (BSDDIALOG_ERROR);
if (bar_checksize(h, w, &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)
return (BSDDIALOG_ERROR);
doupdate();
sizebar = w - HBORDERS - (2 * BARPADDING) - 2;
bigchange = MAX(1, sizebar/10);
wclear(bar);
mvwin(bar, y + h - 6, x + 1 + BARPADDING);
wresize(bar, 3, sizebar + 2);
draw_borders(conf, bar, 3, sizebar+2, RAISED);
prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-7,
x+w-1-TEXTHMARGIN);
barupdate = true;
break;
default:
if (shortcut_buttons(input, &bs)) {
retval = bs.value[bs.curr];
if (shortcut_buttons(input, &d.bs)) {
DRAW_BUTTONS(d);
doupdate();
retval = BUTTONVALUE(d.bs);
loop = false;
}
}
}
delwin(bar);
end_dialog(conf, shadow, widget, textpad);
*value = currvalue;
delwin(b.win);
end_dialog(&d);
return (retval);
}
static int pause_redraw(struct dialog *d, struct bar *b)
{
if (d->built) {
hide_dialog(d);
refresh(); /* Important for decreasing screen */
}
if (dialog_size_position(d, HBOX, MIN_WBOX, NULL) != 0)
return (BSDDIALOG_ERROR);
if (draw_dialog(d) != 0)
return (BSDDIALOG_ERROR);
if (d->built)
refresh(); /* Important to fix grey lines expanding screen */
TEXTPAD(d, HBOX + HBUTTONS);
b->w = WBAR(d);
update_barbox(d, b, true);
b->toupdate = true;
return (0);
}
int
bsddialog_pause(struct bsddialog_conf *conf, const char *text, int rows,
int cols, unsigned int sec)
int cols, unsigned int *seconds)
{
bool loop, buttupdate, barupdate;
int retval, y, x, h, w, tout, sizebar;
bool loop;
int retval, tout;
wint_t input;
float perc;
WINDOW *widget, *textpad, *bar, *shadow;
struct buttons bs;
struct bar b;
struct dialog d;
get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL);
if (set_widget_size(conf, rows, cols, &h, &w) != 0)
CHECK_PTR(seconds);
if (prepare_dialog(conf, text, rows, cols, &d) != 0)
return (BSDDIALOG_ERROR);
if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0)
return (BSDDIALOG_ERROR);
if (bar_checksize(h, w, &bs) != 0)
return (BSDDIALOG_ERROR);
if (set_widget_position(conf, &y, &x, h, w) != 0)
set_buttons(&d, true, OK_LABEL, CANCEL_LABEL);
if ((b.win = newwin(1, 1, 1, 1)) == NULL)
RETURN_ERROR("Cannot build WINDOW bar");
b.y = b.x = 1;
b.fmt = "%d";
if (pause_redraw(&d, &b) != 0)
return (BSDDIALOG_ERROR);
if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs,
true) != 0)
return (BSDDIALOG_ERROR);
doupdate();
prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-7, x+w-1-TEXTHMARGIN);
sizebar = w - HBORDERS - (2 * BARPADDING) - 2;
bar = new_boxed_window(conf, y + h - 6, x + 1 + BARPADDING, 3,
sizebar + 2, RAISED);
tout = sec;
tout = *seconds;
nodelay(stdscr, TRUE);
timeout(1000);
loop = buttupdate = barupdate = true;
loop = true;
while (loop) {
if (barupdate) {
perc = (float)tout * 100 / sec;
draw_bar(bar, 1, 1, sizebar, perc, true, tout);
barupdate = false;
wrefresh(bar);
if (b.toupdate) {
b.perc = (float)tout * 100 / *seconds;
b.label = tout;
draw_bar(&b);
}
if (buttupdate) {
draw_buttons(widget, bs, true);
wrefresh(widget);
buttupdate = false;
}
doupdate();
if (get_wch(&input) == ERR) { /* timeout */
tout--;
if (tout < 0) {
@ -662,14 +605,14 @@ bsddialog_pause(struct bsddialog_conf *conf, const char *text, int rows,
break;
}
else {
barupdate = true;
b.toupdate = true;
continue;
}
}
switch(input) {
case KEY_ENTER:
case 10: /* Enter */
retval = bs.value[bs.curr];
retval = BUTTONVALUE(d.bs);
loop = false;
break;
case 27: /* Esc */
@ -679,72 +622,44 @@ bsddialog_pause(struct bsddialog_conf *conf, const char *text, int rows,
}
break;
case '\t': /* TAB */
bs.curr = (bs.curr + 1) % bs.nbuttons;
buttupdate = true;
case KEY_RIGHT:
d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons;
DRAW_BUTTONS(d);
break;
case KEY_LEFT:
if (bs.curr > 0) {
bs.curr--;
buttupdate = true;
}
break;
case KEY_RIGHT:
if (bs.curr < (int) bs.nbuttons - 1) {
bs.curr++;
buttupdate = true;
}
d.bs.curr--;
if (d.bs.curr < 0)
d.bs.curr = d.bs.nbuttons - 1;
DRAW_BUTTONS(d);
break;
case KEY_F(1):
if (conf->key.f1_file == NULL &&
conf->key.f1_message == NULL)
break;
if (f1help(conf) != 0)
if (f1help_dialog(conf) != 0)
return (BSDDIALOG_ERROR);
/* No break, screen size can change */
if (pause_redraw(&d, &b) != 0)
return (BSDDIALOG_ERROR);
break;
case KEY_RESIZE:
/* Important for decreasing screen */
hide_widget(y, x, h, w, conf->shadow);
refresh();
if (set_widget_size(conf, rows, cols, &h, &w) != 0)
if (pause_redraw(&d, &b) != 0)
return (BSDDIALOG_ERROR);
if (bar_autosize(conf, rows, cols, &h, &w, text,
&bs) != 0)
return (BSDDIALOG_ERROR);
if (bar_checksize(h, w, &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)
return (BSDDIALOG_ERROR);
doupdate();
sizebar = w - HBORDERS - (2 * BARPADDING) - 2;
wclear(bar);
mvwin(bar, y + h - 6, x + 1 + BARPADDING);
wresize(bar, 3, sizebar + 2);
draw_borders(conf, bar, 3, sizebar+2, LOWERED);
prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-7,
x+w-1-TEXTHMARGIN);
barupdate = true;
break;
default:
if (shortcut_buttons(input, &bs)) {
retval = bs.value[bs.curr];
if (shortcut_buttons(input, &d.bs)) {
DRAW_BUTTONS(d);
doupdate();
retval = BUTTONVALUE(d.bs);
loop = false;
}
}
}
nodelay(stdscr, FALSE);
delwin(bar);
end_dialog(conf, shadow, widget, textpad);
*seconds = MAX(tout, 0);
delwin(b.win);
end_dialog(&d);
return (retval);
}
}

View File

@ -1,5 +1,5 @@
.\"
.\" Copyright (c) 2021-2022 Alfonso Sabato Siciliano
.\" Copyright (c) 2021-2023 Alfonso Sabato Siciliano
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
@ -22,13 +22,13 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd September 23, 2022
.Dd July 28, 2023
.Dt BSDDIALOG 3
.Os
.Sh NAME
.Nm bsddialog_backtitle ,
.Nm bsddialog_calendar ,
.Nm bsddialog_clearterminal ,
.Nm bsddialog_clear ,
.Nm bsddialog_color ,
.Nm bsddialog_color_attrs ,
.Nm bsddialog_checklist ,
@ -43,6 +43,7 @@
.Nm bsddialog_init ,
.Nm bsddialog_init_notheme ,
.Nm bsddialog_initconf ,
.Nm bsddialog_inmode ,
.Nm bsddialog_menu ,
.Nm bsddialog_mixedgauge ,
.Nm bsddialog_mixedlist ,
@ -50,6 +51,7 @@
.Nm bsddialog_pause ,
.Nm bsddialog_radiolist ,
.Nm bsddialog_rangebox ,
.Nm bsddialog_refresh ,
.Nm bsddialog_set_theme ,
.Nm bsddialog_set_default_theme ,
.Nm bsddialog_textbox ,
@ -68,9 +70,9 @@
.Fa "const char *text"
.Fa "int rows"
.Fa "int cols"
.Fa "unsigned int *yy"
.Fa "unsigned int *mm"
.Fa "unsigned int *dd"
.Fa "unsigned int *year"
.Fa "unsigned int *month"
.Fa "unsigned int *day"
.Fc
.Ft int
.Fo bsddialog_checklist
@ -83,17 +85,17 @@
.Fa "struct bsddialog_menuitem *items"
.Fa "int *focusitem"
.Fc
.Ft int
.Fn bsddialog_clearterminal "void"
.Ft void
.Fn bsddialog_clear "unsigned int y"
.Ft int
.Fo bsddialog_datebox
.Fa "struct bsddialog_conf *conf"
.Fa "const char *text"
.Fa "int rows"
.Fa "int cols"
.Fa "unsigned int *yy"
.Fa "unsigned int *mm"
.Fa "unsigned int *dd"
.Fa "unsigned int *year"
.Fa "unsigned int *month"
.Fa "unsigned int *day"
.Fc
.Ft int
.Fn bsddialog_end "void"
@ -106,6 +108,7 @@
.Fa "unsigned int formrows"
.Fa "unsigned int nitems"
.Fa "struct bsddialog_formitem *items"
.Fa "int *focusitem"
.Fc
.Ft int
.Fo bsddialog_gauge
@ -116,6 +119,7 @@
.Fa "unsigned int perc"
.Fa "int fd"
.Fa "const char *sep"
.Fa "const char *end"
.Fc
.Ft const char *
.Fn bsddialog_geterror "void"
@ -130,6 +134,8 @@
.Fn bsddialog_init "void"
.Ft int
.Fn bsddialog_init_notheme "void"
.Ft bool
.Fn bsddialog_inmode "void"
.Ft int
.Fn bsddialog_initconf "struct bsddialog_conf *conf"
.Ft int
@ -179,7 +185,7 @@
.Fa "const char *text"
.Fa "int rows"
.Fa "int cols"
.Fa "unsigned int seconds"
.Fa "unsigned int *seconds"
.Fc
.Ft int
.Fo bsddialog_radiolist
@ -202,6 +208,8 @@
.Fa "int max"
.Fa "int *value"
.Fc
.Ft void
.Fn bsddialog_refresh "void"
.Ft int
.Fo bsddialog_textbox
.Fa "struct bsddialog_conf *conf"
@ -251,8 +259,7 @@
.Sh DESCRIPTION
The
.Nm bsddialog
library provides an API to build Text User Interface dialogs and widgets: to
display messages, to get input and to inform about a computation status.
library provides an API to build Text User Interface dialogs and widgets.
.Pp
.Fn bsddialog_init
initializes the library, the only functions that can be called before is
@ -260,10 +267,7 @@ initializes the library, the only functions that can be called before is
described later.
After the initialization the input and output should be handled via the library
API.
.Fn bsddialog_end
restores the screen like before
.Fn bsddialog_init ,
then it is not possible to use the library functions.
.Pp
.Fn bsddialog_init_notheme
is equivalent to
.Fn bsddialog_init
@ -271,41 +275,64 @@ 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
.Dv BSDDIALOG_ERROR
returned value.
.Fn bsddialog_clearterminal
clears the screen.
.Fn bsddialog_end
restores the screen like before
.Fn bsddialog_init .
After the call is not possible to use the library functions.
.Pp
.Fn bsddialog_inmode
returns
.Dv true
after
.Fn bsddialog_init
or
.Fn bsddialog_init_notheme
and before
.Fn bsddialog_end ,
.Dv false
otherwise.
.Pp
.Fn bsddialog_backtitle
prints
.Fa backtitle
on the top of the screen, it is possible to set
on the top of the screen.
The function handles
.Fa conf.ascii_lines
and
.Fa conf.no_lines ;
.Fa conf
is described later.
.Fa conf.no_lines
described later.
.Pp
Each
.Fa char*
argument has to be a well terminated string, it can be a multibyte character
string depending on current locale, see
.Xr setlocale 3 .
.Fn bsddialog_error
returns a string to describe the last error.
The function should be called after a
.Dv BSDDIALOG_ERROR
returned value.
.Pp
.Fn bsddialog_clear
clears the screen from
.Fa y .
.Pp
.Fn bsddialog_refresh
useful to refresh the screen after a terminal mode change, see
.Xr terminfo 5 .
.Ss Dialogs
The dialogs have common arguments.
.Fa text
is a string printed inside the dialog.
Each
.Fa char*
parameter can be a multibyte character string depending on current locale, see
.Xr setlocale 3 .
.Fa rows
and
.Fa cols
are height and width, their value can be between 2 and the screen size,
are height and width, their value can be a fixed size,
.Dv BSDDIALOG_AUTOSIZE
or
.Dv BSDDIALOG_FULLSCREEN .
.Fa conf
is a struct to customize the dialog, it does not set global properties to the
library.
is a struct to customize the current dialog, it does not set global properties
to the library.
.Pp
.Bd -literal -offset indent -compact
struct bsddialog_conf {
@ -331,24 +358,28 @@ struct bsddialog_conf {
} key;
struct {
unsigned int cols_per_row;
bool highlight;
bool escape;
unsigned int tablen;
} text;
struct {
bool align_left;
bool no_desc;
bool no_name;
bool on_without_ok;
bool shortcut_buttons;
} menu;
struct {
char securech;
char *securembch;
bool value_wchar;
bool value_without_ok;
} form;
struct {
const char *format;
} date;
struct {
bool always_active;
const char *left1_label;
const char *left2_label;
const char *left3_label;
bool without_ok;
const char *ok_label;
bool with_extra;
@ -358,8 +389,9 @@ struct bsddialog_conf {
bool default_cancel;
bool with_help;
const char *help_label;
const char *generic1_label;
const char *generic2_label;
const char *right1_label;
const char *right2_label;
const char *right3_label;
const char *default_label;
} button;
};
@ -396,7 +428,7 @@ is
or
.Dv BSDDIALOG_FULLSCREEN .
.It Fa conf.bottomtitle
subtitle at the dialog bottom side.
dialog subtitle.
.It Fa conf.clear
hide the dialog at exit.
.It Fa conf.get_height
@ -414,36 +446,35 @@ draw shadow.
.It Fa conf.sleep
wait before to return, the value is in seconds.
.It Fa conf.title
title at the top dialog side.
dialog title.
.It Fa conf.y
vertical position, 0 is top screen size, can be
dialog vertical position, 0 is top screen, can be
.Dv BSDDIALOG_CENTER .
.It Fa conf.x
horizontal position, 0 is left screen side, can be
dialog horizontal position, 0 is left screen, can be
.Dv BSDDIALOG_CENTER .
.El
.Pp
.Bl -column -compact
.It Fa conf.key.enable_esc
enables
enable
.Dv ESC
key to close the dialog.
.It Fa conf.key.f1_file
file to open if F1 is pressed.
open a file in a textbox if F1 is pressed.
.It Fa conf.key.f1_message
message to display if F1 is pressed.
build a msgbox with message if F1 is pressed.
.El
.Pp
.Bl -column -compact
.It Fa conf.text.cols_per_row
Try to set the number of columns for a row of
.Fa text
with autosizing; default
with autosizing, default
.Dv 10 .
.It Fa conf.text.highlight
enables highlights for
.Fa text ,
properly the following sequences are considered escapes:
.It Fa conf.text.escape
enable escapes in
.Fa text :
.It Dq \eZ0
black.
.It Dq \eZ1
@ -460,14 +491,26 @@ magenta.
cyan.
.It Dq \eZ7
white.
.It Dq \eZr
reverse foreground and background.
.It Dq \eZR
disable reverse.
.It Dq \eZb
bold.
.It Dq \eZB
disable bold.
.It Dq \eZd
Half bright.
.It Dq \eZD
disable half bright.
.It Dq \eZk
Blink.
.It Dq \eZK
disable blinking.
.It Dq \eZr
reverse foreground and background.
.It Dq \eZR
disable reverse.
.It Dq \eZs
Highlight.
.It Dq \eZS
disable highlighting.
.It Dq \eZu
underline.
.It Dq \eZU
@ -484,13 +527,19 @@ function.
.Pp
.Bl -column -compact
.It Fa conf.button.always_active
buttons always active, avoidind focus switch between buttons and input fields or
buttons always active, avoiding focus switch between buttons and input fields or
input boxes in
.Fn bsddialog_form ,
.Fn bsddialog_datebox ,
.Fn bsddialog_calendar
and
.Fn bsddialog_timebox .
.It Fa conf.button.left1_label
add a button with the specified label.
.It Fa conf.button.left2_label
add a button with the specified label.
.It Fa conf.button.left3_label
add a button with the specified label.
.It Fa conf.button.without_ok
disable OK button.
.It Fa conf.button.ok_label
@ -509,9 +558,11 @@ on startup focus on the Cancel button.
add Help button.
.It Fa conf.button.help_label
set a label for Help button.
.It Fa conf.button.generic1_label
.It Fa conf.button.right1_label
add a button with the specified label.
.It Fa conf.button.generic2_label
.It Fa conf.button.right2_label
add a button with the specified label.
.It Fa conf.button.right3_label
add a button with the specified label.
.It Fa conf.button.default_label
focus on the button with the specified label.
@ -532,143 +583,35 @@ to
to
.Dv 10 .
.Pp
.Fn bsddialog_infobox
builds a dialog without buttons and returns instantly.
.Fn bsddialog_msgbox
builds a dialog with OK button.
.Fn bsddialog_yesno
provides a dialog for a
.Dq Yes-No Question ,
the labels on buttons are Yes and No.
.Pp
.Fn bsddialog_pause
builds a dialog waiting until the timeout in
.Fa seconds
expires or a button is pressed.
.Pp
.Fn bsddialog_calendar
builds a dialog to select a date.
.Fa year ,
.Fa month ,
and
.Fn bsddialog_datebox
build a dialog to select a date,
.Fa yy ,
.Fa mm ,
and
.Fa dd
.Fa day
are default values on startup, selected date at exit.
.Fn bsddialog_timebox
builds a dialog to choose a time,
.Fa hh ,
.Fa mm ,
.Pp
.Fn bsddialog_checklist
builds dialogs to select some item from a list via the SPACE key, can be
customized by
.Fa conf.menu.* .
See
.Fn bsddialog_menu .
.Pp
.Fn bsddialog_datebox
builds a dialog to select a date.
.Fa year ,
.Fa month ,
and
.Fa ss
are default values on startup, selected time at exit.
.Pp
.Fn bsddialog_checklist ,
.Fn bsddialog_menu
and
.Fn bsddialog_radiolist
build dialogs to select some item from a list via the SPACE key, an item is
defined like:
.Pp
.Bd -literal -offset indent -compact
struct bsddialog_menuitem {
const char *prefix;
bool on;
unsigned int depth;
const char *name;
const char *desc;
const char *bottomdesc;
};
.Ed
.Pp
.Fa prefix ,
.Fa name
and
.Fa desc
are strings to describe the item and are printed on its row,
.Fa bottomdesc
is printed on the bottom side of the screen,
.Fa depth
is a margin between the
.Fa prefix
and
.Fa name
useful to implement a
.Dq treeview,
.Fa on
is set to
.Dv true
if the item is selected,
.Dv false
otherwise.
.Fa items
is an array of items of
.Fa nitem
elements,
.Fa menurows
specifies the graphical fixed height of the list, if
.Fa cols
is set to
.Dv BSDDIALOG_AUTOSIZE
.Fa menurows
specifies a maximum value.
Finally, if not
.Dv NULL ,
.Fa focusitem
specifies the default item on startup and the last focused item at exit, could
be a negative value if no item is focused.
.Pp
.Fn bsddialog_mixedlist
builds a dialog with collections of checklists, radiolists and separators.
A collection is a set defined like:
.Pp
.Bd -literal -offset indent -compact
enum bsddialog_menutype {
BSDDIALOG_CHECKLIST,
BSDDIALOG_RADIOLIST,
BSDDIALOG_SEPARATOR,
};
struct bsddialog_menugroup {
enum bsddialog_menutype type;
unsigned int nitems;
struct bsddialog_menuitem *items;
};
.Ed
.Pp
.Fa groups
is an array of sets of
.Fa ngroups
elements.
.Fa menurows
is the graphical height size for the list.
If not
.Dv NULL ,
.Fa focuslist
and
.Fa focusitem
specify the default item on startup and the last focused item at exit, could be
a negative value if no item is focused.
.Pp
.Fn bsddialog_checklist ,
.Fn bsddialog_menu ,
.Fn bsddialog_mixedlist
and
.Fn bsddialog_radiolist
can be costomizated by:
.Fa day
are default values on startup, selected date at exit.
The function can be customized by:
.Bl -column -compact
.It Fa conf.menu.align_left
aligns items to left, default center.
.It Fa conf.menu.no_desc
hide description.
.It Fa conf.menu.no_name
hide names.
.It Fa conf.menu.on_without_ok
set items
.Fa on
also if the OK button is not pressed.
.It Fa conf.menu.shortcut_buttons
enable shortcut keys on buttons, default on items.
.It Fa conf.date.format
date format user interface, possible values:
.Dq d/m/y ,
.Dq m/d/y ,
.Dq y/m/d .
.El
.Pp
.Fn bsddialog_form
@ -676,11 +619,16 @@ builds a dialog to display an array of
.Fa items
of
.Fa nitems
elements to get strings in input.
elements to get input strings.
.Fa formrows
specifies the graphical height for the box around the items,
is the graphical height for the items inside the dialog,
.Dv 0
for autosizing.
If not
.Dv NULL
.Fa focusitem
is the default item index on startup and the last focused item at exit, a
negative value if no item is focused.
An item is defined like:
.Pp
.Bd -literal -offset indent -compact
@ -703,7 +651,7 @@ struct bsddialog_formitem {
.Ed
.Pp
.Fa label
is a string to describe the request, it is printed at the position
is a string to describe the request at the position
.Fa ylabel
and
.Fa xlabel .
@ -717,22 +665,20 @@ is its graphical width, while
is the maximum number of characters of the input string.
.Fa init
is the default field value.
If the OK button is pressed
If no error occurs
.Fa value
is the allocated memory with the current field string, its size depends on
the current locale.
is the allocated memory with the current field string at exit, its size depends
on the current locale.
.Fa flags
is an OR value to set the
is an OR value to set the field:
.Dv BSDDIALOG_FIELDHIDDEN ,
.Dv BSDDIALOG_FIELDREADONLY ,
.Dv BSDDIALOG_FIELDNOCOLOR ,
.Dv BSDDIALOG_FIELDCURSOREND ,
.Dv BSDDIALOG_FIELDEXTEND
and
.Dv BSDDIALOG_FIELDEXTEND ,
.Dv BSDDIALOG_FIELDSINGLEBYTE .
flags for the field.
.Fa bottomdesc
is printed on the bottom side of the screen if the item is focused.
is printed at bottom screen if the item is focused.
.Pp
.Fn bsddialog_form
can be customized by:
@ -751,38 +697,108 @@ the allocated
is a
.Em wchar_t*
string.
.It Fa conf.form.value_without_ok
allocate memory and set
.Fa value
also if the OK button is not pressed.
.El
.Pp
.Fn bsddialog_gauge
builds a dialog with a bar to shows
.Fa perc ,
if the file descriptor
builds a dialog with a bar to show
.Fa perc .
If the file descriptor
.Fa fd
is greater or equal to 0 the dialog waits to read
.Fa separator
.Fa sep
from it, then the first string replaces
.Fa perc
and the following strings replace
.Fa text
until the next
.Fa separator ,
.Fa sep ,
the loop ends reading
.Dv EOF .
.Fa end .
.Pp
.Fn bsddialog_infobox
builds a dialog without buttons and returns instantly.
.Pp
.Fn bsddialog_menu
builds a dialog to select an item from a list via SPACE or ENTER.
An item is
defined like:
.Pp
.Bd -literal -offset indent -compact
struct bsddialog_menuitem {
const char *prefix;
bool on;
unsigned int depth;
const char *name;
const char *desc;
const char *bottomdesc;
};
.Ed
.Pp
.Fa prefix ,
.Fa name
and
.Fa desc
are printed at the item row.
.Fa bottomdesc
is printed at bottom screen if the item is focused.
.Fa depth
is a margin between
.Fa prefix
and
.Fa name .
At exit
.Fa on
is set to
.Dv true
if the item is selected,
.Dv false
otherwise.
.Fa items
is an array of items of
.Fa nitem
elements.
.Fa menurows
is the graphical height of the list inside the dialog, if
.Fa cols
is
.Dv BSDDIALOG_AUTOSIZE
.Fa menurows
specifies a maximum value.
if not
.Dv NULL
.Fa focusitem
is the default item index on startup and the last focused item at exit, a
negative value if no item is focused.
.Pp
.Fn bsddialog_checklist ,
.Fn bsddialog_menu ,
.Fn bsddialog_mixedlist
and
.Fn bsddialog_radiolist
can be customized by:
.Bl -column -compact
.It Fa conf.menu.align_left
align items to left, default center.
.It Fa conf.menu.no_desc
hide items description.
.It Fa conf.menu.no_name
hide items name, mutually exclusive with
.Fa conf.menu.no_desc .
.It Fa conf.menu.shortcut_buttons
enable shortcut keys on buttons, default on items.
.El
.Pp
.Fn bsddialog_mixedgauge
draws a main bar with the
builds a dialog with a main bar with the
.Fa mainperc
percentage and
.Fa nminibars
each one with a
.Fa minilabel
and a
.Fa miniperc .
.Fa miniperc
with a value between 0 and 100 or
can be: a positive value to print a bar with a percentace, a negative constant
.Dv BSDDIALOG_MG_SUCCEEDED ,
.Dv BSDDIALOG_MG_FAILED ,
.Dv BSDDIALOG_MG_PASSED ,
@ -791,11 +807,69 @@ with a value between 0 and 100 or
.Dv BSDDIALOG_MG_DONE ,
.Dv BSDDIALOG_MG_SKIPPED ,
.Dv BSDDIALOG_MG_INPROGRESS ,
.Dv BSDDIALOG_MG_BLANK ,
.Dv BSDDIALOG_MG_NA
or
.Dv BSDDIALOG_MG_BLANK
to hide
.Fa miniperc ,
.Dv BSDDIALOG_MG_NA ,
.Dv BSDDIALOG_MG_PENDING
to print a descriptive string.
to print a descriptive string, otherwise
.Dq "UNKNOWN"
is printed.
.Pp
.Fn bsddialog_mixedlist
builds a dialog with collections of checklists, radiolists and separators.
A collection is a set defined like:
.Pp
.Bd -literal -offset indent -compact
enum bsddialog_menutype {
BSDDIALOG_CHECKLIST,
BSDDIALOG_RADIOLIST,
BSDDIALOG_SEPARATOR,
};
struct bsddialog_menugroup {
enum bsddialog_menutype type;
unsigned int nitems;
struct bsddialog_menuitem *items;
unsigned int min_on; /* unused for now */
};
.Ed
.Pp
.Fa groups
is an array of sets of
.Fa ngroups
elements.
.Fa menurows
is the graphical height size for the list.
If not
.Dv NULL ,
.Fa focuslist
and
.Fa focusitem
specify the default item on startup and the last focused item at exit, could be
a negative value if no item is focused.
The dialog can be customized by
.Fa conf.menu.* ,
see
.Fn bsddialog_menu .
.Pp
.Fn bsddialog_msgbox
builds a dialog with OK button.
.Pp
.Fn bsddialog_pause
builds a dialog waiting until the timeout in
.Fa seconds
expires or a button is pressed.
At exit
.Fa seconds
is set like remaining time.
.Pp
.Fn bsddialog_radiolist
builds dialogs to select at most an item from a list via the SPACE key, can be
customized by
.Fa conf.menu.* .
See
.Fn bsddialog_menu .
.Pp
.Fn bsddialog_rangebox
to select a value between
@ -809,12 +883,28 @@ and PAGEDOWN can change it.
.Pp
.Fn bsddialog_textbox
opens and prints
.Fa file
in a dialog, the UP, DOWN, HOME, END, PAGEUP and PAGEDOWN keys are availble to
navigate the file.
OK button is renamed EXIT.
.Fa file .
UP, DOWN, LEFT, RIGHT, HOME, END, PAGEUP and PAGEDOWN keys are available to
navigate the file, TAB changes button.
.Dq OK
button is renamed
.Dq EXIT .
.Pp
.Fn bsddialog_timebox
builds a dialog to choose a time.
.Fa hh ,
.Fa mm ,
and
.Fa ss
are default values on startup, selected time at exit.
.Pp
.Fn bsddialog_yesno
provides a dialog for a
.Dq Yes-No Question ,
the labels on buttons are Yes and No.
.Ss Theme
The graphical properties are global to the library, they are represented by
The graphical properties are global to the library.
They are represented by
.Fa struct bsddialog_theme
and can be customized at runtime via the
.In bsddialog_theme.h
@ -840,17 +930,19 @@ struct bsddialog_theme {
int arrowcolor;
} dialog;
struct {
int f_prefixcolor;
int prefixcolor;
int f_selectorcolor;
int selectorcolor;
int f_namecolor;
int namecolor;
int f_desccolor;
int desccolor;
int namesepcolor;
int descsepcolor;
int f_shortcutcolor;
int shortcutcolor;
int bottomdesccolor;
int sepnamecolor;
int sepdesccolor;
} menu;
struct {
int f_fieldcolor;
@ -867,32 +959,27 @@ struct bsddialog_theme {
unsigned int maxmargin;
char leftdelim;
char rightdelim;
int delimcolor;
int f_delimcolor;
int color;
int delimcolor;
int f_color;
int shortcutcolor;
int color;
int f_shortcutcolor;
int shortcutcolor;
} button;
};
.Ed
.Pp
A member with the
.Dq f_
prefix refers to an element with focus.
refers to focus when an element can be in selected or not selected state.
.Pp
.Fn bsddialog_get_theme
sets
.Fa theme
like the current theme.
.Pp
A color can be set by the value returned by
.Fn bsddialog_color ,
Possible values for
.Fa background
and
.Fa foreground
are:
.Fn bsddialog_color
generates and returns a color to set a
.Fa struct bsddialog_theme
color member.
An
.Fa enum bsddialog_color
can be:
.Dv BSDDIALOG_BLACK ,
.Dv BSDDIALOG_RED ,
.Dv BSDDIALOG_GREEN ,
@ -900,31 +987,30 @@ are:
.Dv BSDDIALOG_BLUE ,
.Dv BSDDIALOG_MAGENTA ,
.Dv BSDDIALOG_CYAN ,
and
.Dv BSDDIALOG_WHITE ,
.Dv BSDDIALOG_WHITE .
.Fa flags
specifies OR-flags, possible values:
is an OR value:
.Dv BSDDIALOG_BLINK ,
.Dv BSDDIALOG_BOLD ,
.Dv BSDDIALOG_REVERSE
and
.Dv BSDDIALOG_HALFBRIGHT ,
.Dv BSDDIALOG_HIGHLIGHT ,
.Dv BSDDIALOG_REVERSE ,
.Dv BSDDIALOG_UNDERLINE .
.Fn bsddialog_color_attrs
gets the properties of a color.
.Pp
.Fn bsddialog_set_theme
.Fn bsddialog_color_attrs
sets, if not NULL,
.Fa foreground ,
.Fa background ,
.Fa flags ,
like the properties of
.Fa color ,
see
.Fn bsddialog_color .
.Pp
.Fn bsddialog_get_theme
sets
.Fa theme
like current theme, the changes takes effect only for dialogs built after the
call.
.Pp
The library provides predefined themes:
.Dv BSDDIALOG_THEME_BLACKWHITE ,
.Dv BSDDIALOG_THEME_BSDDIALOG ,
.Dv BSDDIALOG_THEME_FLAT
and
.Dv BSDDIALOG_THEME_DIALOG ,
they can be set via
.Fn bsddialog_set_default_theme .
like the current runtime theme.
.Pp
.Fn bsddialog_hascolors
returns
@ -932,6 +1018,20 @@ returns
if the terminal provides colors,
.Dv false
otherwise.
.Pp
.Fn bsddialog_set_theme
sets
.Fa theme
like current runtime theme.
Changes take effect only for dialogs built after
the call.
.Pp
.Fn bsddialog_set_default_theme
sets a library default theme like current theme, possible values:
.Dv BSDDIALOG_THEME_BLACKWHITE ,
.Dv BSDDIALOG_THEME_FLAT ,
.Dv BSDDIALOG_THEME_3D .
Changes take effect only for dialogs built after the call.
.Sh RETURN VALUES
The functions return the value
.Dv BSDDIALOG_ERROR
@ -942,9 +1042,12 @@ returned:
.Dv BSDDIALOG_CANCEL ,
.Dv BSDDIALOG_HELP ,
.Dv BSDDIALOG_EXTRA ,
.Dv BSDDIALOG_GENERIC1
or
.Dv BSDDIALOG_GENERIC2 .
.Dv BSDDIALOG_LEFT1 ,
.Dv BSDDIALOG_LEFT2 ,
.Dv BSDDIALOG_LEFT3 ,
.Dv BSDDIALOG_RIGHT1 ,
.Dv BSDDIALOG_RIGHT2 ,
.Dv BSDDIALOG_RIGHT3 .
.Dv BSDDIALOG_YES
and
.Dv BSDDIALOG_NO
@ -990,6 +1093,7 @@ case BSDDIALOG_NO
break;
case BSDDIALOG_ERROR:
printf("Error: %s\\n", bsddialog_geterror());
break;
}
.Ed
.Pp

View File

@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2022 Alfonso Sabato Siciliano
* Copyright (c) 2021-2023 Alfonso Sabato Siciliano
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -30,9 +30,9 @@
#include <stdbool.h>
#define LIBBSDDIALOG_VERSION "0.4"
#define LIBBSDDIALOG_VERSION "1.0"
/* Exit status */
/* Return values */
#define BSDDIALOG_ERROR -1
#define BSDDIALOG_OK 0
#define BSDDIALOG_YES BSDDIALOG_OK
@ -42,8 +42,12 @@
#define BSDDIALOG_EXTRA 3
#define BSDDIALOG_TIMEOUT 4
#define BSDDIALOG_ESC 5
#define BSDDIALOG_GENERIC1 6
#define BSDDIALOG_GENERIC2 7
#define BSDDIALOG_LEFT1 6
#define BSDDIALOG_LEFT2 7
#define BSDDIALOG_LEFT3 8
#define BSDDIALOG_RIGHT1 9
#define BSDDIALOG_RIGHT2 10
#define BSDDIALOG_RIGHT3 11
/* Size and position */
#define BSDDIALOG_FULLSCREEN -1
@ -94,24 +98,28 @@ struct bsddialog_conf {
} key;
struct {
unsigned int cols_per_row;
bool highlight;
bool escape;
unsigned int tablen;
} text;
struct {
bool align_left;
bool no_desc;
bool no_name;
bool on_without_ok;
bool shortcut_buttons;
} menu;
struct {
char securech;
char *securembch;
bool value_wchar;
bool value_without_ok;
} form;
struct {
const char *format;
} date;
struct {
bool always_active;
const char *left1_label;
const char *left2_label;
const char *left3_label;
bool without_ok;
const char *ok_label;
bool with_extra;
@ -121,8 +129,9 @@ struct bsddialog_conf {
bool default_cancel;
bool with_help;
const char *help_label;
const char *generic1_label;
const char *generic2_label;
const char *right1_label;
const char *right2_label;
const char *right3_label;
const char *default_label;
} button;
};
@ -146,6 +155,7 @@ struct bsddialog_menugroup {
enum bsddialog_menutype type;
unsigned int nitems;
struct bsddialog_menuitem *items;
unsigned int min_on; /* unused for now */
};
struct bsddialog_formitem {
@ -166,16 +176,18 @@ struct bsddialog_formitem {
int bsddialog_init(void);
int bsddialog_init_notheme(void);
bool bsddialog_inmode(void);
int bsddialog_end(void);
int bsddialog_backtitle(struct bsddialog_conf *conf, const char *backtitle);
int bsddialog_initconf(struct bsddialog_conf *conf);
int bsddialog_clearterminal(void);
void bsddialog_clear(unsigned int y);
void bsddialog_refresh(void);
const char *bsddialog_geterror(void);
/* Dialogs */
int
bsddialog_calendar(struct bsddialog_conf *conf, const char *text, int rows,
int cols, unsigned int *yy, unsigned int *mm, unsigned int *dd);
int cols, unsigned int *year, unsigned int *month, unsigned int *day);
int
bsddialog_checklist(struct bsddialog_conf *conf, const char *text, int rows,
@ -184,16 +196,16 @@ bsddialog_checklist(struct bsddialog_conf *conf, const char *text, int rows,
int
bsddialog_datebox(struct bsddialog_conf *conf, const char *text, int rows,
int cols, unsigned int *yy, unsigned int *mm, unsigned int *dd);
int cols, unsigned int *year, unsigned int *month, unsigned int *day);
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 *items, int *focusitem);
int
bsddialog_gauge(struct bsddialog_conf *conf, const char *text, int rows,
int cols, unsigned int perc, int fd, const char *sep);
int cols, unsigned int perc, int fd, const char *sep, const char *end);
int
bsddialog_infobox(struct bsddialog_conf *conf, const char *text, int rows,
@ -220,7 +232,7 @@ bsddialog_msgbox(struct bsddialog_conf *conf, const char *text, int rows,
int
bsddialog_pause(struct bsddialog_conf *conf, const char *text, int rows,
int cols, unsigned int seconds);
int cols, unsigned int *seconds);
int
bsddialog_radiolist(struct bsddialog_conf *conf, const char *text, int rows,

View File

@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2022 Alfonso Sabato Siciliano
* Copyright (c) 2021-2023 Alfonso Sabato Siciliano
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -36,14 +36,14 @@
extern bool bsddialog_interruptprogview;
extern bool bsddialog_abortprogview;
extern int bsddialog_total_progview;
extern long long int bsddialog_total_progview;
struct bsddialog_fileminibar {
const char *path;
const char *label;
int status; /* next if BSDDIALOG_MG_DONE or BSDDIALOG_MG_FAILED */
long long size;
long long read;
long long int size;
long long int read;
};
struct bsddialog_progviewconf {

View File

@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2022 Alfonso Sabato Siciliano
* Copyright (c) 2021-2023 Alfonso Sabato Siciliano
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -29,9 +29,12 @@
#define _LIBBSDDIALOG_THEME_H_
/* color flags */
#define BSDDIALOG_BOLD 1U
#define BSDDIALOG_REVERSE 2U
#define BSDDIALOG_UNDERLINE 4U
#define BSDDIALOG_BLINK 1U
#define BSDDIALOG_BOLD 2U
#define BSDDIALOG_HALFBRIGHT 4U
#define BSDDIALOG_HIGHLIGHT 8U
#define BSDDIALOG_REVERSE 16U
#define BSDDIALOG_UNDERLINE 32U
struct bsddialog_theme {
struct {
@ -52,17 +55,19 @@ struct bsddialog_theme {
int arrowcolor;
} dialog;
struct {
int f_prefixcolor;
int prefixcolor;
int f_selectorcolor;
int selectorcolor;
int f_namecolor;
int namecolor;
int f_desccolor;
int desccolor;
int namesepcolor;
int descsepcolor;
int f_shortcutcolor;
int shortcutcolor;
int bottomdesccolor;
int sepnamecolor;
int sepdesccolor;
} menu;
struct {
int f_fieldcolor;
@ -79,20 +84,19 @@ struct bsddialog_theme {
unsigned int maxmargin;
char leftdelim;
char rightdelim;
int delimcolor;
int f_delimcolor;
int color;
int delimcolor;
int f_color;
int shortcutcolor;
int color;
int f_shortcutcolor;
int shortcutcolor;
} button;
};
enum bsddialog_default_theme {
BSDDIALOG_THEME_3D,
BSDDIALOG_THEME_BLACKWHITE,
BSDDIALOG_THEME_BSDDIALOG,
BSDDIALOG_THEME_FLAT,
BSDDIALOG_THEME_DIALOG
BSDDIALOG_THEME_FLAT
};
enum bsddialog_color {

View File

@ -1,520 +0,0 @@
/*-
* 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 <sys/param.h>
#include <curses.h>
#include <stdlib.h>
#include <string.h>
#include "bsddialog.h"
#include "bsddialog_theme.h"
#include "lib_util.h"
#define MINHCAL 13
#define MINWCAL 36 /* 34 calendar, 1 + 1 margins */
#define MINYEAR 1900
#define MAXYEAR 999999999
static int month_days(int yy, int mm)
{
int days;
if (mm == 2)
days = ISLEAP(yy) ? 29 : 28;
else if (mm == 4 || mm == 6 || mm == 9 || mm == 11)
days = 30;
else
days = 31;
return (days);
}
enum operation {
UP_DAY,
DOWN_DAY,
LEFT_DAY,
RIGHT_DAY,
UP_MONTH,
DOWN_MONTH,
UP_YEAR,
DOWN_YEAR
};
static void datectl(enum operation op, int *yy, int *mm, int *dd)
{
int ndays;
ndays = month_days(*yy, *mm);
switch (op) {
case UP_DAY:
if (*dd > 7)
*dd -= 7;
else {
if (*mm == 1) {
*yy -= 1;
*mm = 12;
} else
*mm -= 1;
ndays = month_days(*yy, *mm);
*dd = ndays - abs(7 - *dd);
}
break;
case DOWN_DAY:
if (*dd + 7 < ndays)
*dd += 7;
else {
if (*mm == 12) {
*yy += 1;
*mm = 1;
} else
*mm += 1;
*dd = *dd + 7 - ndays;
}
break;
case LEFT_DAY:
if (*dd > 1)
*dd -= 1;
else {
if (*mm == 1) {
*yy -= 1;
*mm = 12;
} else
*mm -= 1;
*dd = month_days(*yy, *mm);
}
break;
case RIGHT_DAY:
if (*dd < ndays)
*dd += 1;
else {
if (*mm == 12) {
*yy += 1;
*mm = 1;
} else
*mm += 1;
*dd = 1;
}
break;
case UP_MONTH:
if (*mm == 1) {
*mm = 12;
*yy -= 1;
} else
*mm -= 1;
ndays = month_days(*yy, *mm);
if (*dd > ndays)
*dd = ndays;
break;
case DOWN_MONTH:
if (*mm == 12) {
*mm = 1;
*yy += 1;
} else
*mm += 1;
ndays = month_days(*yy, *mm);
if (*dd > ndays)
*dd = ndays;
break;
case UP_YEAR:
*yy -= 1;
ndays = month_days(*yy, *mm);
if (*dd > ndays)
*dd = ndays;
break;
case DOWN_YEAR:
*yy += 1;
ndays = month_days(*yy, *mm);
if (*dd > ndays)
*dd = ndays;
break;
}
if (*yy < MINYEAR) {
*yy = MINYEAR;
*mm = 1;
*dd = 1;
}
if (*yy > MAXYEAR) {
*yy = MAXYEAR;
*mm = 12;
*dd = 31;
}
}
static int week_day(int yy, int mm, int dd)
{
int wd;
dd += mm < 3 ? yy-- : yy - 2;
wd = 23*mm/9 + dd + 4 + yy/4 - yy/100 + yy/400;
wd %= 7;
return (wd);
}
static void
print_calendar(struct bsddialog_conf *conf, WINDOW *win, int yy, int mm, int dd,
bool active)
{
int ndays, i, y, x, wd, h, w;
getmaxyx(win, h, w);
wclear(win);
draw_borders(conf, win, h, w, RAISED);
if (active) {
wattron(win, t.dialog.arrowcolor);
mvwhline(win, 0, 15, conf->ascii_lines ? '^' : ACS_UARROW, 4);
mvwhline(win, h-1, 15, conf->ascii_lines ? 'v' : ACS_DARROW, 4);
mvwvline(win, 3, 0, conf->ascii_lines ? '<' : ACS_LARROW, 3);
mvwvline(win, 3, w-1, conf->ascii_lines ? '>' : ACS_RARROW, 3);
wattroff(win, t.dialog.arrowcolor);
}
mvwaddstr(win, 1, 5, "Sun Mon Tue Wed Thu Fri Sat");
ndays = month_days(yy, mm);
y = 2;
wd = week_day(yy, mm, 1);
for (i = 1; i <= ndays; i++) {
x = 5 + (4 * wd); /* x has to be 6 with week number */
wmove(win, y, x);
mvwprintw(win, y, x, "%2d", i);
if (i == dd) {
wattron(win, t.menu.f_namecolor);
mvwprintw(win, y, x, "%2d", i);
wattroff(win, t.menu.f_namecolor);
}
wd++;
if (wd > 6) {
wd = 0;
y++;
}
}
wrefresh(win);
}
static void
drawsquare(struct bsddialog_conf *conf, WINDOW *win, const char *fmt,
const void *value, bool focus)
{
int h, w;
getmaxyx(win, h, w);
draw_borders(conf, win, h, w, RAISED);
if (focus) {
wattron(win, t.dialog.arrowcolor);
mvwhline(win, 0, 7, conf->ascii_lines ? '^' : ACS_UARROW, 3);
mvwhline(win, 2, 7, conf->ascii_lines ? 'v' : ACS_DARROW, 3);
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
calendar_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h,
int *w, const char *text, struct buttons bs)
{
int htext, wtext;
if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) {
if (text_size(conf, rows, cols, text, &bs, MINHCAL, MINWCAL,
&htext, &wtext) != 0)
return (BSDDIALOG_ERROR);
}
if (cols == BSDDIALOG_AUTOSIZE)
*w = widget_min_width(conf, wtext, MINWCAL, &bs);
if (rows == BSDDIALOG_AUTOSIZE)
*h = widget_min_height(conf, htext, MINHCAL, true);
return (0);
}
static int calendar_checksize(int rows, int cols, struct buttons bs)
{
int mincols;
mincols = MAX(MINWCAL, buttons_min_width(bs));
mincols += VBORDERS;
if (cols < mincols)
RETURN_ERROR("Few cols for this calendar (at least 38)");
if (rows < MINHCAL + 2 + 2) /* 2 buttons + 2 borders */
RETURN_ERROR("Few rows for calendar (at least 17)");
return (0);
}
int
bsddialog_calendar(struct bsddialog_conf *conf, const char *text, int rows,
int cols, unsigned int *yy, unsigned int *mm, unsigned int *dd)
{
bool loop, focusbuttons;
int retval, y, x, h, w, sel, ycal, xcal, year, month, day;
wint_t input;
WINDOW *widget, *textpad, *shadow, *yearwin, *monthwin, *daywin;
struct buttons bs;
const char *m[12] = {
"January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December"
};
if (yy == NULL || mm == NULL || dd == NULL)
RETURN_ERROR("yy / mm / dd cannot be NULL");
year = *yy > MAXYEAR ? MAXYEAR : *yy;
if (year < MINYEAR)
year = MINYEAR;
month = *mm > 12 ? 12 : *mm;
if (month == 0)
month = 1;
day = *dd == 0 ? 1 : *dd;
if(day > month_days(year, month))
day = month_days(year, month);
get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL);
if (set_widget_size(conf, rows, cols, &h, &w) != 0)
return (BSDDIALOG_ERROR);
if (calendar_autosize(conf, rows, cols, &h, &w, text, bs) != 0)
return (BSDDIALOG_ERROR);
if (calendar_checksize(h, w, bs) != 0)
return (BSDDIALOG_ERROR);
if (set_widget_position(conf, &y, &x, h, w) != 0)
return (BSDDIALOG_ERROR);
if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs,
true) != 0)
return (BSDDIALOG_ERROR);
pnoutrefresh(textpad, 0, 0, y+1, x+2, y+h-17, x+w-2);
doupdate();
ycal = y + h - 15;
xcal = x + w/2 - 17;
mvwaddstr(widget, h - 16, w/2 - 17, "Month");
monthwin = new_boxed_window(conf, ycal, xcal, 3, 17, RAISED);
mvwaddstr(widget, h - 16, w/2, "Year");
yearwin = new_boxed_window(conf, ycal, xcal + 17, 3, 17, RAISED);
daywin = new_boxed_window(conf, ycal + 3, xcal, 9, 34, RAISED);
wrefresh(widget);
sel = -1;
loop = focusbuttons = true;
while (loop) {
drawsquare(conf, monthwin, "%15s", m[month - 1], sel == 0);
drawsquare(conf, yearwin, "%15d", &year, sel == 1);
print_calendar(conf, daywin, year, month, day, sel == 2);
if (get_wch(&input) == ERR)
continue;
switch(input) {
case KEY_ENTER:
case 10: /* Enter */
if (focusbuttons || conf->button.always_active) {
retval = bs.value[bs.curr];
loop = false;
}
break;
case 27: /* Esc */
if (conf->key.enable_esc) {
retval = BSDDIALOG_ESC;
loop = false;
}
break;
case '\t': /* TAB */
if (focusbuttons) {
bs.curr++;
if (bs.curr >= (int)bs.nbuttons) {
focusbuttons = false;
sel = 0;
bs.curr = conf->button.always_active ?
0 : -1;
}
} else {
sel++;
if (sel > 2) {
focusbuttons = true;
sel = -1;
bs.curr = 0;
}
}
draw_buttons(widget, bs, true);
wrefresh(widget);
break;
case KEY_RIGHT:
if (focusbuttons) {
bs.curr++;
if (bs.curr >= (int)bs.nbuttons) {
focusbuttons = false;
sel = 0;
bs.curr = conf->button.always_active ?
0 : -1;
}
} else if (sel == 2) {
datectl(RIGHT_DAY, &year, &month, &day);
} else { /* Month or Year*/
sel++;
}
draw_buttons(widget, bs, true);
wrefresh(widget);
break;
case KEY_LEFT:
if (focusbuttons) {
bs.curr--;
if (bs.curr < 0) {
focusbuttons = false;
sel = 2;
bs.curr = conf->button.always_active ?
0 : -1;
}
} else if (sel == 2) {
datectl(LEFT_DAY, &year, &month, &day);
} else if (sel == 1) {
sel = 0;
} else { /* sel = 0, Month */
focusbuttons = true;
sel = -1;
bs.curr = 0;
}
draw_buttons(widget, bs, true);
wrefresh(widget);
break;
case KEY_UP:
if (focusbuttons) {
sel = 2;
focusbuttons = false;
bs.curr = conf->button.always_active ? 0 : -1;
draw_buttons(widget, bs, true);
wrefresh(widget);
} else if (sel == 0) {
datectl(UP_MONTH, &year, &month, &day);
} else if (sel == 1) {
datectl(UP_YEAR, &year, &month, &day);
} else { /* sel = 2 */
datectl(UP_DAY, &year, &month, &day);
}
break;
case KEY_DOWN:
if (focusbuttons) {
break;
} else if (sel == 0) {
datectl(DOWN_MONTH, &year, &month, &day);
} else if (sel == 1) {
datectl(DOWN_YEAR, &year, &month, &day);
} else { /* sel = 2 */
datectl(DOWN_DAY, &year, &month, &day);
}
break;
case KEY_HOME:
datectl(UP_MONTH, &year, &month, &day);
break;
case KEY_END:
datectl(DOWN_MONTH, &year, &month, &day);
break;
case KEY_PPAGE:
datectl(UP_YEAR, &year, &month, &day);
break;
case KEY_NPAGE:
datectl(DOWN_YEAR, &year, &month, &day);
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, screen size can change */
case KEY_RESIZE:
/* Important for decreasing screen */
hide_widget(y, x, h, w, conf->shadow);
refresh();
if (set_widget_size(conf, rows, cols, &h, &w) != 0)
return (BSDDIALOG_ERROR);
if (calendar_autosize(conf, rows, cols, &h, &w, text,
bs) != 0)
return (BSDDIALOG_ERROR);
if (calendar_checksize(h, w, 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)
return (BSDDIALOG_ERROR);
pnoutrefresh(textpad, 0, 0, y+1, x+2, y+h-17, x+w-2);
doupdate();
ycal = y + h - 15;
xcal = x + w/2 - 17;
mvwaddstr(widget, h - 16, w/2 - 17, "Month");
mvwin(monthwin, ycal, xcal);
mvwaddstr(widget, h - 16, w/2, "Year");
mvwin(yearwin, ycal, xcal + 17);
mvwin(daywin, ycal + 3, xcal);
wrefresh(widget);
/* Important to avoid grey lines expanding screen */
refresh();
break;
default:
if (shortcut_buttons(input, &bs)) {
retval = bs.value[bs.curr];
loop = false;
}
}
}
if (retval == BSDDIALOG_OK) {
*yy = year;
*mm = month;
*dd = day;
}
delwin(yearwin);
delwin(monthwin);
delwin(daywin);
end_dialog(conf, shadow, widget, textpad);
return (retval);
}

715
lib/datebox.c Normal file
View File

@ -0,0 +1,715 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022-2023 Alfonso Sabato Siciliano
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <curses.h>
#include <stdlib.h>
#include <string.h>
#include "bsddialog.h"
#include "bsddialog_theme.h"
#include "lib_util.h"
/* Calendar */
#define MIN_YEAR_CAL 0
#define MAX_YEAR_CAL 999999999
#define MINHCAL 13
#define MINWCAL 36 /* 34 calendar, 1 + 1 margins */
/* Datebox */
#define MIN_YEAR_DATE 0
#define MAX_YEAR_DATE 9999
#define MINWDATE 23 /* 3 windows and their borders */
#define ISLEAP(year) ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
static int minyear;
static int maxyear;
static const char *m[12] = {
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
};
enum operation {
UP_DAY,
DOWN_DAY,
LEFT_DAY,
RIGHT_DAY,
UP_MONTH,
DOWN_MONTH,
UP_YEAR,
DOWN_YEAR
};
/* private datebox item */
struct dateitem {
enum operation up;
enum operation down;
WINDOW *win;
int width;
const char *fmt;
int *value;
};
static int month_days(int yy, int mm)
{
int days;
if (mm == 2)
days = ISLEAP(yy) ? 29 : 28;
else if (mm == 4 || mm == 6 || mm == 9 || mm == 11)
days = 30;
else
days = 31;
return (days);
}
static int week_day(int yy, int mm, int dd)
{
int wd;
dd += mm < 3 ? yy-- : yy - 2;
wd = 23*mm/9 + dd + 4 + yy/4 - yy/100 + yy/400;
wd %= 7;
return (wd);
}
static void
init_date(unsigned int *year, unsigned int *month, unsigned int *day, int *yy,
int *mm, int *dd)
{
*yy = MIN(*year, (unsigned int)maxyear);
if (*yy < minyear)
*yy = minyear;
*mm = MIN(*month, 12);
if (*mm == 0)
*mm = 1;
*dd = (*day == 0) ? 1 : *day;
if(*dd > month_days(*yy, *mm))
*dd = month_days(*yy, *mm);
}
static void datectl(enum operation op, int *yy, int *mm, int *dd)
{
int ndays;
ndays = month_days(*yy, *mm);
switch (op) {
case UP_DAY:
if (*dd > 7)
*dd -= 7;
else {
if (*mm == 1) {
*yy -= 1;
*mm = 12;
} else
*mm -= 1;
ndays = month_days(*yy, *mm);
*dd = ndays - abs(7 - *dd);
}
break;
case DOWN_DAY:
if (*dd + 7 < ndays)
*dd += 7;
else {
if (*mm == 12) {
*yy += 1;
*mm = 1;
} else
*mm += 1;
*dd = *dd + 7 - ndays;
}
break;
case LEFT_DAY:
if (*dd > 1)
*dd -= 1;
else {
if (*mm == 1) {
*yy -= 1;
*mm = 12;
} else
*mm -= 1;
*dd = month_days(*yy, *mm);
}
break;
case RIGHT_DAY:
if (*dd < ndays)
*dd += 1;
else {
if (*mm == 12) {
*yy += 1;
*mm = 1;
} else
*mm += 1;
*dd = 1;
}
break;
case UP_MONTH:
if (*mm == 1) {
*mm = 12;
*yy -= 1;
} else
*mm -= 1;
ndays = month_days(*yy, *mm);
if (*dd > ndays)
*dd = ndays;
break;
case DOWN_MONTH:
if (*mm == 12) {
*mm = 1;
*yy += 1;
} else
*mm += 1;
ndays = month_days(*yy, *mm);
if (*dd > ndays)
*dd = ndays;
break;
case UP_YEAR:
*yy -= 1;
ndays = month_days(*yy, *mm);
if (*dd > ndays)
*dd = ndays;
break;
case DOWN_YEAR:
*yy += 1;
ndays = month_days(*yy, *mm);
if (*dd > ndays)
*dd = ndays;
break;
}
if (*yy < minyear) {
*yy = minyear;
*mm = 1;
*dd = 1;
}
if (*yy > maxyear) {
*yy = maxyear;
*mm = 12;
*dd = 31;
}
}
static void
drawsquare(struct bsddialog_conf *conf, WINDOW *win, enum elevation elev,
const char *fmt, int value, bool focus)
{
int h, l, w;
getmaxyx(win, h, w);
draw_borders(conf, win, elev);
if (focus) {
l = 2 + w%2;
wattron(win, t.dialog.arrowcolor);
mvwhline(win, 0, w/2 - l/2,
conf->ascii_lines ? '^' : ACS_UARROW, l);
mvwhline(win, h-1, w/2 - l/2,
conf->ascii_lines ? 'v' : ACS_DARROW, l);
wattroff(win, t.dialog.arrowcolor);
}
if (focus)
wattron(win, t.menu.f_namecolor);
if (strchr(fmt, 's') != NULL)
mvwprintw(win, 1, 1, fmt, m[value - 1]);
else
mvwprintw(win, 1, 1, fmt, value);
if (focus)
wattroff(win, t.menu.f_namecolor);
wnoutrefresh(win);
}
static void
print_calendar(struct bsddialog_conf *conf, WINDOW *win, int yy, int mm, int dd,
bool active)
{
int ndays, i, y, x, wd, h, w;
getmaxyx(win, h, w);
wclear(win);
draw_borders(conf, win, RAISED);
if (active) {
wattron(win, t.dialog.arrowcolor);
mvwhline(win, 0, 15, conf->ascii_lines ? '^' : ACS_UARROW, 4);
mvwhline(win, h-1, 15, conf->ascii_lines ? 'v' : ACS_DARROW, 4);
mvwvline(win, 3, 0, conf->ascii_lines ? '<' : ACS_LARROW, 3);
mvwvline(win, 3, w-1, conf->ascii_lines ? '>' : ACS_RARROW, 3);
wattroff(win, t.dialog.arrowcolor);
}
mvwaddstr(win, 1, 5, "Sun Mon Tue Wed Thu Fri Sat");
ndays = month_days(yy, mm);
y = 2;
wd = week_day(yy, mm, 1);
for (i = 1; i <= ndays; i++) {
x = 5 + (4 * wd); /* x has to be 6 with week number */
wmove(win, y, x);
mvwprintw(win, y, x, "%2d", i);
if (i == dd) {
wattron(win, t.menu.f_namecolor);
mvwprintw(win, y, x, "%2d", i);
wattroff(win, t.menu.f_namecolor);
}
wd++;
if (wd > 6) {
wd = 0;
y++;
}
}
wnoutrefresh(win);
}
static int
calendar_redraw(struct dialog *d, WINDOW *yy_win, WINDOW *mm_win,
WINDOW *dd_win)
{
int ycal, xcal;
if (d->built) {
hide_dialog(d);
refresh(); /* Important for decreasing screen */
}
if (dialog_size_position(d, MINHCAL, MINWCAL, NULL) != 0)
return (BSDDIALOG_ERROR);
if (draw_dialog(d) != 0)
return (BSDDIALOG_ERROR);
if (d->built)
refresh(); /* Important to fix grey lines expanding screen */
TEXTPAD(d, MINHCAL + HBUTTONS);
ycal = d->y + d->h - 15;
xcal = d->x + d->w/2 - 17;
mvwaddstr(d->widget, d->h - 16, d->w/2 - 17, "Month");
update_box(d->conf, mm_win, ycal, xcal, 3, 17, RAISED);
mvwaddstr(d->widget, d->h - 16, d->w/2, "Year");
update_box(d->conf, yy_win, ycal, xcal + 17, 3, 17, RAISED);
update_box(d->conf, dd_win, ycal + 3, xcal, 9, 34, RAISED);
wnoutrefresh(d->widget);
return (0);
}
int
bsddialog_calendar(struct bsddialog_conf *conf, const char *text, int rows,
int cols, unsigned int *year, unsigned int *month, unsigned int *day)
{
bool loop, focusbuttons;
int retval, sel, yy, mm, dd;
wint_t input;
WINDOW *yy_win, *mm_win, *dd_win;
struct dialog d;
CHECK_PTR(year);
CHECK_PTR(month);
CHECK_PTR(day);
minyear = MIN_YEAR_CAL;
maxyear = MAX_YEAR_CAL;
init_date(year, month, day, &yy, &mm, &dd);
if (prepare_dialog(conf, text, rows, cols, &d) != 0)
return (BSDDIALOG_ERROR);
set_buttons(&d, true, OK_LABEL, CANCEL_LABEL);
if ((yy_win = newwin(1, 1, 1, 1)) == NULL)
RETURN_ERROR("Cannot build WINDOW for yy");
wbkgd(yy_win, t.dialog.color);
if ((mm_win = newwin(1, 1, 1, 1)) == NULL)
RETURN_ERROR("Cannot build WINDOW for mm");
wbkgd(mm_win, t.dialog.color);
if ((dd_win = newwin(1, 1, 1, 1)) == NULL)
RETURN_ERROR("Cannot build WINDOW for dd");
wbkgd(dd_win, t.dialog.color);
if (calendar_redraw(&d, yy_win, mm_win, dd_win) != 0)
return (BSDDIALOG_ERROR);
sel = -1;
loop = focusbuttons = true;
while (loop) {
drawsquare(conf, mm_win, RAISED, "%15s", mm, sel == 0);
drawsquare(conf, yy_win, RAISED, "%15d", yy, sel == 1);
print_calendar(conf, dd_win, yy, mm, dd, sel == 2);
doupdate();
if (get_wch(&input) == ERR)
continue;
switch(input) {
case KEY_ENTER:
case 10: /* Enter */
if (focusbuttons || conf->button.always_active) {
retval = BUTTONVALUE(d.bs);
loop = false;
}
break;
case 27: /* Esc */
if (conf->key.enable_esc) {
retval = BSDDIALOG_ESC;
loop = false;
}
break;
case '\t': /* TAB */
if (focusbuttons) {
d.bs.curr++;
if (d.bs.curr >= (int)d.bs.nbuttons) {
focusbuttons = false;
sel = 0;
d.bs.curr = conf->button.always_active ?
0 : -1;
}
} else {
sel++;
if (sel > 2) {
focusbuttons = true;
sel = -1;
d.bs.curr = 0;
}
}
DRAW_BUTTONS(d);
break;
case KEY_RIGHT:
if (focusbuttons) {
d.bs.curr++;
if (d.bs.curr >= (int)d.bs.nbuttons) {
focusbuttons = false;
sel = 0;
d.bs.curr = conf->button.always_active ?
0 : -1;
}
} else if (sel == 2) {
datectl(RIGHT_DAY, &yy, &mm, &dd);
} else { /* Month or Year*/
sel++;
}
DRAW_BUTTONS(d);
break;
case KEY_LEFT:
if (focusbuttons) {
d.bs.curr--;
if (d.bs.curr < 0) {
focusbuttons = false;
sel = 2;
d.bs.curr = conf->button.always_active ?
0 : -1;
}
} else if (sel == 2) {
datectl(LEFT_DAY, &yy, &mm, &dd);
} else if (sel == 1) {
sel = 0;
} else { /* sel = 0, Month */
focusbuttons = true;
sel = -1;
d.bs.curr = 0;
}
DRAW_BUTTONS(d);
break;
case KEY_UP:
if (focusbuttons) {
sel = 2;
focusbuttons = false;
d.bs.curr = conf->button.always_active ? 0 : -1;
DRAW_BUTTONS(d);
} else if (sel == 0) {
datectl(UP_MONTH, &yy, &mm, &dd);
} else if (sel == 1) {
datectl(UP_YEAR, &yy, &mm, &dd);
} else { /* sel = 2 */
datectl(UP_DAY, &yy, &mm, &dd);
}
break;
case KEY_DOWN:
if (focusbuttons) {
break;
} else if (sel == 0) {
datectl(DOWN_MONTH, &yy, &mm, &dd);
} else if (sel == 1) {
datectl(DOWN_YEAR, &yy, &mm, &dd);
} else { /* sel = 2 */
datectl(DOWN_DAY, &yy, &mm, &dd);
}
break;
case KEY_HOME:
datectl(UP_MONTH, &yy, &mm, &dd);
break;
case KEY_END:
datectl(DOWN_MONTH, &yy, &mm, &dd);
break;
case KEY_PPAGE:
datectl(UP_YEAR, &yy, &mm, &dd);
break;
case KEY_NPAGE:
datectl(DOWN_YEAR, &yy, &mm, &dd);
break;
case KEY_F(1):
if (conf->key.f1_file == NULL &&
conf->key.f1_message == NULL)
break;
if (f1help_dialog(conf) != 0)
return (BSDDIALOG_ERROR);
if (calendar_redraw(&d, yy_win, mm_win, dd_win) != 0)
return (BSDDIALOG_ERROR);
break;
case KEY_RESIZE:
if (calendar_redraw(&d, yy_win, mm_win, dd_win) != 0)
return (BSDDIALOG_ERROR);
break;
default:
if (shortcut_buttons(input, &d.bs)) {
DRAW_BUTTONS(d);
doupdate();
retval = BUTTONVALUE(d.bs);
loop = false;
}
}
}
*year = yy;
*month = mm;
*day = dd;
delwin(yy_win);
delwin(mm_win);
delwin(dd_win);
end_dialog(&d);
return (retval);
}
static int datebox_redraw(struct dialog *d, struct dateitem *di)
{
int y, x;
if (d->built) {
hide_dialog(d);
refresh(); /* Important for decreasing screen */
}
if (dialog_size_position(d, 3 /*windows*/, MINWDATE, NULL) != 0)
return (BSDDIALOG_ERROR);
if (draw_dialog(d) != 0)
return (BSDDIALOG_ERROR);
if (d->built)
refresh(); /* Important to fix grey lines expanding screen */
TEXTPAD(d, 3 /*windows*/ + HBUTTONS);
y = d->y + d->h - 6;
x = (d->x + d->w / 2) - 11;
update_box(d->conf, di[0].win, y, x, 3, di[0].width, LOWERED);
mvwaddch(d->widget, d->h - 5, x - d->x + di[0].width, '/');
x += di[0].width + 1;
update_box(d->conf, di[1].win, y, x , 3, di[1].width, LOWERED);
mvwaddch(d->widget, d->h - 5, x - d->x + di[1].width, '/');
x += di[1].width + 1;
update_box(d->conf, di[2].win, y, x, 3, di[2].width, LOWERED);
wnoutrefresh(d->widget);
return (0);
}
static int
build_dateitem(const char *format, int *yy, int *mm, int *dd,
struct dateitem *dt)
{
int i;
wchar_t *wformat;
struct dateitem init[3] = {
{UP_YEAR, DOWN_YEAR, NULL, 6, "%4d", yy},
{UP_MONTH, DOWN_MONTH, NULL, 11, "%9s", mm},
{LEFT_DAY, RIGHT_DAY, NULL, 4, "%02d", dd},
};
for (i = 0; i < 3; i++) {
if ((init[i].win = newwin(1, 1, 1, 1)) == NULL)
RETURN_FMTERROR("Cannot build WINDOW dateitem[%d]", i);
wbkgd(init[i].win, t.dialog.color);
}
if ((wformat = alloc_mbstows(CHECK_STR(format))) == NULL)
RETURN_ERROR("Cannot allocate conf.date.format in wchar_t*");
if (format == NULL || wcscmp(wformat, L"d/m/y") == 0) {
dt[0] = init[2];
dt[1] = init[1];
dt[2] = init[0];
} else if (wcscmp(wformat, L"m/d/y") == 0) {
dt[0] = init[1];
dt[1] = init[2];
dt[2] = init[0];
} else if (wcscmp(wformat, L"y/m/d") == 0) {
dt[0] = init[0];
dt[1] = init[1];
dt[2] = init[2];
} else
RETURN_FMTERROR("Invalid conf.date.format=\"%s\"", format);
free(wformat);
return (0);
}
int
bsddialog_datebox(struct bsddialog_conf *conf, const char *text, int rows,
int cols, unsigned int *year, unsigned int *month, unsigned int *day)
{
bool loop, focusbuttons;
int retval, i, sel, yy, mm, dd;
wint_t input;
struct dateitem di[3];
struct dialog d;
CHECK_PTR(year);
CHECK_PTR(month);
CHECK_PTR(day);
minyear = MIN_YEAR_DATE;
maxyear = MAX_YEAR_DATE;
init_date(year, month, day, &yy, &mm, &dd);
if (prepare_dialog(conf, text, rows, cols, &d) != 0)
return (BSDDIALOG_ERROR);
set_buttons(&d, true, OK_LABEL, CANCEL_LABEL);
if (build_dateitem(conf->date.format, &yy, &mm, &dd, di) != 0)
return (BSDDIALOG_ERROR);
if (datebox_redraw(&d, di) != 0)
return (BSDDIALOG_ERROR);
sel = -1;
loop = focusbuttons = true;
while (loop) {
for (i = 0; i < 3; i++)
drawsquare(conf, di[i].win, LOWERED, di[i].fmt,
*di[i].value, sel == i);
doupdate();
if (get_wch(&input) == ERR)
continue;
switch(input) {
case KEY_ENTER:
case 10: /* Enter */
if (focusbuttons || conf->button.always_active) {
retval = BUTTONVALUE(d.bs);
loop = false;
}
break;
case 27: /* Esc */
if (conf->key.enable_esc) {
retval = BSDDIALOG_ESC;
loop = false;
}
break;
case KEY_RIGHT:
case '\t': /* TAB */
if (focusbuttons) {
d.bs.curr++;
focusbuttons = d.bs.curr < (int)d.bs.nbuttons ?
true : false;
if (focusbuttons == false) {
sel = 0;
d.bs.curr = conf->button.always_active ?
0 : -1;
}
} else {
sel++;
focusbuttons = sel > 2 ? true : false;
if (focusbuttons) {
d.bs.curr = 0;
}
}
DRAW_BUTTONS(d);
break;
case KEY_LEFT:
if (focusbuttons) {
d.bs.curr--;
focusbuttons = d.bs.curr < 0 ? false : true;
if (focusbuttons == false) {
sel = 2;
d.bs.curr = conf->button.always_active ?
0 : -1;
}
} else {
sel--;
focusbuttons = sel < 0 ? true : false;
if (focusbuttons)
d.bs.curr = (int)d.bs.nbuttons - 1;
}
DRAW_BUTTONS(d);
break;
case KEY_UP:
if (focusbuttons) {
sel = 0;
focusbuttons = false;
d.bs.curr = conf->button.always_active ? 0 : -1;
DRAW_BUTTONS(d);
} else {
datectl(di[sel].up, &yy, &mm, &dd);
}
break;
case KEY_DOWN:
if (focusbuttons)
break;
datectl(di[sel].down, &yy, &mm, &dd);
break;
case KEY_F(1):
if (conf->key.f1_file == NULL &&
conf->key.f1_message == NULL)
break;
if (f1help_dialog(conf) != 0)
return (BSDDIALOG_ERROR);
if (datebox_redraw(&d, di) != 0)
return (BSDDIALOG_ERROR);
break;
case KEY_RESIZE:
if (datebox_redraw(&d, di) != 0)
return (BSDDIALOG_ERROR);
break;
default:
if (shortcut_buttons(input, &d.bs)) {
DRAW_BUTTONS(d);
doupdate();
retval = BUTTONVALUE(d.bs);
loop = false;
}
}
}
*year = yy;
*month = mm;
*day = dd;
for (i = 0; i < 3 ; i++)
delwin(di[i].win);
end_dialog(&d);
return (retval);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,96 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-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 <sys/param.h>
#include <curses.h>
#include "bsddialog.h"
#include "lib_util.h"
static int
infobox_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h,
int *w, const char *text)
{
int htext, wtext;
if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) {
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, TEXTHMARGINS + 1, NULL);
if (rows == BSDDIALOG_AUTOSIZE)
*h = widget_min_height(conf, htext, 0, false);
return (0);
}
static int infobox_checksize(int rows, int cols)
{
if (cols < HBORDERS)
RETURN_ERROR("Few cols, infobox needs at least width 2");
if (rows < VBORDERS)
RETURN_ERROR("Infobox needs at least height 2");
return (0);
}
/* API */
int
bsddialog_infobox(struct bsddialog_conf *conf, const char *text, int rows,
int cols)
{
int y, x, h, w;
WINDOW *shadow, *widget, *textpad;
if (set_widget_size(conf, rows, cols, &h, &w) != 0)
return (BSDDIALOG_ERROR);
if (infobox_autosize(conf, rows, cols, &h, &w, text) != 0)
return (BSDDIALOG_ERROR);
if (infobox_checksize(h, w) != 0)
return (BSDDIALOG_ERROR);
if (set_widget_position(conf, &y, &x, h, w) != 0)
return (BSDDIALOG_ERROR);
if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text,
NULL, false) != 0)
return (BSDDIALOG_ERROR);
pnoutrefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-2,
x+w-TEXTHMARGIN);
doupdate();
end_dialog(conf, shadow, widget, textpad);
return (BSDDIALOG_OK);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2022 Alfonso Sabato Siciliano
* Copyright (c) 2021-2023 Alfonso Sabato Siciliano
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -28,117 +28,136 @@
#ifndef _LIBBSDDIALOG_UTIL_H_
#define _LIBBSDDIALOG_UTIL_H_
#define HBORDERS 2
#define VBORDERS 2
#define BORDER 1
#define BORDERS (BORDER + BORDER)
#define TEXTHMARGIN 1
#define TEXTHMARGINS (TEXTHMARGIN + TEXTHMARGIN)
#define HBUTTONS 2
#define OK_LABEL "OK"
#define CANCEL_LABEL "Cancel"
/* theme utils */
/* theme util */
extern struct bsddialog_theme t;
extern bool hastermcolors;
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
/* debug */
#define BSDDIALOG_DEBUG(y,x,fmt, ...) do { \
mvprintw(y, x, fmt, __VA_ARGS__); \
refresh(); \
#define BSDDIALOG_DEBUG(y,x,fmt, ...) do { \
mvprintw(y, x, fmt, __VA_ARGS__); \
refresh(); \
} while (0)
/* error and diagnostic */
#define RETURN_ERROR(str) do { \
set_error_string(str); \
return (BSDDIALOG_ERROR); \
} while (0)
#define RETURN_FMTERROR(fmt, ...) do { \
set_fmt_error_string(fmt, __VA_ARGS__); \
return (BSDDIALOG_ERROR); \
} while (0)
/* check ptr */
#define CHECK_PTR(p) do { \
if (p == NULL) \
RETURN_ERROR("*" #p " is NULL"); \
} while (0)
#define CHECK_ARRAY(nitem, a) do { \
if(nitem > 0 && a == NULL) \
RETURN_FMTERROR(#nitem " is %d but *" #a " is NULL", nitem); \
} while (0)
/* widget utils */
#define TEXTPAD(d, downnotext) rtextpad(d, 0, 0, 0, downnotext)
#define SCREENLINES (getmaxy(stdscr))
#define SCREENCOLS (getmaxx(stdscr))
#define CHECK_STR(s) (s == NULL ? "" : s)
#define DRAW_BUTTONS(d) do { \
draw_buttons(&d); \
wnoutrefresh(d.widget); \
} while (0)
/* date */
#define ISLEAP(year) ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
/* internal types */
enum elevation { RAISED, LOWERED };
/* unicode */
struct buttons {
unsigned int nbuttons;
#define MAXBUTTONS 10 /* 3left + ok + extra + cancel + help + 3 right */
const char *label[MAXBUTTONS];
bool shortcut;
wchar_t first[MAXBUTTONS];
int value[MAXBUTTONS];
int curr;
#define BUTTONVALUE(bs) bs.value[bs.curr]
unsigned int sizebutton; /* including left and right delimiters */
};
struct dialog {
bool built; /* true after the first draw_dialog() */
struct bsddialog_conf *conf; /* Checked API conf */
WINDOW *widget; /* Size and position refer to widget */
int y, x; /* Current position, API conf.[y|x]: -1, >=0 */
int rows, cols; /* API rows and cols: -1, 0, >0 */
int h, w; /* Current height and width */
const char *text; /* Checked API text, at least "" */
WINDOW *textpad; /* Fake for textbox */
struct buttons bs; /* bs.nbuttons = 0 for no buttons */
WINDOW *shadow;
};
/* error and diagnostic */
const char *get_error_string(void);
void set_error_string(const char *string);
void set_fmt_error_string(const char *fmt, ...);
/* multicolumn character string */
unsigned int strcols(const char *mbstring);
int str_props(const char *mbstring, unsigned int *cols, bool *has_multi_col);
void mvwaddwch(WINDOW *w, int y, int x, wchar_t wch);
wchar_t* alloc_mbstows(const char *mbstring);
/* error buffer */
const char *get_error_string(void);
void set_error_string(const char *string);
#define RETURN_ERROR(str) do { \
set_error_string(str); \
return (BSDDIALOG_ERROR); \
} while (0)
/* buttons */
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 */
};
#define BUTTON_OK_LABEL "OK"
#define BUTTON_CANCEL_LABEL "Cancel"
void
get_buttons(struct bsddialog_conf *conf, struct buttons *bs,
const char *yesoklabel, const char *nocancellabel);
void
draw_buttons(WINDOW *window, struct buttons bs, bool shortcut);
int buttons_min_width(struct buttons bs);
set_buttons(struct dialog *d, bool shortcut, const char *oklabel,
const char *canclabel);
void draw_buttons(struct dialog *d);
bool shortcut_buttons(wint_t key, struct buttons *bs);
/* help window with F1 key */
int f1help(struct bsddialog_conf *conf);
/* widget utils */
int hide_dialog(struct dialog *d);
int f1help_dialog(struct bsddialog_conf *conf);
/* cleaner */
int hide_widget(int y, int x, int h, int w, bool withshadow);
void
draw_borders(struct bsddialog_conf *conf, WINDOW *win, enum elevation elev);
/* (auto) size and (auto) position */
#define SCREENLINES (getmaxy(stdscr))
#define SCREENCOLS (getmaxx(stdscr))
void
update_box(struct bsddialog_conf *conf, WINDOW *win, int y, int x, int h, int w,
enum elevation elev);
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 widget_max_height(struct bsddialog_conf *conf);
int widget_max_width(struct bsddialog_conf *conf);
int
widget_min_height(struct bsddialog_conf *conf, int htext, int minwidget,
bool withbuttons);
int
widget_min_width(struct bsddialog_conf *conf, int wtext, int minwidget,
struct buttons *bs);
void
rtextpad(struct dialog *d, int ytext, int xtext, int upnotext, int downnotext);
/* (auto) sizing and (auto) position */
int
set_widget_size(struct bsddialog_conf *conf, int rows, int cols, int *h,
int *w);
int
set_widget_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h,
int *w, const char *text, int *rowstext, struct buttons *bs, int hnotext,
int minw);
int widget_checksize(int h, int w, struct buttons *bs, int hnotext, int minw);
int
set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w);
/* widget builders */
enum elevation { RAISED, LOWERED };
int dialog_size_position(struct dialog *d, int hnotext, int minw, int *htext);
void
draw_borders(struct bsddialog_conf *conf, WINDOW *win, int rows, int cols,
enum elevation elev);
WINDOW *
new_boxed_window(struct bsddialog_conf *conf, int y, int x, int rows, int cols,
enum elevation elev);
/* dialog */
void end_dialog(struct dialog *d);
int draw_dialog(struct dialog *d);
int
new_dialog(struct bsddialog_conf *conf, WINDOW **shadow, WINDOW **widget, int y,
int x, int h, int w, WINDOW **textpad, const char *text, struct buttons *bs,
bool shortcutbuttons);
int
update_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget,
int y, int x, int h, int w, WINDOW *textpad, const char *text,
struct buttons *bs, bool shortcutbuttons);
void
end_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget,
WINDOW *textpad);
prepare_dialog(struct bsddialog_conf *conf, const char *text, int rows,
int cols, struct dialog *d);
#endif

View File

@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2022 Alfonso Sabato Siciliano
* Copyright (c) 2021-2023 Alfonso Sabato Siciliano
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -26,15 +26,15 @@
*/
#include <curses.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "bsddialog.h"
#include "bsddialog_theme.h"
#include "lib_util.h"
#define COLSPERROW 10 /* Default conf.text.columns_per_row */
#define DEFAULT_COLS_PER_ROW 10 /* Default conf.text.columns_per_row */
static bool in_bsddialog_mode = false;
int bsddialog_init_notheme(void)
{
@ -55,6 +55,7 @@ int bsddialog_init_notheme(void)
bsddialog_end();
RETURN_ERROR("Cannot init curses (keypad and cursor)");
}
in_bsddialog_mode = true;
c = 1;
error += start_color();
@ -83,6 +84,7 @@ int bsddialog_init(void)
if (bsddialog_set_default_theme(theme) != 0) {
bsddialog_end();
in_bsddialog_mode = false;
return (BSDDIALOG_ERROR);
}
@ -93,15 +95,18 @@ int bsddialog_end(void)
{
if (endwin() != OK)
RETURN_ERROR("Cannot end curses (endwin)");
in_bsddialog_mode = false;
return (BSDDIALOG_OK);
}
int bsddialog_backtitle(struct bsddialog_conf *conf, const char *backtitle)
{
CHECK_PTR(conf);
move(0, 1);
clrtoeol();
addstr(backtitle);
addstr(CHECK_STR(backtitle));
if (conf->no_lines != true)
mvhline(1, 1, conf->ascii_lines ? '-' : ACS_HLINE,
SCREENCOLS - 2);
@ -111,6 +116,11 @@ int bsddialog_backtitle(struct bsddialog_conf *conf, const char *backtitle)
return (BSDDIALOG_OK);
}
bool bsddialog_inmode(void)
{
return (in_bsddialog_mode);
}
const char *bsddialog_geterror(void)
{
return (get_error_string());
@ -118,25 +128,25 @@ const char *bsddialog_geterror(void)
int bsddialog_initconf(struct bsddialog_conf *conf)
{
if (conf == NULL)
RETURN_ERROR("conf is NULL");
if (sizeof(*conf) != sizeof(struct bsddialog_conf))
RETURN_ERROR("Bad conf size");
CHECK_PTR(conf);
memset(conf, 0, sizeof(struct bsddialog_conf));
conf->y = BSDDIALOG_CENTER;
conf->x = BSDDIALOG_CENTER;
conf->shadow = true;
conf->text.cols_per_row = COLSPERROW;
conf->text.cols_per_row = DEFAULT_COLS_PER_ROW;
return (BSDDIALOG_OK);
}
int bsddialog_clearterminal(void)
void bsddialog_refresh(void)
{
if (clear() != OK)
RETURN_ERROR("Cannot clear the terminal");
refresh();
}
return (BSDDIALOG_OK);
void bsddialog_clear(unsigned int y)
{
move(y, 0);
clrtobot();
refresh();
}

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2022 Alfonso Sabato Siciliano
* Copyright (c) 2021-2023 Alfonso Sabato Siciliano
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -25,209 +25,165 @@
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <curses.h>
#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, bool *hastext, struct buttons bs)
struct scroll {
int ypad; /* y scrollable pad */
int htext; /* real h text to draw, to use with htextpad */
int htextpad; /* h textpad, draw_dialog() set at least 1 */
int printrows; /* h - BORDER - HBUTTONS - BORDER */
};
static void textupdate(struct dialog *d, struct scroll *s)
{
int htext, wtext;
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 (s->htext > 0 && s->htextpad > s->printrows) {
wattron(d->widget, t.dialog.arrowcolor);
mvwprintw(d->widget, d->h - HBUTTONS - BORDER,
d->w - 4 - TEXTHMARGIN - BORDER,
"%3d%%", 100 * (s->ypad + s->printrows) / s->htextpad);
wattroff(d->widget, t.dialog.arrowcolor);
wnoutrefresh(d->widget);
}
rtextpad(d, s->ypad, 0, 0, HBUTTONS);
}
if (cols == BSDDIALOG_AUTOSIZE)
*w = widget_min_width(conf, wtext, 0, &bs);
static int message_size_position(struct dialog *d, int *htext)
{
int minw;
if (rows == BSDDIALOG_AUTOSIZE)
*h = widget_min_height(conf, htext, 0, true);
if (set_widget_size(d->conf, d->rows, d->cols, &d->h, &d->w) != 0)
return (BSDDIALOG_ERROR);
if (set_widget_autosize(d->conf, d->rows, d->cols, &d->h, &d->w,
d->text, (*htext < 0) ? htext : NULL, &d->bs, 0, 0) != 0)
return (BSDDIALOG_ERROR);
minw = (*htext > 0) ? 1 + TEXTHMARGINS : 0 ;
if (widget_checksize(d->h, d->w, &d->bs, MIN(*htext, 1), minw) != 0)
return (BSDDIALOG_ERROR);
if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0)
return (BSDDIALOG_ERROR);
return (0);
}
static int
message_checksize(int rows, int cols, bool hastext, struct buttons bs)
static int message_draw(struct dialog *d, struct scroll *s)
{
int mincols;
int unused;
mincols = VBORDERS;
mincols += buttons_min_width(bs);
if (d->built) {
hide_dialog(d);
refresh(); /* Important for decreasing screen */
}
if (message_size_position(d, &s->htext) != 0)
return (BSDDIALOG_ERROR);
if (draw_dialog(d) != 0)
return (BSDDIALOG_ERROR);
if (d->built)
refresh(); /* Important to fix grey lines expanding screen */
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 4 rows");
if (hastext && rows < HBORDERS + 2 /*buttons*/ + 1 /* text row */)
RETURN_ERROR("Msgbox and Yesno with text need at least 5 rows");
s->printrows = d->h - BORDER - HBUTTONS - BORDER;
s->ypad = 0;
getmaxyx(d->textpad, s->htextpad, unused);
unused++; /* fix unused error */
return (0);
}
static void
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 (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);
}
pnoutrefresh(textpad, ytextpad, 0, y+1, x+2, y+h-4, x+w-2);
}
static int
do_message(struct bsddialog_conf *conf, const char *text, int rows, int cols,
struct buttons bs)
const char *oklabel, const char *cancellabel)
{
bool hastext, loop;
int y, x, h, w, retval, ytextpad, htextpad, printrows, unused;
WINDOW *widget, *textpad, *shadow;
bool loop;
int retval;
wint_t input;
struct scroll s;
struct dialog d;
if (set_widget_size(conf, rows, cols, &h, &w) != 0)
if (prepare_dialog(conf, text, rows, cols, &d) != 0)
return (BSDDIALOG_ERROR);
if (message_autosize(conf, rows, cols, &h, &w, text, &hastext, bs) != 0)
return (BSDDIALOG_ERROR);
if (message_checksize(h, w, hastext, bs) != 0)
return (BSDDIALOG_ERROR);
if (set_widget_position(conf, &y, &x, h, w) != 0)
set_buttons(&d, true, oklabel, cancellabel);
s.htext = -1;
if(message_draw(&d, &s) != 0)
return (BSDDIALOG_ERROR);
if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs,
true) != 0)
return (BSDDIALOG_ERROR);
printrows = h - 4;
ytextpad = 0;
getmaxyx(textpad, htextpad, unused);
unused++; /* fix unused error */
loop = true;
while (loop) {
textupdate(widget, textpad, htextpad, ytextpad, hastext);
textupdate(&d, &s);
doupdate();
if (get_wch(&input) == ERR)
continue;
switch (input) {
case KEY_ENTER:
case 10: /* Enter */
retval = bs.value[bs.curr];
retval = BUTTONVALUE(d.bs);
loop = false;
break;
case 27: /* Esc */
if (conf->key.enable_esc) {
if (d.conf->key.enable_esc) {
retval = BSDDIALOG_ESC;
loop = false;
}
break;
case '\t': /* TAB */
bs.curr = (bs.curr + 1) % bs.nbuttons;
draw_buttons(widget, bs, true);
wnoutrefresh(widget);
case KEY_RIGHT:
d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons;
DRAW_BUTTONS(d);
break;
case KEY_LEFT:
if (bs.curr > 0) {
bs.curr--;
draw_buttons(widget, bs, true);
wnoutrefresh(widget);
}
break;
case KEY_RIGHT:
if (bs.curr < (int)bs.nbuttons - 1) {
bs.curr++;
draw_buttons(widget, bs, true);
wnoutrefresh(widget);
}
d.bs.curr--;
if (d.bs.curr < 0)
d.bs.curr = d.bs.nbuttons - 1;
DRAW_BUTTONS(d);
break;
case KEY_UP:
if (ytextpad > 0)
ytextpad--;
if (s.ypad > 0)
s.ypad--;
break;
case KEY_DOWN:
if (ytextpad + printrows < htextpad)
ytextpad++;
if (s.ypad + s.printrows < s.htextpad)
s.ypad++;
break;
case KEY_HOME:
ytextpad = 0;
s.ypad = 0;
break;
case KEY_END:
ytextpad = htextpad - printrows;
ytextpad = ytextpad < 0 ? 0 : ytextpad;
s.ypad = MAX(s.htextpad - s.printrows, 0);
break;
case KEY_PPAGE:
ytextpad -= printrows;
ytextpad = ytextpad < 0 ? 0 : ytextpad;
s.ypad = MAX(s.ypad - s.printrows, 0);
break;
case KEY_NPAGE:
ytextpad += printrows;
if (ytextpad + printrows > htextpad)
ytextpad = htextpad - printrows;
s.ypad += s.printrows;
if (s.ypad + s.printrows > s.htextpad)
s.ypad = s.htextpad - s.printrows;
break;
case KEY_F(1):
if (conf->key.f1_file == NULL &&
conf->key.f1_message == NULL)
if (d.conf->key.f1_file == NULL &&
d.conf->key.f1_message == NULL)
break;
if (f1help(conf) != 0)
if (f1help_dialog(d.conf) != 0)
return (BSDDIALOG_ERROR);
/* No break, screen size can change */
if(message_draw(&d, &s) != 0)
return (BSDDIALOG_ERROR);
break;
case KEY_RESIZE:
/* Important for decreasing screen */
hide_widget(y, x, h, w, conf->shadow);
refresh();
if (set_widget_size(conf, rows, cols, &h, &w) != 0)
if(message_draw(&d, &s) != 0)
return (BSDDIALOG_ERROR);
if (message_autosize(conf, rows, cols, &h, &w, text,
NULL, bs) != 0)
return (BSDDIALOG_ERROR);
if (message_checksize(h, w, hastext, 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)
return (BSDDIALOG_ERROR);
printrows = h - 4;
getmaxyx(textpad, htextpad, unused);
ytextpad = 0;
textupdate(widget, textpad, htextpad, ytextpad, hastext);
/* Important to fix grey lines expanding screen */
refresh();
break;
default:
if (shortcut_buttons(input, &bs)) {
retval = bs.value[bs.curr];
if (shortcut_buttons(input, &d.bs)) {
DRAW_BUTTONS(d);
doupdate();
retval = BUTTONVALUE(d.bs);
loop = false;
}
}
}
end_dialog(conf, shadow, widget, textpad);
end_dialog(&d);
return (retval);
}
@ -237,20 +193,34 @@ int
bsddialog_msgbox(struct bsddialog_conf *conf, const char *text, int rows,
int cols)
{
struct buttons bs;
get_buttons(conf, &bs, BUTTON_OK_LABEL, NULL);
return (do_message(conf, text, rows, cols, bs));
return (do_message(conf, text, rows, cols, OK_LABEL, NULL));
}
int
bsddialog_yesno(struct bsddialog_conf *conf, const char *text, int rows,
int cols)
{
struct buttons bs;
return (do_message(conf, text, rows, cols, "Yes", "No"));
}
get_buttons(conf, &bs, "Yes", "No");
int
bsddialog_infobox(struct bsddialog_conf *conf, const char *text, int rows,
int cols)
{
int htext;
struct dialog d;
return (do_message(conf, text, rows, cols, bs));
}
if (prepare_dialog(conf, text, rows, cols, &d) != 0)
return (BSDDIALOG_ERROR);
htext = -1;
if (message_size_position(&d, &htext) != 0)
return (BSDDIALOG_ERROR);
if (draw_dialog(&d) != 0)
return (BSDDIALOG_ERROR);
TEXTPAD(&d, 0);
doupdate();
end_dialog(&d);
return (BSDDIALOG_OK);
}

View File

@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2022 Alfonso Sabato Siciliano
* Copyright (c) 2021-2023 Alfonso Sabato Siciliano
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -25,166 +25,177 @@
* SUCH DAMAGE.
*/
#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)
struct scrolltext {
WINDOW *pad;
int ypad;
int xpad;
int ys;
int ye;
int xs;
int xe;
int hpad;
int wpad;
int margin; /* 2 with multicolumn char, 0 otherwise */
int printrows; /* d.h - BORDERS - HBUTTONS */
};
static void updateborders(struct dialog *d, struct scrolltext *st)
{
int h, w;
chtype arrowch, borderch;
getmaxyx(widget, h, w);
if (conf->no_lines)
if (d->conf->no_lines)
borderch = ' ';
else if (conf->ascii_lines)
else if (d->conf->ascii_lines)
borderch = '|';
else
borderch = ACS_VLINE;
if (xpad > 0) {
arrowch = conf->ascii_lines ? '<' : ACS_LARROW;
arrowch |= A_ATTRIBUTES & t.dialog.arrowcolor;
if (st->xpad > 0) {
arrowch = d->conf->ascii_lines ? '<' : ACS_LARROW;
arrowch |= t.dialog.arrowcolor;
} else {
arrowch = borderch;
arrowch |= A_ATTRIBUTES & t.dialog.lineraisecolor;
arrowch |= t.dialog.lineraisecolor;
}
mvwvline(widget, (h / 2) - 2, 0, arrowch, 4);
mvwvline(d->widget, (d->h / 2) - 2, 0, arrowch, 4);
if (xpad + w-2-padmargin < wpad) {
arrowch = conf->ascii_lines ? '>' : ACS_RARROW;
arrowch |= A_ATTRIBUTES & t.dialog.arrowcolor;
if (st->xpad + d->w - 2 - st->margin < st->wpad) {
arrowch = d->conf->ascii_lines ? '>' : ACS_RARROW;
arrowch |= t.dialog.arrowcolor;
} else {
arrowch = borderch;
arrowch |= A_ATTRIBUTES & t.dialog.linelowercolor;
arrowch |= t.dialog.linelowercolor;
}
mvwvline(widget, (h / 2) - 2, w - 1, arrowch, 4);
mvwvline(d->widget, (d->h / 2) - 2, d->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);
if (st->hpad > d->h - 4) {
wattron(d->widget, t.dialog.arrowcolor);
mvwprintw(d->widget, d->h - 3, d->w - 6,
"%3d%%", 100 * (st->ypad + d->h - 4) / st->hpad);
wattroff(d->widget, t.dialog.arrowcolor);
}
}
static void
textbox_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h,
int *w, int hpad, int wpad, int padmargin, struct buttons bs)
static int textbox_size_position(struct dialog *d, struct scrolltext *st)
{
if (cols == BSDDIALOG_AUTOSIZE)
*w = widget_min_width(conf, 0, wpad + padmargin, &bs);
int minw;
if (rows == BSDDIALOG_AUTOSIZE)
*h = widget_min_height(conf, 0, hpad, true);
if (set_widget_size(d->conf, d->rows, d->cols, &d->h, &d->w) != 0)
return (BSDDIALOG_ERROR);
if (set_widget_autosize(d->conf, d->rows, d->cols, &d->h, &d->w,
d->text, NULL, &d->bs, st->hpad, st->wpad + st->margin) != 0)
return (BSDDIALOG_ERROR);
minw = (st->wpad > 0) ? 2 /*multicolumn char*/ + st->margin : 0 ;
if (widget_checksize(d->h, d->w, &d->bs, MIN(st->hpad, 1), minw) != 0)
return (BSDDIALOG_ERROR);
if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0)
return (BSDDIALOG_ERROR);
return (0);
}
static int
textbox_checksize(int rows, int cols, int hpad, struct buttons bs)
static int textbox_draw(struct dialog *d, struct scrolltext *st)
{
int mincols;
if (d->built) {
hide_dialog(d);
refresh(); /* Important for decreasing screen */
}
if (textbox_size_position(d, st) != 0)
return (BSDDIALOG_ERROR);
if (draw_dialog(d) != 0)
return (BSDDIALOG_ERROR);
if (d->built)
refresh(); /* Important to fix grey lines expanding screen */
mincols = VBORDERS;
mincols += buttons_min_width(bs);
if (cols < mincols)
RETURN_ERROR("Few cols for the textbox");
if (rows < 4 /* HBORDERS + button*/ + (hpad > 0 ? 1 : 0))
RETURN_ERROR("Few rows for the textbox");
st->ys = d->y + 1;
st->xs = (st->margin == 0) ? d->x + 1 : d->x + 2;
st->ye = st->ys + d->h - 5;
st->xe = st->xs + d->w - 3 - st->margin;
st->ypad = st->xpad = 0;
st->printrows = d->h-4;
return (0);
}
/* API */
int
bsddialog_textbox(struct bsddialog_conf *conf, const char* file, int rows,
bsddialog_textbox(struct bsddialog_conf *conf, const char *file, int rows,
int cols)
{
bool loop, has_multi_col;
int i, retval, y, x, h, w;
int hpad, wpad, ypad, xpad, ys, ye, xs, xe, padmargin, printrows;
bool loop, has_multicol_ch;
int i, retval;
unsigned int defaulttablen, linecols;
wint_t input;
char buf[BUFSIZ];
FILE *fp;
struct buttons bs;
WINDOW *shadow, *widget, *pad;
struct scrolltext st;
struct dialog d;
if (file == NULL)
RETURN_ERROR("*file is NULL");
if ((fp = fopen(file, "r")) == NULL)
RETURN_ERROR("Cannot open file");
RETURN_FMTERROR("Cannot open file \"%s\"", file);
if (prepare_dialog(conf, "" /* fake */, rows, cols, &d) != 0)
return (BSDDIALOG_ERROR);
set_buttons(&d, true, "EXIT", NULL);
defaulttablen = TABSIZE;
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;
if (conf->text.tablen > 0)
set_tabsize(conf->text.tablen);
st.hpad = 1;
st.wpad = 1;
st.pad = newpad(st.hpad, st.wpad);
wbkgd(st.pad, t.dialog.color);
st.margin = 0;
i = 0;
while (fgets(buf, BUFSIZ, fp) != NULL) {
if (str_props(buf, &linecols, &has_multi_col) != 0)
if (str_props(buf, &linecols, &has_multicol_ch) != 0)
continue;
if ((int)linecols > wpad) {
wpad = linecols;
wresize(pad, hpad, wpad);
if ((int)linecols > st.wpad) {
st.wpad = linecols;
wresize(st.pad, st.hpad, st.wpad);
}
if (i > hpad-1) {
hpad++;
wresize(pad, hpad, wpad);
if (i > st.hpad-1) {
st.hpad++;
wresize(st.pad, st.hpad, st.wpad);
}
mvwaddstr(pad, i, 0, buf);
mvwaddstr(st.pad, i, 0, buf);
i++;
if (has_multi_col)
padmargin = 2;
if (has_multicol_ch)
st.margin = 2;
}
fclose(fp);
set_tabsize(defaulttablen);
set_tabsize(defaulttablen); /* reset because it is curses global */
get_buttons(conf, &bs, "EXIT", NULL);
bs.curr = 0;
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, padmargin, bs);
if (textbox_checksize(h, w, hpad, bs) != 0)
return (BSDDIALOG_ERROR);
if (set_widget_position(conf, &y, &x, h, w) != 0)
if (textbox_draw(&d, &st) != 0)
return (BSDDIALOG_ERROR);
if (new_dialog(conf, &shadow, &widget, y, x, h, w, NULL, NULL, &bs,
true) != 0)
return (BSDDIALOG_ERROR);
ys = y + 1;
xs = (padmargin == 0) ? x + 1 : x + 2;
ye = ys + h - 5;
xe = xs + w - 3 - padmargin;
ypad = xpad = 0;
printrows = h-4;
loop = true;
while (loop) {
updateborders(conf, widget, padmargin, hpad, wpad, ypad, xpad);
updateborders(&d, &st);
/*
* 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);
wrefresh(d.widget);
prefresh(st.pad, st.ypad, st.xpad, st.ys, st.xs, st.ye, st.xe);
if (get_wch(&input) == ERR)
continue;
if (shortcut_buttons(input, &d.bs)) {
DRAW_BUTTONS(d);
doupdate();
retval = BUTTONVALUE(d.bs);
break; /* loop */
}
switch(input) {
case KEY_ENTER:
case 10: /* Enter */
@ -197,84 +208,63 @@ bsddialog_textbox(struct bsddialog_conf *conf, const char* file, int rows,
loop = false;
}
break;
case '\t': /* TAB */
d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons;
DRAW_BUTTONS(d);
break;
case KEY_HOME:
ypad = 0;
st.ypad = 0;
break;
case KEY_END:
ypad = hpad - printrows;
ypad = ypad < 0 ? 0 : ypad;
st.ypad = MAX(st.hpad - st.printrows, 0);
break;
case KEY_PPAGE:
ypad -= printrows;
ypad = ypad < 0 ? 0 : ypad;
st.ypad = MAX(st.ypad - st.printrows, 0);
break;
case KEY_NPAGE:
ypad += printrows;
if (ypad + printrows > hpad)
ypad = hpad - printrows;
st.ypad += st.printrows;
if (st.ypad + st.printrows > st.hpad)
st.ypad = st.hpad - st.printrows;
break;
case '0':
xpad = 0;
st.xpad = 0;
break;
case KEY_LEFT:
case 'h':
xpad = xpad > 0 ? xpad - 1 : 0;
st.xpad = MAX(st.xpad - 1, 0);
break;
case KEY_RIGHT:
case 'l':
xpad = (xpad + w-2-padmargin) < wpad ? xpad + 1 : xpad;
if (st.xpad + d.w - 2 - st.margin < st.wpad)
st.xpad++;
break;
case KEY_UP:
case 'k':
ypad = ypad > 0 ? ypad - 1 : 0;
st.ypad = MAX(st.ypad - 1, 0);
break;
case KEY_DOWN:
case'j':
ypad = ypad + printrows <= hpad -1 ? ypad + 1 : ypad;
if (st.ypad + st.printrows <= st.hpad -1)
st.ypad++;
break;
case KEY_F(1):
if (conf->key.f1_file == NULL &&
conf->key.f1_message == NULL)
break;
if (f1help(conf) != 0)
if (f1help_dialog(conf) != 0)
return (BSDDIALOG_ERROR);
/* No break, screen size can change */
case KEY_RESIZE:
/* Important for decreasing screen */
hide_widget(y, x, h, w, conf->shadow);
refresh();
if (set_widget_size(conf, rows, cols, &h, &w) != 0)
if (textbox_draw(&d, &st) != 0)
return (BSDDIALOG_ERROR);
break;
case KEY_RESIZE:
if (textbox_draw(&d, &st) != 0)
return (BSDDIALOG_ERROR);
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)
return (BSDDIALOG_ERROR);
ys = y + 1;
xs = (padmargin == 0) ? x + 1 : x + 2;
ye = ys + h - 5;
xe = xs + w - 3 - padmargin;
ypad = xpad = 0;
printrows = h - 4;
if (update_dialog(conf, shadow, widget, y, x, h, w,
NULL, NULL, &bs, true) != 0)
return (BSDDIALOG_ERROR);
/* Important to fix grey lines expanding screen */
refresh();
break;
default:
if (shortcut_buttons(input, &bs)) {
retval = bs.value[bs.curr];
loop = false;
}
}
}
end_dialog(conf, shadow, widget, pad);
delwin(st.pad);
end_dialog(&d);
return (retval);
}

View File

@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2022 Alfonso Sabato Siciliano
* Copyright (c) 2021-2023 Alfonso Sabato Siciliano
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -32,132 +32,104 @@
#include "lib_util.h"
#define GET_COLOR(bg, fg) (COLOR_PAIR(bg * 8 + fg +1))
#define WHITE GET_COLOR(COLOR_WHITE, COLOR_BLACK)
#define BLACK GET_COLOR(COLOR_WHITE, COLOR_BLACK) | A_REVERSE
#define NFLAGS 6
struct bsddialog_theme t;
bool hastermcolors;
static struct bsddialog_theme bsddialogtheme = {
.screen.color = GET_COLOR(COLOR_BLACK, COLOR_CYAN),
struct flag_converter {
unsigned int public;
unsigned int private;
};
.shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK),
.shadow.y = 1,
.shadow.x = 2,
.dialog.delimtitle = true,
.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 flag_converter flagconv[NFLAGS] = {
{BSDDIALOG_BLINK, A_BLINK},
{BSDDIALOG_BOLD, A_BOLD},
{BSDDIALOG_HALFBRIGHT, A_DIM},
{BSDDIALOG_HIGHLIGHT, A_STANDOUT},
{BSDDIALOG_REVERSE, A_REVERSE},
{BSDDIALOG_UNDERLINE, A_UNDERLINE}
};
static struct bsddialog_theme blackwhite = {
#define fg COLOR_WHITE
#define bk COLOR_BLACK
.screen.color = GET_COLOR(fg, bk),
.screen.color = WHITE,
.shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK),
.shadow.y = 1,
.shadow.x = 2,
.shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK),
.shadow.y = 1,
.shadow.x = 2,
.dialog.delimtitle = true,
.dialog.titlecolor = GET_COLOR(fg, bk),
.dialog.lineraisecolor = GET_COLOR(fg, bk),
.dialog.linelowercolor = GET_COLOR(fg, bk),
.dialog.color = GET_COLOR(fg, bk),
.dialog.bottomtitlecolor = GET_COLOR(fg, bk),
.dialog.arrowcolor = GET_COLOR(fg, bk),
.dialog.titlecolor = WHITE,
.dialog.lineraisecolor = WHITE,
.dialog.linelowercolor = WHITE,
.dialog.color = WHITE,
.dialog.bottomtitlecolor = WHITE,
.dialog.arrowcolor = WHITE,
.menu.f_selectorcolor = GET_COLOR(fg, bk) | A_REVERSE,
.menu.selectorcolor = GET_COLOR(fg, bk),
.menu.f_desccolor = GET_COLOR(fg, bk) | A_REVERSE,
.menu.desccolor = GET_COLOR(fg, bk),
.menu.f_namecolor = GET_COLOR(fg, bk) | A_REVERSE,
.menu.namecolor = GET_COLOR(fg, bk),
.menu.namesepcolor = GET_COLOR(fg, bk),
.menu.descsepcolor = GET_COLOR(fg, bk),
.menu.f_shortcutcolor = GET_COLOR(fg, bk) | A_UNDERLINE | A_REVERSE,
.menu.shortcutcolor = GET_COLOR(fg, bk) | A_UNDERLINE,
.menu.bottomdesccolor = GET_COLOR(fg, bk),
.menu.f_prefixcolor = WHITE,
.menu.prefixcolor = WHITE,
.menu.f_selectorcolor = BLACK,
.menu.selectorcolor = WHITE,
.menu.f_desccolor = BLACK,
.menu.desccolor = WHITE,
.menu.f_namecolor = BLACK,
.menu.namecolor = WHITE,
.menu.f_shortcutcolor = BLACK | A_UNDERLINE,
.menu.shortcutcolor = WHITE | A_UNDERLINE,
.menu.bottomdesccolor = WHITE,
.menu.sepnamecolor = WHITE,
.menu.sepdesccolor = WHITE,
.form.f_fieldcolor = GET_COLOR(fg, bk) | A_REVERSE,
.form.fieldcolor = GET_COLOR(fg, bk),
.form.readonlycolor = GET_COLOR(fg, bk),
.form.bottomdesccolor = GET_COLOR(fg, bk),
.form.f_fieldcolor = BLACK,
.form.fieldcolor = WHITE,
.form.readonlycolor = WHITE,
.form.bottomdesccolor = WHITE,
.bar.f_color = GET_COLOR(fg, bk) | A_REVERSE,
.bar.color = GET_COLOR(fg, bk),
.bar.f_color = BLACK,
.bar.color = WHITE,
.button.minmargin = 1,
.button.maxmargin = 5,
.button.leftdelim = '[',
.button.rightdelim = ']',
.button.f_delimcolor = GET_COLOR(fg, bk),
.button.delimcolor = GET_COLOR(fg, bk),
.button.f_color = GET_COLOR(fg, bk) | A_UNDERLINE | A_REVERSE,
.button.color = GET_COLOR(fg, bk) | A_UNDERLINE,
.button.f_shortcutcolor = GET_COLOR(fg, bk) | A_UNDERLINE | A_REVERSE,
.button.shortcutcolor = GET_COLOR(fg, bk) | A_UNDERLINE
.button.f_delimcolor = WHITE,
.button.delimcolor = WHITE,
.button.f_color = BLACK,
.button.color = WHITE,
.button.f_shortcutcolor = BLACK | A_UNDERLINE | A_BOLD,
.button.shortcutcolor = WHITE | A_UNDERLINE | A_BOLD
};
static struct bsddialog_theme dialogtheme = {
static struct bsddialog_theme flat = {
.screen.color = GET_COLOR(COLOR_CYAN, COLOR_BLUE) | A_BOLD,
.shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK),
.shadow.y = 1,
.shadow.x = 2,
.shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK),
.shadow.y = 1,
.shadow.x = 2,
.dialog.delimtitle = false,
.dialog.delimtitle = true,
.dialog.titlecolor = GET_COLOR(COLOR_BLUE, COLOR_WHITE) | A_BOLD,
.dialog.lineraisecolor = GET_COLOR(COLOR_WHITE, COLOR_WHITE) | A_BOLD,
.dialog.linelowercolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE) | A_BOLD,
.dialog.lineraisecolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE),
.dialog.linelowercolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE),
.dialog.color = GET_COLOR(COLOR_BLACK, COLOR_WHITE),
.dialog.bottomtitlecolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE) | A_BOLD,
.dialog.arrowcolor = GET_COLOR(COLOR_BLUE, COLOR_WHITE),
.menu.f_prefixcolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE),
.menu.prefixcolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE),
.menu.f_selectorcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE),
.menu.selectorcolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE),
.menu.f_desccolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE),
.menu.desccolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE),
.menu.f_namecolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE),
.menu.namecolor = GET_COLOR(COLOR_BLUE, COLOR_WHITE),
.menu.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),
.menu.sepnamecolor = GET_COLOR(COLOR_RED, COLOR_WHITE),
.menu.sepdesccolor = GET_COLOR(COLOR_RED, COLOR_WHITE),
.form.f_fieldcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE) | A_BOLD,
.form.fieldcolor = GET_COLOR(COLOR_WHITE, COLOR_CYAN) | A_BOLD,
@ -169,8 +141,8 @@ static struct bsddialog_theme dialogtheme = {
.button.minmargin = 1,
.button.maxmargin = 5,
.button.leftdelim = '<',
.button.rightdelim = '>',
.button.leftdelim = '[',
.button.rightdelim = ']',
.button.f_delimcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE) | A_BOLD,
.button.delimcolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE),
.button.f_color = GET_COLOR(COLOR_YELLOW, COLOR_BLUE) | A_BOLD,
@ -196,17 +168,19 @@ set_theme(struct bsddialog_theme *dst, struct bsddialog_theme *src)
dst->dialog.bottomtitlecolor = src->dialog.bottomtitlecolor;
dst->dialog.arrowcolor = src->dialog.arrowcolor;
dst->menu.f_prefixcolor = src->menu.f_prefixcolor;
dst->menu.prefixcolor = src->menu.prefixcolor;
dst->menu.f_selectorcolor = src->menu.f_selectorcolor;
dst->menu.selectorcolor = src->menu.selectorcolor;
dst->menu.f_desccolor = src->menu.f_desccolor;
dst->menu.desccolor = src->menu.desccolor;
dst->menu.f_namecolor = src->menu.f_namecolor;
dst->menu.namecolor = src->menu.namecolor;
dst->menu.namesepcolor = src->menu.namesepcolor;
dst->menu.descsepcolor = src->menu.descsepcolor;
dst->menu.f_shortcutcolor = src->menu.f_shortcutcolor;
dst->menu.shortcutcolor = src->menu.shortcutcolor;
dst->menu.bottomdesccolor = src->menu.bottomdesccolor;
dst->menu.sepnamecolor = src->menu.sepnamecolor;
dst->menu.sepdesccolor = src->menu.sepdesccolor;
dst->form.f_fieldcolor = src->form.f_fieldcolor;
dst->form.fieldcolor = src->form.fieldcolor;
@ -228,17 +202,12 @@ set_theme(struct bsddialog_theme *dst, struct bsddialog_theme *src)
dst->button.shortcutcolor = src->button.shortcutcolor;
bkgd(dst->screen.color);
refresh();
}
/* API */
int bsddialog_get_theme(struct bsddialog_theme *theme)
{
if (theme == NULL)
RETURN_ERROR("theme is NULL");
if (sizeof(*theme) != sizeof(struct bsddialog_theme))
RETURN_ERROR("Bad suze struct bsddialog_theme");
CHECK_PTR(theme);
set_theme(theme, &t);
return (BSDDIALOG_OK);
@ -246,34 +215,31 @@ int bsddialog_get_theme(struct bsddialog_theme *theme)
int bsddialog_set_theme(struct bsddialog_theme *theme)
{
if (theme == NULL)
RETURN_ERROR("theme is NULL");
if (sizeof(*theme) != sizeof(struct bsddialog_theme))
RETURN_ERROR("Bad size struct bsddialog_theme");
CHECK_PTR(theme);
set_theme(&t, theme);
refresh();
return (BSDDIALOG_OK);
}
int bsddialog_set_default_theme(enum bsddialog_default_theme newtheme)
{
if (newtheme == BSDDIALOG_THEME_FLAT) {
bsddialog_set_theme(&dialogtheme);
t.dialog.lineraisecolor = t.dialog.linelowercolor;
t.dialog.delimtitle = true;
t.button.leftdelim = '[';
t.button.rightdelim = ']';
t.dialog.bottomtitlecolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE);
if (newtheme == BSDDIALOG_THEME_3D) {
set_theme(&t, &flat);
t.dialog.lineraisecolor =
GET_COLOR(COLOR_WHITE, COLOR_WHITE) | A_BOLD;
t.dialog.delimtitle = false;
t.dialog.bottomtitlecolor = t.dialog.bottomtitlecolor | A_BOLD;
} else if (newtheme == BSDDIALOG_THEME_BLACKWHITE) {
set_theme(&t, &blackwhite);
} else if (newtheme == BSDDIALOG_THEME_FLAT) {
set_theme(&t, &flat);
} else {
RETURN_FMTERROR("Unknown default theme (%d), "
"to use enum bsddialog_default_theme",
newtheme);
}
else if (newtheme == BSDDIALOG_THEME_BSDDIALOG)
bsddialog_set_theme(&bsddialogtheme);
else if (newtheme == BSDDIALOG_THEME_BLACKWHITE)
bsddialog_set_theme(&blackwhite);
else if (newtheme == BSDDIALOG_THEME_DIALOG)
bsddialog_set_theme(&dialogtheme);
else
RETURN_ERROR("Unknow default theme");
refresh();
return (BSDDIALOG_OK);
}
@ -282,43 +248,41 @@ int
bsddialog_color(enum bsddialog_color foreground,
enum bsddialog_color background, unsigned int flags)
{
unsigned int cursesflags = 0;
unsigned int i, f;
if (flags & BSDDIALOG_BOLD)
cursesflags |= A_BOLD;
if (flags & BSDDIALOG_REVERSE)
cursesflags |= A_REVERSE;
if (flags & BSDDIALOG_UNDERLINE)
cursesflags |= A_UNDERLINE;
f = 0;
for (i=0; i < NFLAGS; i++)
if (flags & flagconv[i].public)
f |= flagconv[i].private;
return (GET_COLOR(foreground, background) | cursesflags);
return (GET_COLOR(foreground, background) | f);
}
int
bsddialog_color_attrs(int color, enum bsddialog_color *foreground,
enum bsddialog_color *background, unsigned int *flags)
{
unsigned int flag;
short f, b;
short fg, bg;
unsigned int i, f;
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)
if (flags != NULL) {
f = 0;
for (i=0; i < NFLAGS; i++)
if (color & flagconv[i].private)
f |= flagconv[i].public;
*flags = f;
}
if (pair_content(PAIR_NUMBER(color), &fg, &bg) != OK)
RETURN_ERROR("Cannot get color attributes");
if (foreground != NULL)
*foreground = f;
*foreground = fg;
if (background != NULL)
*background = b;
*background = bg;
return (BSDDIALOG_OK);
}
bool bsddialog_hascolors(void)
{
return hastermcolors;
}
return (hastermcolors);
}

View File

@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2022 Alfonso Sabato Siciliano
* Copyright (c) 2021-2023 Alfonso Sabato Siciliano
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -25,158 +25,114 @@
* SUCH DAMAGE.
*/
#include <sys/param.h>
#include <ctype.h>
#include <curses.h>
#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 */
#define HBOX 3
#define WBOX 4
struct clock {
unsigned int max;
unsigned int value;
WINDOW *win;
};
static void
drawsquare(struct bsddialog_conf *conf, WINDOW *win, const char *fmt,
const void *value, bool focus)
drawsquare(struct bsddialog_conf *conf, WINDOW *win, unsigned int value,
bool focus)
{
int h, l, w;
getmaxyx(win, h, w);
draw_borders(conf, win, h, w, LOWERED);
draw_borders(conf, win, 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);
mvwhline(win, 0, 1, conf->ascii_lines ? '^' : ACS_UARROW, 2);
mvwhline(win, 2, 1, conf->ascii_lines ? 'v' : ACS_DARROW, 2);
wattroff(win, t.dialog.arrowcolor);
}
if (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));
mvwprintw(win, 1, 1, "%02u", value);
if (focus)
wattroff(win, t.menu.f_namecolor);
wrefresh(win);
wnoutrefresh(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)
static int timebox_redraw(struct dialog *d, struct clock *c)
{
int htext, wtext;
int y, x;
if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) {
if (text_size(conf, rows, cols, text, &bs, 3, minw, &htext,
&wtext) != 0)
return (BSDDIALOG_ERROR);
if (d->built) {
hide_dialog(d);
refresh(); /* Important for decreasing screen */
}
if (dialog_size_position(d, HBOX, MINWTIME, NULL) != 0)
return (BSDDIALOG_ERROR);
if (draw_dialog(d) != 0)
return (BSDDIALOG_ERROR);
if (d->built)
refresh(); /* Important to fix grey lines expanding screen */
TEXTPAD(d, HBOX + HBUTTONS);
if (cols == BSDDIALOG_AUTOSIZE)
*w = widget_min_width(conf, wtext, minw, &bs);
if (rows == BSDDIALOG_AUTOSIZE)
*h = widget_min_height(conf, htext, 3 /* windows */, true);
return (0);
}
static int
datetime_checksize(int rows, int cols, int minw, struct buttons bs)
{
int mincols;
mincols = VBORDERS;
mincols += buttons_min_width(bs);
mincols = MAX(minw, mincols);
if (cols < mincols)
RETURN_ERROR("Few cols for this timebox/datebox");
if (rows < 7) /* 2 button + 2 borders + 3 windows */
RETURN_ERROR("Few rows for this timebox/datebox, at least 7");
y = d->y + d->h - BORDER - HBUTTONS - HBOX;
x = d->x + d->w/2 - 7;
update_box(d->conf, c[0].win, y, x, HBOX, WBOX, LOWERED);
mvwaddch(d->widget, d->h - 5, d->w/2 - 3, ':');
update_box(d->conf, c[1].win, y, x += 5, HBOX, WBOX, LOWERED);
mvwaddch(d->widget, d->h - 5, d->w/2 + 2, ':');
update_box(d->conf, c[2].win, y, x + 5, HBOX, WBOX, LOWERED);
wnoutrefresh(d->widget); /* for mvwaddch(':') */
return (0);
}
/* API */
int
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, retval, y, x, h, w, sel;
int i, retval, sel;
wint_t input;
WINDOW *widget, *textpad, *shadow;
struct buttons bs;
struct myclockstruct {
unsigned int max;
unsigned int value;
WINDOW *win;
};
if (hh == NULL || mm == NULL || ss == NULL)
RETURN_ERROR("hh / mm / ss cannot be NULL");
struct myclockstruct c[3] = {
struct dialog d;
struct clock c[3] = {
{23, *hh, NULL},
{59, *mm, NULL},
{59, *ss, NULL}
};
for (i = 0 ; i < 3; i++) {
if (c[i].value > c[i].max)
c[i].value = c[i].max;
CHECK_PTR(hh);
CHECK_PTR(mm);
CHECK_PTR(ss);
if (prepare_dialog(conf, text, rows, cols, &d) != 0)
return (BSDDIALOG_ERROR);
set_buttons(&d, true, OK_LABEL, CANCEL_LABEL);
for (i=0; i<3; i++) {
if ((c[i].win = newwin(1, 1, 1, 1)) == NULL)
RETURN_FMTERROR("Cannot build WINDOW for time[%d]", i);
wbkgd(c[i].win, t.dialog.color);
c[i].value = MIN(c[i].value, c[i].max);
}
get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL);
if (set_widget_size(conf, rows, cols, &h, &w) != 0)
if (timebox_redraw(&d, c) != 0)
return (BSDDIALOG_ERROR);
if (datetime_autosize(conf, rows, cols, &h, &w, MINWTIME, text,
bs) != 0)
return (BSDDIALOG_ERROR);
if (datetime_checksize(h, w, MINWTIME, bs) != 0)
return (BSDDIALOG_ERROR);
if (set_widget_position(conf, &y, &x, h, w) != 0)
return (BSDDIALOG_ERROR);
if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs,
true) != 0)
return (BSDDIALOG_ERROR);
pnoutrefresh(textpad, 0, 0, y+1, x+2, y+h-7, x+w-2);
doupdate();
c[0].win = new_boxed_window(conf, y+h-6, x + w/2 - 7, 3, 4, LOWERED);
mvwaddch(widget, h - 5, w/2 - 3, ':');
c[1].win = new_boxed_window(conf, y+h-6, x + w/2 - 2, 3, 4, LOWERED);
mvwaddch(widget, h - 5, w/2 + 2, ':');
c[2].win = new_boxed_window(conf, y+h-6, x + w/2 + 3, 3, 4, LOWERED);
wrefresh(widget);
sel = -1;
loop = focusbuttons = true;
while (loop) {
for (i = 0; i < 3; i++)
drawsquare(conf, c[i].win, "%02d", &c[i].value,
sel == i);
drawsquare(conf, c[i].win, c[i].value, sel == i);
doupdate();
if (get_wch(&input) == ERR)
continue;
switch(input) {
case KEY_ENTER:
case 10: /* Enter */
if (focusbuttons || conf->button.always_active) {
retval = bs.value[bs.curr];
retval = BUTTONVALUE(d.bs);
loop = false;
}
break;
@ -189,49 +145,49 @@ bsddialog_timebox(struct bsddialog_conf *conf, const char* text, int rows,
case KEY_RIGHT:
case '\t': /* TAB */
if (focusbuttons) {
bs.curr++;
focusbuttons = bs.curr < (int)bs.nbuttons ?
d.bs.curr++;
focusbuttons = d.bs.curr < (int)d.bs.nbuttons ?
true : false;
if (focusbuttons == false) {
sel = 0;
bs.curr = conf->button.always_active ? 0 : -1;
d.bs.curr =
conf->button.always_active ? 0 : -1;
}
} else {
sel++;
focusbuttons = sel > 2 ? true : false;
if (focusbuttons) {
bs.curr = 0;
d.bs.curr = 0;
}
}
draw_buttons(widget, bs, true);
wrefresh(widget);
DRAW_BUTTONS(d);
break;
case KEY_LEFT:
if (focusbuttons) {
bs.curr--;
focusbuttons = bs.curr < 0 ? false : true;
d.bs.curr--;
focusbuttons = d.bs.curr < 0 ? false : true;
if (focusbuttons == false) {
sel = 2;
bs.curr = conf->button.always_active ? 0 : -1;
d.bs.curr =
conf->button.always_active ? 0 : -1;
}
} else {
sel--;
focusbuttons = sel < 0 ? true : false;
if (focusbuttons)
bs.curr = (int)bs.nbuttons - 1;
d.bs.curr = (int)d.bs.nbuttons - 1;
}
draw_buttons(widget, bs, true);
wrefresh(widget);
DRAW_BUTTONS(d);
break;
case KEY_UP:
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;
d.bs.curr = conf->button.always_active ? 0 : -1;
DRAW_BUTTONS(d);
} else {
c[sel].value = c[sel].value > 0 ?
c[sel].value - 1 : c[sel].max;
}
break;
case KEY_DOWN:
@ -244,297 +200,32 @@ bsddialog_timebox(struct bsddialog_conf *conf, const char* text, int rows,
if (conf->key.f1_file == NULL &&
conf->key.f1_message == NULL)
break;
if (f1help(conf) != 0)
if (f1help_dialog(conf) != 0)
return (BSDDIALOG_ERROR);
/* No break, screen size can change */
if (timebox_redraw(&d, c) != 0)
return (BSDDIALOG_ERROR);
break;
case KEY_RESIZE:
/* Important for decreasing screen */
hide_widget(y, x, h, w, conf->shadow);
refresh();
if (set_widget_size(conf, rows, cols, &h, &w) != 0)
if (timebox_redraw(&d, c) != 0)
return (BSDDIALOG_ERROR);
if (datetime_autosize(conf, rows, cols, &h, &w,
MINWTIME, text, bs) != 0)
return (BSDDIALOG_ERROR);
if (datetime_checksize(h, w, MINWTIME, 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)
return (BSDDIALOG_ERROR);
doupdate();
mvwaddch(widget, h - 5, w/2 - 3, ':');
mvwaddch(widget, h - 5, w/2 + 2, ':');
wrefresh(widget);
prefresh(textpad, 0, 0, y+1, x+2, y+h-7, x+w-2);
wclear(c[0].win);
mvwin(c[0].win, y + h - 6, x + w/2 - 7);
wclear(c[1].win);
mvwin(c[1].win, y + h - 6, x + w/2 - 2);
wclear(c[2].win);
mvwin(c[2].win, y + h - 6, x + w/2 + 3);
/* Important to avoid grey lines expanding screen */
refresh();
break;
default:
if (shortcut_buttons(input, &bs)) {
retval = bs.value[bs.curr];
if (shortcut_buttons(input, &d.bs)) {
DRAW_BUTTONS(d);
doupdate();
retval = BUTTONVALUE(d.bs);
loop = false;
}
}
}
if (retval == BSDDIALOG_OK) {
*hh = c[0].value;
*mm = c[1].value;
*ss = c[2].value;
}
*hh = c[0].value;
*mm = c[1].value;
*ss = c[2].value;
for (i = 0; i < 3; i++)
delwin(c[i].win);
end_dialog(conf, shadow, widget, textpad);
end_dialog(&d);
return (retval);
}
int
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, retval, y, x, h, w, sel;
wint_t input;
WINDOW *widget, *textpad, *shadow;
struct buttons bs;
struct calendar {
int max;
int value;
WINDOW *win;
unsigned int x;
};
struct month {
const char *name;
unsigned int days;
};
if (yy == NULL || mm == NULL || dd == NULL)
RETURN_ERROR("yy / mm / dd cannot be NULL");
struct calendar c[3] = {
{9999, *yy, NULL, 4 },
{12, *mm, NULL, 9 },
{31, *dd, NULL, 2 }
};
struct month m[12] = {
{ "January", 31 }, { "February", 28 }, { "March", 31 },
{ "April", 30 }, { "May", 31 }, { "June", 30 },
{ "July", 31 }, { "August", 31 }, { "September", 30 },
{ "October", 31 }, { "November", 30 }, { "December", 31 }
};
for (i = 0 ; i < 3; i++) {
if (c[i].value > c[i].max)
c[i].value = c[i].max;
if (c[i].value < 1)
c[i].value = 1;
}
c[2].max = m[c[1].value -1].days;
if (c[1].value == 2 && ISLEAP(c[0].value))
c[2].max = 29;
if (c[2].value > c[2].max)
c[2].value = c[2].max;
get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL);
if (set_widget_size(conf, rows, cols, &h, &w) != 0)
return (BSDDIALOG_ERROR);
if (datetime_autosize(conf, rows, cols, &h, &w, MINWDATE, text,
bs) != 0)
return (BSDDIALOG_ERROR);
if (datetime_checksize(h, w, MINWDATE, bs) != 0)
return (BSDDIALOG_ERROR);
if (set_widget_position(conf, &y, &x, h, w) != 0)
return (BSDDIALOG_ERROR);
if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs,
true) != 0)
return (BSDDIALOG_ERROR);
pnoutrefresh(textpad, 0, 0, y+1, x+2, y+h-7, x+w-2);
doupdate();
c[0].win = new_boxed_window(conf, y+h-6, x + w/2 - 11, 3, 6, LOWERED);
mvwaddch(widget, h - 5, w/2 - 5, '/');
c[1].win = new_boxed_window(conf, y+h-6, x + w/2 - 4, 3, 11, LOWERED);
mvwaddch(widget, h - 5, w/2 + 7, '/');
c[2].win = new_boxed_window(conf, y+h-6, x + w/2 + 8, 3, 4, LOWERED);
wrefresh(widget);
sel = -1;
loop = focusbuttons = true;
while (loop) {
drawsquare(conf, c[0].win, "%4d", &c[0].value, sel == 0);
drawsquare(conf, c[1].win, "%9s", m[c[1].value-1].name,
sel == 1);
drawsquare(conf, c[2].win, "%02d", &c[2].value, sel == 2);
if (get_wch(&input) == ERR)
continue;
switch(input) {
case KEY_ENTER:
case 10: /* Enter */
if (focusbuttons || conf->button.always_active) {
retval = bs.value[bs.curr];
loop = false;
}
break;
case 27: /* Esc */
if (conf->key.enable_esc) {
retval = BSDDIALOG_ESC;
loop = false;
}
break;
case KEY_RIGHT:
case '\t': /* TAB */
if (focusbuttons) {
bs.curr++;
focusbuttons = bs.curr < (int)bs.nbuttons ?
true : false;
if (focusbuttons == false) {
sel = 0;
bs.curr = conf->button.always_active ? 0 : -1;
}
} else {
sel++;
focusbuttons = sel > 2 ? true : false;
if (focusbuttons) {
bs.curr = 0;
}
}
draw_buttons(widget, bs, true);
wrefresh(widget);
break;
case KEY_LEFT:
if (focusbuttons) {
bs.curr--;
focusbuttons = bs.curr < 0 ? false : true;
if (focusbuttons == false) {
sel = 2;
bs.curr = conf->button.always_active ? 0 : -1;
}
} else {
sel--;
focusbuttons = sel < 0 ? true : false;
if (focusbuttons)
bs.curr = (int)bs.nbuttons - 1;
}
draw_buttons(widget, bs, true);
wrefresh(widget);
break;
case KEY_UP:
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 && ISLEAP(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)
break;
c[sel].value = c[sel].value < c[sel].max ?
c[sel].value + 1 : 1;
/* if mount change */
c[2].max = m[c[1].value -1].days;
/* if year change */
if (c[1].value == 2 && ISLEAP(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_F(1):
if (conf->key.f1_file == NULL &&
conf->key.f1_message == NULL)
break;
if (f1help(conf) != 0)
return (BSDDIALOG_ERROR);
/* No break, screen size can change */
case KEY_RESIZE:
/* Important for decreasing screen */
hide_widget(y, x, h, w, conf->shadow);
refresh();
if (set_widget_size(conf, rows, cols, &h, &w) != 0)
return (BSDDIALOG_ERROR);
if (datetime_autosize(conf, rows, cols, &h, &w,
MINWDATE, text, bs) != 0)
return (BSDDIALOG_ERROR);
if (datetime_checksize(h, w, MINWDATE, 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)
return (BSDDIALOG_ERROR);
doupdate();
mvwaddch(widget, h - 5, w/2 - 5, '/');
mvwaddch(widget, h - 5, w/2 + 7, '/');
wrefresh(widget);
prefresh(textpad, 0, 0, y+1, x+2, y+h-7, x+w-2);
wclear(c[0].win);
mvwin(c[0].win, y + h - 6, x + w/2 - 11);
wclear(c[1].win);
mvwin(c[1].win, y + h - 6, x + w/2 - 4);
wclear(c[2].win);
mvwin(c[2].win, y + h - 6, x + w/2 + 8);
/* Important to avoid grey lines expanding screen */
refresh();
break;
default:
if (shortcut_buttons(input, &bs)) {
retval = bs.value[bs.curr];
loop = false;
}
}
}
if (retval == BSDDIALOG_OK) {
*yy = c[0].value;
*mm = c[1].value;
*dd = c[2].value;
}
for (i = 0; i < 3; i++)
delwin(c[i].win);
end_dialog(conf, shadow, widget, textpad);
return (retval);
}

View File

@ -1,353 +0,0 @@
/*-
* 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 const char *color[8] = {
"black",
"red",
"green",
"yellow",
"blue",
"magenta",
"cyan",
"white"
};
#define EXIT_FMTERROR(fmt, ...) do { \
bsddialog_end(); \
printf("Error: "); \
printf(fmt, __VA_ARGS__); \
printf(".\n"); \
exit (255); \
} while (0)
void savetheme(const char *file, 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)
EXIT_FMTERROR("cannot save theme: %s", bsddialog_geterror());
if(time(&clock) < 0)
EXIT_FMTERROR("%s", "cannot save profile getting current time");
if ((fp = fopen(file, "w")) == NULL)
EXIT_FMTERROR("cannot open %s to save profile", file);
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);
}
void setdeftheme(enum bsddialog_default_theme theme)
{
if (bsddialog_set_default_theme(theme) != BSDDIALOG_OK)
EXIT_FMTERROR("%s", bsddialog_geterror());
}
void loadtheme(const char *file)
{
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)
EXIT_FMTERROR("Cannot get current theme: %s",
bsddialog_geterror());
if((fp = fopen(file, "r")) == NULL)
EXIT_FMTERROR("Cannot open theme \"%s\"", file);
#define EXIT_ERROR(name, error) do { \
fclose(fp); \
EXIT_FMTERROR("%s for \"%s\"", error, name); \
} 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;
EXIT_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)
EXIT_ERROR(p[i].name, "Cannot get a char");
*((int*)p[i].value) = charvalue;
break;
case INT:
if (sscanf(value, "%d", &intvalue) != 1)
EXIT_ERROR(p[i].name, "Cannot get a int");
*((int*)p[i].value) = intvalue;
break;
case UINT:
if (sscanf(value, "%u", &uintvalue) != 1)
EXIT_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)
EXIT_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)
EXIT_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)
EXIT_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);
}
void bikeshed(struct bsddialog_conf *conf)
{
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)
EXIT_FMTERROR("%s", bsddialog_geterror());
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))
EXIT_FMTERROR("%s", bsddialog_geterror());
/* 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;
}
}

View File

@ -1,36 +0,0 @@
/*-
* 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_
void savetheme(const char *file, const char *version);
void loadtheme(const char *file);
void setdeftheme(enum bsddialog_default_theme theme);
void bikeshed(struct bsddialog_conf *conf);
#endif

33
utility/GNUmakefile Normal file
View File

@ -0,0 +1,33 @@
# PUBLIC DOMAIN - NO WARRANTY, see:
# <http://creativecommons.org/publicdomain/zero/1.0/>
#
# Written in 2021 by Alfonso Sabato Siciliano
OUTPUT = bsddialog
SOURCES = bsddialog.c util_builders.c util_cli.c util_theme.c
OBJECTS = $(SOURCES:.c=.o)
ifneq ($(ENABLEDEBUG),)
CFLAGS += -g
endif
CFLAGS += -D_GNU_SOURCE -Wall -Wextra -I$(LIBPATH)
ifneq ($(DISABLERPATH),)
LDFLAGS += -ltinfo -L$(LIBPATH) -lbsddialog
else
LDFLAGS += -ltinfo -Wl,-rpath=$(LIBPATH) -L$(LIBPATH) -lbsddialog
endif
RM = rm -f
all : $(OUTPUT)
$(OUTPUT): $(OBJECTS)
$(CC) $^ -o $@ $(LDFLAGS)
%.o: %.c
$(CC) $(CFLAGS) -c $<
clean:
$(RM) $(OUTPUT) *.o *~

33
utility/Makefile Normal file
View File

@ -0,0 +1,33 @@
# PUBLIC DOMAIN - NO WARRANTY, see:
# <http://creativecommons.org/publicdomain/zero/1.0/>
#
# Written in 2021 by Alfonso Sabato Siciliano
OUTPUT = bsddialog
SOURCES = bsddialog.c util_builders.c util_cli.c util_theme.c
OBJECTS = ${SOURCES:.c=.o}
.if defined(DEBUG)
CFLAGS += -g
.endif
CFLAGS += -I${LIBPATH} -std=gnu99 -Wall -Wextra -Werror
.if defined(NORPATH)
LDFLAGS += -ltinfow -L${LIBPATH} -lbsddialog
.else
LDFLAGS += -ltinfow -Wl,-rpath=${LIBPATH} -L${LIBPATH} -lbsddialog
.endif
INSTALL = install
RM = rm -f
all : ${OUTPUT}
${OUTPUT}: ${OBJECTS}
${CC} ${LDFLAGS} ${OBJECTS} -o ${.PREFIX}
.c.o:
${CC} ${CFLAGS} -c ${.IMPSRC} -o ${.TARGET}
clean:
${RM} ${OUTPUT} *.o *~ *.core *.gz

View File

@ -1,6 +1,5 @@
.\"
.\"
.\" Copyright (c) 2021-2022 Alfonso Sabato Siciliano
.\" Copyright (c) 2021-2023 Alfonso Sabato Siciliano
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
@ -23,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd September 23, 2022
.Dd July 25, 2023
.Dt BSDDIALOG 1
.Os
.Sh NAME
@ -31,9 +30,7 @@
.Nd TUI dialogs
.Sh SYNOPSIS
.Nm
.Fl Fl help
.Nm
.Fl Fl version
.Fl Fl help | Fl Fl version
.Nm
.Op Fl Fl option
.Fl Fl dialog
@ -41,7 +38,9 @@
.Ar rows
.Ar cols
.Op Ar dialog-argument
.Op Fl Fl option
.Nm
\&...
.Fl Fl dialog1
.Ar ...
.Oo Fl Fl and-dialog
@ -51,25 +50,31 @@
.Sh DESCRIPTION
The
.Nm bsddialog
utility builds Text User Interface dialogs and widgets: to display messages,
to get input and to inform about a computation status.
utility builds Text User Interface dialogs and widgets.
.Pp
The options
The option
.Fl Fl help
and
prints a brief list of features and exits.
The option
.Fl Fl version
print the list of options and the version, respectively, then exit.
prints the version and exits.
The option
.Fl Fl and-dialog
builds another dialog unless the previous one returns Error, ESC or Cancel.
.Pp
Each dialog accepts
.Ar text
is a message printed inside the dialog.
to print a message inside,
.Ar rows
and
.Ar cols
are the height and width, 0 for autosize and -1 for fullscreen.
to set height and width,
.Dv 0
for autosize and
.Dv -1
for fullscreen.
.Pp
The possible input got from the user interface is printed to standard error.
The possible input from the user interface is printed to standard error at exit.
.Ss Options
The following options can change the default behavior of the utility and are
common to some dialog.
@ -80,18 +85,37 @@ If available set alternate screen mode, see
.It Fl Fl ascii-lines
Ascii characters to draw lines.
.It Fl Fl backtitle Ar backtitle
Title on the top side of the screen.
Screen title.
.It Fl Fl begin-x Ar x
Dialog horizontal position, 0 is the left screen side, -1 center.
Set dialog horizontal position,
.Dv -1
center,
.Dv 0
left screen.
.It Fl Fl begin-y Ar y
Dialog vertical position, 0 is the top screen side, -1 center.
Set dialog vertical position,
.Dv -1
center,
.Dv 0
top screen.
.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
Colors, title delimiter, button delimiter; see theme features.
Title margin.
Buttons always active or TAB to switch focus with other components, see
.Fl Fl switch-buttons .
Zero padding with time or date output.
Zero padding with time output; see
.Fl Fl time-format .
Zero padding with date output; see
.Fl Fl date-format .
User Interface date format for
.Fl Fl datebox ;
see
.Fl Fl datebox-format .
.It Fl Fl cancel-exit-code Ar retval
Set an exit code value for the
.Dq Cancel
button.
.It Fl Fl cancel-label Ar label
Label for the
.Dq Cancel
@ -99,41 +123,7 @@ button.
.It Fl Fl clear-dialog
Hide the dialog at exit.
.It Fl Fl clear-screen
Clear the screen, after the dialog exit if a dialog is built.
.It Fl Fl colors
Enable highlights for text, the following sequences are considered escapes:
.Bl -column -compact
.It Dq \eZ0
black.
.It Dq \eZ1
red.
.It Dq \eZ2
green.
.It Dq \eZ3
yellow.
.It Dq \eZ4
blue.
.It Dq \eZ5
magenta.
.It Dq \eZ6
cyan.
.It Dq \eZ7
white.
.It Dq \eZr
reverse foreground and background.
.It Dq \eZR
disable reverse.
.It Dq \eZb
bold.
.It Dq \eZB
disable bold.
.It Dq \eZu
underline.
.It Dq \eZU
disable underline.
.It Dq \eZn
restore normal text.
.El
Clear the screen, wait a dialog if built.
.It Fl Fl columns-per-row Ar columns
Try to set the number of columns for a row of text with autosizing; default
.Dv 10 .
@ -144,13 +134,20 @@ also if it constains a
.Dq \en ,
see
.Fl Fl text-unchanged .
.It Fl Fl datebox-format Ar format
String to customize
.Fl Fl datebox
interface, possible values:
.Dq d/m/y ,
.Dq m/y/d ,
.Dq y/m/d .
.It Fl Fl date-format Ar format
String accepted by
.Xr strftime 3
to customize the output of
.Fl Fl datebox
and
.Fl Fl calendar
.Fl Fl calendar .
.It Fl Fl default-button Ar label
Focus on the button with
.Ar label
@ -167,48 +164,74 @@ or
button on startup.
.It Fl Fl disable-esc
Disable ESC key to quit.
.It Fl Fl esc-return-cancel
ESC key returns
.Dq Cancel
button value.
.It Fl Fl error-exit-code Ar retval
Set an exit code value for the
.Nm
errors.
.It Fl Fl esc-exit-code Ar retval
Set an exit code value for the
.Dv ESC
key.
.It Fl Fl extra-button
Add a button with
.Dq Extra
label.
.It Fl Fl extra-exit-code Ar retval
Set an exit code value for the
.Dq Extra
button.
.It Fl Fl extra-label Ar label
Set
.Ar label
for the
.Dq Extra
button.
.It Fl Fl generic-button1 Ar label
.It Fl Fl left1-button Ar label
Add a button with
.Ar label .
.It Fl Fl generic-button2 Ar label
.It Fl Fl left1-exit-code Ar retval
Set an exit code for
.Fl Fl left1-button .
.It Fl Fl left2-button Ar label
Add a button with
.Ar label .
.It Fl Fl left2-exit-code Ar retval
Set an exit code for
.Fl Fl left2-button .
.It Fl Fl left3-button Ar label
Add a button with
.Ar label .
.It Fl Fl left3-exit-code Ar retval
Set an exit code for
.Fl Fl left3-button .
.It Fl Fl help-button
Add a button with
.Dq Help
label.
.It Fl Fl help-exit-code Ar retval
Set an exit code value for the
.Dq Help
button.
.It Fl Fl help-label Ar label
Set
.Ar label
for
.Dq Help
button.
.It Fl Fl help-print-name
Print the name of the focused item if the Help button is pressed also
with
.Fl Fl item-bottom-desc .
.It Fl Fl help-status
Print also the selected items if the
.It Fl Fl help-print-items
Print also the selected items or form values if the
.Dq Help
button is pressed.
.It Fl Fl help-print-name
Print the name of the focused item if the
.Dq Help
button is pressed also
with
.Fl Fl item-bottom-desc .
.It Fl Fl hfile Ar filename
Open
.Ar filename
in a Textbox if F1 key is pressed,
in a Textbox if F1 key is pressed.
.It Fl Fl hline Ar string
Dialog subtitle.
.It Fl Fl hmsg Ar string
@ -220,7 +243,7 @@ Do not exit with unknown options.
.It Fl Fl insecure
Print
.Sq *
to hide passwords while typing; whitespace otherwise.
to hide passwords while typing, white space otherwise.
.It Fl Fl item-bottom-desc
Set a help string for each item of a Checklist, Form, Menu, Mixedform,
Passwordform, Radiolist and Treeview to display at the bottom screen side.
@ -234,7 +257,7 @@ Load theme from
.It Fl Fl max-input Ar size
Maximum length of the input for
.Fl Fl inputbox
ans
and
.Fl Fl passwordbox ,
default 2048.
.It Fl Fl no-cancel
@ -242,11 +265,15 @@ Do not show
.Dq Cancel
button.
.It Fl Fl no-descriptions
Do not display items desciption, for Checklist, Menu, Radiolist or Treeview.
Do not display items desciption, for Checklist, Menu, Radiolist or Treeview;
mutually exclusive with
.Fl Fl no-names .
.It Fl Fl no-lines
Do not draw borders and lines.
.It Fl Fl no-names
Do not display items name, for Checklist, Menu and Radiolist.
Do not display items name, for Checklist, Menu and Radiolist; mutually exclusive
with
.Fl Fl no-descriptions .
.It Fl Fl no-ok
Do not draw
.Dq OK
@ -259,30 +286,52 @@ Set
for
.Dq OK
button.
.It Fl Fl ok-exit-code Ar retval
Set an exit code value for the
.Dq Ok
button.
.It Fl Fl normal-screen
If available set normal screen mode, see
.Xr terminfo 5 .
.It Fl Fl output-fd Ar fd
Print input from user interface to the specified file descriptor.
.It Fl Fl output-separator Ar sep
Set a sepator for the items in output, default whitespace.
Set a sepator for the items in output, default white space.
.It Fl Fl print-maxsize
Screen size.
This option can be used without a dialog.
.It Fl Fl print-size
Print Dialog height and widget at exit.
Print dialog height and width at exit.
.It Fl Fl print-version
Print version.
This option can be used without a dialog.
.It Fl Fl quoted
Quote items in output, default only when necessary.
.It Fl Fl right1-button Ar label
Add a button with
.Ar label .
.It Fl Fl right1-exit-code Ar retval
Set an exit code for
.Fl Fl right1-button .
.It Fl Fl right2-button Ar label
Add a button with
.Ar label .
.It Fl Fl right2-exit-code Ar retval
Set an exit code for
.Fl Fl right2-button .
.It Fl Fl right3-button Ar label
Add a button with
.Ar label .
.It Fl Fl right3-exit-code Ar retval
Set an exit code for
.Fl Fl right3-button .
.It Fl Fl save-theme Ar file
Save the current theme.
This option can be used without a dialog.
.It Fl Fl separate-output
Separate selected items with a new line and avoid to quote.
Print selected items separated by a new line and avoid to quote.
.It Fl Fl shadow
Show a pseudo shadow for the dialog, enabled by default.
Show a shadow for the dialog, enabled by default.
.It Fl Fl single-quoted
Use single quote for items in output.
.It Fl Fl sleep Ar secs
@ -294,7 +343,7 @@ 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,
Enable focus switching between buttons and input components pressing TAB,
otherwise buttons are always active and ENTER key closes the dialog.
Suitable for:
.Fl Fl form ,
@ -314,24 +363,78 @@ with a tab in
.It Fl Fl tab-len Ar spaces
Number of spaces to print a TAB in
.Ar text .
.It Fl Fl text-escape
Enable escapes in
.Ar text :
.Bl -column -compact
.It Dq \eZ0
black.
.It Dq \eZ1
red.
.It Dq \eZ2
green.
.It Dq \eZ3
yellow.
.It Dq \eZ4
blue.
.It Dq \eZ5
magenta.
.It Dq \eZ6
cyan.
.It Dq \eZ7
white.
.It Dq \eZb
bold.
.It Dq \eZB
disable bold.
.It Dq \eZd
Half bright.
.It Dq \eZD
disable half bright.
.It Dq \eZk
Blink.
.It Dq \eZK
disable blinking.
.It Dq \eZr
reverse foreground and background.
.It Dq \eZR
disable reverse.
.It Dq \eZs
Highlight.
.It Dq \eZS
disable highlighting.
.It Dq \eZu
underline.
.It Dq \eZU
disable underline.
.It Dq \eZn
disable each customization.
.El
.It Fl Fl text-unchanged
By default the
Disable
.Ar text
is changed before to be printed.
modification.
By default
.Ar text
is changed before to be printed in the dialog.
If it contains at least a
.Dq \en
each new line and TAB is converted to a space, subsequent spaces are merged.
Otherwise new line characters are preserved and a TAB becomes a space.
This option disable the
.Ar text
modification.
.It Fl Fl theme Ar theme
Set a graphical style: blackwhite, bsddialog, flat or dialog.
Set theme, possible values:
.Dq 3d ,
.Dq blackwhite ,
.Dq flat .
.It Fl Fl time-format Ar format
String accepted by
.Xr strftime 3
to customize the output of
.Fl Fl timebox .
.It Fl Fl timeout-exit-code Ar retval
Set an exit code value for the
.Fl Fl pause
timeout.
.It Fl Fl title Ar title
Dialog title.
.El
@ -398,7 +501,7 @@ following strings replace
until the next
.Dq XXX ,
the loop ends reading
.Dv EOF .
.Dq EOF .
.It Fl Fl infobox Ar text Ar rows Ar cols
Dialog without buttons to display a message and to exit immediately.
.It Fl Fl inputbox Ar text Ar rows Ar cols Op Ar init
@ -444,11 +547,12 @@ for autosize.
Ar miniperc Oc ...
Dialog to show a main bar to represent
.Ar mainperc
from 0 to 100 and some mini bar with a
from 0 to 100.
Some mini bar with a
.Ar minilabel
string and a
.Ar miniperc
with value from 0 and 100 or negative to print a descriptive string: -1
with a value from 0 and 100 or negative to print a descriptive string: -1
.Dq Succeeded ,
-2
.Dq Failed ,
@ -465,16 +569,19 @@ with value from 0 and 100 or negative to print a descriptive string: -1
-8
.Dq \&In Progress ,
-9
a blank line,
to hide
.Fa miniperc ,
-10
.Dq N/A ,
-11
.Dq Pending .
.Dq Pending ,
otherwise
.Dq UNKNOWN .
.It Fl Fl msgbox Ar text Ar rows Ar cols
Dialog to diplay a message without the
.Dq Cancel
button.
UP, DOWN, HOME, END, PAGEUP and PAGEDOWN keys are availble to navigate the text.
UP, DOWN, HOME, END, PAGEUP and PAGEDOWN keys are availble to scroll the text.
.It Fl Fl passwordbox Ar text Ar rows Ar cols Op Ar init
Dialog to get a password,
.Ar init
@ -486,9 +593,9 @@ Dialog to get a list of passwords, equivalent to
.Fl Fl form
except typed characters are hidden.
.It Fl Fl pause Ar text Ar rows Ar cols Ar seconds
Dialog runs until the timeout in
Dialog runs until
.Ar seconds
expires or a button is pressed.
timeout expires or a button is pressed.
.It Fl Fl radiolist Ar text Ar rows Ar cols Ar menurows Oo Ar name Ar desc \
Ar status Oc ...
Radiolist to select at most an item from a list via the SPACE key.
@ -514,8 +621,9 @@ is the default value, the keys UP, DOWN, HOME, END, PAGEUP and PAGEDOWN can
change it.
.It Fl Fl textbox Ar file Ar rows Ar cols
Opens and prints
.Ar file
the UP, DOWN, HOME, END, PAGEUP and PAGEDOWN keys are availble to navigate;
.Ar file .
UP, DOWN, LEFT, RIGHT, HOME, END, PAGEUP and PAGEDOWN keys are available to
navigate the file, TAB changes button.
.Dq OK
button is renamed
.Dq EXIT .
@ -536,8 +644,29 @@ buttons are renamed
.Dq Yes
and
.Dq \&No .
UP, DOWN, HOME, END, PAGEUP and PAGEDOWN keys are availble to navigate the text.
UP, DOWN, HOME, END, PAGEUP and PAGEDOWN keys are availble to scroll the text.
.El
.Sh ENVIRONMENT
The following environment variables take effect only on startup, other options
can override their setting.
.Bl -tag -width indent
.It Ev NO_COLOR
If present and not an empty string (regardless of its value) equivalent to
.Fl Fl theme Ar blackwhite .
.It Ev BSDDIALOG_ERROR , Ev BSDDIALOG_OK , Ev BSDDIALOG_CANCEL , \
Ev BSDDIALOG_HELP , Ev BSDDIALOG_EXTRA , Ev BSDDIALOG_TIMEOUT , \
Ev BSDDIALOG_ESC , Ev BSDDIALOG_LEFT1 , Ev BSDDIALOG_LEFT2 , \
Ev BSDDIALOG_LEFT3 , Ev BSDDIALOG_RIGHT1 , Ev BSDDIALOG_RIGHT2 , \
Ev BSDDIALOG_RIGHT3
Set exit codes.
.It Ev BSDDIALOG_THEMEFILE Ar file
Equivalent to
.Fl Fl load-theme Ar file .
.El
.Sh FILES
The theme file
.Pa $HOME/.bsddialog.conf
is read on startup if exists.
.Sh EXIT STATUS
The
.Nm
@ -566,9 +695,17 @@ Timeout.
.It 5
ESC key.
.It 6
Generic 1 button.
Left1 generic button.
.It 7
Generic 2 button.
Left2 generic button.
.It 8
Left3 generic button.
.It 9
Right1 generic button.
.It 10
Right2 generic button.
.It 11
Right2 generic button.
.El
.Sh EXAMPLES
Backtitle, title and message:
@ -595,6 +732,7 @@ Multi-dialog:
.Pp
Bikeshed:
.Dl bsddialog --bikeshed --inputbox Example 0 0
.Dl bsddialog --bikeshed --datebox Example 0 0
.Pp
Mixedgauge:
.Dl bsddialog --sleep 3 --mixedgauge Example 10 30 60 L1 \*q -1\*q L2 30
@ -641,8 +779,10 @@ Obsolete Equivalent
--and-widget --and-dialog
--calendar <text> 2 <cols> --calendar <text> 0 <cols>
--clear --clear-screen
--colors --text-escape
--defaultno --default-no
--exit-label --ok-label
--help-status --help-print-items
--help-tags --help-print-name
--item-help --item-bottom-desc
--keep-tite --alternate-screen
@ -655,7 +795,9 @@ Obsolete Equivalent
--yes-label --ok-label
.Ed
.Sh SEE ALSO
.Xr bsddialog 3
.Xr bsddialog 3 ,
.Xr strftime 3 ,
.Xr terminfo 5
.Sh HISTORY
The
.Nm bsddialog
@ -668,7 +810,7 @@ was written by
.Aq Mt asiciliano@FreeBSD.org .
.Pp
.Nm bsddialog
provides a subset of the functionality described in the
provides also a subset of the functionality described in the
.Nm dialog
manual.
The following features were reimplemented:
@ -757,8 +899,9 @@ Compatibility is not a priority for future development.
.An Baptiste Daroussin
.Aq Mt bapt@FreeBSD.org ,
.An \&Ed Maste
.Aq Mt emaste@FreeBSD.org
and
.Aq Mt emaste@FreeBSD.org ,
.An Juraj Lutter
.Aq Mt otis@FreeBSD.org
and
.An Trenton Schulz
for suggestions, help, and testing.

338
utility/bsddialog.c Normal file
View File

@ -0,0 +1,338 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2023 Alfonso Sabato Siciliano
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <getopt.h>
#include <limits.h>
#include <locale.h>
#include <signal.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <term.h>
#include <bsddialog.h>
#include <bsddialog_theme.h>
#include "util.h"
#define EXITCODE(retval) (exitcodes[retval + 1].value)
#define UNUSED_PAR(x) UNUSED_ ## x __attribute__((__unused__))
static void custom_text(struct options *opt, char *text, char *buf);
/* Exit codes */
struct exitcode {
const char *name;
int value;
};
static struct exitcode exitcodes[14] = {
{ "BSDDIALOG_ERROR", 255 },
{ "BSDDIALOG_OK", 0 },
{ "BSDDIALOG_CANCEL", 1 },
{ "BSDDIALOG_HELP", 2 },
{ "BSDDIALOG_EXTRA", 3 },
{ "BSDDIALOG_TIMEOUT", 4 },
{ "BSDDIALOG_ESC", 5 },
{ "BSDDIALOG_LEFT1", 6 },
{ "BSDDIALOG_LEFT2", 7 },
{ "BSDDIALOG_LEFT3", 8 },
{ "BSDDIALOG_RIGHT1", 9 },
{ "BSDDIALOG_RIGHT2", 10 },
{ "BSDDIALOG_RIGHT3", 11 },
{ "BSDDIALOG_ITEM_HELP", 2 } /* like HELP by default */
};
void set_exit_code(int lib_retval, int exitcode)
{
exitcodes[lib_retval + 1].value = exitcode;
}
/* Error */
void exit_error(bool usage, const char *fmt, ...)
{
va_list arg_ptr;
if (bsddialog_inmode())
bsddialog_end();
printf("Error: ");
va_start(arg_ptr, fmt);
vprintf(fmt, arg_ptr);
va_end(arg_ptr);
printf(".\n\n");
if (usage) {
printf("See \'bsddialog --help\' or \'man 1 bsddialog\' ");
printf("for more information.\n");
}
exit (EXITCODE(BSDDIALOG_ERROR));
}
void error_args(const char *dialog, int argc, char **argv)
{
int i;
if (bsddialog_inmode())
bsddialog_end();
printf("Error: %s unexpected argument%s:", dialog, argc > 1 ? "s" : "");
for (i = 0; i < argc; i++)
printf(" \"%s\"", argv[i]);
printf(".\n\n");
printf("See \'bsddialog --help\' or \'man 1 bsddialog\' ");
printf("for more information.\n");
exit (EXITCODE(BSDDIALOG_ERROR));
}
/* init */
static void sigint_handler(int UNUSED_PAR(sig))
{
bsddialog_end();
exit(EXITCODE(BSDDIALOG_ERROR));
}
static void start_bsddialog_mode(void)
{
if (bsddialog_inmode())
return;
if (bsddialog_init() != BSDDIALOG_OK)
exit_error(false, bsddialog_geterror());
signal(SIGINT, sigint_handler);
}
static void getenv_exitcodes(void)
{
int i;
int value;
char *envvalue;
for (i = 0; i < 10; i++) {
envvalue = getenv(exitcodes[i].name);
if (envvalue == NULL || envvalue[0] == '\0')
continue;
value = (int)strtol(envvalue, NULL, 10);
exitcodes[i].value = value;
/* ITEM_HELP follows HELP without explicit setting */
if(i == BSDDIALOG_HELP + 1)
exitcodes[BSDDIALOG_ITEM_HELP + 1].value = value;
}
}
/*
* bsddialog utility: TUI widgets and dialogs.
*/
int main(int argc, char *argv[argc])
{
bool startup;
int i, rows, cols, retval, parsed, nargc, firstoptind;
char *text, **nargv, *pn;
struct bsddialog_conf conf;
struct options opt;
setlocale(LC_ALL, "");
getenv_exitcodes();
firstoptind = optind;
pn = argv[0];
retval = BSDDIALOG_OK;
for (i = 0; i < argc; i++) {
if (strcmp(argv[i], "--version") == 0) {
printf("Version: %s\n", LIBBSDDIALOG_VERSION);
return (BSDDIALOG_OK);
}
if (strcmp(argv[i], "--help") == 0) {
usage();
return (BSDDIALOG_OK);
}
}
startup = true;
while (true) {
parsed = parseargs(argc, argv, &conf, &opt);
nargc = argc - parsed;
nargv = argv + parsed;
argc = parsed - optind;
argv += optind;
if (opt.mandatory_dialog && opt.dialogbuilder == NULL)
exit_error(true, "expected a --<dialog>");
if (opt.dialogbuilder == NULL && argc > 0)
error_args("(no --<dialog>)", argc, argv);
/* --print-maxsize or --print-version */
if (opt.mandatory_dialog == false && opt.clearscreen == false &&
opt.savethemefile == NULL && opt.dialogbuilder == NULL) {
retval = BSDDIALOG_OK;
break;
}
/* --<dialog>, --save-theme or clear-screen */
text = NULL; /* useless inits, fix compiler warnings */
rows = BSDDIALOG_AUTOSIZE;
cols = BSDDIALOG_AUTOSIZE;
if (opt.dialogbuilder != NULL) {
if (argc < 3)
exit_error(true,
"expected <text> <rows> <cols>");
if ((text = strdup(argv[0])) == NULL)
exit_error(false, "cannot allocate <text>");
if (opt.dialogbuilder != textbox_builder)
custom_text(&opt, argv[0], text);
rows = (int)strtol(argv[1], NULL, 10);
cols = (int)strtol(argv[2], NULL, 10);
argc -= 3;
argv += 3;
}
/* bsddialog terminal mode (first iteration) */
start_bsddialog_mode();
if (opt.screen_mode != NULL) {
opt.screen_mode = tigetstr(opt.screen_mode);
if (opt.screen_mode != NULL &&
opt.screen_mode != (char*)-1) {
tputs(opt.screen_mode, 1, putchar);
fflush(stdout);
bsddialog_refresh();
}
}
/* theme */
if (startup)
startuptheme();
startup = false;
if ((int)opt.theme >= 0)
setdeftheme(opt.theme);
if (opt.loadthemefile != NULL)
loadtheme(opt.loadthemefile, false);
if (opt.bikeshed)
bikeshed(&conf);
if (opt.savethemefile != NULL)
savetheme(opt.savethemefile);
/* backtitle and dialog */
if (opt.dialogbuilder == NULL)
break;
if (opt.backtitle != NULL)
if(bsddialog_backtitle(&conf, opt.backtitle))
exit_error(false, bsddialog_geterror());
retval = opt.dialogbuilder(&conf, text, rows, cols, argc, argv,
&opt);
free(text);
if (retval == BSDDIALOG_ERROR)
exit_error(false, bsddialog_geterror());
if (conf.get_height != NULL && conf.get_width != NULL)
dprintf(opt.output_fd, "DialogSize: %d, %d\n",
*conf.get_height, *conf.get_width);
if (opt.clearscreen)
bsddialog_clear(0);
opt.clearscreen = false;
/* --and-dialog ends loop with Cancel or ESC */
if (retval == BSDDIALOG_CANCEL || retval == BSDDIALOG_ESC)
break;
argc = nargc;
argv = nargv;
if (argc <= 0)
break;
/* prepare next parseargs() call */
argc++;
argv--;
argv[0] = pn;
optind = firstoptind;
}
if (bsddialog_inmode()) {
/* --clear-screen can be a single option */
if (opt.clearscreen)
bsddialog_clear(0);
bsddialog_end();
}
/* end bsddialog terminal mode */
return (EXITCODE(retval));
}
void custom_text(struct options *opt, char *text, char *buf)
{
bool trim, crwrap;
int i, j;
if (strstr(text, "\\n") == NULL) {
/* "hasnl" mode */
trim = true;
crwrap = true;
} else {
trim = false;
crwrap = opt->cr_wrap;
}
if (opt->text_unchanged) {
trim = false;
crwrap = true;
}
i = j = 0;
while (text[i] != '\0') {
switch (text[i]) {
case '\\':
buf[j] = '\\';
switch (text[i+1]) {
case 'n': /* implicitly in "hasnl" mode */
buf[j] = '\n';
i++;
if (text[i+1] == '\n')
i++;
break;
case 't':
if (opt->tab_escape) {
buf[j] = '\t';
} else {
j++;
buf[j] = 't';
}
i++;
break;
}
break;
case '\n':
buf[j] = crwrap ? '\n' : ' ';
break;
case '\t':
buf[j] = opt->text_unchanged ? '\t' : ' ';
break;
default:
buf[j] = text[i];
}
i++;
if (!trim || buf[j] != ' ' || j == 0 || buf[j-1] != ' ')
j++;
}
buf[j] = '\0';
}

125
utility/util.h Normal file
View File

@ -0,0 +1,125 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2023 Alfonso Sabato Siciliano
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _BSDDIALOG_UTIL_H_
#define _BSDDIALOG_UTIL_H_
/*
* Exit codes and errors, bsddialog.c
*/
#define BSDDIALOG_ITEM_HELP 12
void set_exit_code(int lib_retval, int exitcode);
void exit_error(bool usage, const char *fmt, ...);
void error_args(const char *dialog, int argc, char **argv);
/*
* Command Line, util_cli.c
*/
struct options {
/* Menus options */
bool item_always_quote;
char *item_default;
bool item_depth;
char *item_output_sep;
bool item_output_sepnl;
bool item_prefix;
bool item_singlequote;
/* Menus and Forms options */
bool help_print_item_name;
bool help_print_items;
bool item_bottomdesc;
/* Forms options */
int unsigned max_input_form;
/* Date and Time options */
char *date_fmt;
char *time_fmt;
/* General options */
int getH;
int getW;
bool ignore;
int output_fd;
/* Text option */
bool cr_wrap;
bool tab_escape;
bool text_unchanged;
/* Theme and Screen options*/
char *backtitle;
bool bikeshed;
enum bsddialog_default_theme theme;
bool clearscreen;
char *loadthemefile;
char *savethemefile;
const char *screen_mode;
/* Dialog */
bool mandatory_dialog;
const char *name;
int (*dialogbuilder)(struct bsddialog_conf *conf, char* text, int rows,
int cols, int argc, char **argv, struct options *opt);
};
void usage(void);
int
parseargs(int argc, char **argv, struct bsddialog_conf *conf,
struct options *opt);
/*
* Dialogs builders, util_builders.c
*/
#define BUILDER_ARGS struct bsddialog_conf *conf, char* text, int rows, \
int cols, int argc, char **argv, struct options *opt
int calendar_builder(BUILDER_ARGS);
int checklist_builder(BUILDER_ARGS);
int datebox_builder(BUILDER_ARGS);
int form_builder(BUILDER_ARGS);
int gauge_builder(BUILDER_ARGS);
int infobox_builder(BUILDER_ARGS);
int inputbox_builder(BUILDER_ARGS);
int menu_builder(BUILDER_ARGS);
int mixedform_builder(BUILDER_ARGS);
int mixedgauge_builder(BUILDER_ARGS);
int msgbox_builder(BUILDER_ARGS);
int passwordbox_builder(BUILDER_ARGS);
int passwordform_builder(BUILDER_ARGS);
int pause_builder(BUILDER_ARGS);
int radiolist_builder(BUILDER_ARGS);
int rangebox_builder(BUILDER_ARGS);
int textbox_builder(BUILDER_ARGS);
int timebox_builder(BUILDER_ARGS);
int treeview_builder(BUILDER_ARGS);
int yesno_builder(BUILDER_ARGS);
/*
* Theme, util_theme.c
*/
void savetheme(const char *file);
void loadtheme(const char *file, bool compatibility);
void setdeftheme(enum bsddialog_default_theme theme);
void bikeshed(struct bsddialog_conf *conf);
void startuptheme(void);
#endif

768
utility/util_builders.c Normal file
View File

@ -0,0 +1,768 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2023 Alfonso Sabato Siciliano
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <bsddialog.h>
#include <bsddialog_theme.h>
#include "util.h"
#define NO_PRINT_VALUES(rv) \
(rv == BSDDIALOG_ERROR || rv == BSDDIALOG_CANCEL || rv == BSDDIALOG_ESC)
/* message */
int infobox_builder(BUILDER_ARGS)
{
if (argc > 0)
error_args(opt->name, argc, argv);
return (bsddialog_infobox(conf, text, rows, cols));
}
int msgbox_builder(BUILDER_ARGS)
{
if (argc > 0)
error_args(opt->name, argc, argv);
return (bsddialog_msgbox(conf, text, rows, cols));
}
int yesno_builder(BUILDER_ARGS)
{
if (argc > 0)
error_args(opt->name, argc, argv);
return (bsddialog_yesno(conf, text, rows, cols));
}
/* textbox */
int textbox_builder(BUILDER_ARGS)
{
if (argc > 0)
error_args(opt->name, argc, argv);
return (bsddialog_textbox(conf, text, rows, cols));
}
/* bar */
int gauge_builder(BUILDER_ARGS)
{
int output;
unsigned int perc;
perc = 0;
if (argc == 1) {
perc = (u_int)strtoul(argv[0], NULL, 10);
perc = perc > 100 ? 100 : perc;
} else if (argc > 1) {
error_args(opt->name, argc - 1, argv + 1);
}
output = bsddialog_gauge(conf, text, rows, cols, perc, STDIN_FILENO,
"XXX", "EOF");
return (output);
}
int mixedgauge_builder(BUILDER_ARGS)
{
int output, *minipercs;
unsigned int i, mainperc, nminibars;
const char **minilabels;
if (argc < 1)
exit_error(true, "%s missing <mainperc>", opt->name);
if (((argc-1) % 2) != 0)
exit_error(true,
"bad %s pair number [<minilabel> <miniperc>]", opt->name);
mainperc = (u_int)strtoul(argv[0], NULL, 10);
mainperc = mainperc > 100 ? 100 : mainperc;
argc--;
argv++;
nminibars = argc / 2;
if ((minilabels = calloc(nminibars, sizeof(char*))) == NULL)
exit_error(false, "Cannot allocate memory for minilabels");
if ((minipercs = calloc(nminibars, sizeof(int))) == NULL)
exit_error(false, "Cannot allocate memory for minipercs");
for (i = 0; i < nminibars; i++) {
minilabels[i] = argv[i * 2];
minipercs[i] = (int)strtol(argv[i * 2 + 1], NULL, 10);
}
output = bsddialog_mixedgauge(conf, text, rows, cols, mainperc,
nminibars, minilabels, minipercs);
return (output);
}
int pause_builder(BUILDER_ARGS)
{
int output;
unsigned int secs;
if (argc == 0)
exit_error(true, "--pause missing <seconds>");
if (argc > 1)
error_args(opt->name, argc - 1, argv + 1);
secs = (u_int)strtoul(argv[0], NULL, 10);
output = bsddialog_pause(conf, text, rows, cols, &secs);
return (output);
}
int rangebox_builder(BUILDER_ARGS)
{
int output, min, max, value;
if (argc < 2)
exit_error(true, "--rangebox missing <min> <max> [<init>]");
if (argc > 3)
error_args("--rangebox", argc - 3, argv + 3);
min = (int)strtol(argv[0], NULL, 10);
max = (int)strtol(argv[1], NULL, 10);
if (argc == 3) {
value = (int)strtol(argv[2], NULL, 10);
value = value < min ? min : value;
value = value > max ? max : value;
} else
value = min;
output = bsddialog_rangebox(conf, text, rows, cols, min, max, &value);
if (NO_PRINT_VALUES(output) == false)
dprintf(opt->output_fd, "%d", value);
return (output);
}
/* date and time */
static int date(BUILDER_ARGS)
{
int rv;
unsigned int yy, mm, dd;
time_t cal;
struct tm *localtm;
char stringdate[1024];
time(&cal);
localtm = localtime(&cal);
yy = localtm->tm_year + 1900;
mm = localtm->tm_mon + 1;
dd = localtm->tm_mday;
if (argc > 3) {
error_args(opt->name, argc - 3, argv + 3);
} else if (argc == 3) {
/* lib checks/sets max and min */
dd = (u_int)strtoul(argv[0], NULL, 10);
mm = (u_int)strtoul(argv[1], NULL, 10);
yy = (u_int)strtoul(argv[2], NULL, 10);
}
if (strcmp(opt->name, "--datebox") == 0)
rv = bsddialog_datebox(conf, text, rows, cols, &yy, &mm, &dd);
else
rv = bsddialog_calendar(conf, text, rows, cols, &yy, &mm, &dd);
if (NO_PRINT_VALUES(rv))
return (rv);
if (opt->date_fmt != NULL) {
time(&cal);
localtm = localtime(&cal);
localtm->tm_year = yy - 1900;
localtm->tm_mon = mm - 1;
localtm->tm_mday = dd;
strftime(stringdate, 1024, opt->date_fmt, localtm);
dprintf(opt->output_fd, "%s", stringdate);
} else if (opt->bikeshed && ~dd & 1) {
dprintf(opt->output_fd, "%u/%u/%u", dd, mm, yy);
} else {
dprintf(opt->output_fd, "%02u/%02u/%u", dd, mm, yy);
}
return (rv);
}
int calendar_builder(BUILDER_ARGS)
{
/* Use height autosizing with rows = 2. Documented in bsddialog(1).
*
* f_dialog_calendar_size() in bsdconfig/share/dialog.subr:1352
* computes height 2 for `dialog --calendar', called by:
* 1) f_dialog_input_expire_password() in
* bsdconfig/usermgmt/share/user_input.subr:517 and
* 2) f_dialog_input_expire_account() in
* bsdconfig/usermgmt/share/user_input.subr:660.
*
* Then use height autosizing with 2 that is min height like dialog.
*/
if (rows == 2)
rows = 0;
return (date(conf, text, rows, cols, argc, argv, opt));
}
int datebox_builder(BUILDER_ARGS)
{
return (date(conf, text, rows, cols, argc, argv, opt));
}
int timebox_builder(BUILDER_ARGS)
{
int output;
unsigned int hh, mm, ss;
time_t clock;
struct tm *localtm;
char stringtime[1024];
time(&clock);
localtm = localtime(&clock);
hh = localtm->tm_hour;
mm = localtm->tm_min;
ss = localtm->tm_sec;
if (argc > 3) {
error_args("--timebox", argc - 3, argv + 3);
} else if (argc == 3) {
hh = (u_int)strtoul(argv[0], NULL, 10);
mm = (u_int)strtoul(argv[1], NULL, 10);
ss = (u_int)strtoul(argv[2], NULL, 10);
}
output = bsddialog_timebox(conf, text, rows, cols, &hh, &mm, &ss);
if (NO_PRINT_VALUES(output))
return (output);
if (opt->time_fmt != NULL) {
time(&clock);
localtm = localtime(&clock);
localtm->tm_hour = hh;
localtm->tm_min = mm;
localtm->tm_sec = ss;
strftime(stringtime, 1024, opt->time_fmt, localtm);
dprintf(opt->output_fd, "%s", stringtime);
} else if (opt->bikeshed && ~ss & 1) {
dprintf(opt->output_fd, "%u:%u:%u", hh, mm, ss);
} else {
dprintf(opt->output_fd, "%02u:%02u:%02u", hh, mm, ss);
}
return (output);
}
/* menu */
static void
get_menu_items(int argc, char **argv, bool setprefix, bool setdepth,
bool setname, bool setdesc, bool setstatus, bool sethelp,
unsigned int *nitems, struct bsddialog_menuitem **items, int *focusitem,
struct options *opt)
{
unsigned int i, j, sizeitem;
*focusitem = -1;
sizeitem = 0;
sizeitem += setprefix ? 1 : 0;
sizeitem += setdepth ? 1 : 0;
sizeitem += setname ? 1 : 0;
sizeitem += setdesc ? 1 : 0;
sizeitem += setstatus ? 1 : 0;
sizeitem += sethelp ? 1 : 0;
if ((argc % sizeitem) != 0)
exit_error(true, "%s bad arguments items number", opt->name);
*nitems = argc / sizeitem;
*items = calloc(*nitems, sizeof(struct bsddialog_menuitem));
if (items == NULL)
exit_error(false, "%s cannot allocate items", opt->name);
j = 0;
for (i = 0; i < *nitems; i++) {
(*items)[i].prefix = setprefix ? argv[j++] : "";
(*items)[i].depth = setdepth ?
(u_int)strtoul(argv[j++], NULL, 0) : 0;
(*items)[i].name = setname ? argv[j++] : "";
(*items)[i].desc = setdesc ? argv[j++] : "";
if (setstatus) {
if (strcasecmp(argv[j], "on") == 0)
(*items)[i].on = true;
else if (strcasecmp(argv[j], "off") == 0)
(*items)[i].on = false;
else
exit_error(true,
"\"%s\" (item %i) invalid status \"%s\"",
(*items)[i].name, i+1, argv[j]);
j++;
} else
(*items)[i].on = false;
(*items)[i].bottomdesc = sethelp ? argv[j++] : "";
if (opt->item_default != NULL && *focusitem == -1)
if (strcmp((*items)[i].name, opt->item_default) == 0)
*focusitem = i;
}
}
static void
print_menu_items(int output, int nitems, struct bsddialog_menuitem *items,
int focusitem, struct options *opt)
{
bool sep, sepbefore, sepafter, sepsecond, toquote, ismenu, ischecklist;
int i;
char quotech;
const char *focusname, *sepstr;
ismenu = (strcmp(opt->name, "--menu") == 0) ? true : false;
ischecklist = (strcmp(opt->name, "--checklist") == 0) ? true : false;
sep = false;
quotech = opt->item_singlequote ? '\'' : '"';
if (NO_PRINT_VALUES(output))
return;
if (output == BSDDIALOG_HELP) {
dprintf(opt->output_fd, "HELP ");
if (focusitem >= 0) {
focusname = items[focusitem].name;
if (opt->item_bottomdesc &&
opt->help_print_item_name == false)
focusname = items[focusitem].bottomdesc;
toquote = false;
if (strchr(focusname, ' ') != NULL) {
toquote = opt->item_always_quote;
if (ismenu == false &&
opt->item_output_sepnl == false)
toquote = true;
}
if (toquote) {
dprintf(opt->output_fd, "%c%s%c",
quotech, focusname, quotech);
} else
dprintf(opt->output_fd, "%s", focusname);
}
if (ismenu || opt->help_print_items == false)
return;
sep = true;
}
sepbefore = false;
sepsecond = false;
if ((sepstr = opt->item_output_sep) == NULL) {
if (opt->item_output_sepnl)
sepstr = "\n";
else {
sepstr = " ";
sepsecond = true;
}
} else
sepbefore = true;
sepafter = false;
if (opt->item_output_sepnl) {
sepbefore = false;
sepafter = true;
}
for (i = 0; i < nitems; i++) {
if (items[i].on == false)
continue;
if (sep || sepbefore)
dprintf(opt->output_fd, "%s", sepstr);
sep = false;
if (sepsecond)
sep = true;
toquote = false;
if (strchr(items[i].name, ' ') != NULL) {
toquote = opt->item_always_quote;
if (ischecklist && opt->item_output_sepnl == false)
toquote = true;
}
if (toquote)
dprintf(opt->output_fd, "%c%s%c",
quotech, items[i].name, quotech);
else
dprintf(opt->output_fd, "%s", items[i].name);
if (sepafter)
dprintf(opt->output_fd, "%s", sepstr);
}
}
int checklist_builder(BUILDER_ARGS)
{
int output, focusitem;
unsigned int menurows, nitems;
struct bsddialog_menuitem *items;
if (argc < 1)
exit_error(true, "--checklist missing <menurows>");
menurows = (u_int)strtoul(argv[0], NULL, 10);
get_menu_items(argc-1, argv+1, opt->item_prefix, opt->item_depth, true,
true, true, opt->item_bottomdesc, &nitems, &items, &focusitem, opt);
output = bsddialog_checklist(conf, text, rows, cols, menurows, nitems,
items, &focusitem);
print_menu_items(output, nitems, items, focusitem, opt);
free(items);
if (output == BSDDIALOG_HELP && opt->item_bottomdesc)
output = BSDDIALOG_ITEM_HELP;
return (output);
}
int menu_builder(BUILDER_ARGS)
{
int output, focusitem;
unsigned int menurows, nitems;
struct bsddialog_menuitem *items;
if (argc < 1)
exit_error(true, "--menu missing <menurows>");
menurows = (u_int)strtoul(argv[0], NULL, 10);
get_menu_items(argc-1, argv+1, opt->item_prefix, opt->item_depth, true,
true, false, opt->item_bottomdesc, &nitems, &items, &focusitem,
opt);
output = bsddialog_menu(conf, text, rows, cols, menurows, nitems,
items, &focusitem);
print_menu_items(output, nitems, items, focusitem, opt);
free(items);
if (output == BSDDIALOG_HELP && opt->item_bottomdesc)
output = BSDDIALOG_ITEM_HELP;
return (output);
}
int radiolist_builder(BUILDER_ARGS)
{
int output, focusitem;
unsigned int menurows, nitems;
struct bsddialog_menuitem *items;
if (argc < 1)
exit_error(true, "--radiolist missing <menurows>");
menurows = (u_int)strtoul(argv[0], NULL, 10);
get_menu_items(argc-1, argv+1, opt->item_prefix, opt->item_depth, true,
true, true, opt->item_bottomdesc, &nitems, &items, &focusitem, opt);
output = bsddialog_radiolist(conf, text, rows, cols, menurows, nitems,
items, &focusitem);
print_menu_items(output, nitems, items, focusitem, opt);
free(items);
if (output == BSDDIALOG_HELP && opt->item_bottomdesc)
output = BSDDIALOG_ITEM_HELP;
return (output);
}
int treeview_builder(BUILDER_ARGS)
{
int output, focusitem;
unsigned int menurows, nitems;
struct bsddialog_menuitem *items;
if (argc < 1)
exit_error(true, "--treeview missing <menurows>");
menurows = (u_int)strtoul(argv[0], NULL, 10);
get_menu_items(argc-1, argv+1, opt->item_prefix, true, true, true, true,
opt->item_bottomdesc, &nitems, &items, &focusitem, opt);
conf->menu.no_name = true;
conf->menu.align_left = true;
output = bsddialog_radiolist(conf, text, rows, cols, menurows, nitems,
items, &focusitem);
print_menu_items(output, nitems, items, focusitem, opt);
free(items);
if (output == BSDDIALOG_HELP && opt->item_bottomdesc)
output = BSDDIALOG_ITEM_HELP;
return (output);
}
/* form */
static void
print_form_items(int output, int nitems, struct bsddialog_formitem *items,
int focusitem, struct options *opt)
{
int i;
const char *helpname;
if (NO_PRINT_VALUES(output))
return;
if (output == BSDDIALOG_HELP) {
dprintf(opt->output_fd, "HELP");
if (focusitem >= 0) {
helpname = items[focusitem].label;
if (opt->item_bottomdesc &&
opt->help_print_item_name == false)
helpname = items[focusitem].bottomdesc;
dprintf(opt->output_fd, " %s", helpname);
}
if(opt->help_print_items == false)
return;
dprintf(opt->output_fd, "\n");
}
for (i = 0; i < nitems; i++) {
dprintf(opt->output_fd, "%s\n", items[i].value);
free(items[i].value);
}
}
int form_builder(BUILDER_ARGS)
{
int output, fieldlen, valuelen, focusitem;
unsigned int i, j, flags, formheight, nitems, sizeitem;
struct bsddialog_formitem *items;
if (argc < 1)
exit_error(true, "--form missing <formheight>");
formheight = (u_int)strtoul(argv[0], NULL, 10);
argc--;
argv++;
sizeitem = opt->item_bottomdesc ? 9 : 8;
if (argc % sizeitem != 0)
exit_error(true, "--form bad number of arguments items");
nitems = argc / sizeitem;
if ((items = calloc(nitems, sizeof(struct bsddialog_formitem))) == NULL)
exit_error(false, "cannot allocate memory for form items");
j = 0;
for (i = 0; i < nitems; i++) {
items[i].label = argv[j++];
items[i].ylabel = (u_int)strtoul(argv[j++], NULL, 10);
items[i].xlabel = (u_int)strtoul(argv[j++], NULL, 10);
items[i].init = argv[j++];
items[i].yfield = (u_int)strtoul(argv[j++], NULL, 10);
items[i].xfield = (u_int)strtoul(argv[j++], NULL, 10);
fieldlen = (int)strtol(argv[j++], NULL, 10);
items[i].fieldlen = abs(fieldlen);
valuelen = (int)strtol(argv[j++], NULL, 10);
items[i].maxvaluelen = valuelen == 0 ? abs(fieldlen) : valuelen;
flags = (fieldlen < 0 ? BSDDIALOG_FIELDREADONLY : 0);
items[i].flags = flags;
items[i].bottomdesc = opt->item_bottomdesc ? argv[j++] : "";
}
focusitem = -1;
output = bsddialog_form(conf, text, rows, cols, formheight, nitems,
items, &focusitem);
print_form_items(output, nitems, items, focusitem, opt);
free(items);
if (output == BSDDIALOG_HELP && opt->item_bottomdesc)
output = BSDDIALOG_ITEM_HELP;
return (output);
}
int inputbox_builder(BUILDER_ARGS)
{
int output;
struct bsddialog_formitem item;
if (argc > 1)
error_args("--inputbox", argc - 1, argv + 1);
item.label = "";
item.ylabel = 0;
item.xlabel = 0;
item.init = argc > 0 ? argv[0] : "";
item.yfield = 0;
item.xfield = 0;
item.fieldlen = 1;
item.maxvaluelen = opt->max_input_form;
item.flags = BSDDIALOG_FIELDNOCOLOR;
item.flags |= BSDDIALOG_FIELDCURSOREND;
item.flags |= BSDDIALOG_FIELDEXTEND;
item.bottomdesc = "";
output = bsddialog_form(conf, text, rows, cols, 1, 1, &item, NULL);
print_form_items(output, 1, &item, -1, opt);
return (output);
}
int mixedform_builder(BUILDER_ARGS)
{
int output, focusitem;
unsigned int i, j, formheight, nitems, sizeitem;
struct bsddialog_formitem *items;
if (argc < 1)
exit_error(true, "--mixedform missing <formheight>");
formheight = (u_int)strtoul(argv[0], NULL, 10);
argc--;
argv++;
sizeitem = opt->item_bottomdesc ? 10 : 9;
if (argc % sizeitem != 0)
exit_error(true, "--mixedform bad number of arguments items");
nitems = argc / sizeitem;
if ((items = calloc(nitems, sizeof(struct bsddialog_formitem))) == NULL)
exit_error(false, "cannot allocate memory for form items");
j = 0;
for (i = 0; i < nitems; i++) {
items[i].label = argv[j++];
items[i].ylabel = (u_int)strtoul(argv[j++], NULL, 10);
items[i].xlabel = (u_int)strtoul(argv[j++], NULL, 10);
items[i].init = argv[j++];
items[i].yfield = (u_int)strtoul(argv[j++], NULL, 10);
items[i].xfield = (u_int)strtoul(argv[j++], NULL, 10);
items[i].fieldlen = (u_int)strtoul(argv[j++], NULL, 10);
items[i].maxvaluelen = (u_int)strtoul(argv[j++], NULL, 10);
items[i].flags = (u_int)strtoul(argv[j++], NULL, 10);
items[i].bottomdesc = opt->item_bottomdesc ? argv[j++] : "";
}
focusitem = -1;
output = bsddialog_form(conf, text, rows, cols, formheight, nitems,
items, &focusitem);
print_form_items(output, nitems, items, focusitem, opt);
free(items);
if (output == BSDDIALOG_HELP && opt->item_bottomdesc)
output = BSDDIALOG_ITEM_HELP;
return (output);
}
int passwordbox_builder(BUILDER_ARGS)
{
int output;
struct bsddialog_formitem item;
if (argc > 1)
error_args("--passwordbox", argc - 1, argv + 1);
item.label = "";
item.ylabel = 0;
item.xlabel = 0;
item.init = argc > 0 ? argv[0] : "";
item.yfield = 0;
item.xfield = 0;
item.fieldlen = 1;
item.maxvaluelen = opt->max_input_form;
item.flags = BSDDIALOG_FIELDHIDDEN;
item.flags |= BSDDIALOG_FIELDNOCOLOR;
item.flags |= BSDDIALOG_FIELDCURSOREND;
item.flags |= BSDDIALOG_FIELDEXTEND;
item.bottomdesc = "";
output = bsddialog_form(conf, text, rows, cols, 1, 1, &item, NULL);
print_form_items(output, 1, &item, -1, opt);
return (output);
}
int passwordform_builder(BUILDER_ARGS)
{
int output, fieldlen, valuelen, focusitem;
unsigned int i, j, flags, formheight, nitems, sizeitem;
struct bsddialog_formitem *items;
if (argc < 1)
exit_error(true, "--passwordform missing <formheight>");
formheight = (u_int)strtoul(argv[0], NULL, 10);
argc--;
argv++;
sizeitem = opt->item_bottomdesc ? 9 : 8;
if (argc % sizeitem != 0)
exit_error(true, "--passwordform bad arguments items number");
flags = BSDDIALOG_FIELDHIDDEN;
nitems = argc / sizeitem;
if ((items = calloc(nitems, sizeof(struct bsddialog_formitem))) == NULL)
exit_error(false, "cannot allocate memory for form items");
j = 0;
for (i = 0; i < nitems; i++) {
items[i].label = argv[j++];
items[i].ylabel = (u_int)strtoul(argv[j++], NULL, 10);
items[i].xlabel = (u_int)strtoul(argv[j++], NULL, 10);
items[i].init = argv[j++];
items[i].yfield = (u_int)strtoul(argv[j++], NULL, 10);
items[i].xfield = (u_int)strtoul(argv[j++], NULL, 10);
fieldlen = (int)strtol(argv[j++], NULL, 10);
items[i].fieldlen = abs(fieldlen);
valuelen = (int)strtol(argv[j++], NULL, 10);
items[i].maxvaluelen = valuelen == 0 ? abs(fieldlen) : valuelen;
flags |= (fieldlen < 0 ? BSDDIALOG_FIELDREADONLY : 0);
items[i].flags = flags;
items[i].bottomdesc = opt->item_bottomdesc ? argv[j++] : "";
}
focusitem = -1;
output = bsddialog_form(conf, text, rows, cols, formheight, nitems,
items, &focusitem);
print_form_items(output, nitems, items, focusitem, opt);
free(items);
if (output == BSDDIALOG_HELP && opt->item_bottomdesc)
output = BSDDIALOG_ITEM_HELP;
return (output);
}

841
utility/util_cli.c Normal file
View File

@ -0,0 +1,841 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021-2023 Alfonso Sabato Siciliano
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/ioctl.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <bsddialog.h>
#include <bsddialog_theme.h>
#include "util.h"
enum OPTS {
/* Options */
ALTERNATE_SCREEN = '?' + 1,
AND_DIALOG,
ASCII_LINES,
BACKTITLE,
BEGIN_X,
BEGIN_Y,
BIKESHED,
CANCEL_EXIT_CODE,
CANCEL_LABEL,
CLEAR_DIALOG,
CLEAR_SCREEN,
COLUMNS_PER_ROW,
CR_WRAP,
DATEBOX_FORMAT,
DATE_FORMAT,
DEFAULT_BUTTON,
DEFAULT_ITEM,
DEFAULT_NO,
DISABLE_ESC,
ERROR_EXIT_CODE,
ESC_EXIT_CODE,
EXIT_LABEL,
EXTRA_BUTTON,
EXTRA_EXIT_CODE,
EXTRA_LABEL,
HELP_BUTTON,
HELP_EXIT_CODE,
HELP_LABEL,
HELP_PRINT_ITEMS,
HELP_PRINT_NAME,
HFILE,
HLINE,
HMSG,
IGNORE,
INSECURE,
ITEM_BOTTOM_DESC,
ITEM_DEPTH,
ITEM_PREFIX,
LEFT1_BUTTON,
LEFT1_EXIT_CODE,
LEFT2_BUTTON,
LEFT2_EXIT_CODE,
LEFT3_BUTTON,
LEFT3_EXIT_CODE,
LOAD_THEME,
MAX_INPUT,
NO_CANCEL,
NO_DESCRIPTIONS,
NO_LINES,
NO_NAMES,
NO_OK,
NO_SHADOW,
NORMAL_SCREEN,
OK_EXIT_CODE,
OK_LABEL,
OUTPUT_FD,
OUTPUT_SEPARATOR,
PRINT_MAXSIZE,
PRINT_SIZE,
PRINT_VERSION,
QUOTED,
RIGHT1_BUTTON,
RIGHT1_EXIT_CODE,
RIGHT2_BUTTON,
RIGHT2_EXIT_CODE,
RIGHT3_BUTTON,
RIGHT3_EXIT_CODE,
SAVE_THEME,
SEPARATE_OUTPUT,
SHADOW,
SINGLE_QUOTED,
SLEEP,
STDERR,
STDOUT,
SWITCH_BUTTONS,
TAB_ESCAPE,
TAB_LEN,
TEXT_ESCAPE,
TEXT_UNCHANGED,
THEME,
TIMEOUT_EXIT_CODE,
TIME_FORMAT,
TITLE,
/* Dialogs */
CALENDAR,
CHECKLIST,
DATEBOX,
FORM,
GAUGE,
INFOBOX,
INPUTBOX,
MENU,
MIXEDFORM,
MIXEDGAUGE,
MSGBOX,
PASSWORDBOX,
PASSWORDFORM,
PAUSE,
RADIOLIST,
RANGEBOX,
TEXTBOX,
TIMEBOX,
TREEVIEW,
YESNO
};
/* options descriptor */
static struct option longopts[] = {
/* Options */
{"alternate-screen", no_argument, NULL, ALTERNATE_SCREEN},
{"and-dialog", no_argument, NULL, AND_DIALOG},
{"and-widget", no_argument, NULL, AND_DIALOG},
{"ascii-lines", no_argument, NULL, ASCII_LINES},
{"backtitle", required_argument, NULL, BACKTITLE},
{"begin-x", required_argument, NULL, BEGIN_X},
{"begin-y", required_argument, NULL, BEGIN_Y},
{"bikeshed", no_argument, NULL, BIKESHED},
{"cancel-exit-code", required_argument, NULL, CANCEL_EXIT_CODE},
{"cancel-label", required_argument, NULL, CANCEL_LABEL},
{"clear", no_argument, NULL, CLEAR_SCREEN},
{"clear-dialog", no_argument, NULL, CLEAR_DIALOG},
{"clear-screen", no_argument, NULL, CLEAR_SCREEN},
{"colors", no_argument, NULL, TEXT_ESCAPE},
{"columns-per-row", required_argument, NULL, COLUMNS_PER_ROW},
{"cr-wrap", no_argument, NULL, CR_WRAP},
{"datebox-format", required_argument, NULL, DATEBOX_FORMAT},
{"date-format", required_argument, NULL, DATE_FORMAT},
{"defaultno", no_argument, NULL, DEFAULT_NO},
{"default-button", required_argument, NULL, DEFAULT_BUTTON},
{"default-item", required_argument, NULL, DEFAULT_ITEM},
{"default-no", no_argument, NULL, DEFAULT_NO},
{"disable-esc", no_argument, NULL, DISABLE_ESC},
{"error-exit-code", required_argument, NULL, ERROR_EXIT_CODE},
{"esc-exit-code", required_argument, NULL, ESC_EXIT_CODE},
{"exit-label", required_argument, NULL, EXIT_LABEL},
{"extra-button", no_argument, NULL, EXTRA_BUTTON},
{"extra-exit-code", required_argument, NULL, EXTRA_EXIT_CODE},
{"extra-label", required_argument, NULL, EXTRA_LABEL},
{"help-button", no_argument, NULL, HELP_BUTTON},
{"help-exit-code", required_argument, NULL, HELP_EXIT_CODE},
{"help-label", required_argument, NULL, HELP_LABEL},
{"help-print-items", no_argument, NULL, HELP_PRINT_ITEMS},
{"help-print-name", no_argument, NULL, HELP_PRINT_NAME},
{"help-status", no_argument, NULL, HELP_PRINT_ITEMS},
{"help-tags", no_argument, NULL, HELP_PRINT_NAME},
{"hfile", required_argument, NULL, HFILE},
{"hline", required_argument, NULL, HLINE},
{"hmsg", required_argument, NULL, HMSG},
{"ignore", no_argument, NULL, IGNORE},
{"insecure", no_argument, NULL, INSECURE},
{"item-bottom-desc", no_argument, NULL, ITEM_BOTTOM_DESC},
{"item-depth", no_argument, NULL, ITEM_DEPTH},
{"item-help", no_argument, NULL, ITEM_BOTTOM_DESC},
{"item-prefix", no_argument, NULL, ITEM_PREFIX},
{"keep-tite", no_argument, NULL, ALTERNATE_SCREEN},
{"left1-button", required_argument, NULL, LEFT1_BUTTON},
{"left1-exit-code", required_argument, NULL, LEFT1_EXIT_CODE},
{"left2-button", required_argument, NULL, LEFT2_BUTTON},
{"left2-exit-code", required_argument, NULL, LEFT2_EXIT_CODE},
{"left3-button", required_argument, NULL, LEFT3_BUTTON},
{"left3-exit-code", required_argument, NULL, LEFT3_EXIT_CODE},
{"load-theme", required_argument, NULL, LOAD_THEME},
{"max-input", required_argument, NULL, MAX_INPUT},
{"no-cancel", no_argument, NULL, NO_CANCEL},
{"nocancel", no_argument, NULL, NO_CANCEL},
{"no-descriptions", no_argument, NULL, NO_DESCRIPTIONS},
{"no-items", no_argument, NULL, NO_DESCRIPTIONS},
{"no-label", required_argument, NULL, CANCEL_LABEL},
{"no-lines", no_argument, NULL, NO_LINES},
{"no-names", no_argument, NULL, NO_NAMES},
{"no-ok", no_argument, NULL, NO_OK},
{"nook", no_argument, NULL, NO_OK},
{"no-shadow", no_argument, NULL, NO_SHADOW},
{"no-tags", no_argument, NULL, NO_NAMES},
{"normal-screen", no_argument, NULL, NORMAL_SCREEN},
{"ok-exit-code", required_argument, NULL, OK_EXIT_CODE},
{"ok-label", required_argument, NULL, OK_LABEL},
{"output-fd", required_argument, NULL, OUTPUT_FD},
{"output-separator", required_argument, NULL, OUTPUT_SEPARATOR},
{"print-maxsize", no_argument, NULL, PRINT_MAXSIZE},
{"print-size", no_argument, NULL, PRINT_SIZE},
{"print-version", no_argument, NULL, PRINT_VERSION},
{"quoted", no_argument, NULL, QUOTED},
{"right1-button", required_argument, NULL, RIGHT1_BUTTON},
{"right1-exit-code", required_argument, NULL, RIGHT1_EXIT_CODE},
{"right2-button", required_argument, NULL, RIGHT2_BUTTON},
{"right2-exit-code", required_argument, NULL, RIGHT2_EXIT_CODE},
{"right3-button", required_argument, NULL, RIGHT3_BUTTON},
{"right3-exit-code", required_argument, NULL, RIGHT3_EXIT_CODE},
{"save-theme", required_argument, NULL, SAVE_THEME},
{"separate-output", no_argument, NULL, SEPARATE_OUTPUT},
{"separator", required_argument, NULL, OUTPUT_SEPARATOR},
{"shadow", no_argument, NULL, SHADOW},
{"single-quoted", no_argument, NULL, SINGLE_QUOTED},
{"sleep", required_argument, NULL, SLEEP},
{"stderr", no_argument, NULL, STDERR},
{"stdout", no_argument, NULL, STDOUT},
{"switch-buttons", no_argument, NULL, SWITCH_BUTTONS},
{"tab-escape", no_argument, NULL, TAB_ESCAPE},
{"tab-len", required_argument, NULL, TAB_LEN},
{"text-escape", no_argument, NULL, TEXT_ESCAPE},
{"text-unchanged", no_argument, NULL, TEXT_UNCHANGED},
{"theme", required_argument, NULL, THEME},
{"timeout-exit-code", required_argument, NULL, TIMEOUT_EXIT_CODE},
{"time-format", required_argument, NULL, TIME_FORMAT},
{"title", required_argument, NULL, TITLE},
{"yes-label", required_argument, NULL, OK_LABEL},
/* Dialogs */
{"calendar", no_argument, NULL, CALENDAR},
{"checklist", no_argument, NULL, CHECKLIST},
{"datebox", no_argument, NULL, DATEBOX},
{"form", no_argument, NULL, FORM},
{"gauge", no_argument, NULL, GAUGE},
{"infobox", no_argument, NULL, INFOBOX},
{"inputbox", no_argument, NULL, INPUTBOX},
{"menu", no_argument, NULL, MENU},
{"mixedform", no_argument, NULL, MIXEDFORM},
{"mixedgauge", no_argument, NULL, MIXEDGAUGE},
{"msgbox", no_argument, NULL, MSGBOX},
{"passwordbox", no_argument, NULL, PASSWORDBOX},
{"passwordform", no_argument, NULL, PASSWORDFORM},
{"pause", no_argument, NULL, PAUSE},
{"radiolist", no_argument, NULL, RADIOLIST},
{"rangebox", no_argument, NULL, RANGEBOX},
{"textbox", no_argument, NULL, TEXTBOX},
{"timebox", no_argument, NULL, TIMEBOX},
{"treeview", no_argument, NULL, TREEVIEW},
{"yesno", no_argument, NULL, YESNO},
/* END */
{ NULL, 0, NULL, 0}
};
void usage(void)
{
printf("usage: bsddialog --help | --version\n");
printf(" bsddialog [--<opt>] --<dialog> <text> <rows> <cols> "
"[<arg>] [--<opt>]\n");
printf(" bsddialog ... --<dialog1> ... [--and-dialog --<dialog2> "
"...] ...\n");
printf("\n");
printf("Options:\n");
printf(" --alternate-screen, --ascii-lines, --backtitle <backtitle>,"
" --begin-x <x>,\n --begin-y <y>, --bikeshed,"
" --cancel-exit-code <retval>, --cancel-label <label>,\n"
" --clear-dialog, --clear-screen, --columns-per-row <columns>,"
" --cr-wrap,\n --datebox-format d/m/y|m/d/y|y/m/d,"
" --date-format <format>,\n --default-button <label>,"
" --default-item <name>, --default-no, --disable-esc,\n"
" --error-exit-code <retval>, --esc-exit-code <retval>,"
" --exit-label <label>,\n --extra-button,"
" --extra-exit-code <retval>, --extra-label <label>,\n"
" --left1-button <label>, --left1-exit-code <retval>,"
" --left2-button <label>,\n --left2-exit-code <retval>,"
" --left3-button <label>, --left3-exit-code <retval>,\n"
" --help-button, --help-exit-code <retval>, --help-label <label>,\n"
" --help-print-items, --help-print-name, --hfile <file>,"
" --hline <string>,\n --hmsg <string>, --ignore, --insecure,"
" --item-bottom-desc, --item-depth,\n --item-prefix,"
" --load-theme <file>, --max-input <size>, --no-cancel,\n"
" --no-descriptions, --no-label <label>, --no-lines, --no-names,"
" --no-ok,\n --no-shadow, --normal-screen, --ok-exit-code <retval>,"
" --ok-label <label>,\n --output-fd <fd>, --output-separator <sep>,"
" --print-maxsize, --print-size,\n --print-version, --quoted,"
" --right1-button <label>,\n --right1-exit-code <retval>,"
" --right2-button <label>,\n --right2-exit-code <retval>,"
" --right3-button <label>,\n --right3-exit-code <retval>,"
" --save-theme <file>, --separate-output,\n --separator <sep>,"
" --shadow, --single-quoted, --sleep <secs>, --stderr,\n --stdout,"
" --switch-buttons, --tab-escape, --tab-len <spaces>,"
" --text-escape,\n --text-unchanged, --theme 3d|blackwhite|flat,"
" --timeout-exit-code <retval>,\n --time-format <format>,"
" --title <title>, --yes-label <label>.");
printf("\n\n");
printf("Dialogs:\n");
printf(" --calendar <text> <rows> <cols> [<dd> <mm> <yy>]\n");
printf(" --checklist <text> <rows> <cols> <menurows> [<name> <desc> "
"on|off] ...\n");
printf(" --datebox <text> <rows> <cols> [<dd> <mm> <yy>]\n");
printf(" --form <text> <rows> <cols> <formrows> [<label> <ylabel> "
"<xlabel> <init> <yfield> <xfield> <fieldlen> <maxletters>] "
"...\n");
printf(" --gauge <text> <rows> <cols> [<perc>]\n");
printf(" --infobox <text> <rows> <cols>\n");
printf(" --inputbox <text> <rows> <cols> [<init>]\n");
printf(" --menu <text> <rows> <cols> <menurows> [<name> <desc>] ...\n");
printf(" --mixedform <text> <rows> <cols> <formrows> [<label> <ylabel> "
"<xlabel> <init> <yfield> <xfield> <fieldlen> <maxletters> "
"0|1|2] ...\n");
printf(" --mixedgauge <text> <rows> <cols> <mainperc> [<minilabel> "
"<miniperc>] ...\n");
printf(" --msgbox <text> <rows> <cols>\n");
printf(" --passwordbox <text> <rows> <cols> [<init>]\n");
printf(" --passwordform <text> <rows> <cols> <formrows> [<label> "
"<ylabel> <xlabel> <init> <yfield> <xfield> <fieldlen> "
"<maxletters>] ...\n");
printf(" --pause <text> <rows> <cols> <secs>\n");
printf(" --radiolist <text> <rows> <cols> <menurows> [<name> <desc> "
"on|off] ...\n");
printf(" --rangebox <text> <rows> <cols> <min> <max> [<init>]\n");
printf(" --textbox <file> <rows> <cols>\n");
printf(" --timebox <text> <rows> <cols> [<hh> <mm> <ss>]\n");
printf(" --treeview <text> <rows> <cols> <menurows> [<depth> <name> "
"<desc> on|off] ...\n");
printf(" --yesno <text> <rows> <cols>\n");
printf("\n");
printf("See 'man 1 bsddialog' for more information.\n");
}
int
parseargs(int argc, char **argv, struct bsddialog_conf *conf,
struct options *opt)
{
int arg, parsed, i;
struct winsize ws;
bsddialog_initconf(conf);
conf->key.enable_esc = true;
conf->button.always_active = true;
memset(opt, 0, sizeof(struct options));
opt->theme = -1;
opt->output_fd = STDERR_FILENO;
opt->max_input_form = 2048;
opt->mandatory_dialog = true;
for (i = 0; i < argc; i++) {
if (strcmp(argv[i], "--and-dialog") == 0 ||
strcmp(argv[i], "--and-widget") == 0) {
argc = i + 1;
break;
}
}
parsed = argc;
while ((arg = getopt_long(argc, argv, "", longopts, NULL)) != -1) {
switch (arg) {
/* Options */
case ALTERNATE_SCREEN:
opt->screen_mode = "smcup";
break;
case AND_DIALOG:
if (opt->dialogbuilder == NULL)
exit_error(true,"--and-dialog without "
"previous --<dialog>");
break;
case ASCII_LINES:
conf->ascii_lines = true;
break;
case BACKTITLE:
opt->backtitle = optarg;
if (conf->y == BSDDIALOG_CENTER)
conf->auto_topmargin = 2;
break;
case BEGIN_X:
conf->x = (int)strtol(optarg, NULL, 10);
if (conf->x < BSDDIALOG_CENTER)
exit_error(false, "--begin-x %d is < %d",
conf->x, BSDDIALOG_CENTER);
break;
case BEGIN_Y:
conf->y = (int)strtol(optarg, NULL, 10);
if (conf->y < BSDDIALOG_CENTER)
exit_error(false, "--begin-y %d is < %d",
conf->y, BSDDIALOG_CENTER);
conf->auto_topmargin = 0;
break;
case BIKESHED:
opt->bikeshed = true;
break;
case CANCEL_EXIT_CODE:
set_exit_code(BSDDIALOG_CANCEL,
(int)strtol(optarg, NULL, 10));
break;
case CANCEL_LABEL:
conf->button.cancel_label = optarg;
break;
case CLEAR_DIALOG:
conf->clear = true;
break;
case CLEAR_SCREEN:
opt->mandatory_dialog = false;
opt->clearscreen = true;
break;
case COLUMNS_PER_ROW:
conf->text.cols_per_row =
(u_int)strtoul(optarg, NULL, 10);
break;
case CR_WRAP:
opt->cr_wrap = true;
break;
case DATEBOX_FORMAT:
if (strcasecmp(optarg, "d/m/y") == 0)
conf->date.format = "d/m/y";
else if (strcasecmp(optarg, "m/d/y") == 0)
conf->date.format = "m/d/y";
else if (strcasecmp(optarg, "y/m/d") == 0)
conf->date.format = "y/m/d";
else
exit_error(true,
"date format \"%s\" is invalid", optarg);
break;
case DATE_FORMAT:
opt->date_fmt = optarg;
break;
case DEFAULT_BUTTON:
conf->button.default_label = optarg;
break;
case DEFAULT_ITEM:
opt->item_default = optarg;
break;
case DEFAULT_NO:
conf->button.default_cancel = true;
break;
case DISABLE_ESC:
conf->key.enable_esc = false;
break;
case ERROR_EXIT_CODE:
set_exit_code(BSDDIALOG_ERROR,
(int)strtol(optarg, NULL, 10));
break;
case ESC_EXIT_CODE:
set_exit_code(BSDDIALOG_ESC,
(int)strtol(optarg, NULL, 10));
break;
case EXIT_LABEL:
conf->button.ok_label = optarg;
break;
case EXTRA_BUTTON:
conf->button.with_extra = true;
break;
case EXTRA_EXIT_CODE:
set_exit_code(BSDDIALOG_EXTRA,
(int)strtol(optarg, NULL, 10));
break;
case EXTRA_LABEL:
conf->button.extra_label = optarg;
break;
case HELP_BUTTON:
conf->button.with_help = true;
break;
case HELP_EXIT_CODE:
i = (int)strtol(optarg, NULL, 10);
set_exit_code(BSDDIALOG_HELP, i);
/* _TEM_HELP follows _HELP */
set_exit_code(BSDDIALOG_ITEM_HELP, i);
break;
case HELP_LABEL:
conf->button.help_label = optarg;
break;
case HELP_PRINT_ITEMS:
opt->help_print_items = true;
break;
case HELP_PRINT_NAME:
opt->help_print_item_name = true;
break;
case HFILE:
conf->key.f1_file = optarg;
break;
case HLINE:
if (optarg[0] != '\0')
conf->bottomtitle = optarg;
break;
case HMSG:
conf->key.f1_message = optarg;
break;
case IGNORE:
opt->ignore = true;
break;
case INSECURE:
conf->form.securech = '*';
break;
case ITEM_BOTTOM_DESC:
opt->item_bottomdesc = true;
break;
case ITEM_DEPTH:
opt->item_depth = true;
break;
case ITEM_PREFIX:
opt->item_prefix = true;
break;
case LEFT1_BUTTON:
conf->button.left1_label = optarg;
break;
case LEFT1_EXIT_CODE:
set_exit_code(BSDDIALOG_LEFT1,
(int)strtol(optarg, NULL, 10));
break;
case LEFT2_BUTTON:
conf->button.left2_label = optarg;
break;
case LEFT2_EXIT_CODE:
set_exit_code(BSDDIALOG_LEFT2,
(int)strtol(optarg, NULL, 10));
break;
case LEFT3_BUTTON:
conf->button.left3_label = optarg;
break;
case LEFT3_EXIT_CODE:
set_exit_code(BSDDIALOG_LEFT3,
(int)strtol(optarg, NULL, 10));
break;
case LOAD_THEME:
opt->loadthemefile = optarg;
break;
case MAX_INPUT:
opt->max_input_form = (u_int)strtoul(optarg, NULL, 10);
break;
case NO_CANCEL:
conf->button.without_cancel = true;
break;
case NO_DESCRIPTIONS:
conf->menu.no_desc = true;
break;
case NO_LINES:
conf->no_lines = true;
break;
case NO_NAMES:
conf->menu.no_name = true;
break;
case NO_OK:
conf->button.without_ok = true;
break;
case NO_SHADOW:
conf->shadow = false;
break;
case NORMAL_SCREEN:
opt->screen_mode = "rmcup";
break;
case OK_EXIT_CODE:
set_exit_code(BSDDIALOG_OK,
(int)strtol(optarg, NULL, 10));
break;
case OK_LABEL:
conf->button.ok_label = optarg;
break;
case OUTPUT_FD:
opt->output_fd = (int)strtol(optarg, NULL, 10);
break;
case OUTPUT_SEPARATOR:
opt->item_output_sep = optarg;
break;
case QUOTED:
opt->item_always_quote = true;
break;
case PRINT_MAXSIZE:
opt->mandatory_dialog = false;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
dprintf(opt->output_fd, "MaxSize: %d, %d\n",
ws.ws_row, ws.ws_col);
break;
case PRINT_SIZE:
conf->get_height = &opt->getH;
conf->get_width = &opt->getW;
break;
case PRINT_VERSION:
opt->mandatory_dialog = false;
dprintf(opt->output_fd, "Version: %s\n",
LIBBSDDIALOG_VERSION);
break;
case RIGHT1_BUTTON:
conf->button.right1_label = optarg;
break;
case RIGHT1_EXIT_CODE:
set_exit_code(BSDDIALOG_RIGHT1,
(int)strtol(optarg, NULL, 10));
break;
case RIGHT2_BUTTON:
conf->button.right2_label = optarg;
break;
case RIGHT2_EXIT_CODE:
set_exit_code(BSDDIALOG_RIGHT2,
(int)strtol(optarg, NULL, 10));
break;
case RIGHT3_BUTTON:
conf->button.right3_label = optarg;
break;
case RIGHT3_EXIT_CODE:
set_exit_code(BSDDIALOG_RIGHT3,
(int)strtol(optarg, NULL, 10));
break;
case SAVE_THEME:
opt->mandatory_dialog = false;
opt->savethemefile = optarg;
break;
case SEPARATE_OUTPUT:
opt->item_output_sepnl = true;
break;
case SHADOW:
conf->shadow = true;
break;
case SINGLE_QUOTED:
opt->item_singlequote = true;
break;
case SLEEP:
conf->sleep = (u_int)strtoul(optarg, NULL, 10);
break;
case STDERR:
opt->output_fd = STDERR_FILENO;
break;
case STDOUT:
opt->output_fd = STDOUT_FILENO;
break;
case SWITCH_BUTTONS:
conf->button.always_active = false;
break;
case TAB_ESCAPE:
opt->tab_escape = true;
break;
case TAB_LEN:
conf->text.tablen = (u_int)strtoul(optarg, NULL, 10);
break;
case TEXT_ESCAPE:
conf->text.escape = true;
break;
case TEXT_UNCHANGED:
opt->text_unchanged = true;
break;
case THEME:
if (strcasecmp(optarg, "blackwhite") == 0)
opt->theme = BSDDIALOG_THEME_BLACKWHITE;
else if (strcasecmp(optarg, "flat") == 0)
opt->theme = BSDDIALOG_THEME_FLAT;
else if (strcasecmp(optarg, "3d") == 0)
opt->theme = BSDDIALOG_THEME_3D;
else
exit_error(true,
"--theme: \"%s\" is unknown", optarg);
break;
case TIMEOUT_EXIT_CODE:
set_exit_code(BSDDIALOG_TIMEOUT,
(int)strtol(optarg, NULL, 10));
break;
case TIME_FORMAT:
opt->time_fmt = optarg;
break;
case TITLE:
conf->title = optarg;
break;
/* Dialogs */
case CALENDAR:
if (opt->dialogbuilder != NULL)
exit_error(true, "%s and --calendar without "
"--and-dialog", opt->name);
opt->name = "--calendar";
opt->dialogbuilder = calendar_builder;
break;
case CHECKLIST:
if (opt->dialogbuilder != NULL)
exit_error(true, "%s and --checklist without "
"--and-dialog", opt->name);
opt->name = "--checklist";
opt->dialogbuilder = checklist_builder;
conf->auto_downmargin = 1;
break;
case DATEBOX:
if (opt->dialogbuilder != NULL)
exit_error(true, "%s and --datebox without "
"--and-dialog", opt->name);
opt->name = "--datebox";
opt->dialogbuilder = datebox_builder;
break;
case FORM:
if (opt->dialogbuilder != NULL)
exit_error(true, "%s and --form without "
"--and-dialog", opt->name);
opt->name = "--form";
opt->dialogbuilder = form_builder;
conf->auto_downmargin = 1;
break;
case GAUGE:
if (opt->dialogbuilder != NULL)
exit_error(true, "%s and --gauge without "
"--and-dialog", opt->name);
opt->name = "--gauge";
opt->dialogbuilder = gauge_builder;
break;
case INFOBOX:
if (opt->dialogbuilder != NULL)
exit_error(true, "%s and --infobox without "
"--and-dialog", opt->name);
opt->name = "--infobox";
opt->dialogbuilder = infobox_builder;
break;
case INPUTBOX:
if (opt->dialogbuilder != NULL)
exit_error(true, "%s and --inputbox without "
"--and-dialog", opt->name);
opt->name = "--inputbox";
opt->dialogbuilder = inputbox_builder;
conf->auto_downmargin = 1;
break;
case MENU:
if (opt->dialogbuilder != NULL)
exit_error(true, "%s and --menu without "
"--and-dialog", opt->name);
opt->name = "--menu";
opt->dialogbuilder = menu_builder;
conf->auto_downmargin = 1;
break;
case MIXEDFORM:
if (opt->dialogbuilder != NULL)
exit_error(true, "%s and --mixedform without "
"--and-dialog", opt->name);
opt->name = "--mixedform";
opt->dialogbuilder = mixedform_builder;
conf->auto_downmargin = 1;
break;
case MIXEDGAUGE:
if (opt->dialogbuilder != NULL)
exit_error(true, "%s and --mixedgauge without "
"--and-dialog", opt->name);
opt->name = "--mixedgauge";
opt->dialogbuilder = mixedgauge_builder;
break;
case MSGBOX:
if (opt->dialogbuilder != NULL)
exit_error(true, "%s and --msgbox without "
"--and-dialog", opt->name);
opt->name = "--";
opt->dialogbuilder = msgbox_builder;
break;
case PAUSE:
if (opt->dialogbuilder != NULL)
exit_error(true, "%s and --pause without "
"--and-dialog", opt->name);
opt->name = "--pause";
opt->dialogbuilder = pause_builder;
break;
case PASSWORDBOX:
if (opt->dialogbuilder != NULL)
exit_error(true, "%s and --passwordbox without "
"--and-dialog", opt->name);
opt->name = "--passwordbox";
opt->dialogbuilder = passwordbox_builder;
conf->auto_downmargin = 1;
break;
case PASSWORDFORM:
if (opt->dialogbuilder != NULL)
exit_error(true, "%s and --passwordform "
"without --and-dialog", opt->name);
opt->name = "--passwordform";
opt->dialogbuilder = passwordform_builder;
conf->auto_downmargin = 1;
break;
case RADIOLIST:
if (opt->dialogbuilder != NULL)
exit_error(true, "%s and --radiolist without "
"--and-dialog", opt->name);
opt->name = "--radiolist";
opt->dialogbuilder = radiolist_builder;
conf->auto_downmargin = 1;
break;
case RANGEBOX:
if (opt->dialogbuilder != NULL)
exit_error(true, "%s and --rangebox without "
"--and-dialog", opt->name);
opt->name = "--rangebox";
opt->dialogbuilder = rangebox_builder;
break;
case TEXTBOX:
if (opt->dialogbuilder != NULL)
exit_error(true, "%s and --textbox without "
"--and-dialog", opt->name);
opt->name = "--textbox";
opt->dialogbuilder = textbox_builder;
break;
case TIMEBOX:
if (opt->dialogbuilder != NULL)
exit_error(true, "%s and --timebox without "
"--and-dialog", opt->name);
opt->name = "--timebox";
opt->dialogbuilder = timebox_builder;
break;
case TREEVIEW:
if (opt->dialogbuilder != NULL)
exit_error(true, "%s and --treeview without "
"--and-dialog", opt->name);
opt->name = "--treeview";
opt->dialogbuilder = treeview_builder;
conf->auto_downmargin = 1;
break;
case YESNO:
if (opt->dialogbuilder != NULL)
exit_error(true, "%s and --yesno without "
"--and-dialog", opt->name);
opt->name = "--yesno";
opt->dialogbuilder = yesno_builder;
break;
default: /* Error */
if (opt->ignore == true)
break;
exit_error(true, "--ignore to continue");
}
}
return (parsed);
}

451
utility/util_theme.c Normal file
View File

@ -0,0 +1,451 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022-2023 Alfonso Sabato Siciliano
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/time.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <bsddialog.h>
#include <bsddialog_theme.h>
#include "util.h"
static struct bsddialog_theme t;
static char title[1024];
#define NPROPERTY 41
#define NCOLOR 8
#define NATTR 6
#define PROP_ERROR(name, error) do { \
fclose(fp); \
exit_error(false, "%s for \"%s\"", error, name); \
} while (0)
enum typeproperty {
BOOL,
CHAR,
INT,
UINT,
COLOR,
COMPAT
};
struct property {
const char *comment;
const char *name;
enum typeproperty type;
void *value;
};
struct namevalue {
const char *name;
unsigned int value;
};
static struct namevalue color[NCOLOR] = {
{"black", BSDDIALOG_BLACK},
{"red", BSDDIALOG_RED},
{"green", BSDDIALOG_GREEN},
{"yellow", BSDDIALOG_YELLOW},
{"blue", BSDDIALOG_BLUE},
{"magenta", BSDDIALOG_MAGENTA},
{"cyan", BSDDIALOG_CYAN},
{"white", BSDDIALOG_WHITE}
};
static struct namevalue attr[NATTR] = {
{"bold", BSDDIALOG_BOLD},
{"reverse", BSDDIALOG_REVERSE},
{"underline", BSDDIALOG_UNDERLINE},
{"blink", BSDDIALOG_BLINK},
{"halfbright", BSDDIALOG_HALFBRIGHT},
{"highlight", BSDDIALOG_HIGHLIGHT}
};
static struct property p[NPROPERTY] = {
{"\n#Terminal\n", "theme.screen.color", COLOR, &t.screen.color},
{"\n# Shadow\n",
"theme.shadow.color", COLOR, &t.shadow.color},
{"# shift down right from main widget\n",
"theme.shadow.y", UINT, &t.shadow.y},
{"", "theme.shadow.x", UINT, &t.shadow.x},
{"\n# Main widget\n",
"theme.dialog.color", COLOR, &t.dialog.color},
{"", "theme.dialog.delimtitle", BOOL, &t.dialog.delimtitle},
{"", "theme.dialog.titlecolor", COLOR, &t.dialog.titlecolor},
{"", "theme.dialog.lineraisecolor", COLOR, &t.dialog.lineraisecolor},
{"", "theme.dialog.linelowercolor", COLOR, &t.dialog.linelowercolor},
{"", "theme.dialog.bottomtitlecolor", COLOR,
&t.dialog.bottomtitlecolor},
{"", "theme.dialog.arrowcolor", COLOR, &t.dialog.arrowcolor},
{"\n# Menus: --checklist, --menu, --radiolist\n"
"# prefix [selector] shortcut name desc bottomdesc\n",
"theme.menu.f_prefixcolor", COLOR, &t.menu.f_prefixcolor},
{"", "theme.menu.prefixcolor", COLOR, &t.menu.prefixcolor},
{"", "theme.menu.f_selectorcolor", COLOR, &t.menu.f_selectorcolor},
{"", "theme.menu.selectorcolor", COLOR, &t.menu.selectorcolor},
{"", "theme.menu.f_namecolor", COLOR, &t.menu.f_namecolor},
{"", "theme.menu.namecolor", COLOR, &t.menu.namecolor},
{"", "theme.menu.f_desccolor", COLOR, &t.menu.f_desccolor},
{"", "theme.menu.desccolor", COLOR, &t.menu.desccolor},
{"", "theme.menu.f_shortcutcolor", COLOR, &t.menu.f_shortcutcolor},
{"", "theme.menu.shortcutcolor", COLOR, &t.menu.shortcutcolor},
{"", "theme.menu.bottomdesccolor", COLOR, &t.menu.bottomdesccolor},
{"# bsddialog_menutype BSDDIALOG_SEPARATOR\n",
"theme.menu.sepnamecolor", COLOR, &t.menu.sepnamecolor},
{"", "theme.menu.sepdesccolor", COLOR, &t.menu.sepdesccolor},
{"\n# Forms\n",
"theme.form.f_fieldcolor", COLOR, &t.form.f_fieldcolor},
{"", "theme.form.fieldcolor", COLOR, &t.form.fieldcolor},
{"", "theme.form.readonlycolor", COLOR, &t.form.readonlycolor},
{"", "theme.form.bottomdesccolor", COLOR, &t.form.bottomdesccolor},
{"\n# Bar of --gauge, --mixedgauge, --pause, --rangebox\n",
"theme.bar.f_color", COLOR, &t.bar.f_color},
{"", "theme.bar.color", COLOR, &t.bar.color},
{"\n# Buttons\n",
"theme.button.minmargin", UINT, &t.button.minmargin},
{"", "theme.button.maxmargin", UINT, &t.button.maxmargin},
{"", "theme.button.leftdelim", CHAR, &t.button.leftdelim},
{"", "theme.button.rightdelim", CHAR, &t.button.rightdelim},
{"", "theme.button.f_delimcolor", COLOR, &t.button.f_delimcolor},
{"", "theme.button.delimcolor", COLOR, &t.button.delimcolor},
{"", "theme.button.f_color", COLOR, &t.button.f_color},
{"", "theme.button.color", COLOR, &t.button.color},
{"", "theme.button.f_shortcutcolor", COLOR, &t.button.f_shortcutcolor},
{"", "theme.button.shortcutcolor", COLOR, &t.button.shortcutcolor},
{"\n#Compatibility. Do not use, can be deleted\n",
"use_shadow", COMPAT, NULL}
};
void savetheme(const char *file)
{
int i, j;
unsigned int flags;
enum bsddialog_color bg, fg;
time_t clock;
FILE *fp;
if (bsddialog_get_theme(&t) != BSDDIALOG_OK)
exit_error(false,
"cannot save theme: %s", bsddialog_geterror());
if(time(&clock) < 0)
exit_error(false, "cannot save profile getting current time");
if ((fp = fopen(file, "w")) == NULL)
exit_error(false, "cannot open %s to save profile", file);
fprintf(fp, "### bsddialog theme - %s\n", ctime(&clock));
fputs("# Colors: ", fp);
fputs("black red green yellow blue magenta cyan white.\n", fp);
fputs("# Attributes: ", fp);
fputs("bold reverse underline blink halfbright highlight.\n", fp);
fputs("# f_* refers to focus for an element with selected or ", fp);
fputs("unselected state.\n\n", fp);
fprintf(fp, "version %s\n", LIBBSDDIALOG_VERSION);
for (i = 0; i < NPROPERTY; i++) {
if (p[i].type == COMPAT)
continue;
fprintf(fp, "%s%s", p[i].comment, p[i].name);
switch (p[i].type) {
case CHAR:
fprintf(fp, " %c\n", *((char*)p[i].value));
break;
case INT:
fprintf(fp, " %d\n", *((int*)p[i].value));
break;
case UINT:
fprintf(fp, " %u\n", *((unsigned int*)p[i].value));
break;
case BOOL:
fprintf(fp, " %s\n",
*((bool*)p[i].value) ? "true" : "false");
break;
case COLOR:
bsddialog_color_attrs(*(int*)p[i].value, &fg, &bg,
&flags);
fprintf(fp, " %s %s", color[fg].name, color[bg].name);
for (j = 0; j < NATTR; j++)
if (flags & attr[j].value)
fprintf(fp, " %s", attr[j].name);
fputs("\n", fp);
break;
case COMPAT:
/* Do not save compat property for now */
break;
}
}
fclose(fp);
}
void loadtheme(const char *file, bool compatibility)
{
bool boolvalue;
char charvalue, *value;
char line[BUFSIZ], name[BUFSIZ], c1[BUFSIZ], c2[BUFSIZ];
int i, j, intvalue;
unsigned int uintvalue, flags;
enum bsddialog_color bg, fg;
FILE *fp;
if (bsddialog_hascolors() == false)
return;
if (bsddialog_get_theme(&t) != BSDDIALOG_OK)
exit_error(false, "Cannot get current theme: %s",
bsddialog_geterror());
if((fp = fopen(file, "r")) == NULL)
exit_error(false, "Cannot open theme \"%s\" file", file);
while(fgets(line, BUFSIZ, fp) != NULL) {
if(line[0] == '#' || line[0] == '\n')
continue; /* superfluous, only for efficiency */
sscanf(line, "%s", name);
value = NULL; /* useless init, fix compiler warning */
for (i = 0; i < NPROPERTY; i++) {
if (strcmp(name, p[i].name) == 0) {
value = &line[strlen(name)];
break;
}
}
if (i >= NPROPERTY) {
/* unknown name in property p[] */
if (strcmp(name, "version") == 0)
continue; /* nothing for now */
else if (compatibility)
continue; /* just ignore */
else
PROP_ERROR(name, "Unknown theme property name");
}
switch (p[i].type) {
case CHAR:
while (value[0] == ' ' || value[0] == '\n' ||
value[0] == '\0')
value++;
if (sscanf(value, "%c", &charvalue) != 1)
PROP_ERROR(p[i].name, "Cannot get a char");
*((int*)p[i].value) = charvalue;
break;
case INT:
if (sscanf(value, "%d", &intvalue) != 1)
PROP_ERROR(p[i].name, "Cannot get a int");
*((int*)p[i].value) = intvalue;
break;
case UINT:
if (sscanf(value, "%u", &uintvalue) != 1)
PROP_ERROR(p[i].name, "Cannot get a uint");
*((unsigned int*)p[i].value) = uintvalue;
break;
case BOOL:
boolvalue = (strstr(value, "true") != NULL) ?
true :false;
*((bool*)p[i].value) = boolvalue;
break;
case COLOR:
if (sscanf(value, "%s %s", c1, c2) != 2)
PROP_ERROR(p[i].name, "Cannot get 2 colors");
/* Foreground */
for (j = 0; j < NCOLOR ; j++)
if ((strstr(c1, color[j].name)) != NULL)
break;
if (j >= NCOLOR)
PROP_ERROR(p[i].name, "Bad foreground");
fg = color[j].value;
/* Background */
for (j = 0; j < NCOLOR ; j++)
if ((strstr(c2, color[j].name)) != NULL)
break;
if (j >= NCOLOR)
PROP_ERROR(p[i].name, "Bad background");
bg = color[j].value;
/* Flags */
flags = 0;
for (j = 0; j < NATTR; j++)
if (strstr(value, attr[j].name) != NULL)
flags |= attr[j].value;
*((int*)p[i].value) = bsddialog_color(fg, bg, flags);
break;
case COMPAT:
/*
* usr.sbin/bsdconfig/share/dialog.subr:2255
* uses this parameter to set NO_SHADOW.
* Set t.shadow.[y|x] for compatibilty.
*/
if (strcmp(name, "use_shadow") == 0) {
if (strcasestr(value, "off") != NULL)
t.shadow.y = t.shadow.x = 0;
}
break;
}
}
fclose(fp);
if(bsddialog_set_theme(&t) != BSDDIALOG_OK)
exit_error(false, bsddialog_geterror());
}
void setdeftheme(enum bsddialog_default_theme theme)
{
if (bsddialog_hascolors() == false)
return;
if (bsddialog_set_default_theme(theme) != BSDDIALOG_OK)
exit_error(false, bsddialog_geterror());
}
void startuptheme(void)
{
bool sep;
char *env, *file, *home, path[PATH_MAX];
env = getenv("NO_COLOR");
if (env != NULL && env[0] != '\0')
setdeftheme(BSDDIALOG_THEME_BLACKWHITE);
if ((home = getenv("HOME")) != NULL) {
sep = (strcmp(home, "/") == 0) ? false : true;
snprintf(path, PATH_MAX, "%s%s.bsddialog.conf",
home, sep ? "/" : "");
if (access(path, F_OK) == 0)
loadtheme(path, false);
if ((file = getenv("BSDDIALOG_COMPATRC")) != NULL) {
snprintf(path, PATH_MAX, "%s%s%s",
home, sep ? "/" : "", file);
if (access(path, F_OK) == 0)
loadtheme(path, true);
}
}
if ((file = getenv("BSDDIALOG_THEMEFILE")) != NULL) {
if (access(file, F_OK) == 0)
loadtheme(file, false);
}
}
void bikeshed(struct bsddialog_conf *conf)
{
int margin, i;
int colors[8] = {0, 0, 0, 0, 0, 0, 0, 0};
char delim[8] = {'[', '<', '(', '|', ']', '>', ')', '|'};
enum bsddialog_color col[6];
struct timeval tv;
/* theme */
if (bsddialog_get_theme(&t) != BSDDIALOG_OK)
exit_error(false, bsddialog_geterror());
gettimeofday(&tv, NULL);
srand(tv.tv_usec);
for (i = 0; i < 6; i++) {
do {
col[i] = rand() % 8;
} while (colors[col[i]] == 1);
colors[col[i]] = 1;
}
t.screen.color = bsddialog_color(col[4], col[3], 0);
t.shadow.color = bsddialog_color(col[0], col[0], 0);
t.shadow.y = 1,
t.shadow.x = 2,
t.dialog.delimtitle = (~rand() & 1) ? true : false;
t.dialog.titlecolor = bsddialog_color(col[3], col[5], 0);
t.dialog.lineraisecolor = bsddialog_color(col[0], col[5], 0);
t.dialog.linelowercolor = bsddialog_color(col[0], col[5], 0);
t.dialog.color = bsddialog_color(col[0], col[5], 0);
t.dialog.bottomtitlecolor = bsddialog_color(col[0], col[5], 0);
t.dialog.arrowcolor = bsddialog_color(col[3], col[5], 0);
t.menu.f_prefixcolor = bsddialog_color(col[5], col[3], 0);
t.menu.prefixcolor = bsddialog_color(col[0], col[5], 0);
t.menu.f_selectorcolor = bsddialog_color(col[5], col[3], 0);
t.menu.selectorcolor = bsddialog_color(col[0], col[5], 0);
t.menu.f_desccolor = bsddialog_color(col[5], col[3], 0);
t.menu.desccolor = bsddialog_color(col[0], col[5], 0);
t.menu.f_namecolor = bsddialog_color(col[5], col[3], 0);
t.menu.namecolor = bsddialog_color(col[3], col[5], 0);
t.menu.f_shortcutcolor = bsddialog_color(col[1], col[3], 0);
t.menu.shortcutcolor = bsddialog_color(col[1], col[5], 0);
t.menu.bottomdesccolor = bsddialog_color(col[4], col[3], 0);
t.menu.sepnamecolor = bsddialog_color(col[1], col[5], 0);
t.menu.sepdesccolor = bsddialog_color(col[1], col[5], 0);
t.form.f_fieldcolor = bsddialog_color(col[5], col[3], 0);
t.form.fieldcolor = bsddialog_color(col[5], col[4], 0);
t.form.readonlycolor = bsddialog_color(col[4], col[5], 0);
t.form.bottomdesccolor = bsddialog_color(col[4], col[3], 0);
t.bar.f_color = bsddialog_color(col[5], col[3], 0);
t.bar.color = bsddialog_color(col[3], col[5], 0);
t.button.minmargin = 1,
t.button.maxmargin = 5,
i = rand() % 4;
t.button.leftdelim = delim[i];
t.button.rightdelim = delim[i + 4];
t.button.f_delimcolor = bsddialog_color(col[5], col[3], 0);
t.button.delimcolor = bsddialog_color(col[0], col[5], 0);
t.button.f_color = bsddialog_color(col[2], col[3], 0);
t.button.color = bsddialog_color(col[0], col[5], 0);
t.button.f_shortcutcolor = bsddialog_color(col[5], col[3], 0);
t.button.shortcutcolor = bsddialog_color(col[1], col[5], 0);
if (bsddialog_set_theme(&t))
exit_error(false, bsddialog_geterror());
/* conf */
conf->button.always_active = (~rand() & 1) ? true : false;
if ((i = rand() % 3) != 0) /* default "d/m/y" */
conf->date.format = (i & 1) ? "m/d/y" : "y/m/d" ;
if (conf->title != NULL) {
memset(title, 0, 1024);
margin = rand() % 5;
memset(title, ' ', margin);
strcpy(title + margin, conf->title);
memset(title + strlen(title), ' ', margin);
conf->title = title;
}
}