Original patch as attached on the bugreport
[midnight-commander.git] / src / color.c
blob1ef19d7046390b11f4a260e9b8096d19964bff72
1 /* Color setup
2 Copyright (C) 1994, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2007 Free Software Foundation, Inc.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
19 #include <config.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
25 #include "global.h"
26 #include "tty.h"
27 #include "setup.h" /* For the externs */
28 #include "color.h"
30 /* Set to force black and white display at program startup */
31 int disable_colors = 0;
33 /* Set if we are actually using colors */
34 int use_colors = 0;
36 /* Color styles for normal and error dialogs */
37 int dialog_colors [4];
38 int alarm_colors [4];
40 #define ELEMENTS(arr) ( sizeof(arr) / sizeof((arr)[0]) )
42 #ifdef HAVE_SLANG
43 # define color_map_fg(n) color_map[n].fg
44 # define color_map_bg(n) color_map[n].bg
45 #else
46 # define color_map_fg(n) (color_map[n].fg & COLOR_WHITE)
47 # define color_map_bg(n) (color_map[n].bg & COLOR_WHITE)
48 #endif
50 struct colorpair {
51 const char *name; /* Name of the entry */
52 CTYPE fg; /* foreground color */
53 CTYPE bg; /* background color */
56 static struct colorpair color_map [] = {
57 { "normal=", 0, 0 }, /* normal */ /* 1 */
58 { "selected=", 0, 0 }, /* selected */
59 { "marked=", 0, 0 }, /* marked */
60 { "markselect=", 0, 0 }, /* marked/selected */
61 { "errors=", 0, 0 }, /* errors */
62 { "menu=", 0, 0 }, /* menu entry */
63 { "reverse=", 0, 0 }, /* reverse */
65 /* Dialog colors */
66 { "dnormal=", 0, 0 }, /* Dialog normal */ /* 8 */
67 { "dfocus=", 0, 0 }, /* Dialog focused */
68 { "dhotnormal=", 0, 0 }, /* Dialog normal/hot */
69 { "dhotfocus=", 0, 0 }, /* Dialog focused/hot */
71 { "viewunderline=", 0, 0 }, /* _\b? sequence in view, underline in editor */
72 { "menusel=", 0, 0 }, /* Menu selected color */ /* 13 */
73 { "menuhot=", 0, 0 }, /* Color for menu hotkeys */
74 { "menuhotsel=", 0, 0 }, /* Menu hotkeys/selected entry */
76 { "helpnormal=", 0, 0 }, /* Help normal */ /* 16 */
77 { "helpitalic=", 0, 0 }, /* Italic in help */
78 { "helpbold=", 0, 0 }, /* Bold in help */
79 { "helplink=", 0, 0 }, /* Not selected hyperlink */
80 { "helpslink=", 0, 0 }, /* Selected hyperlink */
82 { "gauge=", 0, 0 }, /* Color of the progress bar (percentage) *//* 21 */
83 { "input=", 0, 0 },
85 /* Per file types colors */
86 { "directory=", 0, 0 }, /* 23 */
87 { "executable=", 0, 0 },
88 { "link=", 0, 0 }, /* symbolic link (neither stale nor link to directory) */
89 { "stalelink=", 0, 0 }, /* stale symbolic link */
90 { "device=", 0, 0 },
91 { "special=", 0, 0 }, /* sockets, fifo */
92 { "core=", 0, 0 }, /* core files */ /* 29 */
94 { 0, 0, 0 }, /* not usable (DEFAULT_COLOR_INDEX) *//* 30 */
95 { 0, 0, 0 }, /* unused */
96 { 0, 0, 0 }, /* not usable (A_REVERSE) */
97 { 0, 0, 0 }, /* not usable (A_REVERSE_BOLD) */
99 /* editor colors start at 34 */
100 { "editnormal=", 0, 0 }, /* normal */ /* 34 */
101 { "editbold=", 0, 0 }, /* search->found */
102 { "editmarked=", 0, 0 }, /* marked/selected */
103 { "editwhitespace=", 0, 0 }, /* whitespace */
105 /* error dialog colors start at 38 */
106 { "errdhotnormal=", 0, 0 }, /* Error dialog normal/hot */ /* 38 */
107 { "errdhotfocus=", 0, 0 }, /* Error dialog focused/hot */
110 struct color_table_s {
111 const char *name;
112 int value;
116 static struct color_table_s const color_table [] = {
117 { "black", COLOR_BLACK },
118 { "gray", COLOR_BLACK | A_BOLD },
119 { "red", COLOR_RED },
120 { "brightred", COLOR_RED | A_BOLD },
121 { "green", COLOR_GREEN },
122 { "brightgreen", COLOR_GREEN | A_BOLD },
123 { "brown", COLOR_YELLOW },
124 { "yellow", COLOR_YELLOW | A_BOLD },
125 { "blue", COLOR_BLUE },
126 { "brightblue", COLOR_BLUE | A_BOLD },
127 { "magenta", COLOR_MAGENTA },
128 { "brightmagenta", COLOR_MAGENTA | A_BOLD },
129 { "cyan", COLOR_CYAN },
130 { "brightcyan", COLOR_CYAN | A_BOLD },
131 { "lightgray", COLOR_WHITE },
132 { "white", COLOR_WHITE | A_BOLD },
133 { "default", 0 } /* default color of the terminal */
136 static const char *default_colors =
137 "normal=lightgray,blue:"
138 "selected=black,cyan:"
139 "marked=yellow,blue:"
140 "markselect=yellow,cyan:"
141 "errors=white,red:"
142 "menu=white,cyan:"
143 "reverse=black,lightgray:"
144 "dnormal=black,lightgray:"
145 "dfocus=black,cyan:"
146 "dhotnormal=blue,lightgray:"
147 "dhotfocus=blue,cyan:"
148 "viewunderline=brightred,blue:"
149 "menuhot=yellow,cyan:"
150 "menusel=white,black:"
151 "menuhotsel=yellow,black:"
152 "helpnormal=black,lightgray:"
153 "helpitalic=red,lightgray:"
154 "helpbold=blue,lightgray:"
155 "helplink=black,cyan:"
156 "helpslink=yellow,blue:"
157 "gauge=white,black:"
158 "input=black,cyan:"
159 "directory=white,blue:"
160 "executable=brightgreen,blue:"
161 "link=lightgray,blue:"
162 "stalelink=brightred,blue:"
163 "device=brightmagenta,blue:"
164 "core=red,blue:"
165 "special=black,blue:"
166 "editnormal=lightgray,blue:"
167 "editbold=yellow,blue:"
168 "editmarked=black,cyan:"
169 "editwhitespace=brightblue,blue:"
170 "errdhotnormal=yellow,red:"
171 "errdhotfocus=yellow,lightgray";
173 #ifdef HAVE_SLANG
174 # define color_value(i) color_table [i].name
175 # define color_name(i) color_table [i].name
176 #else
177 # define color_value(i) color_table [i].value
178 # define color_name(i) color_table [i].name
179 #endif
181 static void get_color (const char *cpp, CTYPE *colp)
183 size_t i;
185 for (i = 0; i < ELEMENTS(color_table); i++){
186 if (strcmp (cpp, color_name (i)) == 0){
187 *colp = color_value (i);
188 return;
193 static void get_two_colors (char **cpp, struct colorpair *colorpairp)
195 char *p = *cpp;
196 int state;
198 state = 0;
200 for (; *p; p++){
201 if (*p == ':'){
202 *p = 0;
203 get_color (*cpp, state ? &colorpairp->bg : &colorpairp->fg);
204 *p = ':';
205 *cpp = p + 1;
206 return;
209 if (*p == ','){
210 state = 1;
211 *p = 0;
212 get_color (*cpp, &colorpairp->fg);
213 *p = ',';
214 *cpp = p + 1;
217 get_color (*cpp, state ? &colorpairp->bg : &colorpairp->fg);
220 static void configure_colors_string (const char *the_color_string)
222 char *color_string, *p;
223 size_t i;
224 int found;
226 if (!the_color_string)
227 return;
229 p = color_string = g_strdup (the_color_string);
230 while (color_string && *color_string){
231 while (*color_string == ' ' || *color_string == '\t')
232 color_string++;
234 found = 0;
235 for (i = 0; i < ELEMENTS(color_map); i++){
236 int klen;
238 if (!color_map [i].name)
239 continue;
240 klen = strlen (color_map [i].name);
242 if (strncmp (color_string, color_map [i].name, klen) == 0){
243 color_string += klen;
244 get_two_colors (&color_string, &color_map [i]);
245 found = 1;
248 if (!found){
249 while (*color_string && *color_string != ':')
250 color_string++;
251 if (*color_string)
252 color_string++;
255 g_free (p);
258 static void configure_colors (void)
260 extern char *command_line_colors;
262 configure_colors_string (default_colors);
263 configure_colors_string (setup_color_string);
264 configure_colors_string (term_color_string);
265 configure_colors_string (getenv ("MC_COLOR_TABLE"));
266 configure_colors_string (command_line_colors);
269 #ifndef HAVE_SLANG
270 #define MAX_PAIRS 64
271 int attr_pairs [MAX_PAIRS];
272 #endif
274 static void
275 load_dialog_colors (void)
277 dialog_colors [0] = COLOR_NORMAL;
278 dialog_colors [1] = COLOR_FOCUS;
279 dialog_colors [2] = COLOR_HOT_NORMAL;
280 dialog_colors [3] = COLOR_HOT_FOCUS;
282 alarm_colors [0] = ERROR_COLOR;
283 alarm_colors [1] = REVERSE_COLOR;
284 alarm_colors [2] = ERROR_HOT_NORMAL;
285 alarm_colors [3] = ERROR_HOT_FOCUS;
288 void init_colors (void)
290 size_t i;
292 int hascolors;
294 /* FIXME: if S-Lang is used, this function must be called regardless
295 of whether we are interested in its result */
296 hascolors = has_colors ();
298 if (!disable_colors && hascolors){
299 use_colors = 1;
302 if (use_colors){
303 start_color ();
304 configure_colors ();
306 #ifndef HAVE_SLANG
307 if (ELEMENTS (color_map) > MAX_PAIRS){
308 /* This message should only be seen by the developers */
309 fprintf (stderr,
310 "Too many defined colors, resize MAX_PAIRS on color.c");
311 exit (1);
313 #endif /* !HAVE_SLANG */
315 if (use_colors) {
316 #ifdef HAVE_SLANG
318 * We are relying on undocumented feature of
319 * S-Lang to make COLOR_PAIR(DEFAULT_COLOR_INDEX)
320 * the default fg/bg of the terminal.
321 * Hopefully, future versions of S-Lang will
322 * document this feature.
324 SLtt_set_color (DEFAULT_COLOR_INDEX, NULL, "default", "default");
325 #else
326 /* Always white on black */
327 mc_init_pair(DEFAULT_COLOR_INDEX, COLOR_WHITE, COLOR_BLACK);
328 #endif /* !HAVE_SLANG */
331 for (i = 0; i < ELEMENTS (color_map); i++){
332 if (!color_map [i].name)
333 continue;
335 mc_init_pair (i+1, color_map_fg(i), color_map_bg(i));
337 #ifndef HAVE_SLANG
339 * ncurses doesn't remember bold attribute in the color pairs,
340 * so we should keep track of it in a separate array.
342 attr_pairs [i+1] = color_map [i].fg & A_BOLD;
343 #endif /* !HAVE_SLANG */
346 load_dialog_colors ();
349 /* Functions necessary to implement syntax highlighting */
351 static int max_index = 0;
353 static int
354 alloc_color_pair (CTYPE foreground, CTYPE background)
356 mc_init_pair (++max_index, foreground, background);
357 return max_index;
360 static struct colors_avail {
361 struct colors_avail *next;
362 char *fg, *bg;
363 int index;
364 } c = { 0, 0, 0, 0 };
366 #ifdef HAVE_SLANG
367 void
368 mc_init_pair (int index, CTYPE foreground, CTYPE background)
370 if (!background)
371 background = "default";
373 if (!foreground)
374 foreground = "default";
376 SLtt_set_color (index, "", (char *) foreground, (char *) background);
377 if (index > max_index)
378 max_index = index;
382 try_alloc_color_pair (const char *fg, const char *bg)
384 struct colors_avail *p = &c;
386 c.index = EDITOR_NORMAL_COLOR_INDEX;
387 for (;;) {
388 if (((fg && p->fg) ? !strcmp (fg, p->fg) : fg == p->fg) != 0
389 && ((bg && p->bg) ? !strcmp (bg, p->bg) : bg == p->bg) != 0)
390 return p->index;
391 if (!p->next)
392 break;
393 p = p->next;
395 p->next = g_new (struct colors_avail, 1);
396 p = p->next;
397 p->next = 0;
398 p->fg = fg ? g_strdup (fg) : 0;
399 p->bg = bg ? g_strdup (bg) : 0;
400 if (!fg)
401 /* Index in color_map array = COLOR_INDEX - 1 */
402 fg = color_map[EDITOR_NORMAL_COLOR_INDEX - 1].fg;
403 if (!bg)
404 bg = color_map[EDITOR_NORMAL_COLOR_INDEX - 1].bg;
405 p->index = alloc_color_pair (fg, bg);
406 return p->index;
409 #else /* !HAVE_SLANG */
410 void
411 mc_init_pair (int index, CTYPE foreground, CTYPE background)
413 init_pair (index, foreground, background);
414 if (index > max_index)
415 max_index = index;
419 try_alloc_color_pair (const char *fg, const char *bg)
421 int fg_index, bg_index;
422 int bold_attr;
423 struct colors_avail *p = &c;
425 c.index = EDITOR_NORMAL_COLOR_INDEX;
426 for (;;) {
427 if (((fg && p->fg) ? !strcmp (fg, p->fg) : fg == p->fg) != 0
428 && ((bg && p->bg) ? !strcmp (bg, p->bg) : bg == p->bg) != 0)
429 return p->index;
430 if (!p->next)
431 break;
432 p = p->next;
434 p->next = g_new (struct colors_avail, 1);
435 p = p->next;
436 p->next = 0;
437 p->fg = fg ? g_strdup (fg) : 0;
438 p->bg = bg ? g_strdup (bg) : 0;
439 if (!fg)
440 /* Index in color_map array = COLOR_INDEX - 1 */
441 fg_index = color_map[EDITOR_NORMAL_COLOR_INDEX - 1].fg;
442 else
443 get_color (fg, &fg_index);
445 if (!bg)
446 bg_index = color_map[EDITOR_NORMAL_COLOR_INDEX - 1].bg;
447 else
448 get_color (bg, &bg_index);
450 bold_attr = fg_index & A_BOLD;
451 fg_index = fg_index & COLOR_WHITE;
452 bg_index = bg_index & COLOR_WHITE;
454 p->index = alloc_color_pair (fg_index, bg_index);
455 attr_pairs [p->index] = bold_attr;
456 return p->index;
458 #endif /* !HAVE_SLANG */
460 void
461 done_colors (void)
463 struct colors_avail *p, *next;
465 for (p = c.next; p; p = next) {
466 next = p->next;
467 g_free (p->fg);
468 g_free (p->bg);
469 g_free (p);
471 c.next = NULL;