1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2021-2023 Alfonso Sabato Siciliano
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <curses.h>
29
30#include "bsddialog.h"
31#include "bsddialog_theme.h"
32#include "lib_util.h"
33
34#define GET_COLOR(bg, fg) (COLOR_PAIR(bg * 8 + fg +1))
35#define WHITE GET_COLOR(COLOR_WHITE, COLOR_BLACK)
36#define BLACK GET_COLOR(COLOR_WHITE, COLOR_BLACK) | A_REVERSE
37#define NFLAGS 6
38
39struct bsddialog_theme t;
40bool hastermcolors;
41
42struct flag_converter {
43	unsigned int public;
44	unsigned int private;
45};
46
47static struct flag_converter flagconv[NFLAGS] = {
48	{BSDDIALOG_BLINK,      A_BLINK},
49	{BSDDIALOG_BOLD,       A_BOLD},
50	{BSDDIALOG_HALFBRIGHT, A_DIM},
51	{BSDDIALOG_HIGHLIGHT,  A_STANDOUT},
52	{BSDDIALOG_REVERSE,    A_REVERSE},
53	{BSDDIALOG_UNDERLINE,  A_UNDERLINE}
54};
55
56static struct bsddialog_theme blackwhite = {
57	.screen.color = WHITE,
58
59	.shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK),
60	.shadow.y     = 1,
61	.shadow.x     = 2,
62
63	.dialog.delimtitle       = true,
64	.dialog.titlecolor       = WHITE,
65	.dialog.lineraisecolor   = WHITE,
66	.dialog.linelowercolor   = WHITE,
67	.dialog.color            = WHITE,
68	.dialog.bottomtitlecolor = WHITE,
69	.dialog.arrowcolor       = WHITE,
70
71	.menu.f_prefixcolor   = WHITE,
72	.menu.prefixcolor     = WHITE,
73	.menu.f_selectorcolor = BLACK,
74	.menu.selectorcolor   = WHITE,
75	.menu.f_desccolor     = BLACK,
76	.menu.desccolor       = WHITE,
77	.menu.f_namecolor     = BLACK,
78	.menu.namecolor       = WHITE,
79	.menu.f_shortcutcolor = BLACK | A_UNDERLINE,
80	.menu.shortcutcolor   = WHITE | A_UNDERLINE,
81	.menu.bottomdesccolor = WHITE,
82	.menu.sepnamecolor    = WHITE,
83	.menu.sepdesccolor    = WHITE,
84
85	.form.f_fieldcolor    = BLACK,
86	.form.fieldcolor      = WHITE,
87	.form.readonlycolor   = WHITE,
88	.form.bottomdesccolor = WHITE,
89
90	.bar.f_color = BLACK,
91	.bar.color   = WHITE,
92
93	.button.minmargin       = 1,
94	.button.maxmargin       = 5,
95	.button.leftdelim       = '[',
96	.button.rightdelim      = ']',
97	.button.f_delimcolor    = WHITE,
98	.button.delimcolor      = WHITE,
99	.button.f_color         = BLACK,
100	.button.color           = WHITE,
101	.button.f_shortcutcolor = BLACK | A_UNDERLINE | A_BOLD,
102	.button.shortcutcolor   = WHITE | A_UNDERLINE | A_BOLD
103};
104
105static struct bsddialog_theme flat = {
106	.screen.color = GET_COLOR(COLOR_CYAN, COLOR_BLUE) | A_BOLD,
107
108	.shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK),
109	.shadow.y     = 1,
110	.shadow.x     = 2,
111
112	.dialog.delimtitle       = true,
113	.dialog.titlecolor       = GET_COLOR(COLOR_BLUE,  COLOR_WHITE) | A_BOLD,
114	.dialog.lineraisecolor   = GET_COLOR(COLOR_BLACK, COLOR_WHITE),
115	.dialog.linelowercolor   = GET_COLOR(COLOR_BLACK, COLOR_WHITE),
116	.dialog.color            = GET_COLOR(COLOR_BLACK, COLOR_WHITE),
117	.dialog.bottomtitlecolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE) | A_BOLD,
118	.dialog.arrowcolor       = GET_COLOR(COLOR_BLUE,  COLOR_WHITE),
119
120	.menu.f_prefixcolor   = GET_COLOR(COLOR_BLACK, COLOR_WHITE),
121	.menu.prefixcolor     = GET_COLOR(COLOR_BLACK, COLOR_WHITE),
122	.menu.f_selectorcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE),
123	.menu.selectorcolor   = GET_COLOR(COLOR_BLACK, COLOR_WHITE),
124	.menu.f_desccolor     = GET_COLOR(COLOR_WHITE, COLOR_BLUE),
125	.menu.desccolor       = GET_COLOR(COLOR_BLACK, COLOR_WHITE),
126	.menu.f_namecolor     = GET_COLOR(COLOR_WHITE, COLOR_BLUE),
127	.menu.namecolor       = GET_COLOR(COLOR_BLUE,  COLOR_WHITE),
128	.menu.f_shortcutcolor = GET_COLOR(COLOR_RED,   COLOR_BLUE),
129	.menu.shortcutcolor   = GET_COLOR(COLOR_RED,   COLOR_WHITE),
130	.menu.bottomdesccolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE),
131	.menu.sepnamecolor    = GET_COLOR(COLOR_RED,   COLOR_WHITE),
132	.menu.sepdesccolor    = GET_COLOR(COLOR_RED,   COLOR_WHITE),
133
134	.form.f_fieldcolor    = GET_COLOR(COLOR_WHITE, COLOR_BLUE) | A_BOLD,
135	.form.fieldcolor      = GET_COLOR(COLOR_WHITE, COLOR_CYAN) | A_BOLD,
136	.form.readonlycolor   = GET_COLOR(COLOR_CYAN,  COLOR_WHITE)| A_BOLD,
137	.form.bottomdesccolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE),
138
139	.bar.f_color = GET_COLOR(COLOR_WHITE, COLOR_BLUE)  | A_BOLD,
140	.bar.color   = GET_COLOR(COLOR_BLUE,  COLOR_WHITE) | A_BOLD,
141
142	.button.minmargin       = 1,
143	.button.maxmargin       = 5,
144	.button.leftdelim       = '[',
145	.button.rightdelim      = ']',
146	.button.f_delimcolor    = GET_COLOR(COLOR_WHITE,  COLOR_BLUE)  | A_BOLD,
147	.button.delimcolor      = GET_COLOR(COLOR_BLACK,  COLOR_WHITE),
148	.button.f_color         = GET_COLOR(COLOR_YELLOW, COLOR_BLUE)  | A_BOLD,
149	.button.color           = GET_COLOR(COLOR_BLACK,  COLOR_WHITE),
150	.button.f_shortcutcolor = GET_COLOR(COLOR_WHITE,  COLOR_BLUE)  | A_BOLD,
151	.button.shortcutcolor   = GET_COLOR(COLOR_RED,    COLOR_WHITE) | A_BOLD
152};
153
154static void
155set_theme(struct bsddialog_theme *dst, struct bsddialog_theme *src)
156{
157	dst->screen.color = src->screen.color;
158
159	dst->shadow.color = src->shadow.color;
160	dst->shadow.y     = src->shadow.y;
161	dst->shadow.x     = src->shadow.x;
162
163	dst->dialog.delimtitle       = src->dialog.delimtitle;
164	dst->dialog.titlecolor       = src->dialog.titlecolor;
165	dst->dialog.lineraisecolor   = src->dialog.lineraisecolor;
166	dst->dialog.linelowercolor   = src->dialog.linelowercolor;
167	dst->dialog.color            = src->dialog.color;
168	dst->dialog.bottomtitlecolor = src->dialog.bottomtitlecolor;
169	dst->dialog.arrowcolor       = src->dialog.arrowcolor;
170
171	dst->menu.f_prefixcolor   = src->menu.f_prefixcolor;
172	dst->menu.prefixcolor     = src->menu.prefixcolor;
173	dst->menu.f_selectorcolor = src->menu.f_selectorcolor;
174	dst->menu.selectorcolor   = src->menu.selectorcolor;
175	dst->menu.f_desccolor     = src->menu.f_desccolor;
176	dst->menu.desccolor       = src->menu.desccolor;
177	dst->menu.f_namecolor     = src->menu.f_namecolor;
178	dst->menu.namecolor       = src->menu.namecolor;
179	dst->menu.f_shortcutcolor = src->menu.f_shortcutcolor;
180	dst->menu.shortcutcolor   = src->menu.shortcutcolor;
181	dst->menu.bottomdesccolor = src->menu.bottomdesccolor;
182	dst->menu.sepnamecolor    = src->menu.sepnamecolor;
183	dst->menu.sepdesccolor    = src->menu.sepdesccolor;
184
185	dst->form.f_fieldcolor    = src->form.f_fieldcolor;
186	dst->form.fieldcolor      = src->form.fieldcolor;
187	dst->form.readonlycolor   = src->form.readonlycolor;
188	dst->form.bottomdesccolor = src->form.bottomdesccolor;
189
190	dst->bar.f_color = src->bar.f_color;
191	dst->bar.color   = src->bar.color;
192
193	dst->button.minmargin       = src->button.minmargin;
194	dst->button.maxmargin       = src->button.maxmargin;
195	dst->button.leftdelim       = src->button.leftdelim;
196	dst->button.rightdelim      = src->button.rightdelim;
197	dst->button.f_delimcolor    = src->button.f_delimcolor;
198	dst->button.delimcolor      = src->button.delimcolor;
199	dst->button.f_color         = src->button.f_color;
200	dst->button.color           = src->button.color;
201	dst->button.f_shortcutcolor = src->button.f_shortcutcolor;
202	dst->button.shortcutcolor   = src->button.shortcutcolor;
203
204	bkgd(dst->screen.color);
205}
206
207/* API */
208int bsddialog_get_theme(struct bsddialog_theme *theme)
209{
210	CHECK_PTR(theme);
211	set_theme(theme, &t);
212
213	return (BSDDIALOG_OK);
214}
215
216int bsddialog_set_theme(struct bsddialog_theme *theme)
217{
218	CHECK_PTR(theme);
219	set_theme(&t, theme);
220	refresh();
221
222	return (BSDDIALOG_OK);
223}
224
225int bsddialog_set_default_theme(enum bsddialog_default_theme newtheme)
226{
227	if (newtheme == BSDDIALOG_THEME_3D) {
228		set_theme(&t, &flat);
229		t.dialog.lineraisecolor   =
230		    GET_COLOR(COLOR_WHITE, COLOR_WHITE) | A_BOLD;
231		t.dialog.delimtitle       = false;
232		t.dialog.bottomtitlecolor = t.dialog.bottomtitlecolor | A_BOLD;
233	} else if (newtheme == BSDDIALOG_THEME_BLACKWHITE) {
234		set_theme(&t, &blackwhite);
235	} else if (newtheme == BSDDIALOG_THEME_FLAT) {
236		set_theme(&t, &flat);
237	} else {
238		RETURN_FMTERROR("Unknown default theme (%d), "
239		    "to use enum bsddialog_default_theme",
240		    newtheme);
241	}
242	refresh();
243
244	return (BSDDIALOG_OK);
245}
246
247int
248bsddialog_color(enum bsddialog_color foreground,
249    enum bsddialog_color background, unsigned int flags)
250{
251	unsigned int i, f;
252
253	f = 0;
254	for (i=0; i < NFLAGS; i++)
255		if (flags & flagconv[i].public)
256			f |= flagconv[i].private;
257
258	return (GET_COLOR(foreground, background) | f);
259}
260
261int
262bsddialog_color_attrs(int color, enum bsddialog_color *foreground,
263    enum bsddialog_color *background, unsigned int *flags)
264{
265	short fg, bg;
266	unsigned int i, f;
267
268	if (flags != NULL) {
269		f = 0;
270		for (i=0; i < NFLAGS; i++)
271			if (color & flagconv[i].private)
272				f |= flagconv[i].public;
273		*flags = f;
274	}
275	if (pair_content(PAIR_NUMBER(color), &fg, &bg) != OK)
276		RETURN_ERROR("Cannot get color attributes");
277	if (foreground != NULL)
278		*foreground = fg;
279	if (background != NULL)
280		*background = bg;
281
282	return (BSDDIALOG_OK);
283}
284
285bool bsddialog_hascolors(void)
286{
287	return (hastermcolors);
288}
289