1 /*******************************************************************************
2  * Copyright (c) 2013-2021, Andrés Martinelli <andmarti@gmail.com>             *
3  * All rights reserved.                                                        *
4  *                                                                             *
5  * This file is a part of SC-IM                                                *
6  *                                                                             *
7  * SC-IM is a spreadsheet program that is based on SC. The original authors    *
8  * of SC are James Gosling and Mark Weiser, and mods were later added by       *
9  * Chuck Martin.                                                               *
10  *                                                                             *
11  * Redistribution and use in source and binary forms, with or without          *
12  * modification, are permitted provided that the following conditions are met: *
13  * 1. Redistributions of source code must retain the above copyright           *
14  *    notice, this list of conditions and the following disclaimer.            *
15  * 2. Redistributions in binary form must reproduce the above copyright        *
16  *    notice, this list of conditions and the following disclaimer in the      *
17  *    documentation and/or other materials provided with the distribution.     *
18  * 3. All advertising materials mentioning features or use of this software    *
19  *    must display the following acknowledgement:                              *
20  *    This product includes software developed by Andrés Martinelli            *
21  *    <andmarti@gmail.com>.                                                    *
22  * 4. Neither the name of the Andrés Martinelli nor the                        *
23  *   names of other contributors may be used to endorse or promote products    *
24  *   derived from this software without specific prior written permission.     *
25  *                                                                             *
26  * THIS SOFTWARE IS PROVIDED BY ANDRES MARTINELLI ''AS IS'' AND ANY            *
27  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED   *
28  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE      *
29  * DISCLAIMED. IN NO EVENT SHALL ANDRES MARTINELLI BE LIABLE FOR ANY           *
30  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES  *
31  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;*
32  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
33  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT  *
34  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE       *
35  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.           *
36  *******************************************************************************/
37 
38 /**
39  * @file color.c
40  * @author Andrés Martinelli <andmarti@gmail.com>
41  * @date 2017-07-18
42  * @brief File containing color functions
43  *
44  */
45 
46 #include <sys/types.h>
47 #include <string.h>
48 #include <stdlib.h>      // for atoi
49 #include <ctype.h>
50 #include <unistd.h>
51 
52 #include "sc.h"
53 #include "macros.h"
54 #include "utils/dictionary.h"
55 #include "utils/string.h"
56 #include "range.h"
57 #include "tui.h"
58 #include "undo.h"
59 #include "conf.h"
60 #include "cmds.h"
61 
62 struct ucolor ucolors[N_INIT_PAIRS] = {};
63 
64 static struct dictionary * d_colors_param = NULL;
65 
get_d_colors_param()66 struct dictionary * get_d_colors_param() {
67     return d_colors_param;
68 }
69 
70 static struct custom_color * custom_colors = NULL;
71 /**
72  * @brief Generate DEFAULT 'initcolor' colors
73  *
74  * Generate DEFAULT 'initcolor' colors.
75  *
76  * Example usage:
77  * @code
78  *     start_default_ucolors();
79  * @endcode
80  * returns: none
81  */
82 
start_default_ucolors()83 void start_default_ucolors() {
84 
85     // Initialize colors attributes
86     int i;
87     for (i=0; i < N_INIT_PAIRS; i++) {
88         ucolors[ i ].bold      = 0;
89         ucolors[ i ].italic    = 0;
90         ucolors[ i ].dim       = 0;
91         ucolors[ i ].reverse   = 0;
92         ucolors[ i ].standout  = 0;
93         ucolors[ i ].underline = 0;
94         ucolors[ i ].blink     = 0;
95     }
96 
97     // Set some colors attributes
98     // NOTE: always increase N_INIT_PAIRS on each color definition you add.
99     ucolors[ DEFAULT         ].fg = WHITE;
100     ucolors[ DEFAULT         ].bg = DEFAULT_COLOR;
101     ucolors[ HEADINGS        ].bg = YELLOW;
102     ucolors[ HEADINGS        ].fg = BLACK;
103     ucolors[ HEADINGS_ODD    ].bg = YELLOW;
104     ucolors[ HEADINGS_ODD    ].fg = BLACK;
105     ucolors[ GRID_EVEN       ].fg = WHITE;
106     ucolors[ GRID_EVEN       ].bg = DEFAULT_COLOR;
107     ucolors[ GRID_ODD        ].fg = WHITE;
108     ucolors[ GRID_ODD        ].bg = DEFAULT_COLOR;
109     ucolors[ WELCOME         ].fg = YELLOW;
110     ucolors[ WELCOME         ].bg = DEFAULT_COLOR;
111     ucolors[ WELCOME         ].bold = 0;
112     ucolors[ CELL_SELECTION  ].bg = BLACK;                 // cell selection in headings
113     ucolors[ CELL_SELECTION  ].fg = YELLOW;
114     ucolors[ CELL_SELECTION  ].bold = 0;
115     ucolors[ CELL_SELECTION_SC ].fg = BLACK;               // cell selection in spreadsheet
116     ucolors[ CELL_SELECTION_SC ].bg = YELLOW;
117     ucolors[ NUMB            ].fg = CYAN;
118     ucolors[ NUMB            ].bg = DEFAULT_COLOR;
119     ucolors[ STRG            ].fg = MAGENTA;
120     ucolors[ STRG            ].bg = DEFAULT_COLOR;
121     ucolors[ STRG            ].bold = 0;
122     ucolors[ DATEF           ].fg = YELLOW;
123     ucolors[ DATEF           ].bg = DEFAULT_COLOR;
124     ucolors[ EXPRESSION      ].fg = RED;
125     ucolors[ EXPRESSION      ].bg = DEFAULT_COLOR;
126     ucolors[ INFO_MSG        ].fg = CYAN;
127     ucolors[ INFO_MSG        ].bg = DEFAULT_COLOR;
128     ucolors[ INFO_MSG        ].bold = 1;
129     ucolors[ ERROR_MSG       ].bg = RED;
130     ucolors[ ERROR_MSG       ].fg = WHITE;
131     ucolors[ ERROR_MSG       ].bold = 1;
132     ucolors[ MODE            ].fg = WHITE;
133     ucolors[ MODE            ].bg = DEFAULT_COLOR;
134     ucolors[ MODE            ].bold = 1;
135     ucolors[ CELL_ID         ].fg = WHITE;
136     ucolors[ CELL_ID         ].bg = DEFAULT_COLOR;
137     ucolors[ CELL_ID         ].bold = 1;
138     ucolors[ CELL_FORMAT     ].fg = RED;
139     ucolors[ CELL_FORMAT     ].bg = DEFAULT_COLOR;
140     ucolors[ CELL_CONTENT    ].fg = CYAN;
141     ucolors[ CELL_CONTENT    ].bg = DEFAULT_COLOR;
142     ucolors[ CELL_CONTENT    ].bold = 1;
143     ucolors[ INPUT           ].fg = WHITE;
144     ucolors[ INPUT           ].bg = DEFAULT_COLOR;
145     ucolors[ NORMAL          ].fg = WHITE;
146     ucolors[ NORMAL          ].bg = DEFAULT_COLOR;
147     ucolors[ CELL_ERROR      ].fg = RED;
148     ucolors[ CELL_ERROR      ].bg = DEFAULT_COLOR;
149     ucolors[ CELL_ERROR      ].bold = 1;
150     ucolors[ CELL_NEGATIVE   ].fg = GREEN;
151     ucolors[ CELL_NEGATIVE   ].bg = DEFAULT_COLOR;
152     ucolors[ HELP_HIGHLIGHT  ].fg = BLACK;               // cell selection in spreadsheet
153     ucolors[ HELP_HIGHLIGHT  ].bg = YELLOW;
154 
155     ui_start_colors(); // call specific ui startup routine
156 }
157 
158 /**
159  * @brief Create a dictionary that stores correspondence between macros and key values
160  *
161  * Create a dictionary that stores the correspondence between macros and key
162  * values (integers) defined in '.sc' files or through the color command.
163  *
164  * Example usage:
165  * @code
166  *     set_colors_param_dict();
167  * @endcode
168  * returns: none
169  */
170 
set_colors_param_dict()171 void set_colors_param_dict() {
172 
173     d_colors_param = create_dictionary();
174     char str[3];
175     str[0]='\0';
176 
177     sprintf(str, "%d", DEFAULT_COLOR);
178     put(d_colors_param, "DEFAULT_COLOR", str);
179     sprintf(str, "%d", BLACK);
180     put(d_colors_param, "BLACK", str);
181     sprintf(str, "%d", RED);
182     put(d_colors_param, "RED", str);
183     sprintf(str, "%d", GREEN);
184     put(d_colors_param, "GREEN", str);
185     sprintf(str, "%d", YELLOW);
186     put(d_colors_param, "YELLOW", str);
187     sprintf(str, "%d", BLUE);
188     put(d_colors_param, "BLUE", str);
189     sprintf(str, "%d", MAGENTA);
190     put(d_colors_param, "MAGENTA", str);
191     sprintf(str, "%d", CYAN);
192     put(d_colors_param, "CYAN", str);
193     sprintf(str, "%d", WHITE);
194     put(d_colors_param, "WHITE", str);
195     sprintf(str, "%d", HEADINGS);
196     put(d_colors_param, "HEADINGS", str);
197     sprintf(str, "%d", HEADINGS_ODD);
198     put(d_colors_param, "HEADINGS_ODD", str);
199     sprintf(str, "%d", GRID_EVEN);
200     put(d_colors_param, "GRID_EVEN", str);
201     sprintf(str, "%d", GRID_ODD);
202     put(d_colors_param, "GRID_ODD", str);
203     sprintf(str, "%d", WELCOME);
204     put(d_colors_param, "WELCOME", str);
205     sprintf(str, "%d", CELL_SELECTION);
206     put(d_colors_param, "CELL_SELECTION", str);
207     sprintf(str, "%d", CELL_SELECTION_SC);
208     put(d_colors_param, "CELL_SELECTION_SC", str);
209     sprintf(str, "%d", HELP_HIGHLIGHT);
210     put(d_colors_param, "HELP_HIGHLIGHT", str);
211     sprintf(str, "%d", NUMB);
212     put(d_colors_param, "NUMB", str);
213     sprintf(str, "%d", STRG);
214     put(d_colors_param, "STRG", str);
215     sprintf(str, "%d", DATEF);
216     put(d_colors_param, "DATEF", str);
217     sprintf(str, "%d", EXPRESSION);
218     put(d_colors_param, "EXPRESSION", str);
219     sprintf(str, "%d", INFO_MSG);
220     put(d_colors_param, "INFO_MSG", str);
221     sprintf(str, "%d", ERROR_MSG);
222     put(d_colors_param, "ERROR_MSG", str);
223     sprintf(str, "%d", MODE);
224     put(d_colors_param, "MODE", str);
225     sprintf(str, "%d", CELL_ID);
226     put(d_colors_param, "CELL_ID", str);
227     sprintf(str, "%d", CELL_FORMAT);
228     put(d_colors_param, "CELL_FORMAT", str);
229     sprintf(str, "%d", CELL_CONTENT);
230     put(d_colors_param, "CELL_CONTENT", str);
231     sprintf(str, "%d", INPUT);
232     put(d_colors_param, "INPUT", str);
233     sprintf(str, "%d", NORMAL);
234     put(d_colors_param, "NORMAL", str);
235     sprintf(str, "%d", CELL_ERROR);
236     put(d_colors_param, "CELL_ERROR", str);
237     sprintf(str, "%d", CELL_NEGATIVE);
238     put(d_colors_param, "CELL_NEGATIVE", str);
239     sprintf(str, "%d", DEFAULT);
240     put(d_colors_param, "DEFAULT", str);
241 }
242 
243 /**
244  * @brief free the colors parameters dictionary
245  *
246  * Free the colors parameters dictionary
247  *
248  * Example usage:
249  * @code
250  *     free_colors_param_dict();
251  * @endcode
252  * returns: none
253  */
254 
free_colors_param_dict()255 void free_colors_param_dict() {
256     destroy_dictionary(d_colors_param);
257     return;
258 }
259 
260 /**
261  * @brief Function that colorize the different types shown on screen
262  *
263  * Change color definition with user's one STR: color definition from '.sc'
264  * file. It can be obtained at run time with the ':color str' command.
265  *
266  * Example usage:
267  * @code
268  *     chg_color();
269  * @endcode
270  * returns: none
271  */
272 
chg_color(char * str)273 void chg_color(char * str) {
274     if (get_conf_int("nocurses")) return;
275 
276     // Create key-value dictionary for the content of the string
277     struct dictionary * d = create_dictionary();
278 
279     // Remove quotes
280     if (str[0]=='"') del_char(str, 0);
281     if (str[strlen(str)-1]=='"') del_char(str, strlen(str)-1);
282 
283     parse_str(d, str, TRUE);
284     char * cl;
285 
286     // Validate we got enough keys to change a color
287     if (
288         (get(d, "fg") == NULL) ||
289         (get(d, "type") == NULL) ||
290         (get(d, "bg") == NULL)) {
291         sc_error("Color definition incomplete");
292         destroy_dictionary(d);
293         return;
294     }
295 
296     // Validate the values for those keys are correct
297     // bg, fg should have valid values BLACK(0) to WHITE(7) for ncurses stock colors
298     // or a custom color name, or -1, indicating default TERMINAL color
299     if (get(d_colors_param, get(d, "type")) == NULL) {
300         sc_error("Error setting color. Invalid type value: %s", get(d, "type"));
301         destroy_dictionary(d);
302         return;
303     }
304 
305     if (get(d_colors_param, get(d, "fg")) == NULL && get_custom_color(get(d, "fg")) == NULL) {
306         sc_error("Error setting color. Invalid fg value: %s. It is not and ncurses color nor user defined color.", get(d, "fg"));
307         destroy_dictionary(d);
308         return;
309     }
310 
311     if (get(d_colors_param, get(d, "bg")) == NULL && get_custom_color(get(d, "bg")) == NULL) {
312         sc_error("Error setting color. Invalid bg value: %s. It is not and ncurses color nor user defined color.", get(d, "bg"));
313         destroy_dictionary(d);
314         return;
315     }
316 
317     // Change the color
318     int type = get_int(d_colors_param, get(d, "type"));
319     struct custom_color * cc;
320     if ((cc = get_custom_color(get(d, "bg"))) != NULL) { // bg is custom color
321         ucolors[ type ].bg = 7 + cc->number;
322     } else { // bg is stock ncurses color
323         ucolors[ type ].bg = get_int(d_colors_param, get(d, "bg"));
324     }
325 
326     if ((cc = get_custom_color(get(d, "fg"))) != NULL) { // fg is custom color
327         ucolors[ type ].fg = 7 + cc->number;
328     } else { // fg is stock ncurses color
329         ucolors[ type ].fg = get_int(d_colors_param, get(d, "fg"));
330     }
331 
332     if (((cl = get(d, "bold")) != NULL)      && cl[0] != '\0')     ucolors[ type ].bold      = get_int(d, "bold");
333     if (((cl = get(d, "italic")) != NULL)    && cl[0] != '\0')     ucolors[ type ].italic    = get_int(d, "italic");
334     if (((cl = get(d, "dim")) != NULL)       && cl[0] != '\0')     ucolors[ type ].dim       = get_int(d, "dim");
335     if (((cl = get(d, "reverse")) != NULL)   && cl[0] != '\0')     ucolors[ type ].reverse   = get_int(d, "reverse");
336     if (((cl = get(d, "standout")) != NULL)  && cl[0] != '\0')     ucolors[ type ].standout  = get_int(d, "standout");
337     if (((cl = get(d, "blink")) != NULL)     && cl[0] != '\0')     ucolors[ type ].blink     = get_int(d, "blink");
338     if (((cl = get(d, "underline")) != NULL) && cl[0] != '\0')     ucolors[ type ].underline = get_int(d, "underline");
339 
340     // clean temp variable
341     destroy_dictionary(d);
342     return;
343 }
344 
345 /*
346  * this functions is for coloring a cell, or a range of cells.
347  * it also applies a format such as bold or underline.
348  * supports undo / redo
349  */
350 /**
351  * @brief
352  *
353  * Changes coloring and format for cell or range of cells.
354  *
355  * Format options: bold, underline.
356  *
357  * This function supports undo/redo.
358  *
359  * Example usage:
360  * @code
361  *     color_cell(<var1>,<var2>,<var3>,<var4>,<var5>);
362  * @endcode
363  * returns: none
364  */
365 
color_cell(int r,int c,int rf,int cf,char * str)366 void color_cell(int r, int c, int rf, int cf, char * str) {
367     if (any_locked_cells(r, c, rf, cf)) {
368         sc_error("Locked cells encountered. Nothing changed");
369         return;
370     }
371 
372     // parse detail
373     // Create key-value dictionary for the content of the string
374     struct dictionary * d = create_dictionary();
375 
376     // Remove quotes
377     if (str[0]=='"') del_char(str, 0);
378     if (str[strlen(str)-1]=='"') del_char(str, strlen(str)-1);
379 
380     parse_str(d, str, TRUE);
381     char * cl;
382 
383     // Validations
384     if (((cl = get(d, "fg")) != NULL && cl[0] != '\0' && get(d_colors_param, get(d, "fg")) == NULL && get_custom_color(cl) == NULL)) {
385             sc_error("One of the values specified is wrong: %s. Please check the values of type, fg and bg.", cl);
386             destroy_dictionary(d);
387             return;
388     } else if ((cl = get(d, "bg")) != NULL && cl[0] != '\0' && get(d_colors_param, get(d, "bg")) == NULL && get_custom_color(cl) == NULL) {
389             sc_error("One of the values specified is wrong: %s. Please check the values of type, fg and bg.", cl);
390             destroy_dictionary(d);
391             return;
392     }
393 
394     // we apply format in the range
395     struct ent * n;
396     int i, j;
397     for (i=r; i<=rf; i++) {
398         for (j=c; j<=cf; j++) {
399 
400             // if we are not loading the file
401             if (! loading) {
402                 modflg++;
403 
404                 #ifdef UNDO
405                 create_undo_action();
406                 copy_to_undostruct(i, j, i, j, UNDO_DEL, IGNORE_DEPS, NULL);
407                 #endif
408             }
409 
410             // action
411             n = lookat(i, j);
412             if (n->ucolor == NULL) {
413                 n->ucolor = (struct ucolor *) malloc(sizeof(struct ucolor));
414                 n->ucolor->fg = NONE_COLOR;
415                 n->ucolor->bg = NONE_COLOR;
416                 n->ucolor->bold = 0;
417                 n->ucolor->italic = 0;
418                 n->ucolor->dim = 0;
419                 n->ucolor->reverse = 0;
420                 n->ucolor->standout = 0;
421                 n->ucolor->underline = 0;
422                 n->ucolor->blink = 0;
423             }
424 
425             struct custom_color * cc;
426             if ((cl = get(d, "bg")) != NULL && cl[0] != '\0') {
427                 if (get(d_colors_param, get(d, "bg")) != NULL) {
428                     n->ucolor->bg = get_int(d_colors_param, get(d, "bg"));
429                 } else if ((cc = get_custom_color(get(d, "bg"))) != NULL) {
430                     n->ucolor->bg = 7 + cc->number;
431                 } else {
432                     sc_error("error setting bg color. we should not be here.");
433                     n->ucolor->bg = DEFAULT_COLOR;
434                 }
435             }
436             if ((cl = get(d, "fg")) != NULL && cl[0] != '\0') {
437                 if (get(d_colors_param, get(d, "fg")) != NULL) {
438                     n->ucolor->fg = get_int(d_colors_param, get(d, "fg"));
439                 } else if ((cc = get_custom_color(get(d, "fg"))) != NULL) {
440                     n->ucolor->fg = 7 + cc->number;
441                 } else {
442                     sc_error("error setting fg color. we should not be here.");
443                     n->ucolor->fg = DEFAULT_COLOR;
444                 }
445             }
446 
447             if ((cl = get(d, "bold"))      != NULL && cl[0] != '\0')   n->ucolor->bold      = get_int(d, "bold");
448             if ((cl = get(d, "italic"))    != NULL && cl[0] != '\0')   n->ucolor->italic    = get_int(d, "italic");
449             if ((cl = get(d, "dim") )      != NULL && cl[0] != '\0')   n->ucolor->dim       = get_int(d, "dim");
450             if ((cl = get(d, "reverse"))   != NULL && cl[0] != '\0')   n->ucolor->reverse   = get_int(d, "reverse");
451             if ((cl = get(d, "standout"))  != NULL && cl[0] != '\0')   n->ucolor->standout  = get_int(d, "standout");
452             if ((cl = get(d, "blink"))     != NULL && cl[0] != '\0')   n->ucolor->blink     = get_int(d, "blink");
453             if ((cl = get(d, "underline")) != NULL && cl[0] != '\0')   n->ucolor->underline = get_int(d, "underline");
454 
455             if (! loading) {
456                 #ifdef UNDO
457                 copy_to_undostruct(i, j, i, j, UNDO_ADD, IGNORE_DEPS, NULL);
458                 end_undo_action();
459                 #endif
460             }
461         }
462     }
463 
464     destroy_dictionary(d);
465     if (! loading) ui_update(TRUE);
466     return;
467 }
468 
469 /**
470  * @brief
471  *
472  * Cleans format from a range of cells
473  *
474  * Example usage:
475  * @code
476  *     unformat(<var1>,<var2>,<var3>,<var4>);
477  * @endcode
478  * returns: none
479  */
480 
unformat(int r,int c,int rf,int cf)481 void unformat(int r, int c, int rf, int cf) {
482     if (any_locked_cells(r, c, rf, cf)) {
483         sc_error("Locked cells encountered. Nothing changed");
484         return;
485     }
486 
487     // if we are not loading the file
488     if (! loading) {
489         modflg++;
490         #ifdef UNDO
491         create_undo_action();
492         copy_to_undostruct(r, rf, c, cf, UNDO_DEL, IGNORE_DEPS, NULL);
493         #endif
494     }
495 
496     // we remove format in the range
497     struct ent * n;
498     int i, j;
499     for (i=r; i<=rf; i++) {
500         for (j=c; j<=cf; j++) {
501 
502             // action
503             if ( (n = *ATBL(tbl, i, j)) && n->ucolor != NULL) {
504                 free(n->ucolor);
505                 n->ucolor = NULL;
506             }
507 
508        }
509     }
510     if (! loading) {
511         #ifdef UNDO
512         copy_to_undostruct(r, rf, c, cf, UNDO_ADD, IGNORE_DEPS, NULL);
513         end_undo_action();
514         #endif
515         ui_update(TRUE);
516     }
517     return;
518 }
519 
520 /**
521  * @brief
522  *
523  * This function receives two ucolor variables and returns 1 if both have the
524  * same values, returns 0 otherwise.
525  *
526  * Example usage:
527  * @code
528  *     same_color(<ucolor>,<ucolor>);
529  * @endcode
530  * returns: 1 if colors are the same, 0 otherwise
531  */
532 
same_ucolor(struct ucolor * u,struct ucolor * v)533 int same_ucolor(struct ucolor * u, struct ucolor * v) {
534     if (u == NULL || v == NULL)       return 0;
535 
536     if (u->fg != v->fg)               return 0;
537     if (u->bg != v->bg)               return 0;
538     if (u->bold != v->bold)           return 0;
539     if (u->italic != v->italic)       return 0;
540     if (u->dim != v->dim)             return 0;
541     if (u->reverse != v->reverse)     return 0;
542     if (u->standout != v->standout)   return 0;
543     if (u->underline != v->underline) return 0;
544     if (u->blink != v->blink)         return 0;
545 
546     return 1;
547 }
548 
549 /**
550  * @brief Redefine one of the 8 ncurses colors to a new RGB value.
551  *
552  * Redefine stock ncurses color to a new RGB value. Only if terminal supports it.
553  * RGB values must be between 0 and 255.
554  *
555  * Example usage:
556  * @code
557  *     redefine_color(<color>,<r>,<g>,<b>);
558  * @endcode
559  * returns: 0 on success, -1 on error
560  */
561 
redefine_color(char * color,int r,int g,int b)562 int redefine_color(char * color, int r, int g, int b) {
563     #if defined(NCURSES) && defined(USECOLORS)
564     extern void sig_winchg();
565     if (
566         ! get_conf_int("nocurses")
567         && has_colors() && can_change_color()
568        ) {
569            char * s = get(d_colors_param, color);
570            if (s == NULL) {
571                sc_error("Color not found");
572                return -1;
573            }
574 #if defined(NCURSES_VERSION_MAJOR) && (( NCURSES_VERSION_MAJOR > 5 && defined(NCURSES_VERSION_MINOR) && NCURSES_VERSION_MINOR > 0) || NCURSES_VERSION_MAJOR > 6)
575            if (init_extended_color(atoi(s), RGB(r, g, b)) == 0) {
576 #else
577            if (init_color(atoi(s), RGB(r, g, b)) == 0) {
578 #endif
579                sig_winchg();
580                if (! loading) sc_info("Color %s redefined to %d %d %d.", color, r, g, b);
581                return 0;
582            }
583        }
584        if (! loading) sc_error("Could not redefine color");
585     #endif
586     return -1;
587 }
588 
589 /**
590  * @brief Define a custom color
591  *
592  * Define a custom color. If terminal does not support 256 colors, return.
593  *
594  * Example usage:
595  * @code
596  *     define_color(<color>,<r>,<g>,<b>);
597  * @endcode
598  * returns: 0 on success, -1 on error
599  */
600 int define_color(char * color, int r, int g, int b) {
601 
602 #if defined(NCURSES) && defined(USECOLORS)
603     if (get_conf_int("nocurses")) {
604         // this should not be alerted.
605         //sc_error("Could not define color %s. Not using NCURSES.", color);
606         return -1;
607     } else if (! has_colors () || ! can_change_color() || COLORS < 9) {
608         sc_error("Could not define color %s. Not supported by terminal.", color);
609         return -1;
610 
611     } else if (get(d_colors_param, color) != NULL) {
612         sc_error("Could not define custom color %s. That is an ncurses color. Use :redefine for that purpose.", color);
613         return -1;
614 
615     } else if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {
616         sc_error("Could not define color %s. One of the RGB values is invalid.", color);
617         return -1;
618     }
619 
620     int number_defined_colors;
621     struct custom_color * cc;
622 
623     // update color value if it already exists
624     if ((cc = get_custom_color(color)) != NULL) {
625         sc_debug("Color '%s' already defined. Updating its RGB values.", color);
626     } else if ((number_defined_colors = count_custom_colors()) == 24) {
627         sc_error("Could not define color %s. There are already 24 custom colors defined.", color);
628         return -1;
629     } else { // we create a new custom color
630         cc = malloc (sizeof(struct custom_color));
631         cc->number = number_defined_colors+1;
632         cc->name =  (char *) malloc (sizeof(char) * (strlen(color) + 1));
633         strcpy(cc->name, color);
634         cc->p_next = custom_colors;
635         custom_colors = cc;
636     }
637     cc->r = r;
638     cc->g = g;
639     cc->b = b;
640 #if defined(NCURSES_VERSION_MAJOR) && (( NCURSES_VERSION_MAJOR > 5 && defined(NCURSES_VERSION_MINOR) && NCURSES_VERSION_MINOR > 0) || NCURSES_VERSION_MAJOR > 6)
641     init_extended_color(7 + cc->number, RGB(r, g, b));
642 #else
643     init_color(7 + cc->number, RGB(r, g, b));
644 #endif
645 
646     if (! 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);
647     return 0;
648 #endif
649     sc_error("Could not define color %s", color);
650     return -1;
651 }
652 
653 /**
654  * @brief Free memory of custom_colors
655  *
656  * Free custom_colors memory
657  *
658  * Example usage:
659  * @code
660  *     free_custom_colors();
661  * @endcode
662  * returns 0
663  */
664 int free_custom_colors() {
665     struct custom_color * aux;
666     while (custom_colors != NULL) {
667         aux = custom_colors->p_next;
668         free(custom_colors->name);
669         free(custom_colors);
670         custom_colors = aux;
671     }
672     return 0;
673 }
674 
675 /**
676  * @brief get custom color by name
677  *
678  * get custom color by name
679  *
680  * Example usage:
681  * @code
682        get_custom_color("skyblue")
683  * @endcode
684  * returns struct custom_color * if found
685  * returns NULL otherwise
686  */
687 struct custom_color * get_custom_color(char * name) {
688     struct custom_color * aux = custom_colors;
689     while (aux != NULL) {
690         if (! strcasecmp(name, aux->name)) return aux;
691         aux=aux->p_next;
692     }
693     return NULL;
694 }
695 
696 /**
697  * @brief count the defined custom colors
698  *
699  * count the defined custom colors
700  *
701  * Example usage:
702  * @code
703        count_custom_colors()
704  * @endcode
705  * returns int
706  */
707 int count_custom_colors() {
708     int c = 0;
709     struct custom_color * aux = custom_colors;
710     while (aux != NULL) {
711         aux=aux->p_next;
712         c++;
713     }
714     return c;
715 }
716 
717 /**
718  * @brief get custom color by number
719  *
720  * get custom color by number
721  *
722  * Example usage:
723  * @code
724        get_custom_color_by_number(2)
725  * @endcode
726  * returns struct custom_color * if found
727  * returns NULL otherwise
728  */
729 struct custom_color * get_custom_color_by_number(int number) {
730     struct custom_color * aux = custom_colors;
731     while (aux != NULL) {
732         if (number == aux->number) return aux;
733         aux=aux->p_next;
734     }
735     return NULL;
736 }
737