/******************************************************************************* * Copyright (c) 2013-2021, Andrés Martinelli * * All rights reserved. * * * * This file is a part of sc-im * * * * sc-im is a spreadsheet program that is based on sc. The original authors * * of sc are James Gosling and Mark Weiser, and mods were later added by * * Chuck Martin. * * * * 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. * * 3. All advertising materials mentioning features or use of this software * * must display the following acknowledgement: * * This product includes software developed by Andrés Martinelli * * . * * 4. Neither the name of the Andrés Martinelli nor the * * names of other contributors may be used to endorse or promote products * * derived from this software without specific prior written permission. * * * * THIS SOFTWARE IS PROVIDED BY ANDRES MARTINELLI ''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 ANDRES MARTINELLI 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 * * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *******************************************************************************/ /** * @file color.c * @author Andrés Martinelli * @date 2017-07-18 * @brief File containing color functions * */ #include #include #include // for atoi #include #include #include "sc.h" #include "macros.h" #include "utils/dictionary.h" #include "utils/string.h" #include "range.h" #include "tui.h" #include "undo.h" #include "conf.h" #include "cmds/cmds.h" struct ucolor ucolors[N_INIT_PAIRS] = {}; static struct dictionary * d_colors_param = NULL; struct dictionary * get_d_colors_param() { return d_colors_param; } static struct custom_color * custom_colors = NULL; extern struct session * session; /** * @brief Generate DEFAULT 'initcolor' colors * * Generate DEFAULT 'initcolor' colors. * * Example usage: * @code * start_default_ucolors(); * @endcode * returns: none */ void start_default_ucolors() { // Initialize colors attributes int i; for (i=0; i < N_INIT_PAIRS; i++) { ucolors[ i ].bold = 0; ucolors[ i ].italic = 0; ucolors[ i ].dim = 0; ucolors[ i ].reverse = 0; ucolors[ i ].standout = 0; ucolors[ i ].underline = 0; ucolors[ i ].blink = 0; } // Set some colors attributes // NOTE: always increase N_INIT_PAIRS on each color definition you add. ucolors[ DEFAULT ].fg = WHITE; ucolors[ DEFAULT ].bg = DEFAULT_COLOR; ucolors[ HEADINGS ].bg = YELLOW; ucolors[ HEADINGS ].fg = BLACK; ucolors[ HEADINGS_ODD ].bg = YELLOW; ucolors[ HEADINGS_ODD ].fg = BLACK; ucolors[ GRID_EVEN ].fg = WHITE; ucolors[ GRID_EVEN ].bg = DEFAULT_COLOR; ucolors[ GRID_ODD ].fg = WHITE; ucolors[ GRID_ODD ].bg = DEFAULT_COLOR; ucolors[ WELCOME ].fg = YELLOW; ucolors[ WELCOME ].bg = DEFAULT_COLOR; ucolors[ WELCOME ].bold = 0; ucolors[ CELL_SELECTION ].bg = BLACK; // cell selection in headings ucolors[ CELL_SELECTION ].fg = YELLOW; ucolors[ CELL_SELECTION ].bold = 0; ucolors[ CELL_SELECTION_SC ].fg = BLACK; // cell selection in spreadsheet ucolors[ CELL_SELECTION_SC ].bg = YELLOW; ucolors[ NUMB ].fg = CYAN; ucolors[ NUMB ].bg = DEFAULT_COLOR; ucolors[ STRG ].fg = MAGENTA; ucolors[ STRG ].bg = DEFAULT_COLOR; ucolors[ STRG ].bold = 0; ucolors[ DATEF ].fg = YELLOW; ucolors[ DATEF ].bg = DEFAULT_COLOR; ucolors[ EXPRESSION ].fg = RED; ucolors[ EXPRESSION ].bg = DEFAULT_COLOR; ucolors[ INFO_MSG ].fg = CYAN; ucolors[ INFO_MSG ].bg = DEFAULT_COLOR; ucolors[ INFO_MSG ].bold = 1; ucolors[ ERROR_MSG ].bg = RED; ucolors[ ERROR_MSG ].fg = WHITE; ucolors[ ERROR_MSG ].bold = 1; ucolors[ MODE ].fg = WHITE; ucolors[ MODE ].bg = DEFAULT_COLOR; ucolors[ MODE ].bold = 1; ucolors[ CELL_ID ].fg = WHITE; ucolors[ CELL_ID ].bg = DEFAULT_COLOR; ucolors[ CELL_ID ].bold = 1; ucolors[ CELL_FORMAT ].fg = RED; ucolors[ CELL_FORMAT ].bg = DEFAULT_COLOR; ucolors[ CELL_CONTENT ].fg = CYAN; ucolors[ CELL_CONTENT ].bg = DEFAULT_COLOR; ucolors[ CELL_CONTENT ].bold = 1; ucolors[ INPUT ].fg = WHITE; ucolors[ INPUT ].bg = DEFAULT_COLOR; ucolors[ NORMAL ].bold = 1; ucolors[ NORMAL ].fg = WHITE; ucolors[ NORMAL ].bg = DEFAULT_COLOR; ucolors[ CELL_ERROR ].fg = RED; ucolors[ CELL_ERROR ].bg = DEFAULT_COLOR; ucolors[ CELL_ERROR ].bold = 1; ucolors[ CELL_NEGATIVE ].fg = GREEN; ucolors[ CELL_NEGATIVE ].bg = DEFAULT_COLOR; ucolors[ HELP_HIGHLIGHT ].fg = BLACK; // cell selection in spreadsheet ucolors[ HELP_HIGHLIGHT ].bg = YELLOW; ucolors[ CURRENT_SHEET ].fg = CYAN; ucolors[ CURRENT_SHEET ].bg = DEFAULT_COLOR; ucolors[ CURRENT_SHEET ].bold = 1; ucolors[ SHEET ].fg = MAGENTA; ucolors[ SHEET ].bg = DEFAULT_COLOR; ucolors[ FILENM ].fg = GREEN; ucolors[ FILENM ].bg = DEFAULT_COLOR; ui_start_colors(); // call specific ui startup routine } /** * @brief Create a dictionary that stores correspondence between macros and key values * * Create a dictionary that stores the correspondence between macros and key * values (integers) defined in '.sc' files or through the color command. * * Example usage: * @code * set_colors_param_dict(); * @endcode * returns: none */ void set_colors_param_dict() { d_colors_param = create_dictionary(); char str[3]; str[0]='\0'; sprintf(str, "%d", DEFAULT_COLOR); put(d_colors_param, "DEFAULT_COLOR", str); sprintf(str, "%d", NONE_COLOR); put(d_colors_param, "NONE_COLOR", str); sprintf(str, "%d", BLACK); put(d_colors_param, "BLACK", str); sprintf(str, "%d", RED); put(d_colors_param, "RED", str); sprintf(str, "%d", GREEN); put(d_colors_param, "GREEN", str); sprintf(str, "%d", YELLOW); put(d_colors_param, "YELLOW", str); sprintf(str, "%d", BLUE); put(d_colors_param, "BLUE", str); sprintf(str, "%d", MAGENTA); put(d_colors_param, "MAGENTA", str); sprintf(str, "%d", CYAN); put(d_colors_param, "CYAN", str); sprintf(str, "%d", WHITE); put(d_colors_param, "WHITE", str); sprintf(str, "%d", HEADINGS); put(d_colors_param, "HEADINGS", str); sprintf(str, "%d", HEADINGS_ODD); put(d_colors_param, "HEADINGS_ODD", str); sprintf(str, "%d", GRID_EVEN); put(d_colors_param, "GRID_EVEN", str); sprintf(str, "%d", GRID_ODD); put(d_colors_param, "GRID_ODD", str); sprintf(str, "%d", WELCOME); put(d_colors_param, "WELCOME", str); sprintf(str, "%d", CELL_SELECTION); put(d_colors_param, "CELL_SELECTION", str); sprintf(str, "%d", CELL_SELECTION_SC); put(d_colors_param, "CELL_SELECTION_SC", str); sprintf(str, "%d", HELP_HIGHLIGHT); put(d_colors_param, "HELP_HIGHLIGHT", str); sprintf(str, "%d", NUMB); put(d_colors_param, "NUMB", str); sprintf(str, "%d", STRG); put(d_colors_param, "STRG", str); sprintf(str, "%d", DATEF); put(d_colors_param, "DATEF", str); sprintf(str, "%d", EXPRESSION); put(d_colors_param, "EXPRESSION", str); sprintf(str, "%d", INFO_MSG); put(d_colors_param, "INFO_MSG", str); sprintf(str, "%d", ERROR_MSG); put(d_colors_param, "ERROR_MSG", str); sprintf(str, "%d", MODE); put(d_colors_param, "MODE", str); sprintf(str, "%d", CELL_ID); put(d_colors_param, "CELL_ID", str); sprintf(str, "%d", CELL_FORMAT); put(d_colors_param, "CELL_FORMAT", str); sprintf(str, "%d", CELL_CONTENT); put(d_colors_param, "CELL_CONTENT", str); sprintf(str, "%d", INPUT); put(d_colors_param, "INPUT", str); sprintf(str, "%d", NORMAL); put(d_colors_param, "NORMAL", str); sprintf(str, "%d", CELL_ERROR); put(d_colors_param, "CELL_ERROR", str); sprintf(str, "%d", CELL_NEGATIVE); put(d_colors_param, "CELL_NEGATIVE", str); sprintf(str, "%d", DEFAULT); put(d_colors_param, "DEFAULT", str); sprintf(str, "%d", CURRENT_SHEET); put(d_colors_param, "CURRENT_SHEET", str); sprintf(str, "%d", SHEET); put(d_colors_param, "SHEET", str); sprintf(str, "%d", FILENM); put(d_colors_param, "FILENM", str); } /** * @brief free the colors parameters dictionary * * Free the colors parameters dictionary * * Example usage: * @code * free_colors_param_dict(); * @endcode * returns: none */ void free_colors_param_dict() { destroy_dictionary(d_colors_param); return; } /** * @brief Function that colorize the different types shown on screen * * Change color definition with user's one STR: color definition from '.sc' * file. It can be obtained at run time with the ':color str' command. * * Example usage: * @code * chg_color(); * @endcode * returns: none */ void chg_color(char * str) { if (get_conf_int("nocurses")) return; // Create key-value dictionary for the content of the string struct dictionary * d = create_dictionary(); // Remove quotes if (str[0]=='"') del_char(str, 0); if (str[strlen(str)-1]=='"') del_char(str, strlen(str)-1); parse_str(d, str, TRUE); char * cl; // Validate we got enough keys to change a color if ( (get(d, "fg") == NULL) || (get(d, "type") == NULL) || (get(d, "bg") == NULL)) { sc_error("Color definition incomplete"); destroy_dictionary(d); return; } // Validate the values for those keys are correct // bg, fg should have valid values BLACK(0) to WHITE(7) for ncurses stock colors // or a custom color name, or -1, indicating default TERMINAL color if (get(d_colors_param, get(d, "type")) == NULL) { sc_error("Error setting color. Invalid type value: %s", get(d, "type")); destroy_dictionary(d); return; } if (get(d_colors_param, get(d, "fg")) == NULL && get_custom_color(get(d, "fg")) == NULL) { sc_error("Error setting color. Invalid fg value: %s. It is not and ncurses color nor user defined color.", get(d, "fg")); destroy_dictionary(d); return; } if (get(d_colors_param, get(d, "bg")) == NULL && get_custom_color(get(d, "bg")) == NULL) { sc_error("Error setting color. Invalid bg value: %s. It is not and ncurses color nor user defined color.", get(d, "bg")); destroy_dictionary(d); return; } // Change the color int type = get_int(d_colors_param, get(d, "type")); struct custom_color * cc; if (get_int(d_colors_param, get(d, "bg")) == NONE_COLOR) { // Don't change anything } else if ((cc = get_custom_color(get(d, "bg"))) != NULL) { // bg is custom color ucolors[ type ].bg = 7 + cc->number; } else { // bg is stock ncurses color ucolors[ type ].bg = get_int(d_colors_param, get(d, "bg")); } if (get_int(d_colors_param, get(d, "fg")) == NONE_COLOR) { // Don't change anything } else if ((cc = get_custom_color(get(d, "fg"))) != NULL) { // fg is custom color ucolors[ type ].fg = 7 + cc->number; } else { // fg is stock ncurses color ucolors[ type ].fg = get_int(d_colors_param, get(d, "fg")); } if (((cl = get(d, "bold")) != NULL) && cl[0] != '\0') ucolors[ type ].bold = get_int(d, "bold"); if (((cl = get(d, "italic")) != NULL) && cl[0] != '\0') ucolors[ type ].italic = get_int(d, "italic"); if (((cl = get(d, "dim")) != NULL) && cl[0] != '\0') ucolors[ type ].dim = get_int(d, "dim"); if (((cl = get(d, "reverse")) != NULL) && cl[0] != '\0') ucolors[ type ].reverse = get_int(d, "reverse"); if (((cl = get(d, "standout")) != NULL) && cl[0] != '\0') ucolors[ type ].standout = get_int(d, "standout"); if (((cl = get(d, "blink")) != NULL) && cl[0] != '\0') ucolors[ type ].blink = get_int(d, "blink"); if (((cl = get(d, "underline")) != NULL) && cl[0] != '\0') ucolors[ type ].underline = get_int(d, "underline"); // clean temp variable destroy_dictionary(d); return; } /* * this functions is for coloring a cell, or a range of cells. * it also applies a format such as bold or underline. * supports undo / redo */ /** * @brief * * Changes coloring and format for cell or range of cells. * * Format options: bold, underline. * * This function supports undo/redo. * * Example usage: * @code * color_cell(,,,,); * @endcode * returns: none */ void color_cell(struct sheet * sh, int r, int c, int rf, int cf, char * str) { struct roman * roman = session->cur_doc; if (any_locked_cells(sh, r, c, rf, cf)) { sc_error("Locked cells encountered. Nothing changed"); return; } // parse detail // Create key-value dictionary for the content of the string struct dictionary * d = create_dictionary(); // Remove quotes if (str[0]=='"') del_char(str, 0); if (str[strlen(str)-1]=='"') del_char(str, strlen(str)-1); parse_str(d, str, TRUE); char * cl; // Validations if (((cl = get(d, "fg")) != NULL && cl[0] != '\0' && get(d_colors_param, get(d, "fg")) == NULL && get_custom_color(cl) == NULL)) { sc_error("One of the values specified is wrong: %s. Please check the values of type, fg and bg.", cl); destroy_dictionary(d); return; } else if ((cl = get(d, "bg")) != NULL && cl[0] != '\0' && get(d_colors_param, get(d, "bg")) == NULL && get_custom_color(cl) == NULL) { sc_error("One of the values specified is wrong: %s. Please check the values of type, fg and bg.", cl); destroy_dictionary(d); return; } // we apply format in the range struct ent * n; int i, j; for (i=r; i<=rf; i++) { for (j=c; j<=cf; j++) { // if we are not loading the file if (! roman->loading) { roman->modflg++; #ifdef UNDO create_undo_action(); copy_to_undostruct(sh, i, j, i, j, UNDO_DEL, IGNORE_DEPS, NULL); #endif } // action n = lookat(sh, i, j); if (n->ucolor == NULL) { n->ucolor = (struct ucolor *) malloc(sizeof(struct ucolor)); n->ucolor->fg = NONE_COLOR; n->ucolor->bg = NONE_COLOR; n->ucolor->bold = 0; n->ucolor->italic = 0; n->ucolor->dim = 0; n->ucolor->reverse = 0; n->ucolor->standout = 0; n->ucolor->underline = 0; n->ucolor->blink = 0; } struct custom_color * cc; if ((cl = get(d, "bg")) != NULL && cl[0] != '\0') { if (get(d_colors_param, get(d, "bg")) != NULL) { n->ucolor->bg = get_int(d_colors_param, get(d, "bg")); } else if ((cc = get_custom_color(get(d, "bg"))) != NULL) { n->ucolor->bg = 7 + cc->number; } else { sc_error("error setting bg color. we should not be here."); n->ucolor->bg = DEFAULT_COLOR; } } if ((cl = get(d, "fg")) != NULL && cl[0] != '\0') { if (get(d_colors_param, get(d, "fg")) != NULL) { n->ucolor->fg = get_int(d_colors_param, get(d, "fg")); } else if ((cc = get_custom_color(get(d, "fg"))) != NULL) { n->ucolor->fg = 7 + cc->number; } else { sc_error("error setting fg color. we should not be here."); n->ucolor->fg = DEFAULT_COLOR; } } if ((cl = get(d, "bold")) != NULL && cl[0] != '\0') n->ucolor->bold = get_int(d, "bold"); if ((cl = get(d, "italic")) != NULL && cl[0] != '\0') n->ucolor->italic = get_int(d, "italic"); if ((cl = get(d, "dim") ) != NULL && cl[0] != '\0') n->ucolor->dim = get_int(d, "dim"); if ((cl = get(d, "reverse")) != NULL && cl[0] != '\0') n->ucolor->reverse = get_int(d, "reverse"); if ((cl = get(d, "standout")) != NULL && cl[0] != '\0') n->ucolor->standout = get_int(d, "standout"); if ((cl = get(d, "blink")) != NULL && cl[0] != '\0') n->ucolor->blink = get_int(d, "blink"); if ((cl = get(d, "underline")) != NULL && cl[0] != '\0') n->ucolor->underline = get_int(d, "underline"); if (! roman->loading) { #ifdef UNDO copy_to_undostruct(sh, i, j, i, j, UNDO_ADD, IGNORE_DEPS, NULL); end_undo_action(); #endif } } } destroy_dictionary(d); if (! roman->loading) ui_update(TRUE); return; } /** * @brief * * Cleans format from a range of cells * * Example usage: * @code * unformat(,,,); * @endcode * returns: none */ void unformat(struct sheet * sh, int r, int c, int rf, int cf) { struct roman * roman = session->cur_doc; if (any_locked_cells(sh, r, c, rf, cf)) { sc_error("Locked cells encountered. Nothing changed"); return; } // if we are not loading the file if (! roman->loading) { roman->modflg++; #ifdef UNDO create_undo_action(); copy_to_undostruct(sh, r, c, rf, cf, UNDO_DEL, IGNORE_DEPS, NULL); #endif } // we remove format in the range struct ent * n; int i, j; for (i=r; i<=rf; i++) { for (j=c; j<=cf; j++) { // action if ( (n = *ATBL(sh, sh->tbl, i, j)) && n->ucolor != NULL) { free(n->ucolor); n->ucolor = NULL; } } } if (! roman->loading) { #ifdef UNDO copy_to_undostruct(sh, r, c, rf, cf, UNDO_ADD, IGNORE_DEPS, NULL); end_undo_action(); #endif ui_update(TRUE); } return; } /** * @brief * * This function receives two ucolor variables and returns 1 if both have the * same values, returns 0 otherwise. * * Example usage: * @code * same_color(,); * @endcode * returns: 1 if colors are the same, 0 otherwise */ int same_ucolor(struct ucolor * u, struct ucolor * v) { if (u == NULL || v == NULL) return 0; if (u->fg != v->fg) return 0; if (u->bg != v->bg) return 0; if (u->bold != v->bold) return 0; if (u->italic != v->italic) return 0; if (u->dim != v->dim) return 0; if (u->reverse != v->reverse) return 0; if (u->standout != v->standout) return 0; if (u->underline != v->underline) return 0; if (u->blink != v->blink) return 0; return 1; } /** * @brief Redefine one of the 8 ncurses colors to a new RGB value. * * Redefine stock ncurses color to a new RGB value. Only if terminal supports it. * RGB values must be between 0 and 255. * * Example usage: * @code * redefine_color(,,,); * @endcode * returns: 0 on success, -1 on error */ int redefine_color(char * color, int r, int g, int b) { struct roman * roman = session->cur_doc; #if defined(NCURSES) && defined(USECOLORS) extern void sig_winchg(); if ( ! get_conf_int("nocurses") && has_colors() && can_change_color() ) { char * s = get(d_colors_param, color); if (s == NULL) { sc_error("Color not found"); return -1; } #if defined(NCURSES_VERSION_MAJOR) && (( NCURSES_VERSION_MAJOR > 5 && defined(NCURSES_VERSION_MINOR) && NCURSES_VERSION_MINOR > 0) || NCURSES_VERSION_MAJOR > 6) if (init_extended_color(atoi(s), RGB(r, g, b)) == 0) { #else if (init_color(atoi(s), RGB(r, g, b)) == 0) { #endif sig_winchg(); if (! roman->loading) sc_info("Color %s redefined to %d %d %d.", color, r, g, b); return 0; } } if (! roman->loading) sc_error("Could not redefine color"); #endif return -1; } /** * @brief Define a custom color * * Define a custom color. If terminal does not support 256 colors, return. * * Example usage: * @code * define_color(,,,); * @endcode * returns: 0 on success, -1 on error */ int define_color(char * color, int r, int g, int b) { struct roman * roman = session->cur_doc; #if defined(NCURSES) && defined(USECOLORS) if (get_conf_int("nocurses")) { // this should not be alerted. //sc_error("Could not define color %s. Not using NCURSES.", color); return -1; } else if (! has_colors () || ! can_change_color() || COLORS < 9) { sc_error("Could not define color %s. Not supported by terminal.", color); return -1; } else if (get(d_colors_param, color) != NULL) { sc_error("Could not define custom color %s. That is an ncurses color. Use :redefine for that purpose.", color); return -1; } else if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) { sc_error("Could not define color %s. One of the RGB values is invalid.", color); return -1; } int number_defined_colors; struct custom_color * cc; // update color value if it already exists if ((cc = get_custom_color(color)) != NULL) { sc_debug("Color '%s' already defined. Updating its RGB values.", color); } else if ((number_defined_colors = count_custom_colors()) == 24) { sc_error("Could not define color %s. There are already 24 custom colors defined.", color); return -1; } else { // we create a new custom color cc = malloc (sizeof(struct custom_color)); cc->number = number_defined_colors+1; cc->name = (char *) malloc (sizeof(char) * (strlen(color) + 1)); strcpy(cc->name, color); cc->p_next = custom_colors; custom_colors = cc; } cc->r = r; cc->g = g; cc->b = b; #if defined(NCURSES_VERSION_MAJOR) && (( NCURSES_VERSION_MAJOR > 5 && defined(NCURSES_VERSION_MINOR) && NCURSES_VERSION_MINOR > 0) || NCURSES_VERSION_MAJOR > 6) init_extended_color(7 + cc->number, RGB(r, g, b)); #else init_color(7 + cc->number, RGB(r, g, b)); #endif if (! roman->loading) sc_info("Defined custom color #%d with name '%s' and RGB values %d %d %d", cc->number, cc->name, cc->r, cc->g, cc->b); return 0; #endif sc_error("Could not define color %s", color); return -1; } /** * @brief Free memory of custom_colors * * Free custom_colors memory * * Example usage: * @code * free_custom_colors(); * @endcode * returns 0 */ int free_custom_colors() { struct custom_color * aux; while (custom_colors != NULL) { aux = custom_colors->p_next; free(custom_colors->name); free(custom_colors); custom_colors = aux; } return 0; } /** * @brief get custom color by name * * get custom color by name * * Example usage: * @code get_custom_color("skyblue") * @endcode * returns struct custom_color * if found * returns NULL otherwise */ struct custom_color * get_custom_color(char * name) { struct custom_color * aux = custom_colors; while (aux != NULL) { if (! strcasecmp(name, aux->name)) return aux; aux=aux->p_next; } return NULL; } /** * @brief count the defined custom colors * * count the defined custom colors * * Example usage: * @code count_custom_colors() * @endcode * returns int */ int count_custom_colors() { int c = 0; struct custom_color * aux = custom_colors; while (aux != NULL) { aux=aux->p_next; c++; } return c; } /** * @brief get custom color by number * * get custom color by number * * Example usage: * @code get_custom_color_by_number(2) * @endcode * returns struct custom_color * if found * returns NULL otherwise */ struct custom_color * get_custom_color_by_number(int number) { struct custom_color * aux = custom_colors; while (aux != NULL) { if (number == aux->number) return aux; aux=aux->p_next; } return NULL; }