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 262 if (newtheme == BSDDIALOG_THEME_FLAT) { 263 bsddialog_set_theme(&dialogtheme); 264 t.dialog.lineraisecolor = t.dialog.linelowercolor; 265 t.dialog.delimtitle = true; 266 t.button.leftdelim = '['; 267 t.button.rightdelim = ']'; 268 t.dialog.bottomtitlecolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE); 269 } 270 else if (newtheme == BSDDIALOG_THEME_BSDDIALOG) 271 bsddialog_set_theme(&bsddialogtheme); 272 else if (newtheme == BSDDIALOG_THEME_BLACKWHITE) 273 bsddialog_set_theme(&blackwhite); 274 else if (newtheme == BSDDIALOG_THEME_DIALOG) 275 bsddialog_set_theme(&dialogtheme); 276 else 277 RETURN_ERROR("Unknow default theme"); 278 279 return (BSDDIALOG_OK); 280 } 281 282 int 283 bsddialog_color(enum bsddialog_color foreground, 284 enum bsddialog_color background, unsigned int flags) 285 { 286 unsigned int cursesflags = 0; 287 288 if (flags & BSDDIALOG_BOLD) 289 cursesflags |= A_BOLD; 290 if (flags & BSDDIALOG_REVERSE) 291 cursesflags |= A_REVERSE; 292 if (flags & BSDDIALOG_UNDERLINE) 293 cursesflags |= A_UNDERLINE; 294 295 return (GET_COLOR(foreground, background) | cursesflags); 296 } 297 298 int 299 bsddialog_color_attrs(int color, enum bsddialog_color *foreground, 300 enum bsddialog_color *background, unsigned int *flags) 301 { 302 unsigned int flag; 303 short f, b; 304 305 flag = 0U; 306 flag |= (color & A_BOLD) ? BSDDIALOG_BOLD : 0U; 307 flag |= (color & A_REVERSE) ? BSDDIALOG_REVERSE : 0U; 308 flag |= (color & A_UNDERLINE) ? BSDDIALOG_UNDERLINE : 0U; 309 if (flags != NULL) 310 *flags = flag; 311 312 if (pair_content(PAIR_NUMBER(color), &f, &b) != OK) 313 RETURN_ERROR("Cannot get color attributes"); 314 if (foreground != NULL) 315 *foreground = f; 316 if (background != NULL) 317 *background = b; 318 319 return (BSDDIALOG_OK); 320 } 321 322 bool bsddialog_hascolors(void) 323 { 324 return hastermcolors; 325 }