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