theme.c (6292B)
1 /* 2 * Copyright (c) 2022-2024 lumidify <nobody@lumidify.org> 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #include <stdlib.h> 18 #include <string.h> 19 20 #include "graphics.h" 21 #include "util.h" 22 #include "theme.h" 23 #include "memory.h" 24 25 /* FIXME: handle '#' or no '#' in color specification */ 26 static int 27 search_helper(const void *keyv, const void *entryv) { 28 char *key = (char *)keyv; 29 ltk_theme_parseinfo *entry = (ltk_theme_parseinfo *)entryv; 30 return strcmp(key, entry->key); 31 } 32 33 static int 34 sort_helper(const void *entry1v, const void *entry2v) { 35 ltk_theme_parseinfo *entry1 = (ltk_theme_parseinfo *)entry1v; 36 ltk_theme_parseinfo *entry2 = (ltk_theme_parseinfo *)entry2v; 37 return strcmp(entry1->key, entry2->key); 38 } 39 40 /* FIXME: more information for errors */ 41 int 42 ltk_theme_handle_value(ltk_renderdata *renderdata, char *debug_name, const char *prop, const char *value, ltk_theme_parseinfo *parseinfo, size_t len, int *sorted) { 43 if (!*sorted) { 44 qsort(parseinfo, len, sizeof(ltk_theme_parseinfo), &sort_helper); 45 *sorted = 1; 46 } 47 ltk_theme_parseinfo *entry = bsearch(prop, parseinfo, len, sizeof(ltk_theme_parseinfo), &search_helper); 48 if (!entry) { 49 ltk_warn("Invalid property '%s:%s'.\n", debug_name, prop); 50 return 1; 51 } else if (entry->initialized) { 52 ltk_warn("Duplicate setting for property '%s:%s'.\n", debug_name, prop); 53 return 1; 54 } 55 const char *errstr = NULL; 56 switch (entry->type) { 57 case THEME_INT: 58 *(entry->ptr.i) = ltk_strtonum(value, entry->min, entry->max, &errstr); 59 if (errstr) { 60 ltk_warn("Invalid value '%s' for property '%s:%s'.\n", value, debug_name, prop); 61 return 1; 62 } else { 63 entry->initialized = 1; 64 } 65 break; 66 case THEME_SIZE: 67 entry->ptr.size->unit = LTK_UNIT_PX; 68 char *endptr = NULL; 69 /* this already takes care of overflow prevention because entry->min and entry->max are int */ 70 entry->ptr.size->val = ltk_strtoscalednum(value, entry->min, entry->max, &endptr, &errstr); 71 if (errstr) { 72 ltk_warn("Invalid value '%s' for property '%s:%s': %s\n", value, debug_name, prop, errstr); 73 return 1; 74 } 75 if (*endptr == '\0') { 76 /* NOP */ 77 } else if (!strcmp(endptr, "px")) { 78 entry->ptr.size->unit = LTK_UNIT_PX; 79 } else if (!strcmp(endptr, "pt")) { 80 entry->ptr.size->unit = LTK_UNIT_PT; 81 } else if (!strcmp(endptr, "mm")) { 82 entry->ptr.size->unit = LTK_UNIT_MM; 83 } else { 84 ltk_warn("Invalid value '%s' for property '%s:%s'\n", value, debug_name, prop); 85 return 1; 86 } 87 entry->initialized = 1; 88 break; 89 case THEME_STRING: 90 /* FIXME: check if already set? */ 91 *(entry->ptr.str) = ltk_strdup(value); 92 entry->initialized = 1; 93 break; 94 case THEME_COLOR: 95 if (!(*(entry->ptr.color) = ltk_color_create(renderdata, value))) { 96 ltk_warn("Unable to create color '%s' for property '%s:%s'.\n", value, debug_name, prop); 97 return 1; 98 } else { 99 entry->initialized = 1; 100 } 101 break; 102 case THEME_BOOL: 103 if (strcmp(value, "true") == 0) { 104 *(entry->ptr.b) = 1; 105 } else if (strcmp(value, "false") == 0) { 106 *(entry->ptr.b) = 0; 107 } else { 108 ltk_warn("Invalid value '%s' for property '%s:%s'.\n", value, debug_name, prop); 109 return 1; 110 } 111 entry->initialized = 1; 112 break; 113 case THEME_BORDERSIDES: 114 *(entry->ptr.border) = LTK_BORDER_NONE; 115 for (const char *c = value; *c != '\0'; c++) { 116 switch (*c) { 117 case 't': 118 *(entry->ptr.border) |= LTK_BORDER_TOP; 119 break; 120 case 'b': 121 *(entry->ptr.border) |= LTK_BORDER_BOTTOM; 122 break; 123 case 'l': 124 *(entry->ptr.border) |= LTK_BORDER_LEFT; 125 break; 126 case 'r': 127 *(entry->ptr.border) |= LTK_BORDER_RIGHT; 128 break; 129 default: 130 ltk_warn("Invalid value '%s' for property '%s:%s'.\n", value, debug_name, prop); 131 return 1; 132 } 133 } 134 entry->initialized = 1; 135 break; 136 default: 137 ltk_fatal("Invalid theme setting type. This should not happen.\n"); 138 /* TODO: ltk_assert(0); */ 139 } 140 return 0; 141 } 142 143 int 144 ltk_theme_fill_defaults(ltk_renderdata *renderdata, char *debug_name, ltk_theme_parseinfo *parseinfo, size_t len) { 145 for (size_t i = 0; i < len; i++) { 146 ltk_theme_parseinfo *e = &parseinfo[i]; 147 if (e->initialized) 148 continue; 149 switch (e->type) { 150 case THEME_INT: 151 *(e->ptr.i) = e->defaultval.i; 152 e->initialized = 1; 153 break; 154 case THEME_SIZE: 155 *(e->ptr.size) = e->defaultval.size; 156 e->initialized = 1; 157 break; 158 case THEME_STRING: 159 *(e->ptr.str) = ltk_strdup(e->defaultval.str); 160 e->initialized = 1; 161 break; 162 case THEME_COLOR: 163 if (!(*(e->ptr.color) = ltk_color_create(renderdata, e->defaultval.color))) { 164 ltk_warn("Unable to create default color '%s' for property '%s:%s'.\n", e->defaultval.color, debug_name, e->key); 165 return 1; 166 } else { 167 e->initialized = 1; 168 } 169 break; 170 case THEME_BOOL: 171 *(e->ptr.b) = e->defaultval.b; 172 e->initialized = 1; 173 break; 174 case THEME_BORDERSIDES: 175 *(e->ptr.border) = e->defaultval.border; 176 e->initialized = 1; 177 break; 178 default: 179 ltk_fatal("Invalid theme setting type. This should not happen.\n"); 180 /* TODO: ltk_assert(0); */ 181 } 182 } 183 return 0; 184 } 185 186 void 187 ltk_theme_uninitialize(ltk_renderdata *renderdata, ltk_theme_parseinfo *parseinfo, size_t len) { 188 for (size_t i = 0; i < len; i++) { 189 ltk_theme_parseinfo *e = &parseinfo[i]; 190 if (!e->initialized) 191 continue; 192 switch (e->type) { 193 case THEME_STRING: 194 ltk_free(*(e->ptr.str)); 195 e->initialized = 0; 196 break; 197 case THEME_COLOR: 198 ltk_color_destroy(renderdata, *(e->ptr.color)); 199 e->initialized = 0; 200 break; 201 case THEME_SIZE: 202 case THEME_INT: 203 case THEME_BOOL: 204 case THEME_BORDERSIDES: 205 e->initialized = 0; 206 break; 207 default: 208 ltk_fatal("Invalid theme setting type. This should not happen.\n"); 209 } 210 } 211 }