xref: /freebsd/contrib/bsddialog/lib/theme.c (revision ccb59683b98360afaf5b5bb641a68fea22c68d0b)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2021-2022 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 
36 struct bsddialog_theme t;
37 bool hastermcolors;
38 
39 static struct bsddialog_theme bsddialogtheme = {
40 	.screen.color = GET_COLOR(COLOR_BLACK, COLOR_CYAN),
41 
42 	.shadow.color   = GET_COLOR(COLOR_BLACK, COLOR_BLACK),
43 	.shadow.y       = 1,
44 	.shadow.x       = 2,
45 
46 	.dialog.delimtitle       = true,
47 	.dialog.titlecolor       = GET_COLOR(COLOR_YELLOW, COLOR_WHITE),
48 	.dialog.lineraisecolor   = GET_COLOR(COLOR_BLACK,  COLOR_WHITE),
49 	.dialog.linelowercolor   = GET_COLOR(COLOR_BLACK,  COLOR_WHITE),
50 	.dialog.color            = GET_COLOR(COLOR_BLACK,  COLOR_WHITE),
51 	.dialog.bottomtitlecolor = GET_COLOR(COLOR_BLACK,  COLOR_WHITE),
52 	.dialog.arrowcolor       = GET_COLOR(COLOR_YELLOW, COLOR_WHITE),
53 
54 	.menu.f_selectorcolor = GET_COLOR(COLOR_BLACK,  COLOR_YELLOW),
55 	.menu.selectorcolor   = GET_COLOR(COLOR_BLACK,  COLOR_WHITE),
56 	.menu.f_desccolor     = GET_COLOR(COLOR_WHITE,  COLOR_YELLOW),
57 	.menu.desccolor       = GET_COLOR(COLOR_BLACK,  COLOR_WHITE),
58 	.menu.f_namecolor     = GET_COLOR(COLOR_BLACK,  COLOR_YELLOW),
59 	.menu.namecolor       = GET_COLOR(COLOR_BLACK,  COLOR_WHITE),
60 	.menu.namesepcolor    = GET_COLOR(COLOR_YELLOW, COLOR_WHITE),
61 	.menu.descsepcolor    = GET_COLOR(COLOR_YELLOW, COLOR_WHITE),
62 	.menu.f_shortcutcolor = GET_COLOR(COLOR_RED,    COLOR_YELLOW),
63 	.menu.shortcutcolor   = GET_COLOR(COLOR_RED,    COLOR_WHITE),
64 	.menu.bottomdesccolor = GET_COLOR(COLOR_WHITE,  COLOR_CYAN),
65 
66 	.form.f_fieldcolor    = GET_COLOR(COLOR_WHITE, COLOR_BLUE),
67 	.form.fieldcolor      = GET_COLOR(COLOR_WHITE, COLOR_CYAN),
68 	.form.readonlycolor   = GET_COLOR(COLOR_CYAN,  COLOR_WHITE),
69 	.form.bottomdesccolor = GET_COLOR(COLOR_WHITE, COLOR_CYAN),
70 
71 	.bar.f_color = GET_COLOR(COLOR_BLACK, COLOR_YELLOW),
72 	.bar.color   = GET_COLOR(COLOR_BLACK, COLOR_WHITE),
73 
74 	.button.minmargin       = 1,
75 	.button.maxmargin       = 5,
76 	.button.leftdelim       = '[',
77 	.button.rightdelim      = ']',
78 	.button.f_delimcolor    = GET_COLOR(COLOR_BLACK, COLOR_WHITE),
79 	.button.delimcolor      = GET_COLOR(COLOR_BLACK, COLOR_WHITE),
80 	.button.f_color         = GET_COLOR(COLOR_BLACK, COLOR_YELLOW),
81 	.button.color           = GET_COLOR(COLOR_BLACK, COLOR_WHITE),
82 	.button.f_shortcutcolor = GET_COLOR(COLOR_RED,   COLOR_YELLOW),
83 	.button.shortcutcolor   = GET_COLOR(COLOR_RED,   COLOR_WHITE)
84 };
85 
86 static struct bsddialog_theme blackwhite = {
87 #define fg  COLOR_WHITE
88 #define bk  COLOR_BLACK
89 	.screen.color = GET_COLOR(fg, bk),
90 
91 	.shadow.color   = GET_COLOR(COLOR_BLACK, COLOR_BLACK),
92 	.shadow.y       = 1,
93 	.shadow.x       = 2,
94 
95 	.dialog.delimtitle       = true,
96 	.dialog.titlecolor       = GET_COLOR(fg, bk),
97 	.dialog.lineraisecolor   = GET_COLOR(fg, bk),
98 	.dialog.linelowercolor   = GET_COLOR(fg, bk),
99 	.dialog.color            = GET_COLOR(fg, bk),
100 	.dialog.bottomtitlecolor = GET_COLOR(fg, bk),
101 	.dialog.arrowcolor       = GET_COLOR(fg, bk),
102 
103 	.menu.f_selectorcolor = GET_COLOR(fg, bk) | A_REVERSE,
104 	.menu.selectorcolor   = GET_COLOR(fg, bk),
105 	.menu.f_desccolor     = GET_COLOR(fg, bk) | A_REVERSE,
106 	.menu.desccolor       = GET_COLOR(fg, bk),
107 	.menu.f_namecolor     = GET_COLOR(fg, bk) | A_REVERSE,
108 	.menu.namecolor       = GET_COLOR(fg, bk),
109 	.menu.namesepcolor    = GET_COLOR(fg, bk),
110 	.menu.descsepcolor    = GET_COLOR(fg, bk),
111 	.menu.f_shortcutcolor = GET_COLOR(fg, bk) | A_UNDERLINE | A_REVERSE,
112 	.menu.shortcutcolor   = GET_COLOR(fg, bk) | A_UNDERLINE,
113 	.menu.bottomdesccolor = GET_COLOR(fg, bk),
114 
115 	.form.f_fieldcolor    = GET_COLOR(fg, bk) | A_REVERSE,
116 	.form.fieldcolor      = GET_COLOR(fg, bk),
117 	.form.readonlycolor   = GET_COLOR(fg, bk),
118 	.form.bottomdesccolor = GET_COLOR(fg, bk),
119 
120 	.bar.f_color = GET_COLOR(fg, bk) | A_REVERSE,
121 	.bar.color   = GET_COLOR(fg, bk),
122 
123 	.button.minmargin       = 1,
124 	.button.maxmargin       = 5,
125 	.button.leftdelim       = '[',
126 	.button.rightdelim      = ']',
127 	.button.f_delimcolor    = GET_COLOR(fg, bk),
128 	.button.delimcolor      = GET_COLOR(fg, bk),
129 	.button.f_color         = GET_COLOR(fg, bk) | A_UNDERLINE | A_REVERSE,
130 	.button.color           = GET_COLOR(fg, bk) | A_UNDERLINE,
131 	.button.f_shortcutcolor = GET_COLOR(fg, bk) | A_UNDERLINE | A_REVERSE,
132 	.button.shortcutcolor   = GET_COLOR(fg, bk) | A_UNDERLINE
133 };
134 
135 static struct bsddialog_theme dialogtheme = {
136 	.screen.color = GET_COLOR(COLOR_CYAN, COLOR_BLUE) | A_BOLD,
137 
138 	.shadow.color   = GET_COLOR(COLOR_BLACK, COLOR_BLACK),
139 	.shadow.y       = 1,
140 	.shadow.x       = 2,
141 
142 	.dialog.delimtitle       = false,
143 	.dialog.titlecolor       = GET_COLOR(COLOR_BLUE,  COLOR_WHITE) | A_BOLD,
144 	.dialog.lineraisecolor   = GET_COLOR(COLOR_WHITE, COLOR_WHITE) | A_BOLD,
145 	.dialog.linelowercolor   = GET_COLOR(COLOR_BLACK, COLOR_WHITE) | A_BOLD,
146 	.dialog.color            = GET_COLOR(COLOR_BLACK, COLOR_WHITE),
147 	.dialog.bottomtitlecolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE) | A_BOLD,
148 	.dialog.arrowcolor       = GET_COLOR(COLOR_BLUE,  COLOR_WHITE),
149 
150 	.menu.f_selectorcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE),
151 	.menu.selectorcolor   = GET_COLOR(COLOR_BLACK, COLOR_WHITE),
152 	.menu.f_desccolor     = GET_COLOR(COLOR_WHITE, COLOR_BLUE),
153 	.menu.desccolor       = GET_COLOR(COLOR_BLACK, COLOR_WHITE),
154 	.menu.f_namecolor     = GET_COLOR(COLOR_WHITE, COLOR_BLUE),
155 	.menu.namecolor       = GET_COLOR(COLOR_BLUE,  COLOR_WHITE),
156 	.menu.namesepcolor    = GET_COLOR(COLOR_RED,   COLOR_WHITE),
157 	.menu.descsepcolor    = GET_COLOR(COLOR_RED,   COLOR_WHITE),
158 	.menu.f_shortcutcolor = GET_COLOR(COLOR_RED,   COLOR_BLUE),
159 	.menu.shortcutcolor   = GET_COLOR(COLOR_RED,   COLOR_WHITE),
160 	.menu.bottomdesccolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE),
161 
162 	.form.f_fieldcolor    = GET_COLOR(COLOR_WHITE, COLOR_BLUE) | A_BOLD,
163 	.form.fieldcolor      = GET_COLOR(COLOR_WHITE, COLOR_CYAN) | A_BOLD,
164 	.form.readonlycolor   = GET_COLOR(COLOR_CYAN,  COLOR_WHITE)| A_BOLD,
165 	.form.bottomdesccolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE),
166 
167 	.bar.f_color = GET_COLOR(COLOR_WHITE, COLOR_BLUE)  | A_BOLD,
168 	.bar.color   = GET_COLOR(COLOR_BLUE,  COLOR_WHITE) | A_BOLD,
169 
170 	.button.minmargin       = 1,
171 	.button.maxmargin       = 5,
172 	.button.leftdelim       = '<',
173 	.button.rightdelim      = '>',
174 	.button.f_delimcolor    = GET_COLOR(COLOR_WHITE,  COLOR_BLUE)  | A_BOLD,
175 	.button.delimcolor      = GET_COLOR(COLOR_BLACK,  COLOR_WHITE),
176 	.button.f_color         = GET_COLOR(COLOR_YELLOW, COLOR_BLUE)  | A_BOLD,
177 	.button.color           = GET_COLOR(COLOR_BLACK,  COLOR_WHITE),
178 	.button.f_shortcutcolor = GET_COLOR(COLOR_WHITE,  COLOR_BLUE)  | A_BOLD,
179 	.button.shortcutcolor   = GET_COLOR(COLOR_RED,    COLOR_WHITE) | A_BOLD
180 };
181 
182 static void
183 set_theme(struct bsddialog_theme *dst, struct bsddialog_theme *src)
184 {
185 	dst->screen.color = src->screen.color;
186 
187 	dst->shadow.color = src->shadow.color;
188 	dst->shadow.y     = src->shadow.y;
189 	dst->shadow.x     = src->shadow.x;
190 
191 	dst->dialog.delimtitle       = src->dialog.delimtitle;
192 	dst->dialog.titlecolor       = src->dialog.titlecolor;
193 	dst->dialog.lineraisecolor   = src->dialog.lineraisecolor;
194 	dst->dialog.linelowercolor   = src->dialog.linelowercolor;
195 	dst->dialog.color            = src->dialog.color;
196 	dst->dialog.bottomtitlecolor = src->dialog.bottomtitlecolor;
197 	dst->dialog.arrowcolor       = src->dialog.arrowcolor;
198 
199 	dst->menu.f_selectorcolor = src->menu.f_selectorcolor;
200 	dst->menu.selectorcolor   = src->menu.selectorcolor;
201 	dst->menu.f_desccolor     = src->menu.f_desccolor;
202 	dst->menu.desccolor       = src->menu.desccolor;
203 	dst->menu.f_namecolor     = src->menu.f_namecolor;
204 	dst->menu.namecolor       = src->menu.namecolor;
205 	dst->menu.namesepcolor    = src->menu.namesepcolor;
206 	dst->menu.descsepcolor    = src->menu.descsepcolor;
207 	dst->menu.f_shortcutcolor = src->menu.f_shortcutcolor;
208 	dst->menu.shortcutcolor   = src->menu.shortcutcolor;
209 	dst->menu.bottomdesccolor = src->menu.bottomdesccolor;
210 
211 	dst->form.f_fieldcolor    = src->form.f_fieldcolor;
212 	dst->form.fieldcolor      = src->form.fieldcolor;
213 	dst->form.readonlycolor   = src->form.readonlycolor;
214 	dst->form.bottomdesccolor = src->form.bottomdesccolor;
215 
216 	dst->bar.f_color = src->bar.f_color;
217 	dst->bar.color   = src->bar.color;
218 
219 	dst->button.minmargin       = src->button.minmargin;
220 	dst->button.maxmargin       = src->button.maxmargin;
221 	dst->button.leftdelim       = src->button.leftdelim;
222 	dst->button.rightdelim      = src->button.rightdelim;
223 	dst->button.f_delimcolor    = src->button.f_delimcolor;
224 	dst->button.delimcolor      = src->button.delimcolor;
225 	dst->button.f_color         = src->button.f_color;
226 	dst->button.color           = src->button.color;
227 	dst->button.f_shortcutcolor = src->button.f_shortcutcolor;
228 	dst->button.shortcutcolor   = src->button.shortcutcolor;
229 
230 	bkgd(dst->screen.color);
231 	refresh();
232 }
233 
234 /* API */
235 int bsddialog_get_theme(struct bsddialog_theme *theme)
236 {
237 	if (theme == NULL)
238 		RETURN_ERROR("theme is NULL");
239 	if (sizeof(*theme) != sizeof(struct bsddialog_theme))
240 		RETURN_ERROR("Bad suze struct bsddialog_theme");
241 
242 	set_theme(theme, &t);
243 
244 	return (BSDDIALOG_OK);
245 }
246 
247 int bsddialog_set_theme(struct bsddialog_theme *theme)
248 {
249 	if (theme == NULL)
250 		RETURN_ERROR("theme is NULL");
251 	if (sizeof(*theme) != sizeof(struct bsddialog_theme))
252 		RETURN_ERROR("Bad size struct bsddialog_theme");
253 
254 	set_theme(&t, theme);
255 
256 	return (BSDDIALOG_OK);
257 }
258 
259 int bsddialog_set_default_theme(enum bsddialog_default_theme newtheme)
260 {
261 	if (newtheme == BSDDIALOG_THEME_FLAT) {
262 		bsddialog_set_theme(&dialogtheme);
263 		t.dialog.lineraisecolor = t.dialog.linelowercolor;
264 		t.dialog.delimtitle     = true;
265 		t.button.leftdelim      = '[';
266 		t.button.rightdelim     = ']';
267 		t.dialog.bottomtitlecolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE);
268 	}
269 	else if (newtheme == BSDDIALOG_THEME_BSDDIALOG)
270 		bsddialog_set_theme(&bsddialogtheme);
271 	else if (newtheme == BSDDIALOG_THEME_BLACKWHITE)
272 		bsddialog_set_theme(&blackwhite);
273 	else if (newtheme == BSDDIALOG_THEME_DIALOG)
274 		bsddialog_set_theme(&dialogtheme);
275 	else
276 		RETURN_ERROR("Unknow default theme");
277 
278 	return (BSDDIALOG_OK);
279 }
280 
281 int
282 bsddialog_color(enum bsddialog_color foreground,
283     enum bsddialog_color background, unsigned int flags)
284 {
285 	unsigned int cursesflags = 0;
286 
287 	if (flags & BSDDIALOG_BOLD)
288 		cursesflags |= A_BOLD;
289 	if (flags & BSDDIALOG_REVERSE)
290 		cursesflags |= A_REVERSE;
291 	if (flags & BSDDIALOG_UNDERLINE)
292 		cursesflags |= A_UNDERLINE;
293 
294 	return (GET_COLOR(foreground, background) | cursesflags);
295 }
296 
297 int
298 bsddialog_color_attrs(int color, enum bsddialog_color *foreground,
299     enum bsddialog_color *background, unsigned int *flags)
300 {
301 	unsigned int flag;
302 	short f, b;
303 
304 	flag = 0U;
305 	flag |= (color & A_BOLD) ? BSDDIALOG_BOLD : 0U;
306 	flag |= (color & A_REVERSE) ? BSDDIALOG_REVERSE : 0U;
307 	flag |= (color & A_UNDERLINE) ? BSDDIALOG_UNDERLINE : 0U;
308 	if (flags != NULL)
309 		*flags = flag;
310 
311 	if (pair_content(PAIR_NUMBER(color), &f, &b) != OK)
312 		RETURN_ERROR("Cannot get color attributes");
313 	if (foreground != NULL)
314 		*foreground = f;
315 	if (background != NULL)
316 		*background = b;
317 
318 	return (BSDDIALOG_OK);
319 }
320 
321 bool bsddialog_hascolors(void)
322 {
323 	return hastermcolors;
324 }